use std::{
fmt, mem, ops,
ptr::NonNull,
sync::atomic::{AtomicUsize, Ordering},
};
#[derive(Debug)]
pub(super) struct LinearcInner<T: ?Sized> {
pub(super) ref_count: AtomicUsize,
pub(super) data: T,
}
pub struct Linearc<T: ?Sized> {
ptr: NonNull<LinearcInner<T>>,
}
unsafe impl<T: ?Sized> Send for Linearc<T> {}
unsafe impl<T: ?Sized> Sync for Linearc<T> {}
impl<T: ?Sized> Linearc<T> {
#[inline]
pub(super) fn from_inner(inner: Box<LinearcInner<T>>) -> Self {
Self {
ptr: unsafe { NonNull::new_unchecked(Box::into_raw(inner)) },
}
}
#[inline]
pub fn clone(arc: &Self) -> Self {
arc.clone()
}
fn into_box(arc: Self) -> Option<Box<LinearcInner<T>>> {
let count = unsafe { arc.ptr.as_ref() }
.ref_count
.fetch_sub(1, Ordering::AcqRel);
if count == 1 {
let inner = unsafe { Box::from_raw(arc.ptr.as_ptr()) };
mem::forget(arc);
Some(inner)
} else {
mem::forget(arc);
None
}
}
#[inline]
pub fn drop_last(arc: Self) -> bool {
Linearc::into_box(arc).is_some()
}
}
impl<T> Linearc<T> {
#[inline]
pub fn new(data: T) -> Self {
Self::from_inner(Box::new(LinearcInner {
ref_count: AtomicUsize::new(1),
data,
}))
}
pub fn into_inner(arc: Self) -> Option<T> {
Linearc::into_box(arc).map(|inner| inner.data)
}
}
impl<T: ?Sized> Clone for Linearc<T> {
fn clone(&self) -> Self {
unsafe { self.ptr.as_ref() }
.ref_count
.fetch_add(1, Ordering::Release);
Self { ptr: self.ptr }
}
}
impl<T: ?Sized> Drop for Linearc<T> {
fn drop(&mut self) {
let count = unsafe { self.ptr.as_ref() }
.ref_count
.fetch_sub(1, Ordering::AcqRel);
if count == 1 {
let _ = unsafe { Box::from_raw(self.ptr.as_ptr()) };
}
}
}
impl<T: ?Sized> ops::Deref for Linearc<T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
&unsafe { self.ptr.as_ref() }.data
}
}
impl<T: fmt::Debug> fmt::Debug for Linearc<T> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
unsafe { self.ptr.as_ref() }.fmt(formatter)
}
}