Simplify the call to from_be_bytes?

I'm reading data from a fingerprint scanner, and it have different size packages depending on what command it was sent..
The simpler ones are:

let start = u16::from_be_bytes([self.buffer[0], self.buffer[1]]);

However, this is really getting out of hand:

                    fpm_model: u128::from_be_bytes([
                        self.buffer[10],
                        self.buffer[11],
                        self.buffer[12],
                        self.buffer[13],
                        self.buffer[14],
                        self.buffer[15],
                        self.buffer[16],
                        self.buffer[17],
                        self.buffer[18],
                        self.buffer[19],
                        self.buffer[20],
                        self.buffer[21],
                        self.buffer[22],
                        self.buffer[23],
                        self.buffer[24],
                        self.buffer[25],
                    ]),

Looking through examples, this should be possible:

let start = u16::from_be_bytes(self.buffer[0..1].try_into().unwrap());

which would making the retrieval of that FPM model real easy:

fpm_model = u128::from_be_bytes(self.buffer[10..25].try_into().unwrap());

The code would be soooo simpler, easier to read, and more importantly, easier to write :slight_smile: . Because I have quite a number of those big ones left to do..

However, that first one gives me a panic! It compiles fine, but then panics:

called `Result::unwrap()` on an `Err` value: TryFromSliceError(())

The buffer variable is defined in my constructor as:

buffer: heapless::Vec::new()

I've gone over the heapless::Vec, slices, vec's and whatever else docs I could find, but can't find anything that seem .. resonable and usable.

You're off by 1. The ranges are 0..2 and 10..26. Note how 26 - 10 = 16 bytes.

5 Likes

Dang it!! Sorry, I soooo overcomplicated things in my head!

Thanx!

Yes, this was annoying in the past, but Rust 1.77 stabilized a bunch of new things to help with it.

Specifically, for that case you can do something like this:

fpm_model: u128::from_be_bytes(*self.buffer[10..].first_chunk().unwrap())

where type inference will even figure out the length for you automatically. (You can, of course, specify first_chunk::<16> if you'd prefer.)

Or if you're pulling out multiple arrays, you can do things like

let (a, buffer) = buffer.split_first_array::<2>()?;
let (b, buffer) = buffer.split_first_array::<8>()?;
let (c, buffer) = buffer.split_first_array::<16>()?;

to pull out 0..2, 2..10, 10..26, etc.

9 Likes

How does it "know" how big the chunk is?

Because, this is what I ended up with:

                debug!("  Checking product information for ReadProdInfo");
                self.prodinfo = ProductInfo {
                    fpm_model: u128::from_be_bytes(self.buffer[10..26].try_into().unwrap()),
                    batch_nr: u32::from_be_bytes(self.buffer[26..30].try_into().unwrap()),
                    serial_nr: u64::from_be_bytes(self.buffer[30..38].try_into().unwrap()),
                    hw_nr: u16::from_be_bytes(self.buffer[38..40].try_into().unwrap()),
                    fps_model: u64::from_be_bytes(self.buffer[40..48].try_into().unwrap()),
                    fps_width: u16::from_be_bytes(self.buffer[48..50].try_into().unwrap()),
                    fps_height: u16::from_be_bytes(self.buffer[50..52].try_into().unwrap()),
                    tmpl_size: u16::from_be_bytes(self.buffer[52..53].try_into().unwrap()),
                    tmpl_total: u16::from_be_bytes(self.buffer[54..56].try_into().unwrap())
                };

Not sure if it could be made even simpler (or "prettier"), but I'm very happy with that! Previously, that was more than a "page" (in the editor) :slight_smile:, making it really hard to get an overview of what's happening.

From a purely aesthetic point, >I< think this is prettier:

                self.prodinfo = ProductInfo {
                    fpm_model:  u128::from_be_bytes(self.buffer[10..26].try_into().unwrap()),
                    batch_nr:    u32::from_be_bytes(self.buffer[26..30].try_into().unwrap()),
                    serial_nr:   u64::from_be_bytes(self.buffer[30..38].try_into().unwrap()),
                    hw_nr:       u16::from_be_bytes(self.buffer[38..40].try_into().unwrap()),
                    fps_model:   u64::from_be_bytes(self.buffer[40..48].try_into().unwrap()),
                    fps_width:   u16::from_be_bytes(self.buffer[48..50].try_into().unwrap()),
                    fps_height:  u16::from_be_bytes(self.buffer[50..52].try_into().unwrap()),
                    tmpl_size:   u16::from_be_bytes(self.buffer[52..53].try_into().unwrap()),
                    tmpl_total:  u16::from_be_bytes(self.buffer[54..56].try_into().unwrap())
                };

But it seems cargo fmt doesn't agree with that viewpoint :smiley: .

The same way that try_into().unwrap() does: because you're passing it to from_be_bytes which takes an array of a specific length.

Did you run this? That looks like it's going to panic.

2 Likes

Yeah, you're right. That's wrong. Thanx!

And no, I can't run that section yet. The device I'm reading from (a fingerprint scanner) is only sending 32 bytes, then hangs (I'm doing a blocking read), even though it say in the package that it's 49 bytes long (plus headers etc)!

Not sure if I'm supposed to send an ACK or something, then read the rest.. I've gone over the docs for it so many time, and I can't find anything.

So that part is disabled elsewhere.

It’s perfectly appropriate to #[rustfmt::skip] things that are better formatted by hand.

Also, you might want to write a helper function to make the code cleaner.

2 Likes

Oh, I didn't know about that #[rustfmt::skip], nice! Thanx!

I've considered it, but it's not that bad as it is. I'll consider it for the future, thanx.

This is way overkill unless you're doing it for lots of types, but for fun: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=681a054a5c74797d9407b7c1b8cd462c

2 Likes

Now THAT was cool!!!

It gave me a headache, but it was .. was it gorgeous!? Yeah, I think it was!! :smiley: .

But I think I need to learn to crawl before I start competing in the olympics - macros I've stayed VERY clear off!! :smiley:. I can just barely understand the basics of Rust.. That.. Is a VERY (!!) different league :smiley: .

I've saved that, and one day I'll have another look at it, but yeah, nice! THANX!!

1 Like