Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I take a byte array and deserialize it into a struct?

Tags:

rust

I'm reading a series of bytes from a socket and I need to put each segment of n bytes as a item in a struct.

use std::mem;

#[derive(Debug)]
struct Things {
    x: u8,
    y: u16,
}

fn main() {
    let array = [22 as u8, 76 as u8, 34 as u8];
    let foobar: Things;
    unsafe {
        foobar = mem::transmute::<[u8; 3], Things>(array);
    }

    println!("{:?}", foobar);

}

I'm getting errors that say that foobar is 32 bits when array is 24 bits. Shouldn't foobar be 24 bits (8 + 16 = 24)?

like image 846
Fluffy Avatar asked Mar 17 '16 13:03

Fluffy


1 Answers

The issue here is that the y field is 16-bit-aligned. So your memory layout is actually

x
padding
y
y

Note that swapping the order of x and y doesn't help, because Rust's memory layout for structs is actually undefined (and thus still 32 bits for no reason but simplicity in the compiler). If you depend on it you will get undefined behavior.

The reasons for alignment are explained in Purpose of memory alignment.

You can prevent alignment from happening by adding the attribute repr(packed) to your struct, but you'll lose performance and the ability to take references of fields:

#[repr(packed)]
struct Things {
    x: u8,
    y: u16,
}

The best way would be to not use transmute at all, but to extract the values manually and hope the optimizer makes it fast:

let foobar = Things {
    x: array[0],
    y: ((array[1] as u16) << 8) | (array[2] as u16),
};

A crate like byteorder may simplify the process of reading different sizes and endianness from the bytes.

like image 121
oli_obk Avatar answered Oct 16 '22 23:10

oli_obk