Skip to main content

kalloc/
boxed.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 crate::allocator::{AllocError, Allocator, DefaultAllocator};
8use core::mem::MaybeUninit;
9use core::ops::{Deref, DerefMut};
10use core::ptr::NonNull;
11use zerocopy::FromZeros;
12
13/// Helper to construct a dangling slice of a given length.
14fn dangling_slice<T>(len: usize) -> NonNull<[MaybeUninit<T>]> {
15    let dangling = NonNull::<T>::dangling();
16    NonNull::slice_from_raw_parts(dangling.cast::<MaybeUninit<T>>(), len)
17}
18
19/// Helper to allocate an uninitialized slice.
20fn allocate_slice<T, A: Allocator>(
21    allocator: &A,
22    len: usize,
23) -> Result<NonNull<[MaybeUninit<T>]>, AllocError> {
24    let layout = core::alloc::Layout::array::<T>(len).map_err(|_| AllocError)?;
25    if layout.size() == 0 {
26        return Ok(dangling_slice::<T>(len));
27    }
28    let ptr = allocator.allocate(layout)?;
29    let casted_thin = ptr.cast::<MaybeUninit<T>>();
30    Ok(NonNull::slice_from_raw_parts(casted_thin, len))
31}
32
33/// Helper to allocate a zeroed slice.
34fn allocate_zeroed_slice<T, A: Allocator>(
35    allocator: &A,
36    len: usize,
37) -> Result<NonNull<[MaybeUninit<T>]>, AllocError> {
38    let layout = core::alloc::Layout::array::<T>(len).map_err(|_| AllocError)?;
39    if layout.size() == 0 {
40        return Ok(dangling_slice::<T>(len));
41    }
42    let ptr = allocator.allocate_zeroed(layout)?;
43    let casted_thin = ptr.cast::<MaybeUninit<T>>();
44    Ok(NonNull::slice_from_raw_parts(casted_thin, len))
45}
46
47/// Helper to deallocate a slice.
48///
49/// # Safety
50///
51/// The caller must guarantee that `ptr` was allocated by this allocator
52/// with a layout matching `ptr.len()` elements of `T`.
53unsafe fn deallocate_slice<T, A: Allocator>(allocator: &A, ptr: NonNull<[MaybeUninit<T>]>) {
54    let len = ptr.len();
55    // SAFETY: The caller must guarantee that `ptr` was allocated by this allocator.
56    unsafe {
57        let layout = core::alloc::Layout::array::<T>(len).unwrap_unchecked();
58        if layout.size() == 0 {
59            return;
60        }
61        allocator.deallocate(ptr.cast::<u8>(), layout);
62    }
63}
64
65/// Helper to grow a slice.
66///
67/// # Safety
68///
69/// The caller must guarantee that `ptr` was allocated by this allocator
70/// with a layout matching `ptr.len()` elements of `T`.
71unsafe fn grow_slice<T, A: Allocator>(
72    allocator: &A,
73    ptr: NonNull<[MaybeUninit<T>]>,
74    new_len: usize,
75) -> Result<NonNull<[MaybeUninit<T>]>, AllocError> {
76    let old_len = ptr.len();
77    assert!(new_len > old_len);
78
79    let old_layout = core::alloc::Layout::array::<T>(old_len).map_err(|_| AllocError)?;
80    let new_layout = core::alloc::Layout::array::<T>(new_len).map_err(|_| AllocError)?;
81
82    if old_layout.size() == 0 {
83        return allocate_slice(allocator, new_len);
84    }
85
86    // SAFETY: The caller must guarantee that `ptr` was allocated by this allocator.
87    let new_ptr = unsafe { allocator.grow(ptr.cast::<u8>(), old_layout, new_layout)? };
88    Ok(NonNull::slice_from_raw_parts(new_ptr.cast::<MaybeUninit<T>>(), new_len))
89}
90
91/// Helper to shrink a slice.
92///
93/// # Safety
94///
95/// The caller must guarantee that `ptr` was allocated by this allocator
96/// with a layout matching `ptr.len()` elements of `T`.
97unsafe fn shrink_slice<T, A: Allocator>(
98    allocator: &A,
99    ptr: NonNull<[MaybeUninit<T>]>,
100    new_len: usize,
101) -> Result<NonNull<[MaybeUninit<T>]>, AllocError> {
102    let old_len = ptr.len();
103    assert!(new_len < old_len);
104
105    let old_layout = core::alloc::Layout::array::<T>(old_len).map_err(|_| AllocError)?;
106    let new_layout = core::alloc::Layout::array::<T>(new_len).map_err(|_| AllocError)?;
107
108    if new_layout.size() == 0 {
109        // SAFETY: The caller must guarantee that `ptr` was allocated by this allocator.
110        unsafe {
111            deallocate_slice::<T, A>(allocator, ptr);
112        }
113        return Ok(dangling_slice::<T>(new_len));
114    }
115
116    // SAFETY: The caller must guarantee that `ptr` was allocated by this allocator.
117    let new_ptr = unsafe { allocator.shrink(ptr.cast::<u8>(), old_layout, new_layout)? };
118    Ok(NonNull::slice_from_raw_parts(new_ptr.cast::<MaybeUninit<T>>(), new_len))
119}
120
121/// A custom Box type appropriate for fallible allocation.
122pub struct Box<T: ?Sized, A: Allocator = DefaultAllocator> {
123    /// The pointer to the value.
124    ///
125    /// # Invariants
126    ///
127    /// This pointer may originate from:
128    /// 1. An allocation from `allocator`.
129    /// 2. A sentinel value for empty slices (obtained via `NonNull::from_ref(&[])`).
130    /// 3. A dangling pointer for Zero-Sized Types (ZSTs) (obtained via `NonNull::dangling()`).
131    ///
132    /// The implementation must ensure that pointers not originating from `allocator`
133    /// (cases 2 and 3) are never passed to the allocator's `deallocate`, `grow`, or `shrink` methods.
134    ptr: NonNull<T>,
135    allocator: A,
136}
137
138impl<T: ?Sized, A: Allocator> Box<T, A> {
139    /// Creates a Box from a raw pointer.
140    ///
141    /// This does NOT allocate any memory. It simply takes ownership of the memory
142    /// pointed to by `ptr`.
143    ///
144    /// # Safety
145    ///
146    /// - For non-zero-sized types, the pointer must be valid and have been allocated
147    ///   by the same allocator.
148    /// - For zero-sized types (ZSTs), the pointer must be non-null and properly aligned.
149    ///   Consider using `NonNull::dangling()` to obtain such a pointer.
150    pub const unsafe fn from_raw_in(ptr: *mut T, allocator: A) -> Self {
151        // SAFETY: The caller must guarantee `ptr` is valid for its type, which implies
152        // non-null for non-zero-sized types, and properly aligned for zero-sized types.
153        unsafe { Self::from_non_null_in(NonNull::new_unchecked(ptr), allocator) }
154    }
155
156    /// Constructs a box from a NonNull pointer.
157    ///
158    /// # Safety
159    ///
160    /// - For non-zero-sized types, the pointer must be valid and have been allocated
161    ///   by the same allocator.
162    /// - For zero-sized types (ZSTs), the pointer must be non-null and properly aligned.
163    ///   Consider using `NonNull::dangling()` to obtain such a pointer.
164    pub const unsafe fn from_non_null_in(ptr: NonNull<T>, allocator: A) -> Self {
165        Self { ptr, allocator }
166    }
167
168    /// Returns the raw pointer.
169    pub fn as_ptr(this: &Self) -> *mut T {
170        this.ptr.as_ptr()
171    }
172
173    /// Consumes the `Box`, returning a mutable reference to `T`.
174    ///
175    /// The memory will be leaked, and never deallocated.
176    ///
177    /// # Note
178    ///
179    /// The allocator `A` is also leaked. This is intentional and matches the
180    /// behavior of `std::boxed::Box::leak`, ensuring that a stateful allocator
181    /// remains valid as long as the leaked reference.
182    ///
183    /// Consumes the `Box`, returning a raw pointer and the allocator.
184    ///
185    /// The memory will be leaked, and never deallocated unless reconstructed.
186    pub fn into_raw_with_allocator(this: Self) -> (*mut T, A) {
187        let me = core::mem::ManuallyDrop::new(this);
188        let ptr = me.ptr.as_ptr();
189        // SAFETY: We are moving the allocator out of `me`, and `me` is ManuallyDrop
190        // so its Drop implementation (which would deallocate) will not run.
191        let allocator = unsafe { core::ptr::read(&me.allocator) };
192        (ptr, allocator)
193    }
194}
195
196impl<T: ?Sized> Box<T, DefaultAllocator> {
197    /// Creates a Box from a raw pointer using the default allocator.
198    ///
199    /// # Safety
200    ///
201    /// - For non-zero-sized types, the pointer must be valid and have been allocated
202    ///   by the default allocator.
203    /// - For zero-sized types (ZSTs), the pointer must be non-null and properly aligned.
204    ///   Consider using `NonNull::dangling()` to obtain such a pointer.
205    pub const unsafe fn from_raw(ptr: *mut T) -> Self {
206        unsafe { Self::from_raw_in(ptr, DefaultAllocator) }
207    }
208
209    /// Constructs a box from a NonNull pointer using the default allocator.
210    ///
211    /// # Safety
212    ///
213    /// - For non-zero-sized types, the pointer must be valid and have been allocated
214    ///   by the default allocator.
215    /// - For zero-sized types (ZSTs), the pointer must be non-null and properly aligned.
216    ///   Consider using `NonNull::dangling()` to obtain such a pointer.
217    pub const unsafe fn from_non_null(ptr: NonNull<T>) -> Self {
218        unsafe { Self::from_non_null_in(ptr, DefaultAllocator) }
219    }
220
221    /// Consumes the `Box`, returning a raw pointer.
222    ///
223    /// The memory will be leaked, and never deallocated unless reconstructed.
224    pub fn into_raw(this: Self) -> *mut T {
225        let (ptr, _) = Box::into_raw_with_allocator(this);
226        ptr
227    }
228}
229
230impl<T, A: Allocator> Box<[T], A> {
231    /// Creates an empty slice Box.
232    ///
233    /// Infallible because it doesn't allocate memory.
234    pub const fn empty_slice_in(allocator: A) -> Self {
235        // SAFETY: `NonNull::from_ref(&[])` creates a non-null pointer with the
236        // proper alignment.
237        unsafe { Self::from_non_null_in(NonNull::from_ref(&[]), allocator) }
238    }
239
240    /// Tries to allocate a new slice of the given length.
241    pub fn try_new_uninit_slice_in(
242        len: usize,
243        allocator: A,
244    ) -> Result<Box<[MaybeUninit<T>], A>, AllocError> {
245        let fat_ptr = allocate_slice::<T, A>(&allocator, len)?;
246        // SAFETY: `fat_ptr` points to a valid allocation.
247        Ok(unsafe { Box::from_non_null_in(fat_ptr, allocator) })
248    }
249
250    /// Tries to allocate a new zeroed slice of the given length.
251    pub fn try_new_zeroed_uninit_slice_in(
252        len: usize,
253        allocator: A,
254    ) -> Result<Box<[MaybeUninit<T>], A>, AllocError> {
255        let fat_ptr = allocate_zeroed_slice::<T, A>(&allocator, len)?;
256        // SAFETY: `fat_ptr` points to a valid allocation.
257        Ok(unsafe { Box::from_non_null_in(fat_ptr, allocator) })
258    }
259}
260
261impl<T> Box<[T], DefaultAllocator> {
262    pub const fn empty_slice() -> Self {
263        Self::empty_slice_in(DefaultAllocator)
264    }
265
266    pub fn try_new_uninit_slice(
267        len: usize,
268    ) -> Result<Box<[MaybeUninit<T>], DefaultAllocator>, AllocError> {
269        Self::try_new_uninit_slice_in(len, DefaultAllocator)
270    }
271
272    /// Tries to allocate a new zeroed slice of the given length.
273    pub fn try_new_zeroed_uninit_slice(
274        len: usize,
275    ) -> Result<Box<[MaybeUninit<T>], DefaultAllocator>, AllocError> {
276        Self::try_new_zeroed_uninit_slice_in(len, DefaultAllocator)
277    }
278}
279
280impl<T: FromZeros, A: Allocator> Box<[T], A> {
281    /// Tries to allocate a new zeroed slice of the given length.
282    pub fn try_new_zeroed_slice_in(len: usize, allocator: A) -> Result<Self, AllocError> {
283        let fat_ptr = allocate_zeroed_slice::<T, A>(&allocator, len)?;
284        // SAFETY: `fat_ptr` points to a valid zero-initialized allocation.
285        // Since T implements FromZeros, all zeroes is a valid bit pattern for T.
286        let ptr = fat_ptr.as_ptr() as *mut [T];
287        // SAFETY: `ptr` is non-null because it comes from a successful non-null allocation.
288        Ok(unsafe { Self::from_non_null_in(NonNull::new_unchecked(ptr), allocator) })
289    }
290}
291
292impl<T: FromZeros> Box<[T], DefaultAllocator> {
293    /// Tries to allocate a new zeroed slice of the given length.
294    pub fn try_new_zeroed_slice(len: usize) -> Result<Self, AllocError> {
295        Self::try_new_zeroed_slice_in(len, DefaultAllocator)
296    }
297}
298
299impl<T, A: Allocator> Box<[MaybeUninit<T>], A> {
300    /// Converts to `Box<[T], A>`.
301    ///
302    /// # Safety
303    ///
304    /// The caller must guarantee that the values are initialized.
305    pub unsafe fn assume_init(self) -> Box<[T], A> {
306        let (ptr, allocator) = Box::into_raw_with_allocator(self);
307        let ptr = ptr as *mut [core::mem::MaybeUninit<T>] as *mut [T];
308        // SAFETY: The caller must guarantee that the values are initialized.
309        unsafe { Box::from_raw_in(ptr, allocator) }
310    }
311
312    /// Tries to grow the slice to a new length.
313    /// Returns Err if allocation fails, and the box is unchanged.
314    pub fn try_grow(this: &mut Self, new_len: usize) -> Result<(), AllocError> {
315        // SAFETY: `this.ptr` was allocated by this allocator.
316        this.ptr = unsafe { grow_slice::<T, A>(&this.allocator, this.ptr, new_len)? };
317        Ok(())
318    }
319
320    /// Tries to shrink the slice to a new length.
321    /// If `new_len` is 0, the box becomes empty and memory is freed.
322    /// Returns Err if allocation fails, and the box is unchanged.
323    ///
324    /// # Safety
325    ///
326    /// The caller must guarantee that any elements above `new_len` are either
327    /// uninitialized or have already been dropped. This method discards them
328    /// without running their destructors.
329    pub unsafe fn try_shrink(this: &mut Self, new_len: usize) -> Result<(), AllocError> {
330        // SAFETY: `this.ptr` was allocated by this allocator.
331        this.ptr = unsafe { shrink_slice::<T, A>(&this.allocator, this.ptr, new_len)? };
332        Ok(())
333    }
334}
335
336impl<T, A: Allocator> Box<T, A> {
337    /// Constructs a box for a Zero Sized Type (ZST).
338    ///
339    /// # Panics
340    ///
341    /// Panics at compile time if the type is not a ZST.
342    const fn new_zst_in(allocator: A) -> Self {
343        assert!(core::mem::size_of::<T>() == 0);
344        // SAFETY: `NonNull::dangling()` creates a non-null pointer with the proper alignment.
345        Self { ptr: NonNull::<T>::dangling(), allocator }
346    }
347
348    /// Tries to allocate a new instance of T and move the value into it.
349    pub fn try_new_in(value: T, allocator: A) -> Result<Self, AllocError> {
350        let mut b = Self::try_new_uninit_in(allocator)?;
351        b.write(value);
352        Ok(unsafe { b.assume_init() })
353    }
354
355    /// Constructs a new box with uninitialized contents on the heap.
356    pub fn try_new_uninit_in(allocator: A) -> Result<Box<MaybeUninit<T>, A>, AllocError> {
357        if core::mem::size_of::<T>() == 0 {
358            return Ok(Box::<MaybeUninit<T>, A>::new_zst_in(allocator));
359        }
360        let layout = core::alloc::Layout::new::<T>();
361        let ptr = allocator.allocate(layout)?.cast::<MaybeUninit<T>>();
362        // SAFETY: `ptr` points to a valid allocation.
363        Ok(unsafe { Box::from_non_null_in(ptr, allocator) })
364    }
365
366    /// Constructs a new box with uninitialized contents, filled with 0 bytes.
367    pub fn try_new_zeroed_in(allocator: A) -> Result<Box<MaybeUninit<T>, A>, AllocError> {
368        if core::mem::size_of::<T>() == 0 {
369            return Ok(Box::<MaybeUninit<T>, A>::new_zst_in(allocator));
370        }
371        let layout = core::alloc::Layout::new::<T>();
372        let ptr = allocator.allocate_zeroed(layout)?.cast::<MaybeUninit<T>>();
373        // SAFETY: `ptr` points to a valid allocation.
374        Ok(unsafe { Box::from_non_null_in(ptr, allocator) })
375    }
376}
377
378impl<T> Box<T, DefaultAllocator> {
379    pub fn try_new(value: T) -> Result<Self, AllocError> {
380        Self::try_new_in(value, DefaultAllocator)
381    }
382
383    pub fn try_new_uninit() -> Result<Box<MaybeUninit<T>>, AllocError> {
384        Self::try_new_uninit_in(DefaultAllocator)
385    }
386
387    pub fn try_new_zeroed() -> Result<Box<MaybeUninit<T>>, AllocError> {
388        Self::try_new_zeroed_in(DefaultAllocator)
389    }
390}
391
392impl<T, A: Allocator> Box<MaybeUninit<T>, A> {
393    /// Converts to `Box<T, A>`.
394    ///
395    /// # Safety
396    ///
397    /// The caller must guarantee that the value is initialized.
398    pub unsafe fn assume_init(self) -> Box<T, A> {
399        let (ptr, allocator) = Box::into_raw_with_allocator(self);
400        // SAFETY: The caller must guarantee that the value is initialized.
401        unsafe { Box::from_raw_in(ptr as *mut T, allocator) }
402    }
403}
404
405impl<T> Default for Box<[T]> {
406    fn default() -> Self {
407        Self::empty_slice()
408    }
409}
410
411impl<T: ?Sized, A: Allocator> Deref for Box<T, A> {
412    type Target = T;
413    fn deref(&self) -> &Self::Target {
414        // SAFETY: `self.ptr` is valid as long as the `Box` is alive.
415        unsafe { self.ptr.as_ref() }
416    }
417}
418
419impl<T: ?Sized, A: Allocator> DerefMut for Box<T, A> {
420    fn deref_mut(&mut self) -> &mut Self::Target {
421        // SAFETY: `self.ptr` is valid as long as the `Box` is alive.
422        unsafe { self.ptr.as_mut() }
423    }
424}
425
426impl<T: ?Sized, A: Allocator> Drop for Box<T, A> {
427    fn drop(&mut self) {
428        // SAFETY: `self.ptr` is valid and was allocated by this allocator.
429        unsafe {
430            let value = self.ptr.as_mut();
431            let layout = core::alloc::Layout::for_value(value);
432            core::ptr::drop_in_place(value);
433            if layout.size() > 0 {
434                self.allocator.deallocate(self.ptr.cast::<u8>(), layout);
435            }
436        }
437    }
438}
439
440#[cfg(test)]
441mod tests {
442    use super::*;
443    use core::alloc::Layout;
444
445    #[test]
446    fn test_box_default_slice() {
447        let b = Box::<[u32]>::default();
448        assert_eq!(b.len(), 0);
449    }
450
451    #[test]
452    fn test_box_empty_slice() {
453        let b = Box::<[u32]>::empty_slice();
454        assert_eq!(b.len(), 0);
455        assert!(Box::as_ptr(&b) as *mut u8 == NonNull::<u32>::dangling().as_ptr() as *mut u8);
456    }
457
458    #[test]
459    fn test_box_try_new() {
460        let b = Box::<u32>::try_new(42).unwrap();
461        assert_eq!(*b, 42);
462    }
463
464    #[test]
465    fn test_box() {
466        let b = Box::<[u32]>::try_new_uninit_slice(10).unwrap();
467        assert_eq!(b.len(), 10);
468    }
469
470    #[test]
471    fn test_box_deref() {
472        let mut b = Box::<[u32]>::try_new_uninit_slice(1).unwrap();
473        b[0].write(0);
474        // SAFETY: The sole element is initialized, and consuming `b` transfers its unique ownership.
475        let mut b = unsafe { b.assume_init() };
476        assert_eq!(b[0], 0);
477        b[0] = 42;
478        assert_eq!(b[0], 42);
479    }
480
481    #[test]
482    fn test_box_as_ptr() {
483        let b = Box::<[u32]>::try_new_uninit_slice(10).unwrap();
484        let ptr = Box::as_ptr(&b);
485        assert!(!ptr.is_null());
486    }
487
488    #[test]
489    fn test_box_from_raw() {
490        let mut b = Box::<[u32]>::try_new_uninit_slice(10).unwrap();
491        b.fill(MaybeUninit::new(0));
492        let raw_ptr = Box::into_raw(b);
493        let fat_ptr = raw_ptr as *mut [u32];
494
495        // SAFETY: Every element is initialized. `MaybeUninit<u32>` has the size and alignment of
496        // `u32`, and the cast preserves the slice length. `into_raw` consumed the sole owning box
497        // without freeing its DefaultAllocator allocation; no references remain, and this is its
498        // only reconstruction.
499        let b2: Box<[u32]> = unsafe { Box::from_raw(fat_ptr) };
500        assert_eq!(b2.len(), 10);
501        assert_eq!(*b2, [0; 10]);
502        // b2 will free the memory on drop.
503    }
504
505    struct DropObserver<'a> {
506        dropped: &'a core::cell::Cell<bool>,
507    }
508
509    impl<'a> Drop for DropObserver<'a> {
510        fn drop(&mut self) {
511            self.dropped.set(true);
512        }
513    }
514
515    #[test]
516    fn test_box_drops_content() {
517        use core::cell::Cell;
518        let dropped = Cell::new(false);
519        {
520            let observer = DropObserver { dropped: &dropped };
521            let _b: Box<DropObserver<'_>> = Box::try_new(observer).unwrap();
522            assert_eq!(dropped.get(), false);
523        } // b drops here
524        assert_eq!(dropped.get(), true);
525    }
526
527    #[test]
528    #[should_panic]
529    fn test_box_slice_out_of_bounds() {
530        let b = Box::<[u32]>::try_new_uninit_slice(5).unwrap();
531        let _ = b[5]; // Should panic
532    }
533
534    #[derive(Clone, Default)]
535    struct AlwaysFailingAllocator;
536
537    impl Allocator for AlwaysFailingAllocator {
538        fn allocate(&self, _layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
539            Err(AllocError)
540        }
541
542        unsafe fn deallocate(&self, _ptr: NonNull<u8>, _layout: Layout) {
543            panic!("Deallocate called on AlwaysFailingAllocator");
544        }
545
546        unsafe fn grow(
547            &self,
548            _ptr: NonNull<u8>,
549            _old_layout: Layout,
550            _new_layout: Layout,
551        ) -> Result<NonNull<[u8]>, AllocError> {
552            Err(AllocError)
553        }
554
555        unsafe fn shrink(
556            &self,
557            _ptr: NonNull<u8>,
558            _old_layout: Layout,
559            _new_layout: Layout,
560        ) -> Result<NonNull<[u8]>, AllocError> {
561            Err(AllocError)
562        }
563
564        fn allocate_zeroed(&self, _layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
565            Err(AllocError)
566        }
567    }
568
569    #[test]
570    fn test_box_try_new_failing() {
571        let b =
572            Box::<u32, AlwaysFailingAllocator>::try_new_in(42, AlwaysFailingAllocator::default());
573        assert!(b.is_err());
574    }
575
576    #[test]
577    fn test_box_try_new_slice_failing() {
578        let b = Box::<[u32], AlwaysFailingAllocator>::try_new_uninit_slice_in(
579            10,
580            AlwaysFailingAllocator::default(),
581        );
582        assert!(b.is_err());
583    }
584
585    #[test]
586    fn test_box_try_new_zeroed() {
587        let b = Box::<u32>::try_new_zeroed().unwrap();
588        let b = unsafe { b.assume_init() };
589        assert_eq!(*b, 0);
590    }
591
592    #[test]
593    fn test_box_try_new_zeroed_slice() {
594        let b = Box::<[u32]>::try_new_zeroed_slice(3).unwrap();
595        assert_eq!(*b, [0, 0, 0]);
596    }
597
598    #[test]
599    fn test_box_try_grow() {
600        let mut b = Box::<[u32]>::try_new_uninit_slice(2).unwrap();
601        b[0].write(10);
602        b[1].write(20);
603
604        Box::try_grow(&mut b, 5).unwrap();
605        assert_eq!(b.len(), 5);
606        assert_eq!(unsafe { b[0].assume_init() }, 10);
607        assert_eq!(unsafe { b[1].assume_init() }, 20);
608    }
609
610    #[test]
611    fn test_box_try_shrink() {
612        let mut b = Box::<[u32]>::try_new_uninit_slice(5).unwrap();
613        b[0].write(10);
614        b[1].write(20);
615
616        unsafe {
617            Box::try_shrink(&mut b, 2).unwrap();
618        }
619        assert_eq!(b.len(), 2);
620        assert_eq!(unsafe { b[0].assume_init() }, 10);
621        assert_eq!(unsafe { b[1].assume_init() }, 20);
622    }
623
624    #[test]
625    fn test_box_from_non_null() {
626        use core::alloc::Layout;
627        let layout = Layout::new::<u32>();
628        let ptr = DefaultAllocator::default().allocate(layout).unwrap();
629        let casted = ptr.cast::<u32>();
630        unsafe {
631            casted.as_ptr().write(42);
632        }
633        let b: Box<u32, DefaultAllocator> = unsafe { Box::from_non_null(casted) };
634        assert_eq!(*b, 42);
635    }
636
637    #[test]
638    fn test_box_into_raw() {
639        let b = Box::try_new(42u32).unwrap();
640        let ptr = Box::into_raw(b);
641        assert_eq!(unsafe { *ptr }, 42);
642        unsafe {
643            *ptr = 100;
644        }
645        assert_eq!(unsafe { *ptr }, 100);
646
647        // Reconstruct the box to avoid leaking memory in tests.
648        let b = unsafe { Box::from_raw(ptr) };
649        assert_eq!(*b, 100);
650    }
651
652    #[test]
653    fn test_box_into_raw_with_allocator() {
654        let b = Box::try_new(42u32).unwrap();
655        let (ptr, allocator) = Box::into_raw_with_allocator(b);
656        assert_eq!(unsafe { *ptr }, 42);
657
658        // Reconstruct the box to avoid leaking memory in tests.
659        let b = unsafe { Box::from_raw_in(ptr, allocator) };
660        assert_eq!(*b, 42);
661    }
662
663    #[test]
664    fn test_box_assume_init_range() {
665        let mut b = Box::<[u32]>::try_new_uninit_slice(5).unwrap();
666        unsafe {
667            b[1].as_mut_ptr().write(10);
668            b[2].as_mut_ptr().write(20);
669        }
670
671        let slice = unsafe { b[1..3].assume_init_ref() };
672        assert_eq!(slice, [10, 20]);
673
674        let slice_mut = unsafe { b[1..3].assume_init_mut() };
675        slice_mut[0] = 30;
676        assert_eq!(unsafe { b[1].assume_init() }, 30);
677    }
678
679    #[derive(Clone)]
680    struct TrackingAllocator {
681        allocated: alloc::sync::Arc<core::cell::RefCell<alloc::collections::BTreeSet<usize>>>,
682    }
683
684    impl TrackingAllocator {
685        fn new() -> Self {
686            Self {
687                allocated: alloc::sync::Arc::new(core::cell::RefCell::new(
688                    alloc::collections::BTreeSet::new(),
689                )),
690            }
691        }
692    }
693
694    impl Allocator for TrackingAllocator {
695        fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
696            let ptr = DefaultAllocator::default().allocate(layout)?;
697            let addr = ptr.as_ptr() as *mut u8 as usize;
698            self.allocated.borrow_mut().insert(addr);
699            Ok(ptr)
700        }
701
702        fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
703            let ptr = DefaultAllocator::default().allocate_zeroed(layout)?;
704            let addr = ptr.as_ptr() as *mut u8 as usize;
705            self.allocated.borrow_mut().insert(addr);
706            Ok(ptr)
707        }
708
709        unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
710            let addr = ptr.as_ptr() as usize;
711            let mut allocated = self.allocated.borrow_mut();
712            if !allocated.remove(&addr) {
713                panic!("Deallocate called on address not produced by allocator: {:p}", ptr);
714            }
715            // SAFETY: The caller must guarantee that `ptr` was allocated by this allocator.
716            unsafe {
717                DefaultAllocator::default().deallocate(ptr, layout);
718            }
719        }
720
721        unsafe fn grow(
722            &self,
723            ptr: NonNull<u8>,
724            old_layout: Layout,
725            new_layout: Layout,
726        ) -> Result<NonNull<[u8]>, AllocError> {
727            let addr = ptr.as_ptr() as usize;
728            let mut allocated = self.allocated.borrow_mut();
729            if !allocated.remove(&addr) {
730                panic!("Grow called on address not produced by allocator: {:p}", ptr);
731            }
732            // SAFETY: The caller must guarantee that `ptr` was allocated by this allocator.
733            let new_ptr = unsafe { DefaultAllocator::default().grow(ptr, old_layout, new_layout)? };
734            let new_addr = new_ptr.as_ptr() as *mut u8 as usize;
735            allocated.insert(new_addr);
736            Ok(new_ptr)
737        }
738
739        unsafe fn shrink(
740            &self,
741            ptr: NonNull<u8>,
742            old_layout: Layout,
743            new_layout: Layout,
744        ) -> Result<NonNull<[u8]>, AllocError> {
745            let addr = ptr.as_ptr() as usize;
746            let mut allocated = self.allocated.borrow_mut();
747            if !allocated.remove(&addr) {
748                panic!("Shrink called on address not produced by allocator: {:p}", ptr);
749            }
750            // SAFETY: The caller must guarantee that `ptr` was allocated by this allocator.
751            let new_ptr =
752                unsafe { DefaultAllocator::default().shrink(ptr, old_layout, new_layout)? };
753            let new_addr = new_ptr.as_ptr() as *mut u8 as usize;
754            allocated.insert(new_addr);
755            Ok(new_ptr)
756        }
757    }
758
759    #[test]
760    fn test_empty_slice_and_zst_allocator_interactions() {
761        let alloc = TrackingAllocator::new();
762
763        // Test empty slice
764        {
765            let b = Box::<[u32], TrackingAllocator>::empty_slice_in(alloc.clone());
766            assert_eq!(b.len(), 0);
767            // Drop should NOT call deallocate because length is 0.
768        }
769
770        // Test ZST
771        struct Zst;
772        {
773            let _b = Box::<Zst, TrackingAllocator>::try_new_in(Zst, alloc.clone()).unwrap();
774            // Drop should NOT call deallocate because size is 0.
775        }
776
777        // Test growing an empty slice
778        {
779            let mut b = Box::<[core::mem::MaybeUninit<u32>], TrackingAllocator>::empty_slice_in(
780                alloc.clone(),
781            );
782            Box::try_grow(&mut b, 5).unwrap();
783            // Now it should be in the tracking allocator.
784            let addr = Box::as_ptr(&b) as *mut u8 as usize;
785            assert!(alloc.allocated.borrow().contains(&addr));
786        } // Drops here, should call deallocate and succeed.
787
788        // Test shrinking to empty
789        {
790            let mut b =
791                Box::<[u32], TrackingAllocator>::try_new_uninit_slice_in(5, alloc.clone()).unwrap();
792            let addr = Box::as_ptr(&b) as *mut u8 as usize;
793            assert!(alloc.allocated.borrow().contains(&addr));
794
795            unsafe {
796                Box::try_shrink(&mut b, 0).unwrap();
797            }
798            assert_eq!(b.len(), 0);
799            // The old memory should have been deallocated by try_shrink calling deallocate_slice.
800            assert!(!alloc.allocated.borrow().contains(&addr));
801        }
802    }
803
804    #[test]
805    fn test_try_new_zeroed_uninit_slice() {
806        // Zeroed slice of u32 (which implements FromZeros)
807        let b = Box::<[u32]>::try_new_zeroed_uninit_slice(4).unwrap();
808        assert_eq!(b.len(), 4);
809        for x in b.as_ref() {
810            assert_eq!(unsafe { x.assume_init() }, 0);
811        }
812
813        // Zeroed slice of a struct that does not implement FromZeros
814        struct NotZeroable {
815            _a: u32,
816            _b: u32,
817        }
818        let b = Box::<[NotZeroable]>::try_new_zeroed_uninit_slice(4).unwrap();
819        assert_eq!(b.len(), 4);
820    }
821}