#![doc(html_root_url = "https://docs.rs/singleton-cell/0.3.1")]
#![no_std]
use core::cell::UnsafeCell;
use core::marker::PhantomData;
use core::mem;
pub use singleton_trait::{Singleton, Exists, Erased};
pub mod internal {
pub use paste;
}
#[repr(transparent)]
pub struct SCell<Key: ?Sized, T: ?Sized> {
_phantom: PhantomData<fn(Key)>,
inner: UnsafeCell<T>,
}
pub type SingletonCell<Key, T> = SCell<Key, T>;
unsafe impl<Key, T: Send> Send for SCell<Key, T> {}
unsafe impl<Key, T: Send + Sync> Sync for SCell<Key, T> {}
impl<Key: ?Sized, T: ?Sized> SCell<Key, T> {
#[inline(always)]
pub fn from_mut(t: &mut T) -> &mut Self {
unsafe { &mut *(t as *mut T as *mut Self) }
}
#[inline(always)]
pub fn get_mut(&mut self) -> &mut T {
self.inner.get_mut()
}
#[inline(always)]
pub fn as_ptr(&self) -> *mut T {
self.inner.get()
}
}
impl<Key: ?Sized, T> SCell<Key, T> {
#[inline(always)]
pub fn new(t: T) -> Self {
SCell {
inner: UnsafeCell::new(t),
_phantom: PhantomData,
}
}
#[inline(always)]
pub fn into_inner(self) -> T {
self.inner.into_inner()
}
}
impl<Key: ?Sized, T> SCell<Key, [T]> {
#[inline(always)]
pub fn as_slice_of_cells(&self) -> &[SCell<Key, T>] {
unsafe { &*(self.as_ptr() as *mut [SCell<Key, T>]) }
}
}
impl<Key: Singleton + ?Sized, T: ?Sized> SCell<Key, T> {
#[inline(always)]
pub fn borrow<'a>(&'a self, _: impl Exists<&'a Key>) -> &'a T
where
Key: 'a,
{
unsafe { &*self.inner.get() }
}
#[allow(clippy::mut_from_ref)] #[inline(always)]
pub fn borrow_mut<'a>(&'a self, _: impl Exists<&'a mut Key>) -> &'a mut T
where
Key: 'a,
{
unsafe { &mut *self.inner.get() }
}
}
impl<Key: Singleton + ?Sized, T> SCell<Key, T> {
#[inline(always)]
pub fn replace<'a>(&'a self, value: T, token: impl Exists<&'a mut Key>) -> T
where
Key: 'a,
{
mem::replace(self.borrow_mut(token), value)
}
#[inline(always)]
pub fn take<'a>(&'a self, token: impl Exists<&'a mut Key>) -> T
where
Key: 'a,
T: Default,
{
mem::take(self.borrow_mut(token))
}
}
pub struct Token<'brand>(PhantomData<fn(&'brand ()) -> &'brand ()>);
unsafe impl<'brand> Singleton for Token<'brand> {}
unsafe impl<'brand> Send for Token<'brand> {}
unsafe impl<'brand> Sync for Token<'brand> {}
#[inline(always)]
pub fn with_token<R>(f: impl for<'brand> FnOnce(Token<'brand>) -> R) -> R {
let token = Token(PhantomData);
f(token)
}
#[macro_export]
macro_rules! new_singleton {
( $pub:vis $Name:ident ) => {
$crate::internal::paste::paste! {
$pub use [< __ $Name _help__ >]::$Name;
#[allow(nonstandard_style)]
mod [< __ $Name _help__ >] {
pub
struct $Name(());
impl $Name {
pub
fn new () -> Option<Self> {
let first_run = {
static ONCE: ::core::sync::atomic::AtomicBool =
::core::sync::atomic::AtomicBool::new(false);
&ONCE
};
if first_run
.compare_exchange(false, true,
::core::sync::atomic::Ordering::Relaxed,
::core::sync::atomic::Ordering::Relaxed)
.is_ok()
{
Some( $Name(()) )
} else {
None
}
}
#[inline(always)]
pub
fn new_erased() -> Option<$crate::Erased<Self>> {
Self::new().map(|x| $crate::Erased::new(x))
}
}
unsafe
impl $crate::Singleton for $Name {}
}
}
};
}
#[cfg(test)]
mod tests {
extern crate alloc;
extern crate std;
use crate as singleton_cell;
use super::*;
use alloc::string::String;
#[test]
fn ghost_trivial() {
let mut i = String::new();
with_token(|mut tok| {
let i = SCell::from_mut(&mut i);
i.get_mut().push_str("A");
i.borrow_mut(&mut tok).push_str("B");
let first = i.borrow(&tok).clone();
i.borrow_mut(&mut tok).push_str("C");
let last = i.borrow(&tok);
assert_eq!("AB", &*first);
assert_eq!("ABC", &*last);
});
assert_eq!("ABC", &*i);
}
#[inline(never)]
fn write_to<Key: Singleton>(sc: &SCell<Key, String>, k: &mut Key) {
sc.borrow_mut(k).push_str("!");
}
#[test]
fn ghost_read_after_write() {
let result = with_token(|mut tok| {
let mut s = String::from("A");
let s = SCell::from_mut(&mut s);
let borrow = &*s;
write_to(&*s, &mut tok);
write_to(&*s, &mut tok);
borrow.borrow(&tok).clone()
});
assert_eq!("A!!", &*result);
}
#[test]
fn local_type_round_trip() {
let local = ();
let mut tok = {
struct Local<'a>(&'a ());
unsafe impl<'a> Singleton for Local<'a> {}
Local(&local)
};
let mut s = String::from("A");
let s_mut = &mut s;
let s_cell = SCell::from_mut(s_mut);
let s_cell2 = &*s_cell;
write_to(s_cell2, &mut tok);
assert_eq!("A!", s_cell.borrow_mut(&mut tok));
write_to(s_cell2, &mut tok);
assert_eq!("A!!", s_cell.borrow(&tok));
assert_eq!("A!!", s_cell.get_mut());
s_cell.get_mut().push_str("C");
assert_eq!("A!!C", s_mut);
}
#[test]
fn global_singleton_unique() {
new_singleton!(pub A);
let _ = A::new();
assert!(A::new().is_none(), "Second call to A::new() should be None");
}
#[test]
fn global_singleton_distinct() {
new_singleton!(pub A);
new_singleton!(pub B);
let a: Option<A> = A::new();
let b: Option<B> = B::new();
assert!(a.is_some(), "A should be Some");
assert!(b.is_some(), "B should be Some");
}
}