use super::LeasedMut;
use core::{
any::{Any, TypeId},
borrow::Borrow,
fmt,
ops::Deref,
panic::{Location, RefUnwindSafe, UnwindSafe},
pin::Pin,
ptr::NonNull,
};
use std::ops::DerefMut;
pub trait AsIrc<T: ?Sized + IntrusivelyCounted> {
fn as_irc(&self) -> Irc<T>;
}
pub unsafe trait IntrusivelyCounted {
type Inner: IrcBoxed;
fn irc_box(&self) -> &IrcBox<Self::Inner>;
}
pub unsafe trait IrcBoxed {
fn ref_count(&self) -> usize;
fn acquire(&self, _: Private);
fn release(&self, _: Private) -> Option<fn(NonNull<Self>)>;
}
pub struct Irc<T: ?Sized + IntrusivelyCounted>(NonNull<T>);
impl<T: ?Sized + IntrusivelyCounted> Irc<T> {
pub fn new(value: Pin<LeasedMut<'_, T>>) -> Self {
value.irc_box().inner.acquire(Private(()));
Irc(NonNull::from(&mut **unsafe {
Pin::into_inner_unchecked(value)
}))
}
pub unsafe fn new_unchecked(value: Pin<&mut T>) -> Self {
value.irc_box().acquire(Private(()));
Irc(NonNull::from(unsafe { Pin::into_inner_unchecked(value) }))
}
pub fn get_pin(&self) -> Pin<&T> {
unsafe { Pin::new_unchecked(self.0.as_ref()) }
}
pub fn get_pin_mut(&mut self) -> Option<Pin<&mut T>> {
(self.irc_box().ref_count() == 1)
.then(|| unsafe { Pin::new_unchecked(self.0.as_mut()) })
}
pub const fn into_raw(this: Self) -> NonNull<T> {
let inner = this.0;
core::mem::forget(this);
inner
}
pub const unsafe fn from_raw(inner: NonNull<T>) -> Self {
Irc(inner)
}
pub fn map<F, R>(this: Self, f: F) -> Irc<R>
where
F: FnOnce(&T) -> &R,
R: ?Sized + IntrusivelyCounted,
{
let inner = this.irc_box();
let value = f(&*this);
assert!(
core::ptr::addr_eq(inner, value.irc_box()),
"expected the mapping to yield an Irc with the same IrcBox"
);
let res = Irc(with_provenance(NonNull::from(value), this.0));
core::mem::forget(this);
res
}
pub fn downcast<V>(self) -> Result<Irc<V>, Irc<T>>
where
T: Any,
V: 'static + IntrusivelyCounted,
{
if (*self).type_id() == TypeId::of::<V>() {
let res = Irc(self.0.cast::<V>());
core::mem::forget(self);
Ok(res)
} else {
Err(self)
}
}
}
impl<T: ?Sized + IntrusivelyCounted> Deref for Irc<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { self.0.as_ref() }
}
}
impl<T: ?Sized + IntrusivelyCounted> Clone for Irc<T> {
fn clone(&self) -> Self {
self.irc_box().acquire(Private(()));
Irc(self.0)
}
}
impl<T: ?Sized + IntrusivelyCounted> Borrow<T> for Irc<T> {
fn borrow(&self) -> &T {
self
}
}
impl<T: ?Sized + IntrusivelyCounted> Drop for Irc<T> {
fn drop(&mut self) {
if let Some(recycle) = self.irc_box().release(Private(())) {
recycle(NonNull::from(&self.irc_box().inner));
}
}
}
impl<T: ?Sized + IntrusivelyCounted> Unpin for Irc<T> {}
impl<T: ?Sized + IntrusivelyCounted + fmt::Debug> fmt::Debug for Irc<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
unsafe { self.0.as_ref() }.fmt(f)
}
}
impl<T: ?Sized + IntrusivelyCounted + fmt::Display> fmt::Display for Irc<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
unsafe { self.0.as_ref() }.fmt(f)
}
}
unsafe impl<T: Sync + ?Sized + IntrusivelyCounted> Send for Irc<T> {}
unsafe impl<T: Sync + ?Sized + IntrusivelyCounted> Sync for Irc<T> {}
impl<T: RefUnwindSafe + ?Sized + IntrusivelyCounted> UnwindSafe for Irc<T> {}
pub struct Private(());
fn with_provenance<S, T>(mut target: NonNull<T>, provenance: NonNull<S>) -> NonNull<T>
where
S: ?Sized,
T: ?Sized,
{
let target_thin_ptr = provenance.cast::<u8>().with_addr(target.addr());
let ptr_to_fat_ptr = NonNull::from(&mut target).cast::<NonNull<u8>>();
unsafe {
ptr_to_fat_ptr.write(target_thin_ptr);
}
target
}
#[derive(Debug)]
pub struct IrcBox<T: ?Sized + IrcBoxed> {
loc: &'static Location<'static>,
inner: T,
}
impl<T: IrcBoxed> IrcBox<T> {
#[track_caller]
pub const fn new(inner: T) -> Self {
IrcBox::with_location(inner, Location::caller())
}
pub const fn with_location(inner: T, loc: &'static Location<'static>) -> Self {
IrcBox { loc, inner }
}
pub const fn location(this: &Self) -> &'static Location<'static> {
this.loc
}
}
impl<T: IrcBoxed + Default> Default for IrcBox<T> {
#[track_caller]
fn default() -> Self {
Self::new(T::default())
}
}
impl<T: IrcBoxed> Deref for IrcBox<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T: IrcBoxed> DerefMut for IrcBox<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl<T: ?Sized + IrcBoxed> Drop for IrcBox<T> {
fn drop(&mut self) {
let abort_guard = scopeguard::guard(self.loc, |loc| {
panic!("aborting due to dangling `Irc` created at '{loc}'");
});
match self.inner.ref_count() {
0 => core::mem::forget(abort_guard),
n => panic!(
"dropping the value created at '{}' leaves {} reference(s) dangling",
self.loc, n,
),
}
}
}