use core::{
cell::UnsafeCell,
mem::MaybeUninit,
panic::{RefUnwindSafe, UnwindSafe},
};
use crate::{ptr_macros::ptr_to_field, sync::Once};
pub struct OnceLock<T> {
once: Once,
value: UnsafeCell<MaybeUninit<T>>,
}
impl<T> OnceLock<T> {
#[must_use]
pub const unsafe fn init(this: *const Self) -> OnceLock<T> {
let once_ptr = ptr_to_field!(this, once);
OnceLock {
once: unsafe { Once::init(once_ptr) },
value: UnsafeCell::new(MaybeUninit::uninit()),
}
}
#[inline]
pub fn get(&self) -> Option<&T> {
if self.is_initialized() {
Some(unsafe { self.get_unchecked() })
} else {
None
}
}
#[inline]
pub fn get_mut(&mut self) -> Option<&mut T> {
if self.is_initialized() {
Some(unsafe { self.get_unchecked_mut() })
} else {
None
}
}
#[inline]
pub fn set(&self, value: T) -> Result<(), T> {
let mut value = Some(value);
self.initialize(|| unsafe { value.take().unwrap_unchecked() });
value.map_or(Ok(()), Err)
}
#[inline]
pub fn get_or_init<F>(&self, f: F) -> &T
where
F: FnOnce() -> T,
{
self.initialize(f);
unsafe { self.get_unchecked() }
}
#[inline]
pub fn get_mut_or_init<F>(&mut self, f: F) -> &mut T
where
F: FnOnce() -> T,
{
self.initialize(f);
unsafe { self.get_unchecked_mut() }
}
#[inline]
fn is_initialized(&self) -> bool {
self.once.is_completed()
}
#[cold]
fn initialize<F>(&self, f: F)
where
F: FnOnce() -> T,
{
self.once.call_once(|| unsafe {
(*self.value.get()).write(f());
});
}
#[inline]
unsafe fn get_unchecked(&self) -> &T {
debug_assert!(self.is_initialized());
unsafe { (*self.value.get()).assume_init_ref() }
}
#[inline]
unsafe fn get_unchecked_mut(&mut self) -> &mut T {
debug_assert!(self.is_initialized());
unsafe { (*self.value.get()).assume_init_mut() }
}
}
unsafe impl<T: Sync + Send> Sync for OnceLock<T> {}
unsafe impl<T: Send> Send for OnceLock<T> {}
impl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for OnceLock<T> {}
impl<T: UnwindSafe> UnwindSafe for OnceLock<T> {}
impl<T: PartialEq> PartialEq for OnceLock<T> {
#[inline]
fn eq(&self, other: &OnceLock<T>) -> bool {
self.get() == other.get()
}
}
impl<T: Eq> Eq for OnceLock<T> {}
#[macro_export]
macro_rules! once_lock {
($name: ident, $type: ty) => {
static $name: $crate::sync::OnceLock<$type> = {
let ptr = &raw const $name;
unsafe { $crate::sync::OnceLock::init(ptr) }
};
};
}