use crate::gc_box::GcHeader;
use std::{
cell::UnsafeCell,
collections::{BTreeSet, HashSet, VecDeque},
convert::Infallible,
};
pub struct Tracer(UnsafeCell<Vec<*const GcHeader>>);
impl Tracer {
pub(crate) fn new() -> Self {
Self(UnsafeCell::new(Vec::new()))
}
pub(crate) fn append_to(&self, dest: &mut Vec<*const GcHeader>) {
dest.append(unsafe { &mut *self.0.get() });
}
pub(crate) fn steal_from(&self, mut num_items: usize, src: &mut Vec<*const GcHeader>) -> bool {
if src.is_empty() || num_items == 0 {
return false;
}
while num_items > 0 {
if let Some(item) = src.pop() {
unsafe { &mut *self.0.get() }.push(item);
num_items -= 1;
} else {
break;
}
}
true
}
pub(crate) fn pop_work(&self) -> Option<*const GcHeader> {
unsafe { &mut *self.0.get() }.pop()
}
pub(crate) fn has_work(&self) -> bool {
!unsafe { &*self.0.get() }.is_empty()
}
pub fn mark<T: Trace>(&self, ptr: &crate::GcPtr<T>) {
let header_ptr = ptr.header_ptr();
unsafe {
let header = &*header_ptr;
if T::NO_TRACE {
header.color.mark_black();
} else {
self.mark_header(header);
}
}
}
pub(crate) fn mark_header(&self, header: &GcHeader) {
if header.color.mark_white_to_gray() {
unsafe { &mut *self.0.get() }.push(header);
}
}
}
pub unsafe trait Trace {
const NO_TRACE: bool = false;
fn trace(&self, tracer: &Tracer);
}
macro_rules! impl_no_trace {
($(impl$([$($tt:tt)*])? for $ty:ty);* $(;)?) => {
$(
unsafe impl$(<$($tt)*>)? Trace for $ty {
const NO_TRACE: bool = true;
fn trace(&self, _tracer: &Tracer) {
}
}
)*
};
}
impl_no_trace! {
impl for ();
impl for i8;
impl for i16;
impl for i32;
impl for i64;
impl for i128;
impl for isize;
impl for u8;
impl for u16;
impl for u32;
impl for u64;
impl for u128;
impl for usize;
impl for f32;
impl for f64;
impl for bool;
impl for char;
impl for String;
impl for &str;
impl for Infallible;
impl[T] for std::marker::PhantomData<T>;
}
macro_rules! impl_trace_deref {
($(impl<$i:ident> for $ty:ty);* $(;)?) => {
$(
unsafe impl<$i: Trace> Trace for $ty {
const NO_TRACE: bool = $i::NO_TRACE;
fn trace(&self, tracer: &Tracer) {
$i::trace(self, tracer);
}
}
)*
};
}
impl_trace_deref! {
impl<T> for Box<T>;
impl<T> for std::rc::Rc<T>;
impl<T> for std::sync::Arc<T>;
}
macro_rules! impl_trace_iterable {
($(impl<$i:ident> for $ty:ty);* $(;)?) => {
$(
unsafe impl<$i: Trace> Trace for $ty {
const NO_TRACE: bool = $i::NO_TRACE;
fn trace(&self, tracer: &Tracer) {
for item in self {
item.trace(tracer);
}
}
}
)*
};
}
impl_trace_iterable! {
impl<T> for Vec<T>;
impl<T> for VecDeque<T>;
impl<T> for HashSet<T>;
impl<T> for BTreeSet<T>;
}
macro_rules! impl_trace_map {
($(impl<$i:ident, $j:ident> for $ty:ty);* $(;)?) => {
$(
unsafe impl<$i: Trace,$j: Trace> Trace for $ty {
const NO_TRACE: bool = $i::NO_TRACE && $j::NO_TRACE;
fn trace(&self, tracer: &Tracer) {
for (k,v) in self.iter() {
k.trace(tracer);
v.trace(tracer);
}
}
}
)*
};
}
impl_trace_map! {
impl<K,V> for std::collections::HashMap<K,V>;
impl<K,V> for std::collections::BTreeMap<K,V>;
}
unsafe impl<T: Trace, E: Trace> Trace for Result<T, E> {
const NO_TRACE: bool = T::NO_TRACE && E::NO_TRACE;
fn trace(&self, tracer: &Tracer) {
match self {
Ok(value) => value.trace(tracer),
Err(err) => err.trace(tracer),
}
}
}
unsafe impl<T: Trace> Trace for Option<T> {
const NO_TRACE: bool = T::NO_TRACE;
fn trace(&self, tracer: &Tracer) {
if let Some(value) = self {
value.trace(tracer);
}
}
}
unsafe impl<T: Trace, const N: usize> Trace for [T; N] {
const NO_TRACE: bool = T::NO_TRACE;
fn trace(&self, tracer: &Tracer) {
for item in self {
item.trace(tracer);
}
}
}