use alloc::boxed::Box;
use core::{
borrow, cmp, fmt,
marker::PhantomData,
mem::{self, MaybeUninit},
ops::{Deref, DerefMut},
pin::Pin,
ptr::{self, NonNull},
};
#[cfg(feature = "allocator_api")]
pub(crate) use {alloc::alloc::Global, core::alloc::Allocator};
#[cfg(not(feature = "allocator_api"))]
mod dummy_alloc {
#[derive(Clone)]
pub struct Global;
pub trait Allocator {}
impl Allocator for Global {}
}
#[cfg(not(feature = "allocator_api"))]
pub use dummy_alloc::*;
pub unsafe trait Atomicity: private::Sealed {
#[doc(hidden)]
fn new(v: usize) -> Self;
#[doc(hidden)]
fn get(&self) -> usize;
#[doc(hidden)]
fn inc_relaxed(&self) -> usize;
#[doc(hidden)]
fn set_release(&self, value: usize);
#[doc(hidden)]
fn inc_if_nonzero(&self) -> bool;
#[doc(hidden)]
fn dec(&self) -> usize;
#[doc(hidden)]
fn acquire_fence(&self);
}
struct Header<C> {
strong: C,
weak: C,
drop_fn: unsafe fn(*mut Header<C>, bool),
}
#[repr(C)]
struct Allocation<T, C, A> {
header: Header<C>,
alloc: MaybeUninit<A>,
value: MaybeUninit<T>,
}
pub struct Genrc<'a, T: ?Sized, C: Atomicity, A: Allocator = Global, const UNIQ: bool = false> {
header: ptr::NonNull<Header<C>>,
ptr: ptr::NonNull<T>,
phantom: PhantomData<(&'a (), A, T)>,
}
pub struct Weak<'a, T: ?Sized, C: Atomicity, A: Allocator = Global> {
header: ptr::NonNull<Header<C>>,
ptr: ptr::NonNull<T>,
phantom: PhantomData<(&'a (), A, T)>,
}
impl<'a, T, C: Atomicity, const UNIQ: bool> Genrc<'a, T, C, Global, UNIQ> {
pub fn new_unique(value: T) -> Genrc<'a, T, C, Global, true> {
Genrc::<T, C, Global, true>::new(value)
}
pub fn new(value: T) -> Self {
let initial_strong_count = if UNIQ { 0 } else { 1 };
let b = Box::into_raw(Box::new(Allocation {
header: Header {
strong: C::new(initial_strong_count),
weak: C::new(1),
drop_fn: drop_fn::<T, C, Global>,
},
alloc: MaybeUninit::new(Global),
value: MaybeUninit::new(value),
}));
let h = b as *mut Header<C>;
let v = unsafe { ptr::addr_of!((*b).value) as *mut T };
Genrc {
header: unsafe { ptr::NonNull::new_unchecked(h) },
ptr: unsafe { ptr::NonNull::new_unchecked(v) },
phantom: PhantomData,
}
}
pub fn new_cyclic<F>(data_fn: F) -> Genrc<'a, T, C, Global>
where
F: FnOnce(&Weak<'a, T, C>) -> T,
T: 'a,
{
let b = Box::into_raw(Box::new(Allocation {
header: Header {
strong: C::new(0),
weak: C::new(1),
drop_fn: drop_fn::<T, C, Global>,
},
alloc: MaybeUninit::new(Global),
value: MaybeUninit::uninit(),
}));
let weak = Weak {
header: unsafe { ptr::NonNull::new_unchecked(b as *mut Header<C>) },
ptr: unsafe { ptr::NonNull::new_unchecked(ptr::addr_of!((*b).value) as *mut _) },
phantom: PhantomData,
};
unsafe { ptr::write((*b).value.as_mut_ptr(), data_fn(&weak)) };
let h = weak.header();
let old_strong = h.strong.inc_relaxed();
debug_assert_eq!(old_strong, 0, "No prior strong references should exist");
let strong = Genrc {
header: weak.header,
ptr: weak.ptr,
phantom: PhantomData,
};
mem::forget(weak);
strong
}
pub fn pin(value: T) -> Pin<Self> {
let rc: Self = Self::new(value);
unsafe { Pin::new_unchecked(rc) }
}
}
impl<'a, T: ?Sized, C: Atomicity> Genrc<'a, T, C, Global, false> {
pub fn from_ref(value: &'a T) -> Self {
Genrc::project(
Genrc::<'a, &'a T, C, Global, false>::new(value),
Deref::deref,
)
}
#[cfg(not(feature = "allocator_api"))]
pub fn from_box(value: Box<T>) -> Self
where
T: 'a,
{
Genrc::project(Genrc::<Box<T>, C, Global, false>::new(value), |x| &**x)
}
}
impl<'a, T, C: Atomicity, A: Allocator, const UNIQ: bool> Genrc<'a, T, C, A, UNIQ> {
#[cfg(feature = "allocator_api")]
pub fn new_in(value: T, alloc: A) -> Self
where
A: Allocator,
{
let initial_strong_count = if UNIQ { 0 } else { 1 };
let (b, alloc) = Box::into_raw_with_allocator(Box::new_in(
Allocation {
header: Header {
strong: C::new(initial_strong_count),
weak: C::new(1),
drop_fn: drop_fn::<T, C, A>,
},
alloc: MaybeUninit::uninit(),
value: MaybeUninit::new(value),
},
alloc,
));
unsafe { &mut *b }.alloc.write(alloc);
let h = b as *mut Header<C>;
let v = unsafe { ptr::addr_of!((*b).value) as *mut T };
Genrc {
header: unsafe { ptr::NonNull::new_unchecked(h) },
ptr: unsafe { ptr::NonNull::new_unchecked(v) },
phantom: PhantomData,
}
}
pub fn erase_allocator<'b>(this: Self) -> Genrc<'b, T, C, Global, UNIQ>
where
A: 'b,
'a: 'b,
{
let u = Genrc {
header: this.header,
ptr: this.ptr,
phantom: PhantomData,
};
mem::forget(this); u
}
pub fn allocator(this: &Self) -> &A {
let ptr = this.header.as_ptr() as *const Allocation<(), C, A>;
unsafe { (&*ptr).alloc.assume_init_ref() }
}
}
impl<'a, T: ?Sized, C: Atomicity, A: Allocator> Genrc<'a, T, C, A> {
#[cfg(feature = "allocator_api")]
pub fn from_box(value: Box<T, A>) -> Self
where
A: Clone + 'a,
{
let alloc = Box::allocator(&value).clone();
Genrc::project(Genrc::<Box<T, A>, C, A>::new_in(value, alloc), |x| &**x)
}
}
impl<'a, T: ?Sized, C: Atomicity, A: Allocator> Genrc<'a, T, C, A> {
pub fn cast<U: ?Sized>(this: Genrc<'a, T, C, A>) -> Genrc<'a, U, C, A>
where
T: 'a,
U: 'a,
for<'u> &'u U: From<&'u T>,
{
Genrc::project(this, |x| From::from(x))
}
pub fn ptr_eq<A2: Allocator>(this: &Self, other: &Genrc<T, C, A2>) -> bool {
this.ptr == other.ptr
}
pub fn root_ptr_eq<T2: ?Sized, A2: Allocator>(this: &Self, other: &Genrc<T2, C, A2>) -> bool {
this.header == other.header
}
}
impl<'a, T: ?Sized, C: Atomicity, A: Allocator> Genrc<'a, T, C, A, true> {
pub fn project_mut<'b, U: ?Sized, F: FnOnce(&mut T) -> &mut U>(
mut s: Self,
f: F,
) -> Genrc<'b, U, C, A, true>
where
T: 'a,
U: 'b,
'a: 'b,
{
let u = Genrc {
header: s.header,
ptr: f(&mut *s).into(),
phantom: PhantomData,
};
mem::forget(s);
u
}
pub fn shared(this: Genrc<'a, T, C, A, true>) -> Genrc<'a, T, C, A, false> {
debug_assert_eq!(this.header().strong.get(), 0);
this.header().strong.set_release(1);
let header = this.header;
let ptr = this.ptr;
mem::forget(this);
Genrc {
header,
ptr,
phantom: PhantomData,
}
}
}
impl<'a, T: ?Sized, C: Atomicity> Genrc<'a, T, C, Global, true> {
pub fn from_mut_ref(value: &'a mut T) -> Self {
Genrc::project_mut(
Genrc::<&'a mut T, C, Global, true>::new(value),
DerefMut::deref_mut,
)
}
}
impl<'a, T: ?Sized, C: Atomicity, A: Allocator, const UNIQ: bool> Genrc<'a, T, C, A, UNIQ> {
pub fn project<'u, U: ?Sized, F: FnOnce(&T) -> &U>(
this: Self,
f: F,
) -> Genrc<'u, U, C, A, false> {
let ptr = f(this.ptr()).into();
Self::projected(this, ptr)
}
pub fn try_project<'u, U: ?Sized, F: FnOnce(&T) -> Option<&U>>(
this: Self,
f: F,
) -> Option<Genrc<'u, U, C, A, false>> {
match f(this.ptr()) {
None => None,
Some(u) => {
let ptr = u.into();
Some(Self::projected(this, ptr))
}
}
}
fn projected<'u, U: ?Sized>(this: Self, ptr: NonNull<U>) -> Genrc<'u, U, C, A, false> {
if UNIQ {
debug_assert_eq!(this.header().strong.get(), 0);
this.header().strong.set_release(1);
}
let u = Genrc {
header: this.header,
ptr,
phantom: PhantomData,
};
mem::forget(this);
u
}
pub fn downgrade(this: &Genrc<T, C, A, UNIQ>) -> Weak<'a, T, C, A> {
let h = this.header();
h.weak.inc_relaxed();
Weak {
header: this.header,
ptr: this.ptr,
phantom: PhantomData,
}
}
pub unsafe fn get_mut_unchecked(this: &mut Self) -> &mut T {
unsafe { this.ptr.as_mut() }
}
#[inline]
pub fn get_mut(this: &mut Self) -> Option<&mut T> {
if Genrc::is_unique(this) {
unsafe { Some(Genrc::get_mut_unchecked(this)) }
} else {
None
}
}
#[inline]
fn is_unique(this: &Self) -> bool {
UNIQ || (Genrc::weak_count(this) == 0 && Genrc::strong_count(this) == 1)
}
fn ptr(&self) -> &'a T {
unsafe { self.ptr.as_ref() }
}
fn header(&self) -> &Header<C> {
unsafe { self.header.as_ref() }
}
pub fn strong_count(p: &Self) -> usize {
p.header().strong.get()
}
pub fn weak_count(p: &Self) -> usize {
p.header().weak.get() - 1
}
}
impl<'a, T: ?Sized, C: Atomicity, A: Allocator> Weak<'a, T, C, A> {
pub fn upgrade(&self) -> Option<Genrc<'a, T, C>> {
let h = self.header();
if h.strong.inc_if_nonzero() {
Some(Genrc {
header: self.header,
ptr: self.ptr,
phantom: PhantomData,
})
} else {
None
}
}
pub fn strong_count(&self) -> usize {
self.header().strong.get()
}
pub fn weak_count(&self) -> usize {
self.header().weak.get() - 1
}
pub fn is_dangling(&self) -> bool {
self.strong_count() == 0
}
fn header(&self) -> &Header<C> {
unsafe { self.header.as_ref() }
}
}
impl<'a, T: ?Sized, C: Atomicity, A: Allocator, const UNIQ: bool> AsRef<T>
for Genrc<'a, T, C, A, UNIQ>
{
fn as_ref(&self) -> &T {
self
}
}
impl<'a, T: ?Sized, C: Atomicity, A: Allocator, const UNIQ: bool> borrow::Borrow<T>
for Genrc<'a, T, C, A, UNIQ>
{
fn borrow(&self) -> &T {
self
}
}
impl<'a, T: ?Sized, C: Atomicity, A: Allocator> Clone for Genrc<'a, T, C, A> {
fn clone(&self) -> Self {
let h = self.header();
h.strong.inc_relaxed();
Genrc {
header: self.header,
ptr: self.ptr,
phantom: PhantomData,
}
}
}
impl<'a, T: ?Sized, C: Atomicity, A: Allocator> Clone for Weak<'a, T, C, A> {
fn clone(&self) -> Self {
let h = self.header();
h.weak.inc_relaxed();
Weak {
header: self.header,
ptr: self.ptr,
phantom: PhantomData,
}
}
}
impl<'a, T: Default, C: Atomicity, const UNIQ: bool> Default for Genrc<'a, T, C, Global, UNIQ> {
fn default() -> Self {
Genrc::new(T::default())
}
}
impl<'a, T: ?Sized, C: Atomicity, A: Allocator, const UNIQ: bool> Deref
for Genrc<'a, T, C, A, UNIQ>
{
type Target = T;
fn deref(&self) -> &T {
self.ptr()
}
}
impl<'a, T: 'a + ?Sized, C: Atomicity, A: Allocator> DerefMut for Genrc<'a, T, C, A, true> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { self.ptr.as_mut() }
}
}
impl<'a, T: ?Sized, C: Atomicity, A: Allocator, const UNIQ: bool> Drop
for Genrc<'a, T, C, A, UNIQ>
{
fn drop(&mut self) {
let h = self.header();
if !UNIQ && h.strong.dec() != 1 {
return;
}
h.strong.acquire_fence();
unsafe {
let f = h.drop_fn;
f(self.header.as_ptr(), true);
}
if h.weak.dec() != 1 {
return;
}
unsafe {
let h = self.header.as_mut();
let f = h.drop_fn;
f(self.header.as_ptr(), false);
}
}
}
impl<'a, T: ?Sized, C: Atomicity, A: Allocator> Drop for Weak<'a, T, C, A> {
fn drop(&mut self) {
let h = self.header();
if h.weak.dec() != 1 {
return;
}
h.weak.acquire_fence();
unsafe {
let f = h.drop_fn;
f(self.header.as_ptr(), false);
}
}
}
impl<'a, T: 'a, C: Atomicity, A: Allocator> From<Genrc<'a, T, C, A, true>>
for Genrc<'a, T, C, A, false>
{
fn from(uniq: Genrc<'a, T, C, A, true>) -> Self {
Genrc::<'a, T, C, A, true>::shared(uniq)
}
}
impl<'a, T: ?Sized + PartialEq, C: Atomicity, A: Allocator, const Q1: bool, const Q2: bool>
PartialEq<Genrc<'a, T, C, A, Q2>> for Genrc<'a, T, C, A, Q1>
{
#[inline]
fn eq(&self, other: &Genrc<T, C, A, Q2>) -> bool {
*(*self) == *(*other)
}
}
impl<'a, T: ?Sized + PartialOrd, C: Atomicity, A: Allocator, const Q1: bool, const Q2: bool>
PartialOrd<Genrc<'a, T, C, A, Q2>> for Genrc<'a, T, C, A, Q1>
{
fn partial_cmp(&self, other: &Genrc<T, C, A, Q2>) -> Option<cmp::Ordering> {
(**self).partial_cmp(&**other)
}
fn lt(&self, other: &Genrc<T, C, A, Q2>) -> bool {
*(*self) < *(*other)
}
fn le(&self, other: &Genrc<T, C, A, Q2>) -> bool {
*(*self) <= *(*other)
}
fn gt(&self, other: &Genrc<T, C, A, Q2>) -> bool {
*(*self) > *(*other)
}
fn ge(&self, other: &Genrc<T, C, A, Q2>) -> bool {
*(*self) >= *(*other)
}
}
impl<'a, T: 'a + ?Sized + Ord, C: Atomicity, A: Allocator, const UNIQ: bool> cmp::Ord
for Genrc<'a, T, C, A, UNIQ>
{
fn cmp(&self, other: &Self) -> cmp::Ordering {
(**self).cmp(&**other)
}
}
impl<'a, T: 'a + ?Sized + Eq, C: Atomicity, A: Allocator, const UNIQ: bool> Eq
for Genrc<'a, T, C, A, UNIQ>
{
}
impl<'a, T: 'a + ?Sized + fmt::Display, C: Atomicity, A: Allocator, const UNIQ: bool> fmt::Display
for Genrc<'a, T, C, A, UNIQ>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&**self, f)
}
}
impl<'a, T: 'a + ?Sized + fmt::Debug, C: Atomicity, A: Allocator, const UNIQ: bool> fmt::Debug
for Genrc<'a, T, C, A, UNIQ>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl<'a, T: 'a + ?Sized, C: Atomicity, A: Allocator, const UNIQ: bool> fmt::Pointer
for Genrc<'a, T, C, A, UNIQ>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&(&**self as *const T), f)
}
}
impl<'a, T: ?Sized + fmt::Debug, C: Atomicity> fmt::Debug for Weak<'a, T, C> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(Weak)")
}
}
unsafe fn drop_fn<T, C, A: Allocator>(ptr: *mut Header<C>, dropping_value: bool) {
if dropping_value {
drop_value::<T, C, A>(ptr);
} else {
drop_header::<T, C, A>(ptr);
}
}
unsafe fn drop_header<T, C, A: Allocator>(ptr: *mut Header<C>) {
let ptr = ptr as *mut Allocation<T, C, A>;
#[cfg(not(feature = "allocator_api"))]
let _ = Box::from_raw(ptr);
#[cfg(feature = "allocator_api")]
{
let alloc = (&mut *ptr).alloc.assume_init_read();
let _ = Box::from_raw_in(ptr as *mut Allocation<T, C, A>, alloc);
}
}
unsafe fn drop_value<T, C, A>(ptr: *mut Header<C>) {
let bptr = ptr as *mut Allocation<T, C, A>;
let bref = &mut *bptr;
bref.value.assume_init_drop();
}
pub(crate) mod private {
pub trait Sealed {}
}