use crate::polyfill::{FinishNonExhaustive, UncheckedUnwrap};
use core::cell::Cell;
use core::fmt::{Debug, Formatter};
use core::ops::Deref;
enum Void {}
pub struct OnceCell<T>(Cell<Option<T>>);
impl<T> OnceCell<T> {
#[inline]
pub const fn new() -> OnceCell<T> {
OnceCell(Cell::new(None))
}
#[inline]
pub fn get(&self) -> Option<&T> {
unsafe { (&*self.0.as_ptr()).as_ref() }
}
#[inline]
#[cfg_attr(has_track_caller, track_caller)]
pub fn get_or_init(&self, func: impl FnOnce() -> T) -> &'_ T {
self.get()
.unwrap_or_else(|| match self.initialize::<Void, _>(|| Ok(func())) {
Ok(value) => value,
Err(e) => match e {},
})
}
#[inline]
#[cfg_attr(has_track_caller, track_caller)]
pub fn get_or_try_init<E>(&self, func: impl FnOnce() -> Result<T, E>) -> Result<&'_ T, E> {
match self.get() {
Some(existing) => Ok(existing),
None => self.initialize::<E, _>(func),
}
}
#[cold]
#[cfg_attr(has_track_caller, track_caller)]
fn initialize<E, F: FnOnce () -> Result<T, E>>(&self, func: F) -> Result<&T, E> {
debug_assert!(self.get().is_none(), "only call when uninitialized");
let res = func()?;
self.set(res)
.unwrap_or_else(|_| panic!("double initialization"));
unsafe { Ok(self.get().polyfill_unwrap_unchecked()) }
}
#[inline]
pub fn set(&self, value: T) -> Result<(), T> {
if self.get().is_none() {
unsafe { core::ptr::write(self.0.as_ptr() as *mut T, value) }
Ok(())
} else {
Err(value)
}
}
#[inline]
pub fn take(&mut self) -> Option<T> {
self.0.get_mut().take()
}
pub fn into_inner(self) -> Option<T> {
self.0.into_inner()
}
}
unsafe impl<T: Send> Send for OnceCell<T> {}
impl<T> From<T> for OnceCell<T> {
fn from(value: T) -> Self {
OnceCell(Cell::new(Some(value)))
}
}
impl<T: Clone> Clone for OnceCell<T> {
fn clone(&self) -> Self {
match self.get() {
Some(existing) => OnceCell::from(existing.clone()),
None => OnceCell::new(),
}
}
}
impl<T> Default for OnceCell<T> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<T: Debug> Debug for OnceCell<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("OnceCell").field(&self.get()).finish()
}
}
enum LazyState<T, F> {
Initialized(T),
Poisoned,
InProgress,
Uninitialized(F),
}
impl<T, F> Debug for LazyState<T, F> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
LazyState::Initialized(_) => f.write_str("Initialized"),
LazyState::Poisoned => f.write_str("Poisoned"),
LazyState::InProgress => f.write_str("InProgress"),
LazyState::Uninitialized(_) => f.write_str("Uninitialized"),
}
}
}
pub struct LazyCell<T, F = fn() -> T> {
state: Cell<LazyState<T, F>>,
}
impl<T, F> LazyCell<T, F> {
#[inline]
pub const fn new(func: F) -> Self {
LazyCell {
state: Cell::new(LazyState::Uninitialized(func)),
}
}
#[inline]
pub fn get(this: &Self) -> Option<&T> {
match unsafe { &*this.state.as_ptr() } {
LazyState::Initialized(ref result) => Some(result),
LazyState::Poisoned | LazyState::InProgress | LazyState::Uninitialized(_) => None,
}
}
}
impl<T, F: FnOnce() -> T> LazyCell<T, F> {
#[inline]
#[cfg_attr(has_track_caller, track_caller)]
pub fn force(this: &Self) -> &T {
Self::get(this).unwrap_or_else(|| Self::force_fallback(this))
}
#[cold]
#[cfg_attr(has_track_caller, track_caller)]
fn force_fallback(this: &Self) -> &T {
let func = {
let state = unsafe { &mut *this.state.as_ptr() };
match *state {
LazyState::Initialized(_) => {
return unsafe { Self::get(this).polyfill_unwrap_unchecked() };
}
LazyState::Poisoned => panic_poisoned(),
LazyState::InProgress => panic_recurisve_init(),
LazyState::Uninitialized(_) => {
match core::mem::replace(state, LazyState::InProgress) {
LazyState::Uninitialized(func) => func,
_ => {
unsafe { core::hint::unreachable_unchecked() }
}
}
}
}
};
struct PanicGuard<'a, T, F>(&'a Cell<LazyState<T, F>>);
impl<T, F> Drop for PanicGuard<'_, T, F> {
fn drop(&mut self) {
self.0.set(LazyState::Poisoned);
}
}
let guard = PanicGuard(&this.state);
let result = func();
core::mem::forget(guard);
{
let state = unsafe { &mut *this.state.as_ptr() };
match &*state {
LazyState::Uninitialized(_) | LazyState::Poisoned | LazyState::Initialized(_) => {
if cfg!(debug_assertions) {
unreachable!("bad state {:?}", &*state)
} else {
unsafe { core::hint::unreachable_unchecked() }
}
}
LazyState::InProgress => {
*state = LazyState::Initialized(result);
}
}
}
unsafe { Self::get(this).polyfill_unwrap_unchecked() }
}
}
impl<T, F: FnOnce() -> T> Deref for LazyCell<T, F> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
Self::force(self)
}
}
impl<T: Debug, F> Debug for LazyCell<T, F> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_struct("LazyCell")
.field("value", &Self::get(self))
.polyfill_finish_non_exhaustive()
}
}
unsafe impl<T: Send, F: Send> Send for LazyCell<T, F> {}
#[cold]
#[inline(never)]
#[cfg_attr(has_track_caller, track_caller)]
fn panic_poisoned() -> ! {
panic!("LazyCell instance has previously been poisoned")
}
#[cold]
#[inline(never)]
#[cfg_attr(has_track_caller, track_caller)]
fn panic_recurisve_init() -> ! {
panic!("LazyCell detected recursive initialization")
}