leaprs/
sized_with_trailing_data.rs

1use std::alloc::Layout;
2
3/// A DST with a sized part and unsized remainder.
4#[repr(C, packed)]
5pub(crate) struct SizedWithTrailingData<T> {
6    pub sized: T,
7    pub trailing: [u8],
8}
9
10impl<T> SizedWithTrailingData<T> {
11    /// Allocate a SizedWithTrailingData with a given trailing size.
12    /// From: https://stackoverflow.com/questions/64120001/how-to-create-a-smart-pointer-to-an-unsized-type-with-an-embedded-slice
13    pub fn allocate(sized: T, trailing_size: usize) -> Box<Self> {
14        // Create a layout of an `Inner` followed by the array
15        let (layout, arr_base) = Layout::array::<usize>(trailing_size)
16            .and_then(|arr_layout| Layout::new::<T>().extend(arr_layout))
17            .unwrap();
18        let ptr = unsafe { std::alloc::alloc(layout) };
19        // At this point, `ptr` is `*mut u8` and the compiler doesn't know the size of the allocation
20        if ptr.is_null() {
21            panic!("Internal allocation error");
22        }
23
24        unsafe {
25            ptr.cast::<T>().write(sized);
26            let tmp_ptr = ptr.add(arr_base).cast::<usize>();
27            // Initialize the array elements, in this case to 0
28            (0..trailing_size).for_each(|i| tmp_ptr.add(i).write(0));
29
30            // At this point everything is initialized and can safely be converted to `Box`
31            Box::from_raw(
32                std::ptr::slice_from_raw_parts_mut(ptr as *mut usize, trailing_size)
33                    as *mut SizedWithTrailingData<T>,
34            )
35        }
36    }
37
38    pub fn size(&self) -> usize {
39        std::mem::size_of::<T>() + std::mem::size_of_val(&self.trailing)
40    }
41}
42
43#[cfg(test)]
44mod tests {
45    use leap_sys::LEAP_TRACKING_EVENT;
46
47    use super::*;
48
49    #[test]
50    pub fn event_with_trailing() {
51        let mut tracking_event: LEAP_TRACKING_EVENT;
52
53        unsafe {
54            tracking_event = std::mem::zeroed();
55            tracking_event.tracking_frame_id = 42;
56        }
57        let mut dst = SizedWithTrailingData::allocate(tracking_event, 4);
58        dst.sized.tracking_frame_id = 42;
59        dst.trailing[0] = 1;
60        dst.trailing[1] = 3;
61        dst.trailing[2] = 5;
62        dst.trailing[3] = 7;
63
64        let sized = dst.sized;
65        let frame = sized.tracking_frame_id;
66        assert_eq!(frame, 42);
67        assert_eq!(dst.trailing[0], 1);
68        assert_eq!(dst.trailing[1], 3);
69        assert_eq!(dst.trailing[2], 5);
70        assert_eq!(dst.trailing[3], 7);
71    }
72
73    #[test]
74    pub fn string_with_trailing() {
75        let mut tracking_event: LEAP_TRACKING_EVENT;
76
77        unsafe {
78            tracking_event = std::mem::zeroed();
79            tracking_event.tracking_frame_id = 42;
80        }
81        let mut dst = SizedWithTrailingData::allocate("hello".to_string(), 4);
82        dst.trailing[0] = 1;
83        dst.trailing[1] = 3;
84        dst.trailing[2] = 5;
85        dst.trailing[3] = 7;
86
87        let sized = dst.sized;
88        assert_eq!(sized, "hello".to_string());
89        assert_eq!(dst.trailing[0], 1);
90        assert_eq!(dst.trailing[1], 3);
91        assert_eq!(dst.trailing[2], 5);
92        assert_eq!(dst.trailing[3], 7);
93    }
94}