use core::alloc::Layout;
use core::fmt;
use core::marker::PhantomData;
use core::pin::Pin;
use core::ptr::NonNull;
use crate::container::{Emplace, PinEmplace};
use crate::utils::Void;
pub unsafe trait PinConstruct: Sized {
type Object: ?Sized;
fn layout(&self) -> Layout;
unsafe fn construct(self, slot: Slot) -> NonNull<Self::Object>;
}
pub unsafe trait Construct: PinConstruct {}
unsafe impl<T: PinConstruct> PinConstruct for &'_ mut Option<T> {
type Object = T::Object;
fn layout(&self) -> Layout {
self.as_ref()
.expect("constructor has been consumed")
.layout()
}
unsafe fn construct(self, slot: Slot) -> NonNull<Self::Object> {
self.take()
.expect("constructor has been consumed")
.construct(slot)
}
}
unsafe impl<T: Construct> Construct for &'_ mut Option<T> {}
#[must_use = "slot must be consumed"]
pub struct Slot<'a, T: ?Sized = Void>(NonNull<T>, PhantomData<&'a mut T>);
impl<'a> Slot<'a> {
pub unsafe fn new_unchecked(ptr: NonNull<u8>) -> Self {
Self(ptr.cast(), PhantomData)
}
pub unsafe fn write_unchecked<T>(self, object: T) -> NonNull<T> {
let ptr = self.0.cast::<T>();
debug_assert!(ptr.is_aligned());
ptr.write(object);
ptr
}
pub unsafe fn cast<T>(self) -> Slot<'a, T> {
Slot(self.0.cast(), PhantomData)
}
}
impl<'a, T> Slot<'a, T> {
pub fn write(self, object: T) -> &'a mut Opaque<T> {
unsafe {
Slot::new_unchecked(self.into_raw())
.write_unchecked(Opaque(object))
.as_mut()
}
}
}
impl<'a, T: ?Sized> Slot<'a, T> {
pub fn into_raw(self) -> NonNull<u8> {
self.0.cast()
}
pub(crate) fn as_ptr(&self) -> NonNull<u8> {
self.0.cast()
}
}
impl<T: ?Sized> fmt::Debug for Slot<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
#[repr(transparent)]
pub struct Opaque<T: ?Sized>(T);
impl<T: ?Sized> Opaque<T> {
pub(crate) fn as_mut(&mut self) -> &mut T {
&mut self.0
}
}
pub trait Dynify: Construct {
fn init<C>(self, container: C) -> C::Ptr
where
C: Emplace<Self::Object>,
{
self.try_init(container)
.unwrap_or_else(|_| panic!("failed to initialize"))
}
fn try_init<C>(self, container: C) -> Result<C::Ptr, (Self, C::Err)>
where
C: Emplace<Self::Object>,
{
let mut fallible = FallibleConstructor::new(self);
let handle = unsafe { fallible.handle() };
match container.emplace(handle) {
Ok(p) => {
debug_assert!(fallible.consumed());
core::mem::forget(fallible);
Ok(p)
},
Err(e) => Err((fallible.into_inner(), e)),
}
}
fn init2<P, C1, C2>(self, container1: C1, container2: C2) -> P
where
C1: Emplace<Self::Object, Ptr = P>,
C2: Emplace<Self::Object, Ptr = P>,
{
self.try_init2(container1, container2)
.unwrap_or_else(|_| panic!("failed to initialize"))
}
fn try_init2<P, C1, C2>(self, container1: C1, container2: C2) -> Result<P, (Self, C2::Err)>
where
C1: Emplace<Self::Object, Ptr = P>,
C2: Emplace<Self::Object, Ptr = P>,
{
self.try_init(container1)
.or_else(|(this, _)| this.try_init(container2))
}
#[cfg(feature = "alloc")]
fn boxed(self) -> alloc::boxed::Box<Self::Object> {
self.init(crate::container::Boxed)
}
}
impl<T: Construct> Dynify for T {}
pub trait PinDynify: PinConstruct {
fn pin_init<C>(self, container: C) -> Pin<C::Ptr>
where
C: PinEmplace<Self::Object>,
{
self.try_pin_init(container)
.unwrap_or_else(|_| panic!("failed to initialize"))
}
fn try_pin_init<C>(self, container: C) -> Result<Pin<C::Ptr>, (Self, C::Err)>
where
C: PinEmplace<Self::Object>,
{
let mut fallible = FallibleConstructor::new(self);
let handle = unsafe { fallible.handle() };
match container.pin_emplace(handle) {
Ok(p) => {
debug_assert!(fallible.consumed());
core::mem::forget(fallible);
Ok(p)
},
Err(e) => Err((fallible.into_inner(), e)),
}
}
fn pin_init2<P, C1, C2>(self, container1: C1, container2: C2) -> Pin<P>
where
C1: PinEmplace<Self::Object, Ptr = P>,
C2: PinEmplace<Self::Object, Ptr = P>,
{
self.try_pin_init2(container1, container2)
.unwrap_or_else(|_| panic!("failed to initialize"))
}
fn try_pin_init2<P, C1, C2>(
self,
container1: C1,
container2: C2,
) -> Result<Pin<P>, (Self, C2::Err)>
where
C1: PinEmplace<Self::Object, Ptr = P>,
C2: PinEmplace<Self::Object, Ptr = P>,
{
self.try_pin_init(container1)
.or_else(|(this, _)| this.try_pin_init(container2))
}
#[cfg(feature = "alloc")]
fn pin_boxed(self) -> Pin<alloc::boxed::Box<Self::Object>> {
self.pin_init(crate::container::Boxed)
}
}
impl<T: PinConstruct> PinDynify for T {}
struct FallibleConstructor<T>(Option<T>);
impl<T> FallibleConstructor<T> {
pub fn new(constructor: T) -> Self {
Self(Some(constructor))
}
pub fn consumed(&self) -> bool {
self.0.is_none()
}
pub fn into_inner(self) -> T {
debug_assert!(!self.consumed());
unwrap_unchecked(self.0)
}
pub unsafe fn handle(&mut self) -> FallibleHandle<'_, T> {
debug_assert!(!self.consumed());
FallibleHandle(&mut self.0)
}
}
struct FallibleHandle<'a, T>(&'a mut Option<T>);
unsafe impl<T: PinConstruct> PinConstruct for FallibleHandle<'_, T> {
type Object = T::Object;
fn layout(&self) -> Layout {
unwrap_unchecked(self.0.as_ref()).layout()
}
unsafe fn construct(self, slot: Slot) -> NonNull<Self::Object> {
unwrap_unchecked(self.0.take()).construct(slot)
}
}
unsafe impl<T: Construct> Construct for FallibleHandle<'_, T> {}
fn unwrap_unchecked<U>(opt: Option<U>) -> U {
match opt {
Some(t) => t,
None => unsafe { core::hint::unreachable_unchecked() },
}
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
#[path = "constructor_tests.rs"]
mod tests;