use core::mem;
use core::mem::MaybeUninit;
use core::pin::Pin;
use core::ptr;
use crate::drop_flag::DropFlag;
use crate::move_ref::MoveRef;
use crate::new;
use crate::new::New;
use crate::new::TryNew;
#[cfg(doc)]
use {
crate::{move_ref::DerefMove, moveit, slot},
alloc::boxed::Box,
};
pub struct Slot<'frame, T> {
ptr: &'frame mut MaybeUninit<T>,
drop_flag: DropFlag<'frame>,
}
impl<'frame, T> Slot<'frame, T> {
pub unsafe fn new_unchecked(
ptr: &'frame mut MaybeUninit<T>,
drop_flag: DropFlag<'frame>,
) -> Self {
Self { ptr, drop_flag }
}
pub fn put(self, val: T) -> MoveRef<'frame, T> {
unsafe {
Pin::into_inner_unchecked(self.pin(val))
}
}
pub fn pin(self, val: T) -> Pin<MoveRef<'frame, T>> {
self.emplace(new::of(val))
}
pub fn emplace<N: New<Output = T>>(self, new: N) -> Pin<MoveRef<'frame, T>> {
match self.try_emplace(new) {
Ok(x) => x,
Err(e) => match e {},
}
}
pub fn try_emplace<N: TryNew<Output = T>>(
self,
new: N,
) -> Result<Pin<MoveRef<'frame, T>>, N::Error> {
unsafe {
self.drop_flag.inc();
new.try_new(Pin::new_unchecked(self.ptr))?;
Ok(MoveRef::into_pin(MoveRef::new_unchecked(
self.ptr.assume_init_mut(),
self.drop_flag,
)))
}
}
pub fn into_pinned(self) -> Slot<'frame, Pin<T>> {
unsafe { self.cast() }
}
pub unsafe fn cast<U>(self) -> Slot<'frame, U> {
debug_assert!(mem::size_of::<T>() >= mem::size_of::<U>());
debug_assert!(mem::align_of::<T>() >= mem::align_of::<U>());
Slot {
ptr: &mut *self.ptr.as_mut_ptr().cast(),
drop_flag: self.drop_flag,
}
}
}
impl<'frame, T> Slot<'frame, Pin<T>> {
pub fn into_unpinned(self) -> Slot<'frame, T> {
unsafe { self.cast() }
}
}
pub struct DroppingSlot<'frame, T> {
ptr: &'frame mut MaybeUninit<T>,
drop_flag: DropFlag<'frame>,
}
impl<'frame, T> DroppingSlot<'frame, T> {
pub unsafe fn new_unchecked(
ptr: &'frame mut MaybeUninit<T>,
drop_flag: DropFlag<'frame>,
) -> Self {
drop_flag.inc();
Self { ptr, drop_flag }
}
pub fn put(self, val: T) -> (&'frame mut T, DropFlag<'frame>) {
({ self.ptr }.write(val), self.drop_flag)
}
pub unsafe fn pin(self, val: T) -> (Pin<&'frame mut T>, DropFlag<'frame>) {
self.emplace(new::of(val))
}
pub unsafe fn emplace<N: New<Output = T>>(
self,
new: N,
) -> (Pin<&'frame mut T>, DropFlag<'frame>) {
match self.try_emplace(new) {
Ok((x, d)) => (x, d),
Err(e) => match e {},
}
}
pub unsafe fn try_emplace<N: TryNew<Output = T>>(
self,
new: N,
) -> Result<(Pin<&'frame mut T>, DropFlag<'frame>), N::Error> {
self.drop_flag.inc();
new.try_new(Pin::new_unchecked(self.ptr))?;
Ok((
Pin::new_unchecked(self.ptr.assume_init_mut()),
self.drop_flag,
))
}
}
#[doc(hidden)]
#[allow(missing_docs)]
pub mod __macro {
use super::*;
use crate::drop_flag::QuietFlag;
pub use core;
pub struct SlotDropper<T> {
val: MaybeUninit<T>,
drop_flag: QuietFlag,
}
impl<T> SlotDropper<T> {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self {
val: MaybeUninit::uninit(),
drop_flag: QuietFlag::new(),
}
}
pub fn new_unchecked_hygine_hack(&mut self) -> DroppingSlot<T> {
unsafe {
DroppingSlot::new_unchecked(&mut self.val, self.drop_flag.flag())
}
}
}
impl<T> Drop for SlotDropper<T> {
fn drop(&mut self) {
if self.drop_flag.flag().is_dead() {
unsafe { ptr::drop_in_place(self.val.assume_init_mut()) }
}
}
}
pub fn new_unchecked_hygine_hack<'frame, T>(
ptr: &'frame mut MaybeUninit<T>,
drop_flag: DropFlag<'frame>,
) -> Slot<'frame, T> {
unsafe { Slot::new_unchecked(ptr, drop_flag) }
}
}
#[macro_export]
macro_rules! slot {
() => {
$crate::slot::__macro::new_unchecked_hygine_hack(
&mut $crate::slot::__macro::core::mem::MaybeUninit::uninit(),
$crate::drop_flag::TrappedFlag::new().flag(),
)
};
(#[dropping]) => {
$crate::slot::__macro::SlotDropper::new().new_unchecked_hygine_hack()
};
($($name:ident $(: $ty:ty)?),* $(,)*) => {$(
let mut uninit = $crate::slot::__macro::core::mem::MaybeUninit::<
$crate::slot!(@tyof $($ty)?)
>::uninit();let trap = $crate::drop_flag::TrappedFlag::new();
let $name = $crate::slot::__macro::new_unchecked_hygine_hack(
&mut uninit,
trap.flag()
);
)*};
(#[dropping] $($name:ident $(: $ty:ty)?),* $(,)*) => {$(
let mut uninit = $crate::slot::__macro::SlotDropper::<
$crate::slot!(@tyof $($ty)?)
>::new();
#[allow(unsafe_code, unused_unsafe)]
let $name = uninit.new_unchecked_hygine_hack();
)*};
(@tyof) => {_};
(@tyof $ty:ty) => {$ty};
}