nanvm_lib/mem/flexible_array/
mod.rs

1pub mod constructor;
2pub mod header;
3use core::{
4    marker::PhantomData,
5    mem::{align_of, size_of},
6    ptr::drop_in_place,
7    slice::{from_raw_parts, from_raw_parts_mut},
8};
9
10use self::header::FlexibleArrayHeader;
11
12use super::{field_layout::FieldLayout, object::Object};
13
14#[repr(transparent)]
15#[derive(Debug)]
16pub struct FlexibleArray<I, T: FlexibleArrayHeader = usize> {
17    pub header: T,
18    _0: PhantomData<I>,
19}
20
21impl<I, T: FlexibleArrayHeader> FlexibleArray<I, T> {
22    const FLEXIBLE_HEADER_LAYOUT: FieldLayout<T, I> = FieldLayout::align_to(align_of::<I>());
23    #[inline(always)]
24    pub fn items(&self) -> &[I] {
25        unsafe {
26            from_raw_parts(
27                Self::FLEXIBLE_HEADER_LAYOUT.to_adjacent(&self.header),
28                self.header.len(),
29            )
30        }
31    }
32    #[inline(always)]
33    pub fn items_mut(&mut self) -> &mut [I] {
34        unsafe {
35            from_raw_parts_mut(
36                Self::FLEXIBLE_HEADER_LAYOUT.into_adjacent_mut(&mut self.header),
37                self.header.len(),
38            )
39        }
40    }
41    #[inline(always)]
42    pub const fn flexible_size(len: usize) -> usize {
43        Self::FLEXIBLE_HEADER_LAYOUT.size + len * size_of::<I>()
44    }
45}
46
47impl<T: FlexibleArrayHeader, I> Object for FlexibleArray<I, T> {
48    const OBJECT_ALIGN: usize = Self::FLEXIBLE_HEADER_LAYOUT.align;
49    #[inline(always)]
50    fn object_size(&self) -> usize {
51        Self::flexible_size(self.header.len())
52    }
53    unsafe fn object_drop(&mut self) {
54        drop_in_place(self.items_mut());
55        drop_in_place(self);
56    }
57}
58
59#[cfg(test)]
60mod test {
61    use core::{
62        fmt::Debug,
63        marker::PhantomData,
64        mem::{align_of, forget, size_of},
65        sync::atomic::{AtomicUsize, Ordering},
66    };
67
68    use wasm_bindgen_test::wasm_bindgen_test;
69
70    use crate::{
71        common::ref_mut::RefMut,
72        mem::{field_layout::FieldLayout, object::Object},
73    };
74
75    use super::{FlexibleArray, FlexibleArrayHeader};
76
77    struct X<H, I>(H, PhantomData<I>);
78
79    impl<H: Into<usize> + Copy, I> FlexibleArrayHeader for X<H, I> {
80        // type Item = I;
81        fn len(&self) -> usize {
82            self.0.into()
83        }
84    }
85
86    #[repr(C)]
87    struct Y<H, I, const N: usize> {
88        len: H,
89        items: [I; N],
90    }
91
92    fn ptr<T>(x: &mut T) -> *mut T {
93        x as *mut T
94    }
95
96    #[test]
97    #[wasm_bindgen_test]
98    fn test_u8_5() {
99        let mut y = Y::<u16, u8, 5> {
100            len: 5,
101            items: [42, 43, 44, 45, 46],
102        };
103        let v = ptr(&mut y) as *mut FlexibleArray<u8, X<u16, u8>>;
104        unsafe {
105            assert_eq!((*v).header.len(), 5);
106            assert_eq!((*v).object_size(), 7);
107            let items = (*v).items_mut();
108            assert_eq!(items, &[42, 43, 44, 45, 46]);
109        }
110    }
111
112    #[test]
113    #[wasm_bindgen_test]
114    fn test_u32_3() {
115        let mut y = Y::<u16, u32, 3> {
116            len: 3,
117            items: [42, 43, 44],
118        };
119        let v = ptr(&mut y) as *mut FlexibleArray<u32, X<u16, u32>>;
120        unsafe {
121            assert_eq!((*v).header.len(), 3);
122            assert_eq!((*v).object_size(), 16);
123            let items = (*v).items_mut();
124            assert_eq!(items, &[42, 43, 44]);
125        }
126    }
127
128    fn generic_test<
129        H: Into<usize> + Copy + TryFrom<usize>,
130        I: PartialEq + Debug + Copy,
131        const N: usize,
132    >(
133        items: [I; N],
134        size: usize,
135    ) {
136        let old = items;
137        let mut y = Y::<H, I, N> {
138            len: unsafe { N.try_into().unwrap_unchecked() },
139            items,
140        };
141        let v = ptr(&mut y) as *mut FlexibleArray<I, X<H, I>>;
142        unsafe {
143            assert_eq!((*v).header.len(), N);
144            assert_eq!((*v).object_size(), size);
145            assert_eq!(&*(*v).items_mut(), &old[..]);
146        }
147    }
148
149    #[test]
150    #[wasm_bindgen_test]
151    fn test1() {
152        generic_test::<u8, u8, 1>([42], 2);
153        generic_test::<u16, u32, 6>([42, 56, 78, 90, 101, 102], 28);
154        generic_test::<u16, u8, 3>([90, 101, 102], 5);
155    }
156
157    #[repr(transparent)]
158    struct DropCount(*const AtomicUsize);
159
160    impl Drop for DropCount {
161        fn drop(&mut self) {
162            unsafe {
163                (*self.0).fetch_add(1, Ordering::Relaxed);
164            }
165        }
166    }
167
168    impl FlexibleArrayHeader for DropCount {
169        // type Item = DropCount;
170        fn len(&self) -> usize {
171            3
172        }
173    }
174
175    #[repr(C)]
176    struct DropCountX {
177        header: DropCount,
178        items: [DropCount; 3],
179    }
180
181    #[test]
182    #[wasm_bindgen_test]
183    fn drop_test() {
184        let i = AtomicUsize::new(0);
185        {
186            let mut x = DropCountX {
187                header: DropCount(&i as *const AtomicUsize),
188                items: [
189                    DropCount(&i as *const AtomicUsize),
190                    DropCount(&i as *const AtomicUsize),
191                    DropCount(&i as *const AtomicUsize),
192                ],
193            };
194            let v = unsafe { x.to_mut_ptr() as *mut FlexibleArray<DropCount, DropCount> };
195            unsafe {
196                assert_eq!((*v).header.len(), 3);
197                assert_eq!((*v).object_size(), size_of::<DropCountX>());
198                assert_eq!((*v).object_size(), size_of::<DropCount>() * 4);
199                let a = &*(*v).items_mut();
200                assert_eq!(a.len(), 3);
201                assert_eq!(a[0].0, &i as *const AtomicUsize);
202                assert_eq!(a[1].0, &i as *const AtomicUsize);
203                assert_eq!(a[2].0, &i as *const AtomicUsize);
204            }
205            assert_eq!(i.load(Ordering::Relaxed), 0);
206            unsafe { (*v).object_drop() };
207            assert_eq!(i.load(Ordering::Relaxed), 4);
208            forget(x);
209        }
210        assert_eq!(i.load(Ordering::Relaxed), 4);
211    }
212
213    #[repr(C)]
214    struct StaticVariable<T: FlexibleArrayHeader, I, const L: usize> {
215        header: T,
216        items: [I; L],
217    }
218
219    struct E();
220
221    impl FlexibleArrayHeader for E {
222        // type Item = u64;
223        fn len(&self) -> usize {
224            3
225        }
226    }
227
228    const _: () = assert!(size_of::<StaticVariable<E, u64, 3>>() == 24);
229
230    const _: () = assert!(FlexibleArray::<u64, E>::OBJECT_ALIGN == 8);
231    const _: () = assert!(FlexibleArray::<u64, E>::FLEXIBLE_HEADER_LAYOUT.align == 8);
232    const _: () = assert!(size_of::<E>() == 0);
233    const _FL: FieldLayout<E, u64> = FieldLayout::align_to(align_of::<u64>());
234    const _: () = assert!(_FL.align == 8);
235    const _: () = assert!(_FL.size == 0);
236    const _: () = assert!(FlexibleArray::<u64, E>::FLEXIBLE_HEADER_LAYOUT.size == 0);
237
238    #[test]
239    #[wasm_bindgen_test]
240    fn empty_header_test() {
241        static mut I: u8 = 0;
242        unsafe { I = 0 };
243        struct EmptyHeader();
244        impl Drop for EmptyHeader {
245            fn drop(&mut self) {
246                unsafe { I += 1 };
247            }
248        }
249        impl FlexibleArrayHeader for EmptyHeader {
250            // type Item = u64;
251            fn len(&self) -> usize {
252                3
253            }
254        }
255        let items: [u64; 3] = [0x1234567890abcdef, 0x1234567890abcdef, 0x1234567890abcdef];
256        {
257            let mut x = StaticVariable::<EmptyHeader, u64, 3> {
258                header: EmptyHeader(),
259                items,
260            };
261            let y = unsafe { &mut *(x.to_mut_ptr() as *mut FlexibleArray<u64, EmptyHeader>) };
262            assert_eq!(size_of::<StaticVariable<EmptyHeader, u64, 3>>(), 24);
263            assert_eq!(size_of::<EmptyHeader>(), 0);
264            assert_eq!(y.object_size(), 24);
265            assert_eq!(
266                y.items_mut(),
267                &[0x1234567890abcdef, 0x1234567890abcdef, 0x1234567890abcdef]
268            );
269            assert_eq!(unsafe { I }, 0);
270            unsafe { y.object_drop() };
271            assert_eq!(unsafe { I }, 1);
272            forget(x)
273        }
274        assert_eq!(unsafe { I }, 1);
275    }
276}