Reputation: 2859
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.
Upvotes: 3
Views: 605
Reputation: 299810
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.
Upvotes: 4