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