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 b = Box::<[u32]>::try_new_uninit_slice(1).unwrap();
473        let mut b = unsafe { b.assume_init() };
474        b[0] = 42;
475        assert_eq!(b[0], 42);
476    }
477
478    #[test]
479    fn test_box_as_ptr() {
480        let b = Box::<[u32]>::try_new_uninit_slice(10).unwrap();
481        let ptr = Box::as_ptr(&b);
482        assert!(!ptr.is_null());
483    }
484
485    #[test]
486    fn test_box_from_raw() {
487        let b = Box::<[u32]>::try_new_uninit_slice(10).unwrap();
488        let raw_ptr = Box::into_raw(b);
489        let fat_ptr = raw_ptr as *mut [u32];
490
491        // Create a new box from the pointer
492        let b2: Box<[u32]> = unsafe { Box::from_raw(fat_ptr) };
493        assert_eq!(b2.len(), 10);
494        // b2 will free the memory on drop.
495    }
496
497    struct DropObserver<'a> {
498        dropped: &'a core::cell::Cell<bool>,
499    }
500
501    impl<'a> Drop for DropObserver<'a> {
502        fn drop(&mut self) {
503            self.dropped.set(true);
504        }
505    }
506
507    #[test]
508    fn test_box_drops_content() {
509        use core::cell::Cell;
510        let dropped = Cell::new(false);
511        {
512            let observer = DropObserver { dropped: &dropped };
513            let _b: Box<DropObserver<'_>> = Box::try_new(observer).unwrap();
514            assert_eq!(dropped.get(), false);
515        } // b drops here
516        assert_eq!(dropped.get(), true);
517    }
518
519    #[test]
520    #[should_panic]
521    fn test_box_slice_out_of_bounds() {
522        let b = Box::<[u32]>::try_new_uninit_slice(5).unwrap();
523        let _ = b[5]; // Should panic
524    }
525
526    #[derive(Clone, Default)]
527    struct AlwaysFailingAllocator;
528
529    impl Allocator for AlwaysFailingAllocator {
530        fn allocate(&self, _layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
531            Err(AllocError)
532        }
533
534        unsafe fn deallocate(&self, _ptr: NonNull<u8>, _layout: Layout) {
535            panic!("Deallocate called on AlwaysFailingAllocator");
536        }
537
538        unsafe fn grow(
539            &self,
540            _ptr: NonNull<u8>,
541            _old_layout: Layout,
542            _new_layout: Layout,
543        ) -> Result<NonNull<[u8]>, AllocError> {
544            Err(AllocError)
545        }
546
547        unsafe fn shrink(
548            &self,
549            _ptr: NonNull<u8>,
550            _old_layout: Layout,
551            _new_layout: Layout,
552        ) -> Result<NonNull<[u8]>, AllocError> {
553            Err(AllocError)
554        }
555
556        fn allocate_zeroed(&self, _layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
557            Err(AllocError)
558        }
559    }
560
561    #[test]
562    fn test_box_try_new_failing() {
563        let b =
564            Box::<u32, AlwaysFailingAllocator>::try_new_in(42, AlwaysFailingAllocator::default());
565        assert!(b.is_err());
566    }
567
568    #[test]
569    fn test_box_try_new_slice_failing() {
570        let b = Box::<[u32], AlwaysFailingAllocator>::try_new_uninit_slice_in(
571            10,
572            AlwaysFailingAllocator::default(),
573        );
574        assert!(b.is_err());
575    }
576
577    #[test]
578    fn test_box_try_new_zeroed() {
579        let b = Box::<u32>::try_new_zeroed().unwrap();
580        let b = unsafe { b.assume_init() };
581        assert_eq!(*b, 0);
582    }
583
584    #[test]
585    fn test_box_try_new_zeroed_slice() {
586        let b = Box::<[u32]>::try_new_zeroed_slice(3).unwrap();
587        assert_eq!(*b, [0, 0, 0]);
588    }
589
590    #[test]
591    fn test_box_try_grow() {
592        let mut b = Box::<[u32]>::try_new_uninit_slice(2).unwrap();
593        unsafe {
594            b[0].as_mut_ptr().write(10);
595            b[1].as_mut_ptr().write(20);
596        }
597
598        Box::try_grow(&mut b, 5).unwrap();
599        assert_eq!(b.len(), 5);
600        assert_eq!(unsafe { b[0].assume_init() }, 10);
601        assert_eq!(unsafe { b[1].assume_init() }, 20);
602    }
603
604    #[test]
605    fn test_box_try_shrink() {
606        let mut b = Box::<[u32]>::try_new_uninit_slice(5).unwrap();
607        unsafe {
608            b[0].as_mut_ptr().write(10);
609            b[1].as_mut_ptr().write(20);
610        }
611
612        unsafe {
613            Box::try_shrink(&mut b, 2).unwrap();
614        }
615        assert_eq!(b.len(), 2);
616        assert_eq!(unsafe { b[0].assume_init() }, 10);
617        assert_eq!(unsafe { b[1].assume_init() }, 20);
618    }
619
620    #[test]
621    fn test_box_from_non_null() {
622        use core::alloc::Layout;
623        let layout = Layout::new::<u32>();
624        let ptr = DefaultAllocator::default().allocate(layout).unwrap();
625        let thin_ptr = unsafe { NonNull::new_unchecked(ptr.as_ptr() as *mut u8) };
626        let casted = thin_ptr.cast::<u32>();
627        unsafe {
628            casted.as_ptr().write(42);
629        }
630        let b: Box<u32, DefaultAllocator> = unsafe { Box::from_non_null(casted) };
631        assert_eq!(*b, 42);
632    }
633
634    #[test]
635    fn test_box_into_raw() {
636        let b = Box::try_new(42u32).unwrap();
637        let ptr = Box::into_raw(b);
638        assert_eq!(unsafe { *ptr }, 42);
639        unsafe {
640            *ptr = 100;
641        }
642        assert_eq!(unsafe { *ptr }, 100);
643
644        // Reconstruct the box to avoid leaking memory in tests.
645        let b = unsafe { Box::from_raw(ptr) };
646        assert_eq!(*b, 100);
647    }
648
649    #[test]
650    fn test_box_into_raw_with_allocator() {
651        let b = Box::try_new(42u32).unwrap();
652        let (ptr, allocator) = Box::into_raw_with_allocator(b);
653        assert_eq!(unsafe { *ptr }, 42);
654
655        // Reconstruct the box to avoid leaking memory in tests.
656        let b = unsafe { Box::from_raw_in(ptr, allocator) };
657        assert_eq!(*b, 42);
658    }
659
660    #[test]
661    fn test_box_assume_init_range() {
662        let mut b = Box::<[u32]>::try_new_uninit_slice(5).unwrap();
663        unsafe {
664            b[1].as_mut_ptr().write(10);
665            b[2].as_mut_ptr().write(20);
666        }
667
668        let slice = unsafe { b[1..3].assume_init_ref() };
669        assert_eq!(slice, [10, 20]);
670
671        let slice_mut = unsafe { b[1..3].assume_init_mut() };
672        slice_mut[0] = 30;
673        assert_eq!(unsafe { b[1].assume_init() }, 30);
674    }
675
676    #[derive(Clone)]
677    struct TrackingAllocator {
678        allocated: alloc::sync::Arc<core::cell::RefCell<alloc::collections::BTreeSet<usize>>>,
679    }
680
681    impl TrackingAllocator {
682        fn new() -> Self {
683            Self {
684                allocated: alloc::sync::Arc::new(core::cell::RefCell::new(
685                    alloc::collections::BTreeSet::new(),
686                )),
687            }
688        }
689    }
690
691    impl Allocator for TrackingAllocator {
692        fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
693            let ptr = DefaultAllocator::default().allocate(layout)?;
694            let addr = ptr.as_ptr() as *mut u8 as usize;
695            self.allocated.borrow_mut().insert(addr);
696            Ok(ptr)
697        }
698
699        fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
700            let ptr = DefaultAllocator::default().allocate_zeroed(layout)?;
701            let addr = ptr.as_ptr() as *mut u8 as usize;
702            self.allocated.borrow_mut().insert(addr);
703            Ok(ptr)
704        }
705
706        unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
707            let addr = ptr.as_ptr() as usize;
708            let mut allocated = self.allocated.borrow_mut();
709            if !allocated.remove(&addr) {
710                panic!("Deallocate called on address not produced by allocator: {:p}", ptr);
711            }
712            // SAFETY: The caller must guarantee that `ptr` was allocated by this allocator.
713            unsafe {
714                DefaultAllocator::default().deallocate(ptr, layout);
715            }
716        }
717
718        unsafe fn grow(
719            &self,
720            ptr: NonNull<u8>,
721            old_layout: Layout,
722            new_layout: Layout,
723        ) -> Result<NonNull<[u8]>, AllocError> {
724            let addr = ptr.as_ptr() as usize;
725            let mut allocated = self.allocated.borrow_mut();
726            if !allocated.remove(&addr) {
727                panic!("Grow called on address not produced by allocator: {:p}", ptr);
728            }
729            // SAFETY: The caller must guarantee that `ptr` was allocated by this allocator.
730            let new_ptr = unsafe { DefaultAllocator::default().grow(ptr, old_layout, new_layout)? };
731            let new_addr = new_ptr.as_ptr() as *mut u8 as usize;
732            allocated.insert(new_addr);
733            Ok(new_ptr)
734        }
735
736        unsafe fn shrink(
737            &self,
738            ptr: NonNull<u8>,
739            old_layout: Layout,
740            new_layout: Layout,
741        ) -> Result<NonNull<[u8]>, AllocError> {
742            let addr = ptr.as_ptr() as usize;
743            let mut allocated = self.allocated.borrow_mut();
744            if !allocated.remove(&addr) {
745                panic!("Shrink called on address not produced by allocator: {:p}", ptr);
746            }
747            // SAFETY: The caller must guarantee that `ptr` was allocated by this allocator.
748            let new_ptr =
749                unsafe { DefaultAllocator::default().shrink(ptr, old_layout, new_layout)? };
750            let new_addr = new_ptr.as_ptr() as *mut u8 as usize;
751            allocated.insert(new_addr);
752            Ok(new_ptr)
753        }
754    }
755
756    #[test]
757    fn test_empty_slice_and_zst_allocator_interactions() {
758        let alloc = TrackingAllocator::new();
759
760        // Test empty slice
761        {
762            let b = Box::<[u32], TrackingAllocator>::empty_slice_in(alloc.clone());
763            assert_eq!(b.len(), 0);
764            // Drop should NOT call deallocate because length is 0.
765        }
766
767        // Test ZST
768        struct Zst;
769        {
770            let _b = Box::<Zst, TrackingAllocator>::try_new_in(Zst, alloc.clone()).unwrap();
771            // Drop should NOT call deallocate because size is 0.
772        }
773
774        // Test growing an empty slice
775        {
776            let mut b = Box::<[core::mem::MaybeUninit<u32>], TrackingAllocator>::empty_slice_in(
777                alloc.clone(),
778            );
779            Box::try_grow(&mut b, 5).unwrap();
780            // Now it should be in the tracking allocator.
781            let addr = Box::as_ptr(&b) as *mut u8 as usize;
782            assert!(alloc.allocated.borrow().contains(&addr));
783        } // Drops here, should call deallocate and succeed.
784
785        // Test shrinking to empty
786        {
787            let mut b =
788                Box::<[u32], TrackingAllocator>::try_new_uninit_slice_in(5, alloc.clone()).unwrap();
789            let addr = Box::as_ptr(&b) as *mut u8 as usize;
790            assert!(alloc.allocated.borrow().contains(&addr));
791
792            unsafe {
793                Box::try_shrink(&mut b, 0).unwrap();
794            }
795            assert_eq!(b.len(), 0);
796            // The old memory should have been deallocated by try_shrink calling deallocate_slice.
797            assert!(!alloc.allocated.borrow().contains(&addr));
798        }
799    }
800
801    #[test]
802    fn test_try_new_zeroed_uninit_slice() {
803        // Zeroed slice of u32 (which implements FromZeros)
804        let b = Box::<[u32]>::try_new_zeroed_uninit_slice(4).unwrap();
805        assert_eq!(b.len(), 4);
806        for x in b.as_ref() {
807            assert_eq!(unsafe { x.assume_init() }, 0);
808        }
809
810        // Zeroed slice of a struct that does not implement FromZeros
811        struct NotZeroable {
812            _a: u32,
813            _b: u32,
814        }
815        let b = Box::<[NotZeroable]>::try_new_zeroed_uninit_slice(4).unwrap();
816        assert_eq!(b.len(), 4);
817    }
818}