Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a struct "view" over a buffer that includes dynamically sized arrays

Tags:

rust

In C I can use something like the following to create a struct "view" over data received over the network. I'm new to Rust. What would be the best way to do this in Rust?

#define JOB(_a_, _b_, _c_) \
struct { \
    int64 size; \
    int64 sizes[_a_]; \
    int16 destinationsCount[_a_]; \
    struct Address destinations[_b_]; \
    unsigned char data[_c_]; \
} __packed 

And use it like:

char * buf = ...;
JOB(1, 3, 1024) * job = (typeof(job))buf;
size_t size = sizeof(typeof(*job));

where the 1, 3 and 1024 are determined dynamically (which requires a GCC extension).

How can I best do this, ideally using the same struct "view" approach, in Rust? (The data is actually dramatically more complex than the above, and this is the nicest approach to work with.)

Here's what I've tried:

#[repr(C)]
struct Job<sizes, destinationsCount, destinations, data> {
    size: u64,
    sizes: sizes,
    destinationsCount: destinationsCount,
    destinations: destinations,
    program: data,
}
let job: Job<[u64;1], [u16;1], [Address;3], [u8;1024]>;

But of course as soon as I set the 1, 3 and 1024 dynamically it fails to compile.

like image 924
Max Avatar asked Dec 09 '25 09:12

Max


1 Answers

Functionally speaking you do not need a struct that is dynamically built, you can instead do with a view (though the struct could potentially be faster).

It is unclear to me whether you require ownership of the buffer or not, so I will assume borrowing here. The basic idea is exposed below; although of course you would want something a bit more sturdy...

struct JobViewer<'a> {
    nb_sizes: isize,
    nb_dests: isize,
    data_size: isize,
    data: &'a [u8],
}

impl<'a> JobViewer<'a> {
    fn new(nb_sizes: isize, nb_dests: isize, data_size: isize, data: &'a [u8])
        -> JobViewer<'a>
    {
        JobViewer {
            nb_sizes: nb_sizes, nb_dests: nb_dests, data_size: data_size, data: data
        }
    }

    fn size(&self) -> i64 {
        unsafe { *mem::transmute(self.raw()) }
    }

    fn sizes(&self) -> &[i64] {
        slice::from_raw_parts(
            unsafe { mem::transmute(self.raw().offset(8)) },
            self.nb_sizes
        )
    }

    fn destinations_count(&self) -> &[i16] {
        slice::from_raw_parts(
            unsafe { mem::transmute(self.raw().offset(8 + 8 * self.nb_sizes)) },
            self.nb_sizes
        )
    }

    fn destinations(&'b self) -> AddressViewer<'b> {
        AddressViewer::new(
            self.nb_dests,
            self.data[(8 + 8 * self.nb_sizes + 2 * self.nb_sizes)..]
        )
    }

    fn data(&self) -> &[u8] {
        self.data[
            (8 + 8 * self.nb_sizes + 2 * self.nb_sizes + ADDRESS_SIZE * self.nb_dests)..
        ]
    }

    unsafe fn raw(&self) -> *const u8 {
        let slice: raw::Slice<u8> = self.data;
        slice.data
    }
}

Note: as in your code, a change of endianness is NOT handled.

like image 82
Matthieu M. Avatar answered Dec 12 '25 01:12

Matthieu M.



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!