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