use std::fmt;
use crate::cell::UnsafeCell;
use crate::init::Init;
pub struct Storage<T> {
item: UnsafeCell<Option<T>>,
init: Init
}
impl<T> Storage<T> {
#[cfg(not(loom))]
pub const fn new() -> Storage<T> {
Storage {
item: UnsafeCell::new(None),
init: Init::new()
}
}
#[cfg(loom)]
pub fn new() -> Storage<T> {
Storage {
item: UnsafeCell::new(None),
init: Init::new()
}
}
}
impl<T> Default for Storage<T> {
fn default() -> Self {
Storage::new()
}
}
impl<T: Send + Sync> Storage<T> {
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
}
#[inline]
pub fn try_get(&self) -> Option<&T> {
if !self.init.has_completed() {
return None
}
unsafe {
self.item.with(|ptr| (*ptr).as_ref())
}
}
#[inline]
pub fn get(&self) -> &T {
self.try_get()
.expect("storage::get(): called get() before set()")
}
#[inline]
pub fn get_or_set<F: FnOnce() -> T>(&self, from: F) -> &T {
if let Some(value) = self.try_get() {
value
} else {
self.set(from());
self.get()
}
}
pub fn try_get_mut(&mut self) -> Option<&mut T> {
self.item.get_mut().as_mut()
}
pub fn into_inner(self) -> Option<T> {
self.item.into_inner()
}
pub fn map<U: Send + Sync, F: FnOnce(T) -> U>(self, f: F) -> Storage<U> {
self.into_inner()
.map(|v| Storage::from(f(v)))
.unwrap_or_else(|| Storage::new())
}
}
unsafe impl<T: Send + Sync> Sync for Storage<T> { }
unsafe impl<T: Send + Sync> Send for Storage<T> { }
impl<T: fmt::Debug + Send + Sync> fmt::Debug for Storage<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self.try_get() {
Some(object) => object.fmt(f),
None => write!(f, "[uninitialized storage]")
}
}
}
impl<T: Send + Sync> From<T> for Storage<T> {
fn from(value: T) -> Storage<T> {
let storage = Storage::new();
assert!(storage.set(value));
storage
}
}
impl<T: Clone + Send + Sync> Clone for Storage<T> {
fn clone(&self) -> Storage<T> {
match self.try_get() {
Some(val) => Storage::from(val.clone()),
None => Storage::new()
}
}
}