use std::{
cell::RefCell,
collections::{BTreeMap, HashMap, VecDeque},
hash::{BuildHasher, Hash},
rc::Rc,
sync::Arc,
};
use crate::{rubysys::gc, Object};
pub struct Marker {
pinned: bool,
}
impl Marker {
pub(crate) fn new(pinned: bool) -> Self {
Marker { pinned }
}
pub fn pinning() -> Self {
Marker { pinned: true }
}
pub fn is_pinning(&self) -> bool {
self.pinned
}
pub fn mark<T: Object>(&self, object: &T) {
#[cfg(ruby_gte_2_7)]
unsafe {
if self.pinned {
gc::rb_gc_mark(object.value());
} else {
gc::rb_gc_mark_movable(object.value());
}
}
#[cfg(not(ruby_gte_2_7))]
unsafe {
gc::rb_gc_mark(object.value());
}
}
}
pub trait Mark {
fn mark(&self, marker: &Marker);
fn compact(&mut self) {}
}
impl<T: Object> Mark for T {
fn mark(&self, marker: &Marker) {
marker.mark(self);
}
#[cfg(ruby_gte_2_7)]
fn compact(&mut self) {
let moved = unsafe { gc::rb_gc_location(self.value()) };
if moved != self.value() {
*self = T::from(moved);
}
}
}
macro_rules! nothing_to_mark {
($($ty: ty),* $(,)?) => {
$(
impl Mark for $ty {
#[inline]
fn mark(&self, _marker: &Marker) {}
}
)*
};
}
nothing_to_mark!(
(),
bool,
char,
i8,
i16,
i32,
i64,
i128,
isize,
u8,
u16,
u32,
u64,
u128,
usize,
f32,
f64,
String,
std::path::PathBuf,
std::time::Duration,
);
impl Mark for str {
fn mark(&self, _marker: &Marker) {}
}
impl<T: Mark> Mark for Option<T> {
fn mark(&self, marker: &Marker) {
if let Some(value) = self {
value.mark(marker);
}
}
fn compact(&mut self) {
if let Some(value) = self {
value.compact();
}
}
}
impl<T: Mark + ?Sized> Mark for Rc<T> {
fn mark(&self, _marker: &Marker) {
(**self).mark(&Marker::pinning());
}
}
impl<T: Mark + ?Sized> Mark for Arc<T> {
fn mark(&self, _marker: &Marker) {
(**self).mark(&Marker::pinning());
}
}
impl<T: Mark> Mark for RefCell<T> {
fn mark(&self, marker: &Marker) {
match self.try_borrow_mut() {
Ok(value) => value.mark(marker),
Err(_) => unsafe { (*self.as_ptr()).mark(&Marker::pinning()) },
}
}
fn compact(&mut self) {
self.get_mut().compact();
}
}
impl<T: Mark> Mark for [T] {
fn mark(&self, marker: &Marker) {
for value in self {
value.mark(marker);
}
}
fn compact(&mut self) {
for value in self {
value.compact();
}
}
}
impl<T: Mark, const N: usize> Mark for [T; N] {
fn mark(&self, marker: &Marker) {
self[..].mark(marker);
}
fn compact(&mut self) {
self[..].compact();
}
}
impl<T: Mark> Mark for Vec<T> {
fn mark(&self, marker: &Marker) {
self[..].mark(marker);
}
fn compact(&mut self) {
self[..].compact();
}
}
impl<T: Mark> Mark for VecDeque<T> {
fn mark(&self, marker: &Marker) {
for value in self {
value.mark(marker);
}
}
fn compact(&mut self) {
for value in self {
value.compact();
}
}
}
impl<K: Mark + Eq + Hash, V: Mark, S: BuildHasher> Mark for HashMap<K, V, S> {
fn mark(&self, marker: &Marker) {
let pinning = Marker::pinning();
for (key, value) in self {
key.mark(&pinning);
value.mark(marker);
}
}
fn compact(&mut self) {
for value in self.values_mut() {
value.compact();
}
}
}
impl<K: Mark + Ord, V: Mark> Mark for BTreeMap<K, V> {
fn mark(&self, marker: &Marker) {
let pinning = Marker::pinning();
for (key, value) in self {
key.mark(&pinning);
value.mark(marker);
}
}
fn compact(&mut self) {
for value in self.values_mut() {
value.compact();
}
}
}
macro_rules! mark_tuple {
($($name: ident . $index: tt),+) => {
impl<$($name: Mark),+> Mark for ($($name,)+) {
fn mark(&self, marker: &Marker) {
$(self.$index.mark(marker);)+
}
fn compact(&mut self) {
$(self.$index.compact();)+
}
}
};
}
mark_tuple!(A.0, B.1);
mark_tuple!(A.0, B.1, C.2);
mark_tuple!(A.0, B.1, C.2, D.3);
#[doc(hidden)]
pub trait MarkField {
fn __rutie_mark(&self, marker: &Marker);
}
impl<T: Mark + ?Sized> MarkField for T {
#[inline]
fn __rutie_mark(&self, marker: &Marker) {
self.mark(marker);
}
}
#[doc(hidden)]
pub trait MarkFieldFallback {
fn __rutie_mark(&self, marker: &Marker);
}
impl<T: ?Sized> MarkFieldFallback for &T {
#[inline]
fn __rutie_mark(&self, _marker: &Marker) {}
}
#[doc(hidden)]
pub trait CompactField {
fn __rutie_compact(&mut self);
}
impl<T: Mark + ?Sized> CompactField for T {
#[inline]
fn __rutie_compact(&mut self) {
self.compact();
}
}
#[doc(hidden)]
pub trait CompactFieldFallback {
fn __rutie_compact(&mut self);
}
impl<T: ?Sized> CompactFieldFallback for &mut T {
#[inline]
fn __rutie_compact(&mut self) {}
}