use crate::soul::SEVERED;
use crate::sync::{self, AtomicU32, Ordering};
use core::{
borrow::Borrow,
fmt,
mem::forget,
ops::Deref,
ptr::NonNull,
};
pub struct Lich<T: ?Sized> {
pub(crate) value: NonNull<T>,
pub(crate) count: NonNull<AtomicU32>,
}
unsafe impl<T: ?Sized> Send for Lich<T> where for<'a> &'a T: Send {}
unsafe impl<T: ?Sized> Sync for Lich<T> where for<'a> &'a T: Sync {}
impl<T: ?Sized> Lich<T> {
#[must_use]
pub fn bindings(&self) -> usize {
let raw = self.count_ref().load(Ordering::Relaxed);
raw.wrapping_add(1).saturating_sub(1) as _
}
pub fn redeem(self) -> usize {
let count = unsafe { self.redeem_unchecked() };
forget(self);
count
}
unsafe fn redeem_unchecked(&self) -> usize {
let count = self.count_ref();
let remain = decrement(count);
if remain == 0 {
sync::wake_all(count);
}
remain as _
}
fn count_ref(&self) -> &AtomicU32 {
unsafe { self.count.as_ref() }
}
fn data_ref(&self) -> &T {
unsafe { self.value.as_ref() }
}
}
impl<T: ?Sized> Clone for Lich<T> {
fn clone(&self) -> Self {
increment(self.count_ref());
Self {
value: self.value,
count: self.count,
}
}
}
impl<T: ?Sized> Borrow<T> for Lich<T> {
fn borrow(&self) -> &T {
self.data_ref()
}
}
impl<T: ?Sized> Deref for Lich<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.data_ref()
}
}
impl<T: ?Sized> AsRef<T> for Lich<T> {
fn as_ref(&self) -> &T {
self.data_ref()
}
}
impl<T: fmt::Debug + ?Sized> fmt::Debug for Lich<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Lich")
.field("value", &self.data_ref())
.field("bindings", &self.bindings())
.finish()
}
}
impl<T: fmt::Display + ?Sized> fmt::Display for Lich<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self.data_ref(), f)
}
}
impl<T: ?Sized> fmt::Pointer for Lich<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&self.value.as_ptr(), f)
}
}
impl<T: PartialEq + ?Sized> PartialEq for Lich<T> {
fn eq(&self, other: &Self) -> bool {
self.data_ref() == other.data_ref()
}
}
impl<T: Eq + ?Sized> Eq for Lich<T> {}
impl<T: PartialOrd + ?Sized> PartialOrd for Lich<T> {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
PartialOrd::partial_cmp(self.data_ref(), other.data_ref())
}
}
impl<T: Ord + ?Sized> Ord for Lich<T> {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
Ord::cmp(self.data_ref(), other.data_ref())
}
}
impl<T: core::hash::Hash + ?Sized> core::hash::Hash for Lich<T> {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.data_ref().hash(state);
}
}
impl<T: ?Sized> Drop for Lich<T> {
fn drop(&mut self) {
unsafe { self.redeem_unchecked() };
}
}
pub(crate) fn increment(count: &AtomicU32) -> u32 {
let result = count.fetch_update(Ordering::Acquire, Ordering::Relaxed, |value| {
if value < SEVERED - 1 {
Some(value + 1)
} else {
None
}
});
match result {
Ok(value) => value,
Err(SEVERED) => unreachable!("bind called on a severed Soul"),
Err(_) => panic!("maximum number of `Lich`es reached"),
}
}
pub(crate) fn decrement(count: &AtomicU32) -> u32 {
match count.fetch_sub(1, Ordering::Release) {
0 | SEVERED => unreachable!(),
value => value - 1,
}
}