use crate::{Pointer, SmartPointer};
use alloc::boxed::Box;
use atomic_traits::{
Atomic, NumOps,
fetch::{Add, Sub},
};
use core::fmt;
use core::marker::PhantomData;
use core::ops::Deref;
use core::ptr::NonNull;
use core::sync::atomic::{
Ordering::{Acquire, Relaxed, Release},
fence,
};
pub unsafe trait IrcItem<Tag = (), P = Box<Self>>: Sized + Send + Sync
where
<Self::Counter as Atomic>::Type: From<u8> + Into<usize> + PartialEq,
P: Pointer<Target = Self>,
{
type Counter: NumOps;
fn counter(&self) -> &Self::Counter;
#[inline(always)]
fn on_drop(_this: P) {}
#[inline]
fn strong_count(&self) -> usize {
self.counter().load(Relaxed).into()
}
}
pub struct Irc<T, Tag = (), P = Box<T>>
where
T: IrcItem<Tag, P>,
P: Pointer<Target = T>,
{
inner: NonNull<T>,
_phan: PhantomData<fn(&Tag, &P)>,
}
impl<T, Tag, P> Irc<T, Tag, P>
where
T: IrcItem<Tag, P>,
P: SmartPointer<Target = T>,
{
#[inline]
pub fn new(inner: T) -> Self {
Self::from(P::new(inner))
}
}
impl<T: IrcItem<Tag, P>, Tag, P> SmartPointer for Irc<T, Tag, P>
where
T: IrcItem<Tag, P>,
P: SmartPointer<Target = T>,
{
#[inline]
fn new(inner: T) -> Self {
Irc::new(inner)
}
}
impl<T, Tag, P> From<P> for Irc<T, Tag, P>
where
T: IrcItem<Tag, P>,
P: Pointer<Target = T>,
{
#[inline]
fn from(inner: P) -> Self {
inner.as_ref().counter().store(1u8.into(), Relaxed);
Self {
inner: unsafe { NonNull::new_unchecked(inner.into_raw() as *mut T) },
_phan: Default::default(),
}
}
}
impl<T, Tag, P> Irc<T, Tag, P>
where
T: IrcItem<Tag, P>,
P: Pointer<Target = T>,
{
#[inline(always)]
fn get_inner(&self) -> &T {
unsafe { self.inner.as_ref() }
}
#[inline]
pub fn ptr_eq(this: &Self, other: &Self) -> bool {
this.inner == other.inner
}
#[inline]
pub unsafe fn with_unchecked(inner: P) -> Self {
inner.as_ref().counter().fetch_add(1u8.into(), Relaxed);
Self {
inner: unsafe { NonNull::new_unchecked(inner.into_raw() as *mut T) },
_phan: Default::default(),
}
}
#[inline]
pub fn is_unique(&self) -> bool {
self.counter().load(Acquire) == 1u8.into()
}
#[inline]
pub fn get_mut(this: &mut Self) -> Option<&mut T> {
if this.is_unique() { Some(unsafe { this.inner.as_mut() }) } else { None }
}
}
impl<T, Tag, P> Irc<T, Tag, P>
where
T: IrcItem<Tag, P> + Clone,
P: SmartPointer<Target = T>,
{
#[inline]
pub fn make_mut(this: &mut Self) -> &mut T {
if !this.is_unique() {
let cloned_item = this.get_inner().clone();
let mut new_irc = Self::new(cloned_item);
core::mem::swap(this, &mut new_irc);
}
unsafe { this.inner.as_mut() }
}
}
impl<T, Tag, P> Deref for Irc<T, Tag, P>
where
T: IrcItem<Tag, P>,
P: Pointer<Target = T>,
{
type Target = T;
#[inline(always)]
fn deref(&self) -> &Self::Target {
self.get_inner()
}
}
impl<T, Tag, P> AsRef<T> for Irc<T, Tag, P>
where
T: IrcItem<Tag, P>,
P: Pointer<Target = T>,
{
#[inline(always)]
fn as_ref(&self) -> &T {
self.get_inner()
}
}
unsafe impl<T, Tag, P> Send for Irc<T, Tag, P>
where
T: IrcItem<Tag, P>,
P: Pointer<Target = T>,
{
}
unsafe impl<T, Tag, P> Sync for Irc<T, Tag, P>
where
T: IrcItem<Tag, P>,
P: Pointer<Target = T>,
{
}
impl<T, Tag, P> Clone for Irc<T, Tag, P>
where
T: IrcItem<Tag, P>,
P: Pointer<Target = T>,
{
#[inline]
fn clone(&self) -> Self {
self.get_inner().counter().fetch_add(1u8.into(), Relaxed);
Self { inner: self.inner, _phan: Default::default() }
}
}
impl<T, Tag, P> Drop for Irc<T, Tag, P>
where
T: IrcItem<Tag, P>,
P: Pointer<Target = T>,
{
#[inline]
fn drop(&mut self) {
let p = self.inner.as_ptr();
unsafe {
if (*p).counter().fetch_sub(1u8.into(), Release) == 1u8.into() {
fence(Acquire);
let inner = P::from_raw(p);
IrcItem::<Tag, P>::on_drop(inner);
}
}
}
}
impl<T, Tag, P> fmt::Debug for Irc<T, Tag, P>
where
T: IrcItem<Tag, P> + fmt::Debug,
P: Pointer<Target = T>,
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.get_inner().fmt(f)
}
}
impl<T, Tag, P> fmt::Display for Irc<T, Tag, P>
where
T: IrcItem<Tag, P> + fmt::Display,
P: Pointer<Target = T>,
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.get_inner().fmt(f)
}
}
impl<T: IrcItem<Tag, P>, Tag, P> Pointer for Irc<T, Tag, P>
where
T: IrcItem<Tag, P>,
P: Pointer<Target = T>,
{
type Target = T;
#[inline]
fn as_ref(&self) -> &Self::Target {
unsafe { self.inner.as_ref() }
}
#[inline]
unsafe fn from_raw(p: *const Self::Target) -> Self {
Self {
inner: unsafe { NonNull::new_unchecked(p as *mut Self::Target) },
_phan: Default::default(),
}
}
#[inline]
fn into_raw(self) -> *const Self::Target {
let p = self.inner.as_ptr();
core::mem::forget(self);
p
}
}