use core::{
fmt::{self, Debug, Display, Pointer},
marker::PhantomData,
mem,
ops::Deref,
ptr::{self, NonNull},
};
use crate::{
barrier::{Unlock, Write},
collect::Collect,
context::{Collection, Mutation},
gc_weak::GcWeak,
types::{GcBox, GcBoxInner, Invariant},
};
pub struct Gc<'gc, T: ?Sized + 'gc> {
pub(crate) ptr: NonNull<GcBoxInner<T>>,
pub(crate) _invariant: Invariant<'gc>,
}
impl<'gc, T: Debug + ?Sized + 'gc> Debug for Gc<'gc, T> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&**self, fmt)
}
}
impl<'gc, T: ?Sized + 'gc> Pointer for Gc<'gc, T> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt::Pointer::fmt(&Gc::as_ptr(*self), fmt)
}
}
impl<'gc, T: Display + ?Sized + 'gc> Display for Gc<'gc, T> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&**self, fmt)
}
}
impl<'gc, T: ?Sized + 'gc> Copy for Gc<'gc, T> {}
impl<'gc, T: ?Sized + 'gc> Clone for Gc<'gc, T> {
#[inline]
fn clone(&self) -> Gc<'gc, T> {
*self
}
}
unsafe impl<'gc, T: ?Sized + 'gc> Collect for Gc<'gc, T> {
#[inline]
fn trace(&self, cc: &Collection) {
unsafe {
cc.trace(GcBox::erase(self.ptr));
}
}
}
impl<'gc, T: ?Sized + 'gc> Deref for Gc<'gc, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { &self.ptr.as_ref().value }
}
}
impl<'gc, T: ?Sized + 'gc> AsRef<T> for Gc<'gc, T> {
#[inline]
fn as_ref(&self) -> &T {
unsafe { &self.ptr.as_ref().value }
}
}
impl<'gc, T: Collect + 'gc> Gc<'gc, T> {
#[inline]
pub fn new(mc: &Mutation<'gc>, t: T) -> Gc<'gc, T> {
Gc {
ptr: mc.allocate(t),
_invariant: PhantomData,
}
}
}
impl<'gc, T: 'gc> Gc<'gc, T> {
#[inline]
pub unsafe fn cast<U: 'gc>(this: Gc<'gc, T>) -> Gc<'gc, U> {
Gc {
ptr: NonNull::cast(this.ptr),
_invariant: PhantomData,
}
}
#[inline]
pub unsafe fn from_ptr(ptr: *const T) -> Gc<'gc, T> {
let header_offset = {
let base = mem::MaybeUninit::<GcBoxInner<T>>::uninit();
let base_ptr = base.as_ptr();
let val_ptr = ptr::addr_of!((*base_ptr).value);
(base_ptr as isize) - (val_ptr as isize)
};
let ptr = (ptr as *mut T)
.cast::<u8>()
.offset(header_offset)
.cast::<GcBoxInner<T>>();
Gc {
ptr: NonNull::new_unchecked(ptr),
_invariant: PhantomData,
}
}
}
impl<'gc, T: Unlock + ?Sized + 'gc> Gc<'gc, T> {
#[inline]
pub fn unlock(self, mc: &Mutation<'gc>) -> &'gc T::Unlocked {
Gc::write(mc, self);
unsafe { self.as_ref().unlock_unchecked() }
}
}
impl<'gc, T: ?Sized + 'gc> Gc<'gc, T> {
#[inline]
pub fn as_ref(self: Gc<'gc, T>) -> &'gc T {
unsafe { &self.ptr.as_ref().value }
}
#[inline]
pub fn downgrade(this: Gc<'gc, T>) -> GcWeak<'gc, T> {
GcWeak { inner: this }
}
#[inline]
pub fn write(mc: &Mutation<'gc>, gc: Self) -> &'gc Write<T> {
unsafe {
mc.write_barrier(GcBox::erase(gc.ptr));
Write::assume(gc.as_ref())
}
}
#[inline]
pub fn ptr_eq(this: Gc<'gc, T>, other: Gc<'gc, T>) -> bool {
Gc::as_ptr(this) == Gc::as_ptr(other)
}
#[inline]
pub fn as_ptr(gc: Gc<'gc, T>) -> *const T {
unsafe {
let inner = gc.ptr.as_ptr();
core::ptr::addr_of!((*inner).value) as *const T
}
}
}