| // SPDX-License-Identifier: GPL-2.0 |
| |
| //! Extensions to [`Box`] for fallible allocations. |
| |
| use super::{AllocError, Flags}; |
| use alloc::boxed::Box; |
| use core::{mem::MaybeUninit, ptr, result::Result}; |
| |
| /// Extensions to [`Box`]. |
| pub trait BoxExt<T>: Sized { |
| /// Allocates a new box. |
| /// |
| /// The allocation may fail, in which case an error is returned. |
| fn new(x: T, flags: Flags) -> Result<Self, AllocError>; |
| |
| /// Allocates a new uninitialised box. |
| /// |
| /// The allocation may fail, in which case an error is returned. |
| fn new_uninit(flags: Flags) -> Result<Box<MaybeUninit<T>>, AllocError>; |
| |
| /// Drops the contents, but keeps the allocation. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// use kernel::alloc::{flags, box_ext::BoxExt}; |
| /// let value = Box::new([0; 32], flags::GFP_KERNEL)?; |
| /// assert_eq!(*value, [0; 32]); |
| /// let mut value = Box::drop_contents(value); |
| /// // Now we can re-use `value`: |
| /// value.write([1; 32]); |
| /// // SAFETY: We just wrote to it. |
| /// let value = unsafe { value.assume_init() }; |
| /// assert_eq!(*value, [1; 32]); |
| /// # Ok::<(), Error>(()) |
| /// ``` |
| fn drop_contents(this: Self) -> Box<MaybeUninit<T>>; |
| } |
| |
| impl<T> BoxExt<T> for Box<T> { |
| fn new(x: T, flags: Flags) -> Result<Self, AllocError> { |
| let mut b = <Self as BoxExt<_>>::new_uninit(flags)?; |
| b.write(x); |
| // SAFETY: We just wrote to it. |
| Ok(unsafe { b.assume_init() }) |
| } |
| |
| #[cfg(any(test, testlib))] |
| fn new_uninit(_flags: Flags) -> Result<Box<MaybeUninit<T>>, AllocError> { |
| Ok(Box::new_uninit()) |
| } |
| |
| #[cfg(not(any(test, testlib)))] |
| fn new_uninit(flags: Flags) -> Result<Box<MaybeUninit<T>>, AllocError> { |
| let ptr = if core::mem::size_of::<MaybeUninit<T>>() == 0 { |
| core::ptr::NonNull::<_>::dangling().as_ptr() |
| } else { |
| let layout = core::alloc::Layout::new::<MaybeUninit<T>>(); |
| |
| // SAFETY: Memory is being allocated (first arg is null). The only other source of |
| // safety issues is sleeping on atomic context, which is addressed by klint. Lastly, |
| // the type is not a SZT (checked above). |
| let ptr = |
| unsafe { super::allocator::krealloc_aligned(core::ptr::null_mut(), layout, flags) }; |
| if ptr.is_null() { |
| return Err(AllocError); |
| } |
| |
| ptr.cast::<MaybeUninit<T>>() |
| }; |
| |
| // SAFETY: For non-zero-sized types, we allocate above using the global allocator. For |
| // zero-sized types, we use `NonNull::dangling`. |
| Ok(unsafe { Box::from_raw(ptr) }) |
| } |
| |
| fn drop_contents(this: Self) -> Box<MaybeUninit<T>> { |
| let ptr = Box::into_raw(this); |
| // SAFETY: `ptr` is valid, because it came from `Box::into_raw`. |
| unsafe { ptr::drop_in_place(ptr) }; |
| |
| // CAST: `MaybeUninit<T>` is a transparent wrapper of `T`. |
| let ptr = ptr.cast::<MaybeUninit<T>>(); |
| |
| // SAFETY: `ptr` is valid for writes, because it came from `Box::into_raw` and it is valid for |
| // reads, since the pointer came from `Box::into_raw` and the type is `MaybeUninit<T>`. |
| unsafe { Box::from_raw(ptr) } |
| } |
| } |