use crate::{Allocator, GenericAlloc, NullAlloc, Pool, PoolAlloc, Result};
use core::alloc::Layout;
use core::any::Any;
use core::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
use core::convert::{AsMut, AsRef, From};
use core::fmt::{self, Debug, Display, Formatter};
use core::hash::{Hash, Hasher};
use core::marker::PhantomData;
use core::mem::{needs_drop, ManuallyDrop, MaybeUninit};
use core::ops::{Deref, DerefMut};
use core::ptr::{self, NonNull};
use core::slice;
#[repr(C)]
pub struct Boxed<'a, T: ?Sized + 'a, A: Allocator = PoolAlloc> {
ptr: NonNull<T>,
layout: Layout,
alloc: &'a A,
mark: PhantomData<T>,
}
unsafe impl<T: ?Sized + Send, A: Allocator + Pool> Send for Boxed<'static, T, A> {}
unsafe impl<T: ?Sized + Sync, A: Allocator> Sync for Boxed<'static, T, A> {}
impl<T> Boxed<'static, T, PoolAlloc> {
pub fn new(val: T) -> Result<Self> {
Boxed::new_in(&PoolAlloc, val)
}
}
impl Boxed<'static, (), PoolAlloc> {
pub fn zeroed<T>() -> Result<Boxed<'static, MaybeUninit<T>, PoolAlloc>> {
Boxed::zeroed_in::<T>(&PoolAlloc)
}
pub fn uninit<T>() -> Result<Boxed<'static, MaybeUninit<T>, PoolAlloc>> {
Boxed::uninit_in::<T>(&PoolAlloc)
}
pub fn zeroed_slice<T>(len: usize) -> Result<Boxed<'static, [MaybeUninit<T>], PoolAlloc>> {
Boxed::zeroed_slice_in(&PoolAlloc, len)
}
pub fn uninit_slice<T>(len: usize) -> Result<Boxed<'static, [MaybeUninit<T>], PoolAlloc>> {
Boxed::uninit_slice_in(&PoolAlloc, len)
}
pub fn new_then<T, F>(f: F) -> Result<Boxed<'static, T, PoolAlloc>>
where
F: FnOnce() -> Result<T>,
{
Boxed::new_then_in(&PoolAlloc, f)
}
pub fn new_slice_then<T, F>(len: usize, f: F) -> Result<Boxed<'static, [T], PoolAlloc>>
where
F: FnMut(usize) -> Result<T>,
{
Boxed::new_slice_then_in(&PoolAlloc, len, f)
}
}
impl Boxed<'static, [u8], PoolAlloc> {
pub fn new_buf(layout: Layout) -> Result<Self> {
Self::new_buf_then(layout, |_| Ok(()))
}
pub fn new_buf_then<F>(layout: Layout, f: F) -> Result<Self>
where
F: FnOnce(NonNull<[u8]>) -> Result<()>,
{
Self::new_buf_then_in(&PoolAlloc, layout, f)
}
}
impl<T> Boxed<'static, [T], PoolAlloc> {
pub fn new_slice(len: usize, val: T) -> Result<Self>
where
T: Clone,
{
Self::new_slice_in(&PoolAlloc, len, val)
}
}
impl<'a, T, A: Allocator> Boxed<'a, T, A> {
pub fn new_in(alloc: &'a A, val: T) -> Result<Self> {
let ptr = unsafe { alloc.init(val)? };
Ok(unsafe { Self::from_with(ptr, Layout::new::<T>(), alloc) })
}
}
impl<'a, A: Allocator> Boxed<'a, (), A> {
pub fn zeroed_in<T>(alloc: &'a A) -> Result<Boxed<'a, MaybeUninit<T>, A>> {
let ptr = unsafe { alloc.zeroed::<T>()? };
Ok(unsafe { Boxed::from_with(ptr, Layout::new::<T>(), alloc) })
}
pub fn uninit_in<T>(alloc: &'a A) -> Result<Boxed<'a, MaybeUninit<T>, A>> {
let ptr = unsafe { alloc.uninit::<T>()? };
Ok(unsafe { Boxed::from_with(ptr, Layout::new::<T>(), alloc) })
}
pub fn zeroed_slice_in<T>(alloc: &'a A, len: usize) -> Result<Boxed<'a, [MaybeUninit<T>], A>> {
let ptr = unsafe { alloc.zeroed_slice::<T>(len)? };
Ok(unsafe { Boxed::from_with(ptr, Layout::array::<T>(len).unwrap(), alloc) })
}
pub fn uninit_slice_in<T>(alloc: &'a A, len: usize) -> Result<Boxed<'a, [MaybeUninit<T>], A>> {
let ptr = unsafe { alloc.uninit_slice::<T>(len)? };
Ok(unsafe { Boxed::from_with(ptr, Layout::array::<T>(len).unwrap(), alloc) })
}
pub fn new_then_in<T, F>(alloc: &'a A, f: F) -> Result<Boxed<'a, T, A>>
where
F: FnOnce() -> Result<T>,
{
let ptr = unsafe { alloc.alloc_then(f)? };
Ok(unsafe { Boxed::from_with(ptr, Layout::new::<T>(), alloc) })
}
pub fn new_slice_then_in<T, F>(alloc: &'a A, len: usize, f: F) -> Result<Boxed<'a, [T], A>>
where
F: FnMut(usize) -> Result<T>,
{
let ptr = unsafe { alloc.alloc_slice_then(len, f)? };
Ok(unsafe { Boxed::from_with(ptr, Layout::array::<T>(len).unwrap(), alloc) })
}
}
impl<'a, A: Allocator> Boxed<'a, [u8], A> {
pub fn new_buf_in(alloc: &'a A, layout: Layout) -> Result<Self> {
Self::new_buf_then_in(alloc, layout, |_| Ok(()))
}
pub fn new_buf_then_in<F>(alloc: &'a A, layout: Layout, f: F) -> Result<Self>
where
F: FnOnce(NonNull<[u8]>) -> Result<()>,
{
let ptr = unsafe { alloc.alloc_buf(layout, f)? };
Ok(unsafe { Self::from_with(ptr, layout, alloc) })
}
}
impl<'a, T, A: Allocator> Boxed<'a, [T], A> {
pub fn new_slice_in(alloc: &'a A, len: usize, val: T) -> Result<Self>
where
T: Clone,
{
let ptr = unsafe { alloc.init_slice(len, val)? };
Ok(unsafe { Self::from_with(ptr, Layout::array::<T>(len).unwrap(), alloc) })
}
}
impl<'a, T> Boxed<'a, T, NullAlloc> {
pub unsafe fn from_raw(data: *mut T) -> Self {
Self {
ptr: NonNull::new(data).unwrap(),
layout: Layout::new::<T>(),
alloc: &NullAlloc,
mark: PhantomData,
}
}
}
impl<T> Boxed<'_, [T], NullAlloc> {
pub unsafe fn from_raw_slice(data: *mut T, len: usize) -> Self {
let slice = unsafe { slice::from_raw_parts(data, len) };
Self {
ptr: NonNull::from(slice),
layout: Layout::array::<T>(len).unwrap(),
alloc: &NullAlloc,
mark: PhantomData,
}
}
}
impl<'a, T, A: Allocator> Boxed<'a, MaybeUninit<T>, A> {
pub fn write(mut self, val: T) -> Boxed<'a, T, A> {
let _ = (*self).write(val);
unsafe { Self::cast_unchecked::<T>(self) }
}
}
impl<'a, T, A: Allocator> Boxed<'a, [MaybeUninit<T>], A> {
pub fn write_slice_then<F>(mut self, mut f: F) -> Result<Boxed<'a, [T], A>>
where
F: FnMut(usize) -> Result<T>,
{
for (n, uninit) in self.iter_mut().enumerate() {
match f(n) {
Err(e) => {
for uninit in &mut self[0..n] {
unsafe {
uninit.assume_init_drop();
}
}
return Err(e);
},
Ok(val) => { uninit.write(val); },
}
}
let len = self.len();
Ok(unsafe { Self::cast_slice_unchecked::<T>(self, len) })
}
}
impl<'a, T: ?Sized + 'a, A: Allocator> Boxed<'a, T, A> {
pub unsafe fn from_with(ptr: NonNull<T>, layout: Layout, alloc: &'a A) -> Self {
Self {
ptr,
layout,
alloc,
mark: PhantomData,
}
}
pub unsafe fn from_boxed(data: Boxed<'a, T, NullAlloc>, alloc: &'a A) -> Self {
let data = ManuallyDrop::new(data);
Self {
ptr: data.ptr,
layout: data.layout,
alloc,
mark: PhantomData,
}
}
pub const fn as_ptr(&self) -> *mut T {
self.ptr.as_ptr()
}
pub unsafe fn as_other<U>(&self) -> &U {
assert!(Layout::new::<U>().size() <= self.layout.size());
unsafe { self.ptr.cast::<U>().as_ref() }
}
pub unsafe fn as_other_mut<U>(&mut self) -> &mut U {
assert!(Layout::new::<U>().size() <= self.layout.size());
unsafe { self.ptr.cast::<U>().as_mut() }
}
pub unsafe fn as_slice<U>(&self, len: usize) -> &[U] {
assert!(Layout::array::<U>(len).unwrap().size() <= self.layout.size());
unsafe { slice::from_raw_parts(self.as_other::<U>(), len) }
}
pub unsafe fn as_slice_mut<U>(&mut self, len: usize) -> &mut [U] {
assert!(Layout::array::<U>(len).unwrap().size() <= self.layout.size());
unsafe { slice::from_raw_parts_mut(self.as_other_mut::<U>(), len) }
}
pub unsafe fn cast<U>(self) -> core::result::Result<Boxed<'a, U, A>, Self> {
let layout = Layout::new::<U>();
if layout.size() <= self.layout.size() {
Ok(self.cast_unchecked::<U>())
} else {
Err(self)
}
}
pub unsafe fn cast_unchecked<U>(self) -> Boxed<'a, U, A> {
assert!(Layout::new::<U>().size() <= self.layout.size());
let this = ManuallyDrop::new(self);
let ptr = NonNull::from(unsafe { this.as_other::<U>() });
Boxed::from_with(ptr, this.layout, this.alloc)
}
pub unsafe fn cast_slice<U>(self, len: usize) -> core::result::Result<Boxed<'a, [U], A>, Self> {
let Ok(layout) = Layout::array::<U>(len) else {
return Err(self);
};
if layout.size() <= self.layout.size() {
Ok(self.cast_slice_unchecked::<U>(len))
} else {
Err(self)
}
}
pub unsafe fn cast_slice_unchecked<U>(self, len: usize) -> Boxed<'a, [U], A> {
assert!(Layout::array::<U>(len).unwrap().size() <= self.layout.size());
let this = ManuallyDrop::new(self);
let ptr = NonNull::from(this.as_slice::<U>(len));
Boxed::from_with(ptr, this.layout, this.alloc)
}
pub fn leak(self) -> (&'a mut T, Layout, &'a A) {
let mut this = ManuallyDrop::new(self);
(unsafe { this.ptr.as_mut() }, this.layout, this.alloc)
}
pub fn leak_boxed(self) -> Boxed<'a, T, NullAlloc> {
let this = ManuallyDrop::new(self);
unsafe { Boxed::from_with(NonNull::from(this.as_ref()), this.layout, &NullAlloc) }
}
}
impl<T: ?Sized, A: Allocator> Drop for Boxed<'_, T, A> {
#[inline]
fn drop(&mut self) {
if needs_drop::<T>() {
unsafe { ptr::drop_in_place(self.ptr.as_ptr()) };
}
unsafe { self.alloc.release_with(self.ptr, self.layout) };
}
}
impl<T: ?Sized, A: Allocator> AsRef<T> for Boxed<'_, T, A> {
#[inline(always)]
fn as_ref(&self) -> &T {
unsafe { self.ptr.as_ref() }
}
}
impl<T: ?Sized, A: Allocator> AsMut<T> for Boxed<'_, T, A> {
#[inline(always)]
fn as_mut(&mut self) -> &mut T {
unsafe { self.ptr.as_mut() }
}
}
impl<T: ?Sized + Unpin, A: Allocator> Unpin for Boxed<'_, T, A> {}
impl<T: ?Sized, A: Allocator> Deref for Boxed<'_, T, A> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { self.ptr.as_ref() }
}
}
impl<T: ?Sized, A: Allocator> DerefMut for Boxed<'_, T, A> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { self.ptr.as_mut() }
}
}
impl<T: ?Sized + Display, A: Allocator> Display for Boxed<'_, T, A> {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&**self, f)
}
}
impl<T: ?Sized + Debug, A: Allocator> Debug for Boxed<'_, T, A> {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Debug::fmt(&**self, f)
}
}
impl<T: ?Sized, A: Allocator> fmt::Pointer for Boxed<'_, T, A> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let ptr: *const T = &**self;
fmt::Pointer::fmt(&ptr, f)
}
}
impl<T: ?Sized + PartialEq, A: Allocator> PartialEq for Boxed<'_, T, A> {
#[inline]
fn eq(&self, other: &Self) -> bool {
PartialEq::eq(&**self, &**other)
}
}
impl<T: ?Sized + PartialOrd, A: Allocator> PartialOrd for Boxed<'_, T, A> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
PartialOrd::partial_cmp(&**self, &**other)
}
#[inline]
fn lt(&self, other: &Self) -> bool {
PartialOrd::lt(&**self, &**other)
}
#[inline]
fn le(&self, other: &Self) -> bool {
PartialOrd::le(&**self, &**other)
}
#[inline]
fn gt(&self, other: &Self) -> bool {
PartialOrd::gt(&**self, &**other)
}
#[inline]
fn ge(&self, other: &Self) -> bool {
PartialOrd::ge(&**self, &**other)
}
}
impl<T: ?Sized + Ord, A: Allocator> Ord for Boxed<'_, T, A> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
Ord::cmp(&**self, &**other)
}
}
impl<T: ?Sized + Eq, A: Allocator> Eq for Boxed<'_, T, A> {}
impl<T: ?Sized + Hash, A: Allocator> Hash for Boxed<'_, T, A> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
Hash::hash(&**self, state)
}
}
impl<T: ?Sized + Hasher, A: Allocator> Hasher for Boxed<'_, T, A> {
#[inline]
fn finish(&self) -> u64 {
Hasher::finish(&**self)
}
#[inline]
fn write(&mut self, bytes: &[u8]) {
Hasher::write(&mut **self, bytes)
}
#[inline]
fn write_u8(&mut self, data: u8) {
Hasher::write_u8(&mut **self, data)
}
#[inline]
fn write_u16(&mut self, data: u16) {
Hasher::write_u16(&mut **self, data)
}
#[inline]
fn write_u32(&mut self, data: u32) {
Hasher::write_u32(&mut **self, data)
}
#[inline]
fn write_u64(&mut self, data: u64) {
Hasher::write_u64(&mut **self, data)
}
#[inline]
fn write_u128(&mut self, data: u128) {
Hasher::write_u128(&mut **self, data)
}
#[inline]
fn write_usize(&mut self, data: usize) {
Hasher::write_usize(&mut **self, data)
}
#[inline]
fn write_isize(&mut self, data: isize) {
Hasher::write_isize(&mut **self, data)
}
#[inline]
fn write_i8(&mut self, data: i8) {
Hasher::write_i8(&mut **self, data)
}
#[inline]
fn write_i16(&mut self, data: i16) {
Hasher::write_i16(&mut **self, data)
}
#[inline]
fn write_i32(&mut self, data: i32) {
Hasher::write_i32(&mut **self, data)
}
#[inline]
fn write_i64(&mut self, data: i64) {
Hasher::write_i64(&mut **self, data)
}
#[inline]
fn write_i128(&mut self, data: i128) {
Hasher::write_i128(&mut **self, data)
}
#[cfg(feature = "nightly")]
#[inline]
fn write_str(&mut self, s: &str) {
Hasher::write_str(&mut **self, s)
}
#[cfg(feature = "nightly")]
#[inline]
fn write_length_prefix(&mut self, len: usize) {
Hasher::write_length_prefix(&mut **self, len)
}
}
unsafe impl<'a, T: ?Sized + Allocator, A: Allocator> Allocator for Boxed<'a, T, A> {
unsafe fn alloc_buf<F>(&self, layout: Layout, f: F) -> Result<NonNull<[u8]>>
where
F: FnOnce(NonNull<[u8]>) -> Result<()>,
{
Allocator::alloc_buf(&**self, layout, f)
}
unsafe fn free_buf(&self, ptr: NonNull<[u8]>, layout: Layout) {
Allocator::free_buf(&**self, ptr, layout)
}
}
unsafe impl<T: ?Sized + Allocator + Pool, A: Allocator> Pool for Boxed<'_, T, A> {}
impl<'a, T: Any, A: Allocator> Boxed<'a, T, A> {
pub fn to_any(self) -> Boxed<'a, dyn Any, A> {
let this = ManuallyDrop::new(self);
let any: &dyn Any = this.as_ref();
Boxed {
ptr: any.into(),
layout: this.layout,
alloc: this.alloc,
mark: PhantomData,
}
}
}
impl<'a, A: Allocator> Boxed<'a, dyn Any + 'a, A> {
pub fn downcast<T: Any>(self) -> core::result::Result<Boxed<'a, T, A>, Self> {
if <dyn Any>::is::<T>(self.as_ref()) {
let this = ManuallyDrop::new(self);
Ok(Boxed {
ptr: this.ptr.cast::<T>(),
layout: this.layout,
alloc: this.alloc,
mark: PhantomData,
})
} else {
Err(self)
}
}
}
impl<'a, T: ?Sized, A: Allocator> Boxed<'a, T, A> {
pub fn upcast<U: ?Sized>(self, f: impl FnOnce(&T) -> &U) -> core::result::Result<Boxed<'a, U, A>, Self> {
let ptr: NonNull<U> = f(self.as_ref()).into();
if !ptr::eq(ptr.cast::<u8>().as_ptr(), self.ptr.cast::<u8>().as_ptr()) {
return Err(self);
}
let this = ManuallyDrop::new(self);
Ok(Boxed {
ptr,
layout: this.layout,
alloc: this.alloc,
mark: PhantomData,
})
}
}
impl<'a, T: ?Sized, A: Allocator> Boxed<'a, T, A> {
pub fn layout(&self) -> Layout {
self.layout
}
pub(crate) fn allocator(&self) -> &'a A {
self.alloc
}
}
#[cfg(test)]
mod test {
extern crate std;
use std::format;
use std::string::ToString;
use super::Boxed;
use std::collections::HashSet;
#[test]
fn test_drop() {
struct Foo;
static mut DROP: usize = 0;
impl Drop for Foo {
fn drop(&mut self) {
unsafe {
DROP += 1;
}
}
}
unsafe {
DROP = 0;
}
let mut foo = Foo;
{
let _ = unsafe { Boxed::from_raw(&mut foo) };
}
unsafe {
assert_eq!(1, DROP);
}
}
#[test]
fn test_hash() {
let mut val = 100;
let mut other = 100;
let pbox2 = unsafe { Boxed::from_raw(&mut other) };
let pbox1 = unsafe { Boxed::from_raw(&mut val) };
let mut set = HashSet::new();
let ret = set.insert(pbox1);
assert!(ret);
let ret = set.insert(pbox2);
assert!(!ret);
}
#[test]
fn test_deref() {
let mut val = 1;
{
let mut pbox = unsafe { Boxed::from_raw(&mut val) };
assert_eq!(1, *pbox);
*pbox = 100;
let s = format!("val = {}", pbox);
assert_eq!(s, "val = 100".to_string());
let s = format!("val = {:?}", pbox);
assert_eq!(s, "val = 100".to_string());
}
assert_eq!(val, 100);
}
#[test]
fn test_any() {
static mut D: i32 = 0;
#[derive(Debug)]
struct Foo(i32);
trait Bar {}
impl Bar for Foo {}
impl Drop for Foo {
fn drop(&mut self) {
unsafe {
D = 1;
}
}
}
{
let v32 = Boxed::new(Foo(100)).unwrap();
let any = v32.to_any();
let v32 = any.downcast::<Foo>().unwrap();
assert_eq!(v32.0, 100);
let x = v32.upcast::<dyn Bar>(|val| val).unwrap();
let v32 = unsafe { x.cast_unchecked::<Foo>() };
assert_eq!(v32.0, 100);
}
unsafe {
assert_eq!(D, 1);
}
}
}