use crate::*;
use std::{
alloc::{handle_alloc_error, AllocError, Allocator, Global, Layout},
ptr::NonNull,
rc::Rc,
sync::Arc,
};
pub trait SmartPtr: Sized {
type Content: ?Sized;
type Rebind<U: ?Sized>: SmartPtr<Content = U>;
unsafe fn from_alloc(p: *mut Self::Content) -> Self;
unsafe fn rebind<U>(self) -> Self::Rebind<U>
where
U: Pointee<Metadata = <Self::Content as Pointee>::Metadata> + ?Sized;
}
impl<T: ?Sized> SmartPtr for Box<T> {
type Content = T;
type Rebind<U: ?Sized> = Box<U>;
unsafe fn from_alloc(p: *mut Self::Content) -> Self {
Self::from_raw(p)
}
unsafe fn rebind<U>(self) -> Self::Rebind<U>
where
U: Pointee<Metadata = <Self::Content as Pointee>::Metadata> + ?Sized,
{
let (ptr, metadata) = Box::into_raw(self).to_raw_parts();
Box::from_raw(std::ptr::from_raw_parts_mut(ptr, metadata))
}
}
impl<T: ?Sized> SmartPtr for Rc<T> {
type Content = T;
type Rebind<U: ?Sized> = Rc<U>;
unsafe fn from_alloc(p: *mut Self::Content) -> Self {
Box::from_alloc(p).into()
}
unsafe fn rebind<U>(self) -> Self::Rebind<U>
where
U: Pointee<Metadata = <Self::Content as Pointee>::Metadata> + ?Sized,
{
let (ptr, metadata) = Rc::into_raw(self).to_raw_parts();
Rc::from_raw(std::ptr::from_raw_parts(ptr, metadata))
}
}
impl<T: ?Sized> SmartPtr for Arc<T> {
type Content = T;
type Rebind<U: ?Sized> = Arc<U>;
unsafe fn from_alloc(p: *mut Self::Content) -> Self {
Box::from_alloc(p).into()
}
unsafe fn rebind<U>(self) -> Self::Rebind<U>
where
U: Pointee<Metadata = <Self::Content as Pointee>::Metadata> + ?Sized,
{
let (ptr, metadata) = Arc::into_raw(self).to_raw_parts();
Arc::from_raw(std::ptr::from_raw_parts(ptr, metadata))
}
}
type RebindPtr<P, U> = <P as SmartPtr>::Rebind<U>;
mod sealed {
pub trait Sealed {}
}
use sealed::Sealed;
impl<P: SmartPtr> Sealed for P {}
pub trait NewUninit: Sealed + SmartPtr
where
Self::Content: MaybeUninitProject,
{
fn new_uninit_unsized(
metadata: <Self::Content as Pointee>::Metadata,
) -> RebindPtr<Self, <Self::Content as MaybeUninitProject>::Target> {
unsafe {
RebindPtr::<Self, <Self::Content as MaybeUninitProject>::Target>::from_alloc(
alloc_with_metadata::<Self::Content>(metadata),
)
}
}
fn new_zeroed_unsized(
metadata: <Self::Content as Pointee>::Metadata,
) -> RebindPtr<Self, <Self::Content as MaybeUninitProject>::Target> {
unsafe {
RebindPtr::<Self, <Self::Content as MaybeUninitProject>::Target>::from_alloc(
zeroed_with_metadata::<Self::Content>(metadata),
)
}
}
unsafe fn new_unsized_with(
metadata: <Self::Content as Pointee>::Metadata,
f: impl FnOnce(&mut <Self::Content as MaybeUninitProject>::Target),
) -> Self
where
Self::Rebind<<Self::Content as MaybeUninitProject>::Target>:
SmartPtr<Rebind<Self::Content> = Self>,
{
let ptr = alloc_with_metadata::<Self::Content>(metadata);
f(&mut *ptr);
RebindPtr::<Self, <Self::Content as MaybeUninitProject>::Target>::from_alloc(ptr)
.rebind::<Self::Content>()
}
}
impl<P: SmartPtr> NewUninit for P where P::Content: MaybeUninitProject {}
unsafe fn alloc_with_metadata_impl<T: ?Sized + MaybeUninitProject>(
metadata: <T as Pointee>::Metadata,
alloc: impl FnOnce(Layout) -> Result<NonNull<[u8]>, AllocError>,
) -> *mut T::Target {
let null_ptr: *mut T = std::ptr::from_raw_parts_mut(std::ptr::null_mut::<()>(), metadata);
let layout = Layout::for_value_raw(null_ptr);
if let Ok(ptr) = alloc(layout) {
std::ptr::from_raw_parts_mut(ptr.as_mut_ptr() as *mut (), metadata)
} else {
handle_alloc_error(layout)
}
}
unsafe fn alloc_with_metadata<T: ?Sized + MaybeUninitProject>(
metadata: <T as Pointee>::Metadata,
) -> *mut T::Target {
alloc_with_metadata_impl::<T>(metadata, |layout| Global.allocate(layout))
}
unsafe fn zeroed_with_metadata<T: ?Sized + MaybeUninitProject>(
metadata: <T as Pointee>::Metadata,
) -> *mut T::Target {
alloc_with_metadata_impl::<T>(metadata, |layout| Global.allocate_zeroed(layout))
}
pub trait AssumeInit<T: ?Sized + MaybeUninitProject>:
Sealed + SmartPtr<Content = T::Target>
{
unsafe fn assume_init(self) -> RebindPtr<Self, T> {
self.rebind()
}
}
impl<T: ?Sized + MaybeUninitProject, P: SmartPtr<Content = T::Target>> AssumeInit<T> for P {}
#[cfg(test)]
mod test {
use crate::*;
use std::{rc::Rc, sync::Arc};
#[test]
fn sized() {
let s: Box<u32> = unsafe { Box::<u32>::new_zeroed_unsized(()).assume_init() };
assert_eq!(s.as_ref(), &0);
}
#[derive(MaybeUninitProject)]
#[repr(transparent)]
struct SliceWrapper([u8]);
#[test]
fn slice_wrapper() {
let s: Box<SliceWrapper> =
unsafe { Box::<SliceWrapper>::new_zeroed_unsized(64).assume_init() };
assert_eq!(&s.0, &[0u8; 64]);
let s: Rc<SliceWrapper> =
unsafe { Rc::<SliceWrapper>::new_zeroed_unsized(64).assume_init() };
assert_eq!(&s.0, &[0u8; 64]);
let s: Arc<SliceWrapper> =
unsafe { Arc::<SliceWrapper>::new_zeroed_unsized(64).assume_init() };
assert_eq!(&s.0, &[0u8; 64]);
}
}
#[cfg(test)]
mod bench {
use crate::*;
use test::{black_box, Bencher};
const SLICE_LEN: usize = 10000;
#[bench]
fn dst_uninit(b: &mut Bencher) {
b.iter(|| unsafe {
let b: Box<UnsizedSlice<(), u32>> =
Box::<UnsizedSlice<(), u32>>::new_uninit_unsized(SLICE_LEN).assume_init();
black_box(b)
})
}
#[bench]
fn dst_uninit_write(b: &mut Bencher) {
b.iter(|| unsafe {
let b = Box::<UnsizedSlice<(), u32>>::new_unsized_with(SLICE_LEN, |slice| {
slice.slice.write_copy_of_slice(&[0; SLICE_LEN]);
});
black_box(b)
})
}
#[bench]
fn dst_zeroed(b: &mut Bencher) {
b.iter(|| unsafe {
let b: Box<UnsizedSlice<(), u32>> =
Box::<UnsizedSlice<(), u32>>::new_zeroed_unsized(SLICE_LEN).assume_init();
black_box(b)
})
}
#[bench]
fn box_from(b: &mut Bencher) {
b.iter(|| unsafe {
let b: Box<[u32]> = Box::new_zeroed_slice(SLICE_LEN).assume_init();
black_box(b)
})
}
#[bench]
fn vec_into(b: &mut Bencher) {
b.iter(|| {
let b = vec![0u32; SLICE_LEN].into_boxed_slice();
black_box(b)
})
}
}