use std::cell::UnsafeCell;
use std::mem::MaybeUninit;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
pub struct ValueCell<T> {
initialized: AtomicBool,
value: UnsafeCell<MaybeUninit<T>>,
}
unsafe impl<T: Sync + Send> Sync for ValueCell<T> {}
unsafe impl<T: Send> Send for ValueCell<T> {}
impl<T> ValueCell<T> {
pub const fn new() -> Self {
Self {
initialized: AtomicBool::new(false),
value: UnsafeCell::new(MaybeUninit::uninit()),
}
}
pub const fn from_value(value: T) -> Self {
Self {
initialized: AtomicBool::new(true),
value: UnsafeCell::new(MaybeUninit::new(value)),
}
}
pub fn is_initialized(&self) -> bool {
self.initialized.load(Ordering::Acquire)
}
pub fn is_initialized_mut(&mut self) -> bool {
*self.initialized.get_mut()
}
pub fn get(&self) -> Option<&T> {
if self.is_initialized() {
Some(unsafe { self.get_unchecked() })
} else {
None
}
}
pub fn get_mut(&mut self) -> Option<&mut T> {
if self.is_initialized_mut() {
Some(unsafe { self.get_unchecked_mut() })
} else {
None
}
}
pub fn into_inner(mut self) -> Option<T> {
self.take()
}
pub fn take(&mut self) -> Option<T> {
if self.is_initialized_mut() {
*self.initialized.get_mut() = false;
Some(unsafe { self.value.get_mut().assume_init_read() })
} else {
None
}
}
pub unsafe fn set(&self, value: T) -> &T {
debug_assert!(!self.is_initialized());
let value_ptr = self.value.get();
unsafe { value_ptr.write(MaybeUninit::new(value)) };
self.initialized.store(true, Ordering::Release);
unsafe { self.get_unchecked() }
}
pub fn set_mut(&mut self, value: T) -> &mut T {
debug_assert!(!self.is_initialized_mut());
let value = self.value.get_mut().write(value);
*self.initialized.get_mut() = true;
value
}
unsafe fn get_unchecked(&self) -> &T {
debug_assert!(self.is_initialized());
unsafe { (&*self.value.get()).assume_init_ref() }
}
unsafe fn get_unchecked_mut(&mut self) -> &mut T {
debug_assert!(self.is_initialized_mut());
unsafe { (&mut *self.value.get()).assume_init_mut() }
}
}
impl<T> Drop for ValueCell<T> {
fn drop(&mut self) {
if self.is_initialized_mut() {
unsafe { self.value.get_mut().assume_init_drop() };
}
}
}