use crate::allocator_api::{AllocError, Allocator};
use alloc::alloc::handle_alloc_error;
use core::alloc::Layout;
use core::marker::PhantomData;
use core::mem::{ManuallyDrop, MaybeUninit};
use core::ops::{Deref, DerefMut};
use core::ptr::NonNull;
pub struct Box<T: ?Sized, A: Allocator> {
ptr: NonNull<T>,
_phantom_heapmem: PhantomData<T>,
alloc: A,
}
impl<T: ?Sized, A: Allocator> Box<T, A> {
unsafe fn from_raw_parts(ptr: NonNull<T>, alloc: A) -> Self {
Self {
ptr,
alloc,
_phantom_heapmem: PhantomData::<T>,
}
}
fn into_raw_parts(self) -> (NonNull<T>, A) {
let ptr = self.ptr;
let me = ManuallyDrop::new(self);
let alloc_ptr = &me.deref().alloc as *const A;
let alloc = unsafe { alloc_ptr.read() };
(ptr, alloc)
}
}
impl<T, A: Allocator> Box<T, A> {
#[inline]
pub fn new_in(x: T, alloc: A) -> Self {
let mut boxed = Self::new_uninit_in(alloc);
unsafe {
boxed.as_mut_ptr().write(x);
boxed.assume_init()
}
}
#[inline]
pub fn try_new_in(x: T, alloc: A) -> Result<Self, AllocError> {
let mut boxed = Self::try_new_uninit_in(alloc)?;
unsafe {
boxed.as_mut_ptr().write(x);
Ok(boxed.assume_init())
}
}
pub fn new_uninit_in(alloc: A) -> Box<MaybeUninit<T>, A> {
let layout = Layout::new::<MaybeUninit<T>>();
match Box::try_new_uninit_in(alloc) {
Ok(m) => m,
Err(_) => handle_alloc_error(layout),
}
}
pub fn try_new_uninit_in(alloc: A) -> Result<Box<MaybeUninit<T>, A>, AllocError> {
let layout = Layout::new::<MaybeUninit<T>>();
let ptr: NonNull<MaybeUninit<T>> = alloc.allocate(layout)?.cast();
unsafe { Ok(Box::from_raw_parts(ptr, alloc)) }
}
}
impl<T, A: Allocator> Box<MaybeUninit<T>, A> {
#[inline]
pub unsafe fn assume_init(self) -> Box<T, A> {
let (ptr, alloc) = Box::into_raw_parts(self);
let ptr_init: NonNull<T> = ptr.cast();
unsafe { Box::from_raw_parts(ptr_init, alloc) }
}
}
impl<T: ?Sized, A: Allocator> Deref for Box<T, A> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.ptr.as_ptr() }
}
}
impl<T: ?Sized, A: Allocator> DerefMut for Box<T, A> {
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.ptr.as_ptr() }
}
}
impl<T: ?Sized, A: Allocator> Drop for Box<T, A> {
fn drop(&mut self) {
let ref_to_inner: &T = self.deref();
let layout = Layout::for_value::<T>(ref_to_inner);
unsafe {
self.ptr.as_ptr().drop_in_place();
}
let ptr: NonNull<u8> = self.ptr.cast();
unsafe {
self.alloc.deallocate(ptr, layout);
}
}
}
#[cfg(test)]
mod tests {
use super::Box;
use std::alloc::System;
use std::mem::MaybeUninit;
#[test]
fn new_in() {
let boxed = Box::new_in([37; 256], System);
assert_eq!(*boxed, [37; 256]);
}
#[test]
fn try_new_in() {
let boxed = Box::try_new_in([37; 256], System).expect("error creating box");
assert_eq!(*boxed, [37; 256]);
}
#[test]
fn uninit_initialise() {
let mut boxed: Box<MaybeUninit<[u8; 256]>, System> =
Box::<[u8; 256], _>::new_uninit_in(System);
unsafe {
boxed.as_mut_ptr().write([37; 256]);
}
let boxed: Box<[u8; 256], System> = unsafe { boxed.assume_init() };
assert_eq!(*boxed, [37; 256]);
}
}