use core::cell::UnsafeCell;
use core::mem::MaybeUninit;
use core::ptr::slice_from_raw_parts;
#[repr(transparent)]
pub struct UnsafeSyncCell<T>(UnsafeCell<MaybeUninit<T>>);
impl<T> Drop for UnsafeSyncCell<T> {
fn drop(&mut self) {
if !UnsafeSyncCell::check_zeroed(self.0.get_mut().as_mut_ptr()) {
unsafe { self.0.get_mut().assume_init_drop() }
}
}
}
unsafe impl<T: Sync> Sync for UnsafeSyncCell<T> {}
impl<T: Default> Default for UnsafeSyncCell<T> {
fn default() -> UnsafeSyncCell<T> {
UnsafeSyncCell::new(Default::default())
}
}
impl<T> From<T> for UnsafeSyncCell<T> {
fn from(t: T) -> UnsafeSyncCell<T> {
UnsafeSyncCell::new(t)
}
}
impl<T: Clone> Clone for UnsafeSyncCell<T> {
fn clone(&self) -> Self {
if Self::check_zeroed(self.as_mut_ptr() as _) {
UnsafeSyncCell::new_zeroed()
} else {
unsafe { Self::from(self.inner_ref().clone()) }
}
}
}
impl<T> UnsafeSyncCell<T> {
#[inline]
pub(crate) fn new(value: T) -> Self {
Self(UnsafeCell::new(MaybeUninit::new(value)))
}
#[inline]
pub(crate) fn new_zeroed() -> Self {
Self(UnsafeCell::new(MaybeUninit::zeroed()))
}
#[inline]
pub fn check_zeroed(ptr: *const T) -> bool {
unsafe {
(*slice_from_raw_parts(ptr as *const u8, size_of::<T>()))
.iter()
.all(|x| *x == 0)
}
}
#[inline]
pub unsafe fn take_inner(&self) -> T {
unsafe { core::mem::replace(&mut *self.0.get(), MaybeUninit::<T>::zeroed()).assume_init() }
}
#[inline]
pub unsafe fn inner_duplicate(&self) -> T {
unsafe { (*self.0.get()).assume_init_read() }
}
#[inline]
pub unsafe fn inner_ref<'a>(&self) -> &'a T {
unsafe { (*self.0.get()).assume_init_ref() }
}
#[inline]
#[allow(clippy::mut_from_ref)]
pub unsafe fn inner_ref_mut<'a>(&self) -> &'a mut T {
unsafe { (*self.0.get()).assume_init_mut() }
}
#[inline]
pub fn as_mut_ptr(&self) -> *mut T {
unsafe { (*self.0.get()).as_mut_ptr() }
}
}