dreck 0.1.1

A safe tracing garbage collection library with minimal safety bookkeeping
Documentation
use std::{
    cell::Cell,
    marker::PhantomData,
    ptr::{addr_of, NonNull},
};

use crate::{Bound, Owner, Root, Trace, marker::Invariant};

#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub enum Color {
    White,
    Gray,
    Black,
}

/// The pointer type contained within a [`Gc`] pointer.
pub(crate) type DynGcBoxPtr<'own, 'v> = NonNull<GcBox<'own, dyn Trace<'own> + 'v>>;

/// The pointer type contained within a [`Gc`] pointer.
pub type GcBoxPtr<'own,T> = NonNull<GcBox<'own, T>>;


pub(crate) struct GcBoxHead<'own> {
    pub next: Cell<Option<DynGcBoxPtr<'own, 'static>>>,
    pub color: Cell<Color>,
}

/// The structure containing a gc allocated value.
#[repr(C)]
pub struct GcBox<'own, T: ?Sized> {
    pub(crate) head: GcBoxHead<'own>,
    pub(crate) value: T,
}

/// A pointer to a gc allocated value.
pub struct Gc<'gc, 'own, T: Trace<'own> + ?Sized> {
    // Point to the GcBoxHead instead of GcBox, so T is hidden, to avoid an invariant 'gc
    pub(crate) ptr: NonNull<GcBox<'own, T>>,
    pub(crate) _gc: PhantomData<&'gc ()>,
    pub(crate) _own: Invariant<'own>,
}

impl<'gc, 'own, T: Trace<'own> + ?Sized> Copy for Gc<'gc, 'own, T> {}

impl<'gc, 'own, T: Trace<'own> + ?Sized> Clone for Gc<'gc, 'own, T> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<'r,'gc: 'r, 'own, T: Trace<'own> + Sized> Gc<'gc, 'own, T> {
    pub fn borrow(self, _owner: &'r Owner<'own>) -> &'r T {
        unsafe { &(*self.ptr.as_ptr().cast::<GcBox<T>>()).value }
    }
}


impl<'r,'gc: 'r, 'own, T> Gc<'gc, 'own, T> 
where
    T : Trace<'own> + Bound<'r> + Sized + 'r
{
    pub fn borrow_mut(self, _owner: &'r mut Owner<'own>, root: &Root<'own>) -> &'r mut T::Rebound {
        root.write_barrier(self);
        unsafe { crate::rebind(&mut (*self.ptr.as_ptr().cast::<GcBox<T>>()).value) }
    }

    pub fn borrow_mut_untraced(self, _owner: &'r mut Owner<'own>) -> &'r mut T {
        assert!(
            !T::needs_trace(),
            "called borrow_mut_untraced on pointer which requires tracing ({}::needs_trace() returns true)",
            std::any::type_name::<T>()
        );
        unsafe { &mut (*self.ptr.as_ptr().cast::<GcBox<T>>()).value }
    }

    pub fn as_raw(this: Gc<'gc, 'own, T>) -> *const T {
        let ptr: *mut GcBox<T> = this.ptr.as_ptr().cast();

        unsafe { addr_of!((*ptr).value) }
    }

    pub unsafe fn from_raw(ptr: *const T) -> Self {
        let offset = value_field_offset::<T>();

        let gc_ptr = (ptr as *mut u8).offset(-offset).cast::<GcBox<T>>();

        Gc {
            ptr: NonNull::new_unchecked(gc_ptr),
            _gc: PhantomData,
            _own: Invariant::new(),
        }
    }

    pub unsafe fn into_ptr(this: Self) -> GcBoxPtr<'own,T> {
        this.ptr
    }

    pub unsafe fn from_ptr(ptr: GcBoxPtr<'own,T>) -> Gc<'gc, 'own, T> {
        Gc {
            ptr,
            _gc: PhantomData,
            _own: Invariant::new(),
        }
    }
}

#[inline]
fn value_field_offset<T: Sized>() -> isize {
    let uninit = std::mem::MaybeUninit::<GcBox<T>>::uninit();
    let field_ptr = unsafe { addr_of!((*uninit.as_ptr()).value) };
    unsafe {
        field_ptr
            .cast::<u8>()
            .offset_from(uninit.as_ptr().cast::<u8>())
    }
}