#![warn(missing_docs)]
extern crate self as fortify;
mod lower;
pub use fortify_derive::*;
pub use lower::*;
use std::future::Future;
use std::mem::{transmute_copy, ManuallyDrop, MaybeUninit};
use std::pin::Pin;
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
pub struct Fortify<T> {
value: ManuallyDrop<T>,
data_raw: *mut (),
data_drop_fn: unsafe fn(*mut ()),
}
impl<T> Fortify<T> {
pub fn new(value: T) -> Self {
Self {
value: ManuallyDrop::new(value),
data_raw: std::ptr::null_mut(),
data_drop_fn: drop_nop,
}
}
}
impl<'a, T: Refers<'a>> Fortify<T> {
pub fn new_dep<O: 'a, C>(owned: O, cons: C) -> Self
where
C: for<'b> FnOnce(&'b mut O) -> Lowered<'b, T>,
{
Self::new_box_dep(Box::new(owned), cons)
}
pub fn new_box_dep<O: 'a, C>(owned: Box<O>, cons: C) -> Self
where
C: for<'b> FnOnce(&'b mut O) -> Lowered<'b, T>,
{
let owned = Box::into_raw(owned);
let value = cons(unsafe { &mut *owned });
Self {
value: ManuallyDrop::new(value.value),
data_raw: owned as *mut (),
data_drop_fn: drop_box_from_raw::<O>,
}
}
pub fn new_async<C, F>(cons: C) -> Self
where
C: 'a + FnOnce(FortifyYielder<T>) -> F,
F: 'a + Future<Output = ()>,
{
let waker = nop_waker();
let mut cx = Context::from_waker(&waker);
let mut data = FortifyYielderData {
value: MaybeUninit::uninit(),
tracker: FortifyYielderTracker {
cx_ptr: &cx as *const Context as *const (),
has_awaited: false,
},
};
let future = Box::into_raw(Box::new(cons(FortifyYielder(&mut data))));
match Future::poll(unsafe { Pin::new_unchecked(&mut *future) }, &mut cx) {
Poll::Ready(_) => {
unsafe { drop_box_from_raw::<F>(future as *mut ()) };
panic!("Future must await on FortifyYielder::yield_")
}
Poll::Pending => {
if data.tracker.has_awaited {
Self {
value: ManuallyDrop::new(unsafe {
transmute_copy(data.value.assume_init_ref())
}),
data_raw: future as *mut (),
data_drop_fn: drop_box_from_raw::<F>,
}
} else {
unsafe { drop_box_from_raw::<F>(future as *mut ()) };
panic!("Future may only await on FortifyYielder::yield_")
}
}
}
}
}
impl<'a, T: Lower<'a, Target = T>> Fortify<&'a T> {
pub fn new_box_ref(value: Box<T>) -> Self {
Self::new_box_dep(value, |inner| Lowered::new(&*inner))
}
}
impl<'a, T: 'a> Fortify<&'a mut T> {
pub fn new_box_mut(value: Box<T>) -> Self {
Self::new_box_dep(value, |inner| Lowered::new(inner))
}
}
impl<'a, T: Lower<'a>> Fortify<T> {
#[allow(clippy::should_implement_trait)]
pub fn borrow(&'a self) -> &'a <T as Lower<'a>>::Target {
let value = &*self.value;
unsafe { transmute_copy(&value) }
}
}
impl<T: for<'a> Lower<'a>> Fortify<T> {
pub fn with_ref<'a, F, R>(&'a self, f: F) -> R
where
F: for<'b> FnOnce(&'a <T as Lower<'b>>::Target) -> R,
{
let value = &*self.value;
f(unsafe { transmute_copy(&value) })
}
pub fn with_mut<'a, F, R>(&'a mut self, f: F) -> R
where
F: for<'b> FnOnce(&'a mut <T as Lower<'b>>::Target) -> R,
{
let value = &mut *self.value;
f(unsafe { transmute_copy(&value) })
}
pub fn with_inner<F, R>(self, f: F) -> R
where
for<'a> <T as Lower<'a>>::Target: Sized,
F: for<'a> FnOnce(<T as Lower<'a>>::Target) -> R,
{
self.split(|inner| (Lowered::new(()), f(Lowered::unwrap(inner))))
.1
}
}
impl<'a, T: 'a> Fortify<T> {
pub fn split<F, N, R>(mut self, f: F) -> (Fortify<N>, R)
where
N: Refers<'a>,
F: for<'b> FnOnce(Lowered<'b, T>) -> (Lowered<'b, N>, R),
{
let value = unsafe { ManuallyDrop::take(&mut self.value) };
let data_raw = self.data_raw;
let data_drop_fn = self.data_drop_fn;
std::mem::forget(self);
let (value, res) = f(Lowered {
value,
marker: std::marker::PhantomData,
});
(
Fortify {
value: ManuallyDrop::new(value.value),
data_raw,
data_drop_fn,
},
res,
)
}
pub fn map<F, N>(self, f: F) -> Fortify<N>
where
N: Refers<'a>,
F: for<'b> FnOnce(Lowered<'b, T>) -> Lowered<'b, N>,
{
self.split(|inner| (f(inner), ())).0
}
}
pub trait Refers<'a> {}
impl<'a, T: Lower<'a, Target = T>> Refers<'a> for T {}
impl<T> From<T> for Fortify<T> {
fn from(value: T) -> Self {
Fortify::new(value)
}
}
impl<'a, T: Lower<'a, Target = T>> From<Box<T>> for Fortify<&'a T> {
fn from(value: Box<T>) -> Self {
Fortify::new_box_ref(value)
}
}
impl<'a, T> From<Box<T>> for Fortify<&'a mut T> {
fn from(value: Box<T>) -> Self {
Fortify::new_box_mut(value)
}
}
impl<T: Default> Default for Fortify<T> {
fn default() -> Self {
Fortify::new(T::default())
}
}
impl<T> std::fmt::Debug for Fortify<T>
where
for<'a> T: Lower<'a>,
for<'a> <T as Lower<'a>>::Target: std::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.borrow().fmt(f)
}
}
impl<T> std::fmt::Display for Fortify<T>
where
for<'a> T: Lower<'a>,
for<'a> <T as Lower<'a>>::Target: std::fmt::Display,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.borrow().fmt(f)
}
}
unsafe impl<'a, T: Lower<'a>> Lower<'a> for Fortify<T>
where
T::Target: Sized,
{
type Target = Fortify<T::Target>;
}
impl<T: Iterator> Iterator for Fortify<T>
where
for<'a> T: Lower<'a>,
for<'a> <T as Lower<'a>>::Target: Iterator<Item = T::Item>,
{
type Item = T::Item;
fn next(&mut self) -> Option<Self::Item> {
self.with_mut(|inner| inner.next())
}
}
impl<T> Drop for Fortify<T> {
fn drop(&mut self) {
unsafe {
ManuallyDrop::drop(&mut self.value);
(self.data_drop_fn)(self.data_raw);
}
}
}
unsafe fn drop_nop(_: *mut ()) {
}
unsafe fn drop_box_from_raw<T>(raw: *mut ()) {
std::ptr::drop_in_place(raw as *mut T);
let layout = std::alloc::Layout::new::<T>();
if layout.size() > 0 {
std::alloc::dealloc(raw as *mut u8, layout);
}
}
fn nop_waker() -> Waker {
const VTABLE: &RawWakerVTable = &RawWakerVTable::new(clone, nop, nop, nop);
unsafe fn clone(data: *const ()) -> RawWaker {
RawWaker::new(data, VTABLE)
}
unsafe fn nop(_: *const ()) {}
unsafe { Waker::from_raw(RawWaker::new(std::ptr::null(), VTABLE)) }
}
pub struct FortifyYielder<T>(*mut FortifyYielderData<T>);
impl<T> FortifyYielder<T> {
pub fn yield_(self, value: Lowered<T>) -> impl Future<Output = ()> + '_ {
unsafe {
let target = &mut *self.0;
target.value.write(value.value);
FortifyYielderFuture(&mut target.tracker)
}
}
}
struct FortifyYielderData<T> {
value: MaybeUninit<T>,
tracker: FortifyYielderTracker,
}
struct FortifyYielderTracker {
cx_ptr: *const (),
has_awaited: bool,
}
struct FortifyYielderFuture(*mut FortifyYielderTracker);
impl Future for FortifyYielderFuture {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
unsafe {
let tracker = &mut *self.as_ref().0;
if tracker.cx_ptr == (cx as *const Context as *const ()) {
tracker.has_awaited = true;
}
}
Poll::Pending
}
}
#[macro_export]
macro_rules! fortify {
(@INNER $y:ident , yield $res:expr ;) => {
$y.yield_(Lowered::new($res)).await
};
(@INNER $y:ident , $st:stmt ; $($t:tt)*) => {
{ $st fortify!(@INNER $y , $($t)*) }
};
($($t:tt)*) => {
$crate::Fortify::new_async(move |y| async move {
fortify!(@INNER y , $($t)*)
})
};
}
#[cfg(test)]
mod tests;