#![allow(
clippy::type_repetition_in_bounds,
clippy::cast_ptr_alignment,
reason = "trait-impl `where` clauses are kept uniform across all forwarding impls; pointer casts target the chunk header which is CHUNK_ALIGN-aligned by construction"
)]
use core::marker::PhantomData;
use core::mem::{MaybeUninit, forget, needs_drop};
use core::ptr::{NonNull, addr_eq, slice_from_raw_parts_mut};
use core::sync::atomic::Ordering;
use allocator_api2::alloc::{Allocator, Global};
use crate::internal::drop_list::{drop_shim_one, drop_shim_slice};
use crate::internal::in_chunk::InLocalChunk;
use crate::internal::local_chunk::LocalChunk;
use crate::vec::Vec;
pub struct Rc<T: ?Sized, A: Allocator + Clone = Global> {
ptr: InLocalChunk<T, A>,
_phantom: PhantomData<(*const T, A)>,
}
impl<T: ?Sized, A: Allocator + Clone> Rc<T, A> {
#[inline]
pub(crate) unsafe fn from_value_ptr(ptr: NonNull<T>) -> Self {
unsafe { Self::from_in_chunk(InLocalChunk::new(ptr)) }
}
#[inline]
pub(crate) const unsafe fn from_in_chunk(ptr: InLocalChunk<T, A>) -> Self {
Self {
ptr,
_phantom: PhantomData,
}
}
#[inline]
pub(crate) fn from_owned_in_chunk(owned: crate::internal::owned_in_chunk::OwnedInLocalChunk<T, A>) -> Self {
Self {
ptr: owned.into_in_chunk(),
_phantom: PhantomData,
}
}
}
impl<T: ?Sized, A: Allocator + Clone> Rc<T, A> {
#[inline]
#[must_use]
pub const fn as_ptr(&self) -> *const T {
self.ptr.as_ptr()
}
#[inline]
fn chunk(&self) -> &LocalChunk<A> {
unsafe { self.ptr.chunk_ptr().as_ref() }
}
#[inline]
#[must_use]
pub fn ptr_eq(a: &Self, b: &Self) -> bool {
addr_eq(a.ptr.as_ptr(), b.ptr.as_ptr())
}
#[must_use]
#[inline]
pub fn into_pin(this: Self) -> core::pin::Pin<Self> {
unsafe { core::pin::Pin::new_unchecked(this) }
}
}
impl<T: ?Sized, A: Allocator + Clone> From<Rc<T, A>> for core::pin::Pin<Rc<T, A>> {
#[inline]
fn from(rc: Rc<T, A>) -> Self {
Rc::into_pin(rc)
}
}
impl<T, A: Allocator + Clone> Rc<MaybeUninit<T>, A> {
#[inline]
#[must_use]
pub unsafe fn assume_init(self) -> Rc<T, A> {
let ptr = self.ptr.as_non_null().cast::<T>();
if needs_drop::<T>() {
let chunk = self.chunk();
let data_addr = unsafe { LocalChunk::<A>::data_ptr(NonNull::from(chunk)) }.as_ptr() as usize;
let value_offset = self.ptr.as_ptr() as *const u8 as usize - data_addr;
let entry = chunk
.drop_entries()
.iter()
.find(|e| e.value_offset as usize == value_offset)
.expect(
"Rc::<MaybeUninit<T>>::assume_init: no drop entry reserved for this allocation. \
The allocation must come from `Arena::alloc_uninit_rc::<T>()` / `alloc_zeroed_rc`; \
`Arena::alloc_rc(MaybeUninit::new(...))` does not reserve an entry and would silently leak `T::drop`.",
);
entry.store_drop_fn(drop_shim_one::<T>, Ordering::Relaxed);
}
forget(self);
unsafe { Rc::from_value_ptr(ptr) }
}
#[must_use]
#[inline]
pub unsafe fn assume_init_pin(this: core::pin::Pin<Self>) -> core::pin::Pin<Rc<T, A>>
where
A: 'static,
{
unsafe {
let inner = core::pin::Pin::into_inner_unchecked(this);
core::pin::Pin::new_unchecked(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 old_ptr = self.ptr.as_non_null();
let len = old_ptr.len();
if needs_drop::<T>() {
let chunk = self.chunk();
let data_addr = unsafe { LocalChunk::<A>::data_ptr(NonNull::from(chunk)) }.as_ptr() as usize;
let value_offset = old_ptr.as_ptr() as *const u8 as usize - data_addr;
let entry = chunk
.drop_entries()
.iter()
.find(|e| e.value_offset as usize == value_offset)
.expect(
"Rc::<[MaybeUninit<T>]>::assume_init: no drop entry reserved for this allocation. \
Use `Arena::alloc_uninit_slice_rc::<T>()` / `alloc_zeroed_slice_rc`; \
`alloc_slice_*` of `MaybeUninit<T>` does not reserve an entry and would silently leak.",
);
entry.store_drop_fn(drop_shim_slice::<T>, Ordering::Relaxed);
}
forget(self);
let data = old_ptr.as_ptr().cast::<T>();
let fat = slice_from_raw_parts_mut(data, len);
unsafe { Rc::from_value_ptr(NonNull::new_unchecked(fat)) }
}
#[must_use]
#[inline]
pub unsafe fn assume_init_pin_slice(this: core::pin::Pin<Self>) -> core::pin::Pin<Rc<[T], A>>
where
A: 'static,
{
unsafe {
let inner = core::pin::Pin::into_inner_unchecked(this);
core::pin::Pin::new_unchecked(inner.assume_init())
}
}
}
impl<T: ?Sized, A: Allocator + Clone> Clone for Rc<T, A> {
#[inline]
fn clone(&self) -> Self {
self.chunk().inc_ref();
Self {
ptr: self.ptr,
_phantom: PhantomData,
}
}
}
impl<T: ?Sized, A: Allocator + Clone> Drop for Rc<T, A> {
#[inline]
fn drop(&mut self) {
let chunk = self.ptr.chunk_ptr();
unsafe { LocalChunk::dec_ref(chunk) };
}
}
crate::smart_ptr_macros::impl_smart_ptr_forwarding_traits!(Rc);
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_arena_rc()
}
}