1
\$\begingroup\$

I have a function in an embedded system which translates an image encoded in a 32-bit integer to 3x9 matrix (an array of arrays):

 pub fn image_to_preformated_vector(image: u32) -> [[u8; 9]; 3] {
 [
 [
 1 & (image >> 0) as u8,
 1 & (image >> 1) as u8,
 1 & (image >> 2) as u8,
 1 & (image >> 3) as u8,
 1 & (image >> 4) as u8,
 1 & (image >> 5) as u8,
 1 & (image >> 6) as u8,
 1 & (image >> 7) as u8,
 1 & (image >> 8) as u8,
 ],
 [
 1 & (image >> 9) as u8,
 1 & (image >> 10) as u8,
 1 & (image >> 11) as u8,
 1 & (image >> 12) as u8,
 1 & (image >> 13) as u8,
 1 & (image >> 14) as u8,
 1 & (image >> 15) as u8,
 1 & (image >> 16) as u8,
 1 & (image >> 17) as u8,
 ],
 [
 1 & (image >> 18) as u8,
 1 & (image >> 19) as u8,
 1 & (image >> 20) as u8,
 1 & (image >> 21) as u8,
 1 & (image >> 22) as u8,
 1 & (image >> 23) as u8,
 1 & (image >> 24) as u8,
 1 & (image >> 25) as u8,
 1 & (image >> 26) as u8,
 ],
 ]
 }

Alternative implementation

 pub fn image_to_preformated_vector(image: u32) -> [[u8; 9]; 3] {
 [
 [0, 1, 2, 3, 4, 5, 6, 7, 8],
 [9, 10, 11, 12, 13, 14, 15, 16, 17],
 [18, 19, 20, 21, 22, 23, 24, 25, 26],
 ]
 .map(|v| v.map(|i| 1_u8 & (image >> i) as u8))
 }

Which is the preferred implementation, and why? Is there an even better way of writing it?

toolic
14.6k5 gold badges29 silver badges204 bronze badges
asked May 14 at 6:38
\$\endgroup\$
3
  • \$\begingroup\$ I was not able to use a range without a mutable temporary variable. \$\endgroup\$ Commented May 14 at 6:39
  • 2
    \$\begingroup\$ Did you profile? The second snippet is clearly way more readable, but the former might be faster. \$\endgroup\$ Commented May 14 at 15:38
  • 1
    \$\begingroup\$ The execution time for this snippet is not critical, but readability and maintainability is. \$\endgroup\$ Commented May 15 at 7:16

2 Answers 2

2
\$\begingroup\$

You could use macros to write your function in a manner which works like your first version, but with an improved readability. Something such as:

pub fn image_to_preformated_vector(image: u32) -> [[u8; 9]; 3] {
 macro_rules! shift {
 ($idx:expr) => { 1 & (image >> $idx) as u8 };
 }
 macro_rules! row {
 ($idx:literal) => {
 [
 shift!(0 + $idx), shift!(1 + $idx), shift!(2 + $idx),
 shift!(3 + $idx), shift!(4 + $idx), shift!(5 + $idx),
 shift!(6 + $idx), shift!(7 + $idx), shift!(8 + $idx),
 ]
 };
 }
 [ row!(0), row!(9), row!(18), ]
}
answered May 21 at 19:02
\$\endgroup\$
4
\$\begingroup\$

I'd raise the question whether you really need to convert the image. If you need access on the matrix's elements, you can implement an appropriate associated function for an image new type, since a u32 obviously requires less storage than a [[u8; 9]; 3].

So if and when you need to access the respective matrix's fields, you can use a constant and rather cheapTM on the fly operation like so:

#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Image(u32);
impl Image {
 pub const fn get(&self, outer: u8, inner: u8) -> u8 {
 1 & (self.0 >> (outer * 9) + inner) as u8
 }
}

Of course you may want to implement some additional guards to limit the indices to valid values.

Playground

If you use a library for matrix operations, you can implement its respective traits for the Image(u32) new type and treat it as a matrix.

answered Jul 7 at 11:51
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.