#[cfg(feature = "parking_lot")]
extern crate parking_lot;
#[macro_use]
pub mod unsync {
use std::{
ops::Deref,
cell::UnsafeCell,
};
#[derive(Debug, Default)]
pub struct OnceCell<T> {
inner: UnsafeCell<Option<T>>,
}
impl<T> OnceCell<T> {
pub const INIT: OnceCell<T> = OnceCell { inner: UnsafeCell::new(None) };
pub fn new() -> OnceCell<T> {
OnceCell { inner: UnsafeCell::new(None) }
}
pub fn get(&self) -> Option<&T> {
unsafe { &*self.inner.get() }.as_ref()
}
pub fn set(&self, value: T) -> Result<(), T> {
let slot = unsafe { &mut *self.inner.get() };
if slot.is_some() {
return Err(value);
}
*slot = Some(value);
Ok(())
}
pub fn get_or_init<F: FnOnce() -> T>(&self, f: F) -> &T {
enum Void {}
match self.get_or_try_init(|| Ok::<T, Void>(f())) {
Ok(val) => val,
Err(void) => match void {},
}
}
pub fn get_or_try_init<F: FnOnce() -> Result<T, E>, E>(&self, f: F) -> Result<&T, E> {
if let Some(val) = self.get() {
return Ok(val);
}
let val = f()?;
assert!(self.set(val).is_ok(), "reentrant init");
Ok(self.get().unwrap())
}
}
#[derive(Debug)]
pub struct Lazy<T, F: Fn() -> T = fn() -> T> {
#[doc(hidden)]
pub __cell: OnceCell<T>,
#[doc(hidden)]
pub __init: F,
}
impl<T, F: Fn() -> T> Lazy<T, F> {
pub fn new(init: F) -> Lazy<T, F> {
Lazy {
__cell: OnceCell::INIT,
__init: init,
}
}
pub fn force(this: &Lazy<T, F>) -> &T {
this.__cell.get_or_init(|| (this.__init)())
}
}
impl<T, F: Fn() -> T> Deref for Lazy<T, F> {
type Target = T;
fn deref(&self) -> &T {
Lazy::force(self)
}
}
#[macro_export]
macro_rules! unsync_lazy {
($($block:tt)*) => {
$crate::unsync::Lazy {
__cell: $crate::unsync::OnceCell::INIT,
__init: || { $($block)* },
}
};
}
}
#[macro_use]
pub mod sync {
use std::{
ptr,
sync::{atomic::{AtomicPtr, Ordering::Relaxed}},
};
#[cfg(feature = "parking_lot")]
use parking_lot::{Once, ONCE_INIT};
#[cfg(not(feature = "parking_lot"))]
use std::sync::{Once, ONCE_INIT};
#[derive(Debug)]
pub struct OnceCell<T> {
inner: AtomicPtr<T>,
once: Once,
}
unsafe impl<T: Sync + Send> Sync for OnceCell<T> {}
unsafe impl<T: Send> Send for OnceCell<T> {}
impl<T> OnceCell<T> {
pub const INIT: OnceCell<T> = OnceCell {
inner: AtomicPtr::new(ptr::null_mut()),
once: ONCE_INIT,
};
pub fn new() -> OnceCell<T> {
OnceCell {
inner: AtomicPtr::new(ptr::null_mut()),
once: Once::new(),
}
}
pub fn get(&self) -> Option<&T> {
let ptr = self.inner.load(Relaxed);
unsafe { ptr.as_ref() }
}
pub fn set(&self, value: T) -> Result<(), T> {
let mut value = Some(value);
self.once.call_once(|| {
let value = value.take().unwrap();
unsafe { self.set_inner(value) }
});
match value {
None => Ok(()),
Some(value) => Err(value)
}
}
pub fn get_or_init<F: FnOnce() -> T>(&self, f: F) -> &T {
self.once.call_once(|| {
let value = f();
unsafe { self.set_inner(value); }
});
self.get().unwrap()
}
unsafe fn set_inner(&self, value: T) {
let ptr = Box::into_raw(Box::new(value));
self.inner.store(ptr, Relaxed);
}
}
impl<T> Drop for OnceCell<T> {
fn drop(&mut self) {
let ptr = self.inner.load(Relaxed);
if !ptr.is_null() {
drop(unsafe { Box::from_raw(ptr) })
}
}
}
#[derive(Debug)]
pub struct Lazy<T, F: Fn() -> T = fn() -> T> {
#[doc(hidden)]
pub __cell: OnceCell<T>,
#[doc(hidden)]
pub __init: F,
}
impl<T, F: Fn() -> T> Lazy<T, F> {
pub fn new(f: F) -> Lazy<T, F> {
Lazy {
__cell: OnceCell::new(),
__init: f,
}
}
pub fn force(this: &Lazy<T, F>) -> &T {
this.__cell.get_or_init(|| (this.__init)())
}
}
impl<T, F: Fn() -> T> ::std::ops::Deref for Lazy<T, F> {
type Target = T;
fn deref(&self) -> &T {
Lazy::force(self)
}
}
#[macro_export]
macro_rules! sync_lazy {
($($block:tt)*) => {
$crate::sync::Lazy {
__cell: $crate::sync::OnceCell::INIT,
__init: || { $($block)* },
}
};
}
}