#![allow(
clippy::type_repetition_in_bounds,
reason = "trait-impl `where` clauses are kept uniform across all forwarding impls"
)]
use core::marker::PhantomData;
use core::mem::{self, MaybeUninit};
use core::pin::Pin;
use core::ptr::{self, NonNull};
use allocator_api2::alloc::{Allocator, Global};
use ptr_meta::Pointee;
use crate::internal::chunk_ref::ChunkRef;
use crate::internal::constants::refcount_overflow_abort;
use crate::internal::thin_dst;
use crate::thin_smart_ptr_common::impl_thin_smart_ptr_common;
use crate::vec::Vec;
const MAX_STRONG_REFCOUNT: u32 = u32::MAX >> 1;
pub struct Rc<T: ?Sized + Pointee, A: Allocator + Clone = Global> {
ptr: NonNull<u8>,
_phantom: PhantomData<(*const T, A)>,
}
impl<T: ?Sized + Pointee, A: Allocator + Clone> Rc<T, A> {
#[inline]
pub(crate) unsafe fn from_raw(thin: NonNull<u8>) -> Self {
Self {
ptr: thin,
_phantom: PhantomData,
}
}
#[inline]
pub(crate) fn thin_ptr(&self) -> NonNull<u8> {
self.ptr
}
#[inline]
#[must_use]
pub fn ptr_eq(a: &Self, b: &Self) -> bool {
ptr::addr_eq(a.ptr.as_ptr(), b.ptr.as_ptr())
}
}
impl_thin_smart_ptr_common!(Rc);
impl<T, A: Allocator + Clone> Rc<MaybeUninit<T>, A> {
#[inline]
#[must_use]
pub unsafe fn assume_init(self) -> Rc<T, A> {
let thin = self.ptr;
mem::forget(self);
unsafe { Rc::from_raw(thin) }
}
#[must_use]
#[inline]
pub unsafe fn assume_init_pin(this: Pin<Self>) -> Pin<Rc<T, A>>
where
A: 'static,
{
unsafe {
let inner: Self = Pin::into_inner_unchecked(this);
Rc::into_pin(inner.assume_init())
}
}
}
impl<T, A: Allocator + Clone> Rc<[MaybeUninit<T>], A> {
#[inline]
#[must_use]
pub unsafe fn assume_init(self) -> Rc<[T], A> {
let thin = self.ptr;
mem::forget(self);
unsafe { Rc::from_raw(thin) }
}
#[must_use]
#[inline]
pub unsafe fn assume_init_pin_slice(this: Pin<Self>) -> Pin<Rc<[T], A>>
where
A: 'static,
{
unsafe {
let inner: Self = Pin::into_inner_unchecked(this);
Rc::into_pin(inner.assume_init())
}
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[inline(never)]
#[cold]
fn strong_overflow_abort() -> ! {
refcount_overflow_abort()
}
impl<T: ?Sized + Pointee, A: Allocator + Clone> Clone for Rc<T, A> {
#[inline]
fn clone(&self) -> Self {
let value_align = mem::align_of_val::<T>(&**self);
let strong = unsafe { thin_dst::local_strong_ptr::<T>(self.ptr, value_align) };
unsafe {
let prev = ptr::read_unaligned(strong);
if prev >= MAX_STRONG_REFCOUNT {
strong_overflow_abort();
}
ptr::write_unaligned(strong, prev + 1);
}
Self {
ptr: self.ptr,
_phantom: PhantomData,
}
}
}
impl<T: ?Sized + Pointee, A: Allocator + Clone> Drop for Rc<T, A> {
#[inline]
fn drop(&mut self) {
let value_align = mem::align_of_val::<T>(&**self);
let strong = unsafe { thin_dst::local_strong_ptr::<T>(self.ptr, value_align) };
let prev = unsafe { ptr::read_unaligned(strong) };
if prev != 1 {
unsafe { ptr::write_unaligned(strong, prev - 1) };
return;
}
unsafe {
let _chunk: ChunkRef<A> = ChunkRef::from_value_ptr(self.ptr);
let fat = self.as_fat_ptr();
ptr::drop_in_place(fat.as_ptr());
}
}
}
impl<'a, T, A: Allocator + Clone> From<Vec<'a, T, A>> for Rc<[T], A> {
#[inline]
fn from(v: Vec<'a, T, A>) -> Self {
v.into_rc_slice()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Arena;
#[test]
fn max_strong_refcount_is_u32_half_range() {
assert_eq!(MAX_STRONG_REFCOUNT, u32::MAX >> 1);
assert_eq!(MAX_STRONG_REFCOUNT, 0x7FFF_FFFF);
}
#[test]
fn clone_below_max_refcount_threshold_does_not_abort() {
let arena = Arena::new();
let rc = arena.alloc_rc(0xABCD_u32);
let strong = unsafe { thin_dst::local_strong_ptr::<u32>(rc.thin_ptr(), mem::align_of::<u32>()) };
unsafe { ptr::write_unaligned(strong, MAX_STRONG_REFCOUNT - 1) };
#[expect(clippy::redundant_clone, reason = "exercising the overflow guard at the threshold is the point")]
let clone = rc.clone();
assert_eq!(*clone, 0xABCD);
unsafe { ptr::write_unaligned(strong, 2) };
}
#[test]
#[should_panic(expected = "refcount overflow")]
fn clone_at_max_refcount_threshold_aborts() {
let arena = Arena::new();
let rc = arena.alloc_rc(0xABCD_u32);
let strong = unsafe { thin_dst::local_strong_ptr::<u32>(rc.thin_ptr(), mem::align_of::<u32>()) };
unsafe { ptr::write_unaligned(strong, MAX_STRONG_REFCOUNT) };
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let _c = rc.clone();
}));
unsafe { ptr::write_unaligned(strong, 1) };
std::panic::resume_unwind(result.expect_err("clone at the threshold must panic"));
}
}