#![no_std]
#![doc(
html_logo_url = "https://ardaku.github.io/mm/logo.svg",
html_favicon_url = "https://ardaku.github.io/mm/icon.svg",
html_root_url = "https://docs.rs/pure_cell"
)]
#![warn(
anonymous_parameters,
missing_copy_implementations,
missing_debug_implementations,
missing_docs,
nonstandard_style,
rust_2018_idioms,
single_use_lifetimes,
trivial_casts,
trivial_numeric_casts,
unreachable_pub,
unused_extern_crates,
unused_qualifications,
variant_size_differences
)]
use core::{cell::UnsafeCell, mem::ManuallyDrop};
#[derive(Debug)]
pub struct PureCell<T> {
value: UnsafeCell<ManuallyDrop<T>>,
}
impl<T> PureCell<T> {
pub const fn new(value: T) -> Self {
Self {
value: UnsafeCell::new(ManuallyDrop::new(value)),
}
}
pub fn get(&mut self) -> &mut T {
self.value.get_mut()
}
pub unsafe fn with<R, F>(&self, f: F) -> R
where
F: FnOnce(&mut ManuallyDrop<T>) -> R,
{
f(&mut *self.value.get())
}
}
impl<T> Drop for PureCell<T> {
fn drop(&mut self) {
unsafe {
let _ = ManuallyDrop::take(&mut *self.value.get());
}
}
}
#[macro_export]
macro_rules! pure_cell {
(
$pure_cell:expr,
$input:expr,
|$state:ident: $ty:ty, $args:ident: $argty:ty| -> $ret:ty $block:block
) => ({
#[inline(always)]
const fn const_fn(mut $state: $ty, mut $args: $argty) -> ($ty, $ret) {
let (output, state) = ($block, $state);
(state, output)
}
fn wrapper_fn(
state: &mut core::mem::ManuallyDrop<$ty>,
input: $argty,
) -> $ret {
unsafe {
let (new, out) = const_fn(
core::mem::ManuallyDrop::take(state),
input,
);
*state = core::mem::ManuallyDrop::new(new);
out
}
}
unsafe {
$pure_cell.with(move |state| wrapper_fn(state, $input))
}
});
($pure_cell:expr, $input:expr, |$state:ident: $ty:ty, $args:ident: $argty:ty| $block:block) => (
$crate::pure_cell!($pure_cell, $input, |$state: $ty, $args: $argty| -> () $block)
);
}