Skip to main content

fbl/
array.rs

1// Copyright 2026 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7use core::mem::MaybeUninit;
8use core::ops::{Deref, DerefMut};
9use core::ptr::slice_from_raw_parts_mut;
10use kalloc::{AllocError, Allocator, Box, DefaultAllocator};
11use zerocopy::FromZeros;
12
13/// A fixed-size array that takes ownership of its elements.
14/// This is a Rust analog to `fbl::Array` in C++.
15pub struct Array<T, A: Allocator = DefaultAllocator> {
16    buf: Box<[T], A>,
17}
18
19zr::static_assert!(core::mem::size_of::<Array<u32>>() == 16);
20zr::static_assert!(core::mem::align_of::<Array<u32>>() == 8);
21
22impl<T, A: Allocator> Array<T, A> {
23    /// Creates an empty array with the given allocator.
24    pub const fn new_in(allocator: A) -> Self {
25        Self { buf: Box::empty_slice_in(allocator) }
26    }
27
28    /// Creates an array from a Box.
29    pub fn from_box(buf: Box<[T], A>) -> Self {
30        Self { buf }
31    }
32
33    /// Allocates a new array of the given length, default-constructing each element.
34    pub fn try_new_in(len: usize, allocator: A) -> Result<Self, AllocError>
35    where
36        T: Default,
37    {
38        let mut b = Box::<[T], A>::try_new_uninit_slice_in(len, allocator)?;
39        for i in 0..len {
40            b[i].write(T::default());
41        }
42        // SAFETY: All elements have been initialized.
43        Ok(Self { buf: unsafe { b.assume_init() } })
44    }
45
46    /// Allocates a new uninitialized array of the given length with the given allocator.
47    pub fn try_new_uninit_slice_in(
48        len: usize,
49        allocator: A,
50    ) -> Result<Array<MaybeUninit<T>, A>, AllocError> {
51        Ok(Array { buf: Box::<[T], A>::try_new_uninit_slice_in(len, allocator)? })
52    }
53
54    /// Consumes the array, returning a raw slice pointer and the allocator.
55    ///
56    /// The memory will be leaked, and never deallocated unless reconstructed.
57    pub fn into_raw_with_allocator(self) -> (*mut [T], A) {
58        Box::into_raw_with_allocator(self.buf)
59    }
60
61    /// Consumes the array, returning a pointer to the first element, the element count, and the
62    /// allocator.
63    ///
64    /// The memory will be leaked, and never deallocated unless reconstructed.
65    pub fn into_raw_parts_with_allocator(self) -> (*mut T, usize, A) {
66        let (slice_ptr, allocator) = self.into_raw_with_allocator();
67        (slice_ptr as *mut T, slice_ptr.len(), allocator)
68    }
69
70    /// Constructs an array from a raw slice pointer and allocator.
71    ///
72    /// # Safety
73    ///
74    /// - For non-zero-sized types, the pointer must be valid and have been allocated
75    ///   by `allocator` with a layout matching the slice length elements of `T`.
76    pub unsafe fn from_raw_in(ptr: *mut [T], allocator: A) -> Self {
77        Self { buf: unsafe { Box::from_raw_in(ptr, allocator) } }
78    }
79
80    /// Constructs an array from a raw pointer to elements and length with the given allocator.
81    ///
82    /// This is particularly convenient when receiving an array from C/FFI boundaries.
83    ///
84    /// # Safety
85    ///
86    /// - For non-zero-sized types, the pointer must be valid and have been allocated
87    ///   by `allocator` with a layout matching `len` elements of `T`.
88    pub unsafe fn from_raw_parts_in(ptr: *mut T, len: usize, allocator: A) -> Self {
89        let slice_ptr = slice_from_raw_parts_mut(ptr, len);
90        unsafe { Self::from_raw_in(slice_ptr, allocator) }
91    }
92
93    /// Returns the number of elements in the array.
94    pub fn len(&self) -> usize {
95        self.buf.len()
96    }
97
98    /// Returns true if the array is empty.
99    pub fn is_empty(&self) -> bool {
100        self.buf.is_empty()
101    }
102
103    /// Consumes the array and returns the inner Box.
104    pub fn into_box(self) -> Box<[T], A> {
105        self.buf
106    }
107}
108
109impl<T: FromZeros, A: Allocator> Array<T, A> {
110    /// Allocates a new zero-initialized array of the given length with the given allocator.
111    pub fn try_new_zeroed_slice_in(len: usize, allocator: A) -> Result<Self, AllocError> {
112        Ok(Self { buf: Box::<[T], A>::try_new_zeroed_slice_in(len, allocator)? })
113    }
114}
115
116impl<T: FromZeros> Array<T, DefaultAllocator> {
117    /// Allocates a new zero-initialized array of the given length using the default allocator.
118    pub fn try_new_zeroed_slice(len: usize) -> Result<Self, AllocError> {
119        Self::try_new_zeroed_slice_in(len, DefaultAllocator)
120    }
121}
122
123impl<T> Array<T, DefaultAllocator> {
124    /// Creates an empty array using the default allocator.
125    pub const fn new() -> Self {
126        Self { buf: Box::empty_slice() }
127    }
128
129    /// Allocates a new array of the given length, default-constructing each element.
130    pub fn try_new(len: usize) -> Result<Self, AllocError>
131    where
132        T: Default,
133    {
134        Self::try_new_in(len, DefaultAllocator)
135    }
136
137    /// Allocates a new uninitialized array of the given length using the default allocator.
138    pub fn try_new_uninit_slice(
139        len: usize,
140    ) -> Result<Array<MaybeUninit<T>, DefaultAllocator>, AllocError> {
141        Self::try_new_uninit_slice_in(len, DefaultAllocator)
142    }
143
144    /// Consumes the array, returning a raw slice pointer.
145    ///
146    /// The memory will be leaked, and never deallocated unless reconstructed.
147    pub fn into_raw(self) -> *mut [T] {
148        let (ptr, _) = self.into_raw_with_allocator();
149        ptr
150    }
151
152    /// Consumes the array, returning a pointer to the first element and the element count.
153    ///
154    /// This is particularly convenient when passing an array across C/FFI boundaries.
155    /// The memory will be leaked, and never deallocated unless reconstructed.
156    pub fn into_raw_parts(self) -> (*mut T, usize) {
157        let (ptr, len, _) = self.into_raw_parts_with_allocator();
158        (ptr, len)
159    }
160
161    /// Constructs an array from a raw slice pointer using the default allocator.
162    ///
163    /// # Safety
164    ///
165    /// - For non-zero-sized types, the pointer must be valid and have been allocated
166    ///   by the default allocator with a layout matching the slice length elements of `T`.
167    pub unsafe fn from_raw(ptr: *mut [T]) -> Self {
168        unsafe { Self::from_raw_in(ptr, DefaultAllocator) }
169    }
170
171    /// Constructs an array from a raw pointer to elements and length using the default allocator.
172    ///
173    /// This is particularly convenient when receiving an array from C/FFI boundaries.
174    ///
175    /// # Safety
176    ///
177    /// - For non-zero-sized types, the pointer must be valid and have been allocated
178    ///   by the default allocator with a layout matching `len` elements of `T`.
179    pub unsafe fn from_raw_parts(ptr: *mut T, len: usize) -> Self {
180        unsafe { Self::from_raw_parts_in(ptr, len, DefaultAllocator) }
181    }
182}
183
184impl<T, A: Allocator> Array<MaybeUninit<T>, A> {
185    /// Converts to `Array<T, A>`.
186    ///
187    /// # Safety
188    ///
189    /// The caller must guarantee that all elements of the array are initialized.
190    pub unsafe fn assume_init(self) -> Array<T, A> {
191        Array { buf: unsafe { self.buf.assume_init() } }
192    }
193}
194
195impl<T, A: Allocator> Deref for Array<T, A> {
196    type Target = [T];
197
198    fn deref(&self) -> &Self::Target {
199        &self.buf
200    }
201}
202
203impl<T, A: Allocator> DerefMut for Array<T, A> {
204    fn deref_mut(&mut self) -> &mut Self::Target {
205        &mut self.buf
206    }
207}
208
209impl<T> Default for Array<T, DefaultAllocator> {
210    fn default() -> Self {
211        Self::new()
212    }
213}
214
215#[cfg(test)]
216mod tests {
217    use super::*;
218    use core::cell::Cell;
219    use core::ptr::NonNull;
220
221    #[derive(Debug, PartialEq, Eq)]
222    struct TestState {
223        live_obj_count: Cell<usize>,
224        ctor_count: Cell<usize>,
225        dtor_count: Cell<usize>,
226        alloc_count: Cell<usize>,
227        fail_threshold: Cell<usize>,
228    }
229
230    impl Default for TestState {
231        fn default() -> Self {
232            Self {
233                live_obj_count: Cell::new(0),
234                ctor_count: Cell::new(0),
235                dtor_count: Cell::new(0),
236                alloc_count: Cell::new(0),
237                fail_threshold: Cell::new(usize::MAX),
238            }
239        }
240    }
241
242    #[derive(Clone)]
243    struct TestAllocator<'a> {
244        state: &'a TestState,
245    }
246
247    impl<'a> kalloc::Allocator for TestAllocator<'a> {
248        fn allocate(&self, layout: core::alloc::Layout) -> Result<NonNull<[u8]>, AllocError> {
249            let current = self.state.alloc_count.get();
250            self.state.alloc_count.set(current + 1);
251            if current >= self.state.fail_threshold.get() {
252                return Err(AllocError);
253            }
254            DefaultAllocator::default().allocate(layout)
255        }
256
257        unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: core::alloc::Layout) {
258            unsafe { DefaultAllocator::default().deallocate(ptr, layout) }
259        }
260
261        unsafe fn grow(
262            &self,
263            ptr: NonNull<u8>,
264            old_layout: core::alloc::Layout,
265            new_layout: core::alloc::Layout,
266        ) -> Result<NonNull<[u8]>, AllocError> {
267            let current = self.state.alloc_count.get();
268            self.state.alloc_count.set(current + 1);
269            if current >= self.state.fail_threshold.get() {
270                return Err(AllocError);
271            }
272            unsafe { DefaultAllocator::default().grow(ptr, old_layout, new_layout) }
273        }
274
275        unsafe fn shrink(
276            &self,
277            ptr: NonNull<u8>,
278            old_layout: core::alloc::Layout,
279            new_layout: core::alloc::Layout,
280        ) -> Result<NonNull<[u8]>, AllocError> {
281            let current = self.state.alloc_count.get();
282            self.state.alloc_count.set(current + 1);
283            if current >= self.state.fail_threshold.get() {
284                return Err(AllocError);
285            }
286            unsafe { DefaultAllocator::default().shrink(ptr, old_layout, new_layout) }
287        }
288
289        fn allocate_zeroed(
290            &self,
291            layout: core::alloc::Layout,
292        ) -> Result<NonNull<[u8]>, AllocError> {
293            let current = self.state.alloc_count.get();
294            self.state.alloc_count.set(current + 1);
295            if current >= self.state.fail_threshold.get() {
296                return Err(AllocError);
297            }
298            DefaultAllocator::default().allocate_zeroed(layout)
299        }
300    }
301
302    #[derive(Debug, Eq, PartialEq)]
303    struct TestObject<'a> {
304        val: usize,
305        alive: bool,
306        state: &'a TestState,
307    }
308
309    impl<'a> TestObject<'a> {
310        fn new(val: usize, state: &'a TestState) -> Self {
311            state.live_obj_count.set(state.live_obj_count.get() + 1);
312            state.ctor_count.set(state.ctor_count.get() + 1);
313            TestObject { val, alive: true, state }
314        }
315    }
316
317    impl<'a> Drop for TestObject<'a> {
318        fn drop(&mut self) {
319            if self.alive {
320                self.state.live_obj_count.set(self.state.live_obj_count.get() - 1);
321                self.state.dtor_count.set(self.state.dtor_count.get() + 1);
322            }
323        }
324    }
325
326    #[test]
327    fn test_empty_array() {
328        let a: Array<u32> = Array::new();
329        assert_eq!(a.len(), 0);
330        assert!(a.is_empty());
331    }
332
333    #[test]
334    fn test_try_new() {
335        let a = Array::<u32>::try_new(5).unwrap();
336        assert_eq!(a.len(), 5);
337        for i in 0..5 {
338            assert_eq!(a[i], 0);
339        }
340    }
341
342    #[test]
343    fn test_deref() {
344        let mut a = Array::<u32>::try_new(2).unwrap();
345        a[0] = 10;
346        a[1] = 20;
347
348        let slice: &[u32] = &a;
349        assert_eq!(slice, &[10, 20]);
350
351        let slice_mut: &mut [u32] = &mut a;
352        slice_mut[0] = 30;
353        assert_eq!(a[0], 30);
354    }
355
356    #[test]
357    fn test_drop_behavior() {
358        let state = TestState::default();
359        {
360            let mut b = Box::<[TestObject<'_>], TestAllocator<'_>>::try_new_uninit_slice_in(
361                2,
362                TestAllocator { state: &state },
363            )
364            .unwrap();
365            b[0].write(TestObject::new(1, &state));
366            b[1].write(TestObject::new(2, &state));
367            let _a = Array::from_box(unsafe { b.assume_init() });
368            assert_eq!(state.live_obj_count.get(), 2);
369        }
370        assert_eq!(state.live_obj_count.get(), 0);
371        assert_eq!(state.dtor_count.get(), 2);
372    }
373
374    #[test]
375    fn test_allocation_failure() {
376        let state = TestState::default();
377        state.fail_threshold.set(0); // Fail immediately
378
379        let res = Array::<u32, TestAllocator<'_>>::try_new_in(5, TestAllocator { state: &state });
380        assert!(res.is_err());
381    }
382
383    #[test]
384    fn test_try_new_zero_sized() {
385        let state = TestState::default();
386        let a = Array::<u32, TestAllocator<'_>>::try_new_in(0, TestAllocator { state: &state })
387            .unwrap();
388        assert_eq!(a.len(), 0);
389        assert!(a.is_empty());
390    }
391
392    #[test]
393    fn test_non_trivial_default() {
394        #[derive(Debug, PartialEq, Eq)]
395        struct MyInt {
396            value: i32,
397        }
398        impl Default for MyInt {
399            fn default() -> Self {
400                Self { value: 42 }
401            }
402        }
403
404        let a = Array::<MyInt>::try_new(5).unwrap();
405        assert_eq!(a.len(), 5);
406        for i in 0..5 {
407            assert_eq!(a[i].value, 42);
408        }
409    }
410
411    #[test]
412    fn test_array_new_in() {
413        let state = TestState::default();
414        let alloc = TestAllocator { state: &state };
415        let a = Array::<u32, TestAllocator<'_>>::new_in(alloc.clone());
416        assert!(a.is_empty());
417    }
418
419    #[test]
420    fn test_array_default() {
421        let a_def: Array<u32> = Default::default();
422        assert!(a_def.is_empty());
423    }
424
425    #[test]
426    fn test_array_into_box() {
427        let a_try = Array::<u32>::try_new(3).unwrap();
428        let b = a_try.into_box();
429        assert_eq!(b.len(), 3);
430    }
431
432    #[test]
433    fn test_array_test_allocator_happy() {
434        use kalloc::Allocator;
435        let state = TestState::default();
436        let alloc = TestAllocator { state: &state };
437        let layout = core::alloc::Layout::new::<u32>();
438        let ptr = alloc.allocate_zeroed(layout).unwrap();
439
440        let ptr = unsafe {
441            alloc.grow(ptr.cast(), layout, core::alloc::Layout::array::<u32>(2).unwrap()).unwrap()
442        };
443
444        let ptr = unsafe {
445            alloc.shrink(ptr.cast(), core::alloc::Layout::array::<u32>(2).unwrap(), layout).unwrap()
446        };
447
448        unsafe {
449            alloc.deallocate(ptr.cast(), layout);
450        }
451    }
452
453    #[test]
454    fn test_array_test_allocator_failure() {
455        use kalloc::Allocator;
456        let state = TestState::default();
457        let alloc = TestAllocator { state: &state };
458        let layout = core::alloc::Layout::new::<u32>();
459
460        // Set fail threshold to fail immediately
461        state.fail_threshold.set(0);
462
463        assert!(alloc.allocate_zeroed(layout).is_err());
464
465        let dummy_ptr = core::ptr::NonNull::<u8>::dangling();
466        assert!(
467            unsafe {
468                alloc.grow(dummy_ptr.cast(), layout, core::alloc::Layout::array::<u32>(2).unwrap())
469            }
470            .is_err()
471        );
472        assert!(
473            unsafe {
474                alloc.shrink(
475                    dummy_ptr.cast(),
476                    core::alloc::Layout::array::<u32>(2).unwrap(),
477                    layout,
478                )
479            }
480            .is_err()
481        );
482    }
483
484    #[test]
485    fn test_try_new_zeroed_slice() {
486        let arr = Array::<u32>::try_new_zeroed_slice(4).unwrap();
487        assert_eq!(arr.len(), 4);
488        for &val in arr.iter() {
489            assert_eq!(val, 0);
490        }
491
492        let state = TestState::default();
493        let arr_alloc = Array::<u32, TestAllocator<'_>>::try_new_zeroed_slice_in(
494            3,
495            TestAllocator { state: &state },
496        )
497        .unwrap();
498        assert_eq!(arr_alloc.len(), 3);
499        assert_eq!(state.alloc_count.get(), 1);
500        for &val in arr_alloc.iter() {
501            assert_eq!(val, 0);
502        }
503    }
504
505    #[test]
506    fn test_try_new_uninit_slice_and_assume_init() {
507        let mut uninit = Array::<u32>::try_new_uninit_slice(3).unwrap();
508        assert_eq!(uninit.len(), 3);
509        for i in 0..3 {
510            uninit[i].write((i * 10) as u32);
511        }
512        let arr = unsafe { uninit.assume_init() };
513        assert_eq!(arr.len(), 3);
514        assert_eq!(&arr[..], &[0, 10, 20]);
515    }
516
517    #[test]
518    fn test_uninit_slice_drop_behavior() {
519        let state = TestState::default();
520        {
521            let mut uninit = Array::<TestObject<'_>, TestAllocator<'_>>::try_new_uninit_slice_in(
522                2,
523                TestAllocator { state: &state },
524            )
525            .unwrap();
526            uninit[0].write(TestObject::new(10, &state));
527            uninit[1].write(TestObject::new(20, &state));
528            let arr = unsafe { uninit.assume_init() };
529            assert_eq!(state.live_obj_count.get(), 2);
530            assert_eq!(arr[0].val, 10);
531            assert_eq!(arr[1].val, 20);
532        }
533        assert_eq!(state.live_obj_count.get(), 0);
534        assert_eq!(state.dtor_count.get(), 2);
535    }
536
537    #[test]
538    fn test_into_and_from_raw_parts() {
539        let mut arr = Array::<u32>::try_new(3).unwrap();
540        arr[0] = 100;
541        arr[1] = 200;
542        arr[2] = 300;
543
544        let (ptr, len) = arr.into_raw_parts();
545        assert_eq!(len, 3);
546        assert!(!ptr.is_null());
547
548        let reconstructed = unsafe { Array::<u32>::from_raw_parts(ptr, len) };
549        assert_eq!(reconstructed.len(), 3);
550        assert_eq!(&reconstructed[..], &[100, 200, 300]);
551    }
552
553    #[test]
554    fn test_into_and_from_raw() {
555        let mut arr = Array::<u32>::try_new(2).unwrap();
556        arr[0] = 42;
557        arr[1] = 84;
558
559        let slice_ptr = arr.into_raw();
560        assert_eq!(unsafe { &*slice_ptr }, &[42, 84]);
561
562        let reconstructed = unsafe { Array::<u32>::from_raw(slice_ptr) };
563        assert_eq!(&reconstructed[..], &[42, 84]);
564    }
565
566    #[test]
567    fn test_raw_parts_with_allocator() {
568        let state = TestState::default();
569        let alloc = TestAllocator { state: &state };
570        let mut arr = Array::<u32, TestAllocator<'_>>::try_new_in(2, alloc.clone()).unwrap();
571        arr[0] = 7;
572        arr[1] = 9;
573
574        let (ptr, len, returned_alloc) = arr.into_raw_parts_with_allocator();
575        assert_eq!(len, 2);
576
577        let reconstructed =
578            unsafe { Array::<u32, TestAllocator<'_>>::from_raw_parts_in(ptr, len, returned_alloc) };
579        assert_eq!(&reconstructed[..], &[7, 9]);
580    }
581}