use super::*;
use portable_atomic::{AtomicU32, Ordering};
pub unsafe trait VTableMetaDropInPlace: VTableMeta {
unsafe fn drop_in_place(vtable: &Self::VTable, ptr: *mut u8) -> vrc::Layout;
unsafe fn dealloc(vtable: &Self::VTable, ptr: *mut u8, layout: vrc::Layout);
}
pub struct Dyn(PhantomData<*mut ()>);
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Layout {
pub size: usize,
pub align: usize,
}
impl From<core::alloc::Layout> for Layout {
fn from(layout: core::alloc::Layout) -> Self {
Self { size: layout.size(), align: layout.align() }
}
}
impl core::convert::TryFrom<Layout> for core::alloc::Layout {
type Error = core::alloc::LayoutError;
fn try_from(value: Layout) -> Result<Self, Self::Error> {
Self::from_size_align(value.size, value.align)
}
}
#[repr(C)]
struct VRcInner<'vt, VTable: VTableMeta, X> {
vtable: &'vt VTable::VTable,
strong_ref: AtomicU32,
weak_ref: AtomicU32,
data_offset: u16,
data: X,
}
impl<VTable: VTableMeta, X> VRcInner<'_, VTable, X> {
unsafe fn data_ptr(s: *const Self) -> *const X {
(s as *const u8).add(*core::ptr::addr_of!((*s).data_offset) as usize) as *const X
}
fn as_ref(&self) -> &X {
let ptr = self as *const Self as *const u8;
unsafe { &*(ptr.add(self.data_offset as usize) as *const X) }
}
}
#[repr(transparent)]
pub struct VRc<VTable: VTableMetaDropInPlace + 'static, X = Dyn> {
inner: NonNull<VRcInner<'static, VTable, X>>,
}
impl<VTable: VTableMetaDropInPlace + 'static, X> Drop for VRc<VTable, X> {
fn drop(&mut self) {
unsafe {
let inner = self.inner.as_ptr();
if (*inner).strong_ref.fetch_sub(1, Ordering::SeqCst) == 1 {
let data =
(inner as *mut u8).add(*core::ptr::addr_of!((*inner).data_offset) as usize);
let vtable = core::ptr::addr_of!((*inner).vtable);
let mut layout = VTable::drop_in_place(*vtable, data);
layout = core::alloc::Layout::new::<VRcInner<VTable, ()>>()
.extend(layout.try_into().unwrap())
.unwrap()
.0
.pad_to_align()
.into();
if (*core::ptr::addr_of!((*inner).weak_ref)).load(Ordering::SeqCst) > 1 {
*(VRcInner::data_ptr(self.inner.cast::<VRcInner<VTable, Layout>>().as_ptr())
as *mut Layout) = layout;
}
if (*core::ptr::addr_of!((*inner).weak_ref)).fetch_sub(1, Ordering::SeqCst) == 1 {
VTable::dealloc(*vtable, self.inner.cast().as_ptr(), layout);
}
}
}
}
}
impl<VTable: VTableMetaDropInPlace + 'static, X> core::fmt::Debug for VRc<VTable, X> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("VRc").field("inner", &self.inner).finish()
}
}
impl<VTable: VTableMetaDropInPlace, X: HasStaticVTable<VTable>> VRc<VTable, X> {
pub fn new(data: X) -> Self {
let layout = core::alloc::Layout::new::<VRcInner<VTable, X>>().pad_to_align();
let layout_with_layout = core::alloc::Layout::new::<VRcInner<VTable, Layout>>();
let layout = core::alloc::Layout::from_size_align(
layout.size().max(layout_with_layout.size()),
layout.align().max(layout_with_layout.align()),
)
.unwrap();
let mem = unsafe { alloc::alloc::alloc(layout) as *mut VRcInner<VTable, X> };
let inner = NonNull::new(mem).unwrap();
assert!(!mem.is_null());
unsafe {
mem.write(VRcInner {
vtable: X::static_vtable(),
strong_ref: AtomicU32::new(1),
weak_ref: AtomicU32::new(1), data_offset: 0,
data,
});
(*mem).data_offset =
(&(*mem).data as *const _ as usize - mem as *const _ as usize) as u16;
VRc { inner }
}
}
pub fn into_dyn(this: Self) -> VRc<VTable, Dyn>
where
Self: 'static,
{
unsafe { core::mem::transmute(this) }
}
}
impl<VTable: VTableMetaDropInPlace + 'static, X: HasStaticVTable<VTable> + 'static> VRc<VTable, X> {
pub fn map<MappedType: ?Sized>(
this: Self,
map_fn: impl for<'r> FnOnce(Pin<&'r X>) -> Pin<&'r MappedType>,
) -> VRcMapped<VTable, MappedType> {
VRcMapped {
parent_strong: Self::into_dyn(this.clone()),
object: map_fn(this.as_pin_ref()).get_ref(),
}
}
}
impl<VTable: VTableMetaDropInPlace + 'static> VRc<VTable, Dyn> {
pub fn map_dyn<MappedType: ?Sized>(
this: Self,
map_fn: impl for<'r> FnOnce(Pin<VRef<'r, VTable>>) -> Pin<&'r MappedType>,
) -> VRcMapped<VTable, MappedType> {
VRcMapped { parent_strong: this.clone(), object: map_fn(Self::borrow_pin(&this)).get_ref() }
}
}
impl<VTable: VTableMetaDropInPlace, X> VRc<VTable, X> {
pub fn as_pin_ref(&self) -> Pin<&X> {
unsafe { Pin::new_unchecked(self) }
}
pub fn borrow(this: &Self) -> VRef<'_, VTable> {
unsafe {
let inner = this.inner.cast::<VRcInner<VTable, u8>>();
VRef::from_raw(
NonNull::from(*::core::ptr::addr_of!((*inner.as_ptr()).vtable)),
NonNull::new_unchecked(VRcInner::data_ptr(inner.as_ptr()) as *mut u8),
)
}
}
pub fn borrow_pin(this: &Self) -> Pin<VRef<'_, VTable>> {
unsafe { Pin::new_unchecked(Self::borrow(this)) }
}
pub fn downgrade(this: &Self) -> VWeak<VTable, X> {
let inner = unsafe { this.inner.as_ref() };
inner.weak_ref.fetch_add(1, Ordering::SeqCst);
VWeak { inner: Some(this.inner) }
}
pub fn strong_count(this: &Self) -> usize {
unsafe { this.inner.as_ref().strong_ref.load(Ordering::SeqCst) as usize }
}
pub fn ptr_eq(this: &Self, other: &Self) -> bool {
this.inner == other.inner
}
}
impl<VTable: VTableMetaDropInPlace + 'static, X> Clone for VRc<VTable, X> {
fn clone(&self) -> Self {
let inner = unsafe { self.inner.as_ref() };
inner.strong_ref.fetch_add(1, Ordering::SeqCst);
Self { inner: self.inner }
}
}
impl<VTable: VTableMetaDropInPlace, X > Deref for VRc<VTable, X> {
type Target = X;
fn deref(&self) -> &Self::Target {
let inner = unsafe { self.inner.as_ref() };
inner.as_ref()
}
}
unsafe impl<VTable: VTableMetaDropInPlace + Send + Sync + 'static, X: Send + Sync> Send
for VRc<VTable, X>
{
}
unsafe impl<VTable: VTableMetaDropInPlace + Send + Sync + 'static, X: Send + Sync> Sync
for VRc<VTable, X>
{
}
#[repr(transparent)]
pub struct VWeak<VTable: VTableMetaDropInPlace + 'static, X = Dyn> {
inner: Option<NonNull<VRcInner<'static, VTable, X>>>,
}
impl<VTable: VTableMetaDropInPlace + 'static, X> Default for VWeak<VTable, X> {
fn default() -> Self {
Self { inner: None }
}
}
impl<VTable: VTableMetaDropInPlace + 'static, X> Clone for VWeak<VTable, X> {
fn clone(&self) -> Self {
if let Some(inner) = self.inner {
let inner = unsafe { inner.as_ref() };
inner.weak_ref.fetch_add(1, Ordering::SeqCst);
}
VWeak { inner: self.inner }
}
}
impl<T: VTableMetaDropInPlace + 'static, X> Drop for VWeak<T, X> {
fn drop(&mut self) {
if let Some(i) = self.inner {
unsafe {
if (*core::ptr::addr_of!((*i.as_ptr()).weak_ref)).fetch_sub(1, Ordering::SeqCst)
== 1
{
let vtable = &*core::ptr::addr_of!((*i.as_ptr()).vtable);
let layout = *(VRcInner::data_ptr(i.cast::<VRcInner<T, Layout>>().as_ptr()));
T::dealloc(vtable, i.cast().as_ptr(), layout);
}
}
}
}
}
impl<VTable: VTableMetaDropInPlace + 'static, X> VWeak<VTable, X> {
pub fn upgrade(&self) -> Option<VRc<VTable, X>> {
if let Some(i) = self.inner {
let inner = unsafe { i.as_ref() };
if inner.strong_ref.load(Ordering::SeqCst) == 0 {
None
} else {
inner.strong_ref.fetch_add(1, Ordering::SeqCst);
Some(VRc { inner: i })
}
} else {
None
}
}
pub fn ptr_eq(this: &Self, other: &Self) -> bool {
this.inner == other.inner
}
}
impl<VTable: VTableMetaDropInPlace + 'static, X: HasStaticVTable<VTable> + 'static>
VWeak<VTable, X>
{
pub fn into_dyn(self) -> VWeak<VTable, Dyn> {
unsafe { core::mem::transmute(self) }
}
}
unsafe impl<VTable: VTableMetaDropInPlace + 'static, X> stable_deref_trait::StableDeref
for VRc<VTable, X>
{
}
unsafe impl<VTable: VTableMetaDropInPlace + 'static, X> stable_deref_trait::CloneStableDeref
for VRc<VTable, X>
{
}
pub struct VRcMapped<VTable: VTableMetaDropInPlace + 'static, MappedType: ?Sized> {
parent_strong: VRc<VTable, Dyn>,
object: *const MappedType,
}
impl<VTable: VTableMetaDropInPlace + 'static, MappedType: ?Sized> Clone
for VRcMapped<VTable, MappedType>
{
fn clone(&self) -> Self {
Self { parent_strong: self.parent_strong.clone(), object: self.object }
}
}
impl<VTable: VTableMetaDropInPlace + 'static, MappedType: ?Sized> VRcMapped<VTable, MappedType> {
pub fn downgrade(this: &Self) -> VWeakMapped<VTable, MappedType> {
VWeakMapped { parent_weak: VRc::downgrade(&this.parent_strong), object: this.object }
}
pub fn as_pin_ref(&self) -> Pin<&MappedType> {
unsafe { Pin::new_unchecked(self) }
}
pub fn map<ReMappedType: ?Sized>(
this: Self,
map_fn: impl for<'r> FnOnce(Pin<&'r MappedType>) -> Pin<&'r ReMappedType>,
) -> VRcMapped<VTable, ReMappedType> {
VRcMapped {
parent_strong: this.parent_strong.clone(),
object: map_fn(this.as_pin_ref()).get_ref(),
}
}
pub fn origin(this: &Self) -> VRc<VTable> {
this.parent_strong.clone()
}
}
impl<VTable: VTableMetaDropInPlace + 'static, MappedType: ?Sized> Deref
for VRcMapped<VTable, MappedType>
{
type Target = MappedType;
fn deref(&self) -> &Self::Target {
unsafe { &*self.object }
}
}
pub struct VWeakMapped<VTable: VTableMetaDropInPlace + 'static, MappedType: ?Sized> {
parent_weak: VWeak<VTable, Dyn>,
object: *const MappedType,
}
impl<VTable: VTableMetaDropInPlace + 'static, MappedType: ?Sized> VWeakMapped<VTable, MappedType> {
pub fn upgrade(&self) -> Option<VRcMapped<VTable, MappedType>> {
self.parent_weak
.upgrade()
.map(|parent| VRcMapped { parent_strong: parent, object: self.object })
}
}
impl<VTable: VTableMetaDropInPlace + 'static, MappedType: ?Sized> Clone
for VWeakMapped<VTable, MappedType>
{
fn clone(&self) -> Self {
Self { parent_weak: self.parent_weak.clone(), object: self.object }
}
}
impl<VTable: VTableMetaDropInPlace + 'static, MappedType> Default
for VWeakMapped<VTable, MappedType>
{
fn default() -> Self {
Self { parent_weak: VWeak::default(), object: core::ptr::null() }
}
}