use std::fmt;
use crate::shim::cell::UnsafeCell;
use crate::init::Init;
pub struct InitCell<T> {
item: UnsafeCell<Option<T>>,
init: Init
}
impl<T> Default for InitCell<T> {
fn default() -> Self {
InitCell::new()
}
}
impl<T> InitCell<T> {
#[cfg(not(loom))]
pub const fn new() -> InitCell<T> {
InitCell {
item: UnsafeCell::new(None),
init: Init::new()
}
}
#[cfg(loom)]
pub fn new() -> InitCell<T> {
InitCell {
item: UnsafeCell::new(None),
init: Init::new()
}
}
pub fn set(&self, value: T) -> bool {
if self.init.needed() {
unsafe { self.item.with_mut(|ptr| *ptr = Some(value)); }
self.init.mark_complete();
return true;
}
false
}
pub fn reset(&mut self) {
*self = Self::new();
}
#[inline]
pub fn get_or_init<F: FnOnce() -> T>(&self, f: F) -> &T {
if let Some(value) = self.try_get() {
value
} else {
self.set(f());
self.try_get().expect("cell::get_or_init(): set() => get() ok")
}
}
#[inline]
pub fn wait(&self) -> &T {
self.init.wait_until_complete();
self.try_get().expect("cell::wait(): broken (init await complete w/o value)")
}
#[inline]
pub fn try_get(&self) -> Option<&T> {
if self.init.has_completed() {
unsafe { self.item.with(|ptr| (*ptr).as_ref()) }
} else {
None
}
}
pub fn try_get_mut(&mut self) -> Option<&mut T> {
self.item.get_mut().as_mut()
}
#[inline]
pub fn get(&self) -> &T {
self.try_get().expect("cell::get(): called get() before set()")
}
pub fn take(&mut self) -> Option<T> {
std::mem::replace(self, Self::new()).into_inner()
}
pub fn into_inner(self) -> Option<T> {
self.item.into_inner()
}
pub fn update<F: FnOnce(T) -> T>(&mut self, f: F) {
self.take().map(|v| self.set(f(v)));
}
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> InitCell<U> {
self.into_inner().map_or_else(InitCell::new, |v| InitCell::from(f(v)))
}
}
unsafe impl<T: Send> Send for InitCell<T> { }
unsafe impl<T: Send + Sync> Sync for InitCell<T> { }
impl<T: fmt::Debug> fmt::Debug for InitCell<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self.try_get() {
Some(object) => object.fmt(f),
None => write!(f, "[uninitialized cell]")
}
}
}
impl<T> From<T> for InitCell<T> {
fn from(value: T) -> InitCell<T> {
let cell = InitCell::new();
assert!(cell.set(value));
cell
}
}
impl<T: Clone> Clone for InitCell<T> {
fn clone(&self) -> InitCell<T> {
match self.try_get() {
Some(val) => InitCell::from(val.clone()),
None => InitCell::new()
}
}
}