use crate::{
Allocator, Ephemeron, GcErasedPointer, Tracer, WeakGc, custom_trace, finalizer_safe,
internals::{EphemeronBox, GcBox, VTable},
trace::{Finalize, Trace},
};
use std::{
any::TypeId,
cmp::Ordering,
fmt::{self, Debug, Display},
hash::{Hash, Hasher},
marker::PhantomData,
ops::Deref,
ptr::NonNull,
rc::Rc,
};
pub(crate) struct NonTraceable(());
impl Finalize for NonTraceable {
fn finalize(&self) {
unreachable!()
}
}
unsafe impl Trace for NonTraceable {
unsafe fn trace(&self, _tracer: &mut Tracer) {
unreachable!()
}
unsafe fn trace_non_roots(&self) {
unreachable!()
}
fn run_finalizer(&self) {
unreachable!()
}
}
impl Drop for NonTraceable {
fn drop(&mut self) {
unreachable!()
}
}
#[repr(transparent)]
pub struct GcErased {
inner: Gc<NonTraceable>,
}
impl GcErased {
#[inline]
#[must_use]
pub fn new<T: Trace>(gc: Gc<T>) -> Self {
let inner_ptr = gc.inner_ptr;
std::mem::forget(gc);
Self {
inner: Gc {
inner_ptr: inner_ptr.cast(),
marker: PhantomData,
},
}
}
#[must_use]
pub fn ptr_eq(this: &Self, other: &Self) -> bool {
Gc::ptr_eq(&this.inner, &other.inner)
}
#[inline]
#[must_use]
pub fn type_id(&self) -> TypeId {
Gc::type_id(&self.inner)
}
#[inline]
#[must_use]
pub fn is<T: Trace + 'static>(&self) -> bool {
Gc::is::<T>(&self.inner)
}
#[inline]
#[must_use]
pub fn downcast<T: Trace + 'static>(self) -> Option<Gc<T>> {
Gc::downcast::<T>(self.inner)
}
#[inline]
#[must_use]
pub unsafe fn downcast_unchecked<T: Trace + 'static>(self) -> Gc<T> {
unsafe { Gc::cast_unchecked::<T>(self.inner) }
}
#[inline]
#[must_use]
pub unsafe fn downcast_ref_unchecked<T: Trace + 'static>(&self) -> &Gc<T> {
unsafe { Gc::cast_ref_unchecked::<T>(&self.inner) }
}
}
impl Debug for GcErased {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("GcErased")
.field("inner", &self.inner.inner_ptr)
.finish()
}
}
impl Finalize for GcErased {
fn finalize(&self) {}
}
unsafe impl Trace for GcErased {
custom_trace!(this, mark, {
mark(&this.inner);
});
}
impl Clone for GcErased {
#[inline]
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
pub struct Gc<T: Trace + ?Sized + 'static> {
pub(crate) inner_ptr: NonNull<GcBox<T>>,
pub(crate) marker: PhantomData<Rc<T>>,
}
impl<T: Trace + ?Sized> Gc<T> {
#[must_use]
pub fn new(value: T) -> Self
where
T: Sized,
{
let inner_ptr = Allocator::alloc_gc(GcBox::new(value));
Self {
inner_ptr,
marker: PhantomData,
}
}
#[must_use]
pub fn new_cyclic<F>(data_fn: F) -> Self
where
F: FnOnce(&WeakGc<T>) -> T,
T: Sized,
{
let weak = unsafe {
Ephemeron::from_raw(Allocator::alloc_ephemeron(EphemeronBox::new_empty())).into()
};
let gc = Self::new(data_fn(&weak));
unsafe { weak.inner().inner_ptr().as_mut().set(&gc, ()) }
gc
}
#[must_use]
pub fn into_raw(this: Self) -> NonNull<GcBox<T>> {
let ptr = this.inner_ptr();
std::mem::forget(this);
ptr
}
#[must_use]
pub fn ptr_eq<U: Trace + ?Sized>(this: &Self, other: &Gc<U>) -> bool {
std::ptr::addr_eq(this.inner(), other.inner())
}
#[must_use]
pub const unsafe fn from_raw(inner_ptr: NonNull<GcBox<T>>) -> Self {
Self {
inner_ptr,
marker: PhantomData,
}
}
pub(crate) fn as_erased_pointer(&self) -> GcErasedPointer {
self.inner_ptr.cast()
}
#[inline]
#[must_use]
pub fn type_id(this: &Self) -> TypeId {
this.vtable().type_id()
}
#[inline]
#[must_use]
pub fn is<U: Trace + 'static>(this: &Self) -> bool {
Gc::type_id(this) == TypeId::of::<U>()
}
#[inline]
#[must_use]
pub fn downcast<U: Trace + 'static>(this: Self) -> Option<Gc<U>> {
if !Gc::is::<U>(&this) {
return None;
}
Some(unsafe { Gc::cast_unchecked::<U>(this) })
}
#[inline]
#[must_use]
pub unsafe fn cast_unchecked<U: Trace + 'static>(this: Self) -> Gc<U> {
let inner_ptr = this.inner_ptr.cast::<U>();
core::mem::forget(this); Gc {
inner_ptr: inner_ptr.cast(),
marker: PhantomData,
}
}
#[inline]
#[must_use]
pub unsafe fn cast_ref_unchecked<U: Trace + 'static>(this: &Self) -> &Gc<U> {
unsafe { &(*(&raw const *this).cast::<Gc<U>>()) }
}
}
impl<T: Trace + ?Sized> Gc<T> {
pub(crate) fn vtable(&self) -> &'static VTable {
unsafe { self.inner_ptr.as_ref() }.vtable
}
#[inline(always)]
#[allow(clippy::inline_always)]
pub(crate) fn inner_ptr(&self) -> NonNull<GcBox<T>> {
debug_assert!(finalizer_safe());
self.inner_ptr
}
fn inner(&self) -> &GcBox<T> {
unsafe { self.inner_ptr().as_ref() }
}
}
impl<T: Trace + ?Sized> Finalize for Gc<T> {
fn finalize(&self) {
unsafe {
self.inner_ptr.as_ref().dec_ref_count();
}
}
}
unsafe impl<T: Trace + ?Sized> Trace for Gc<T> {
unsafe fn trace(&self, tracer: &mut Tracer) {
tracer.enqueue(self.as_erased_pointer());
}
unsafe fn trace_non_roots(&self) {
self.inner().inc_non_root_count();
}
fn run_finalizer(&self) {
Finalize::finalize(self);
}
}
impl<T: Trace + ?Sized> Clone for Gc<T> {
fn clone(&self) -> Self {
let ptr = self.inner_ptr();
unsafe {
ptr.as_ref().inc_ref_count();
Self::from_raw(ptr)
}
}
}
impl<T: Trace + ?Sized> Deref for Gc<T> {
type Target = T;
fn deref(&self) -> &T {
self.inner().value()
}
}
impl<T: Trace + ?Sized> Drop for Gc<T> {
fn drop(&mut self) {
if finalizer_safe() {
Finalize::finalize(self);
}
}
}
impl<T: Trace + Default> Default for Gc<T> {
fn default() -> Self {
Self::new(Default::default())
}
}
#[allow(clippy::inline_always)]
impl<T: Trace + ?Sized + PartialEq> PartialEq for Gc<T> {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
**self == **other
}
}
impl<T: Trace + ?Sized + Eq> Eq for Gc<T> {}
#[allow(clippy::inline_always)]
impl<T: Trace + ?Sized + PartialOrd> PartialOrd for Gc<T> {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
(**self).partial_cmp(&**other)
}
#[inline(always)]
fn lt(&self, other: &Self) -> bool {
**self < **other
}
#[inline(always)]
fn le(&self, other: &Self) -> bool {
**self <= **other
}
#[inline(always)]
fn gt(&self, other: &Self) -> bool {
**self > **other
}
#[inline(always)]
fn ge(&self, other: &Self) -> bool {
**self >= **other
}
}
impl<T: Trace + ?Sized + Ord> Ord for Gc<T> {
fn cmp(&self, other: &Self) -> Ordering {
(**self).cmp(&**other)
}
}
impl<T: Trace + ?Sized + Hash> Hash for Gc<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}
impl<T: Trace + ?Sized + Display> Display for Gc<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&**self, f)
}
}
impl<T: Trace + ?Sized + Debug> Debug for Gc<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(&**self, f)
}
}
impl<T: Trace + ?Sized> fmt::Pointer for Gc<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&self.inner(), f)
}
}
impl<T: Trace + ?Sized> std::borrow::Borrow<T> for Gc<T> {
fn borrow(&self) -> &T {
self
}
}
impl<T: Trace + ?Sized> AsRef<T> for Gc<T> {
fn as_ref(&self) -> &T {
self
}
}