use std::{marker::PhantomData, ops::Range};
use rb_sys::{
rb_gc_adjust_memory_usage, rb_gc_count, rb_gc_disable, rb_gc_enable, rb_gc_location,
rb_gc_mark, rb_gc_mark_locations, rb_gc_mark_movable, rb_gc_register_address,
rb_gc_register_mark_object, rb_gc_start, rb_gc_stat, rb_gc_unregister_address, VALUE,
};
use crate::{
error::{protect, Error},
r_hash::RHash,
symbol::IntoSymbol,
value::{private::ReprValue as _, ReprValue, Value},
Ruby,
};
pub(crate) mod private {
use super::*;
pub trait Mark {
fn raw(self) -> VALUE;
}
pub trait Locate {
fn raw(self) -> VALUE;
fn from_raw(val: VALUE) -> Self;
}
}
pub trait Mark: private::Mark {}
impl<T> private::Mark for T
where
T: ReprValue,
{
fn raw(self) -> VALUE {
self.as_rb_value()
}
}
impl<T> Mark for T where T: ReprValue {}
impl<T> private::Mark for crate::value::Opaque<T>
where
T: ReprValue,
{
fn raw(self) -> VALUE {
unsafe { Ruby::get_unchecked() }
.get_inner(self)
.as_rb_value()
}
}
impl<T> Mark for crate::value::Opaque<T> where T: ReprValue {}
pub struct Marker(PhantomData<*mut ()>);
impl Marker {
pub(crate) fn new() -> Self {
Self(PhantomData)
}
pub fn mark<T>(&self, value: T)
where
T: Mark,
{
unsafe { rb_gc_mark(value.raw()) };
}
pub fn mark_slice<T>(&self, values: &[T])
where
T: Mark,
{
let Range { start, end } = values.as_ptr_range();
unsafe { rb_gc_mark_locations(start as *const VALUE, end as *const VALUE) }
}
pub fn mark_movable<T>(&self, value: T)
where
T: Mark,
{
unsafe { rb_gc_mark_movable(value.raw()) };
}
}
pub trait Locate: private::Locate {}
impl<T> private::Locate for T
where
T: ReprValue,
{
fn raw(self) -> VALUE {
self.as_rb_value()
}
fn from_raw(val: VALUE) -> Self {
unsafe { Self::from_value_unchecked(Value::new(val)) }
}
}
impl<T> Locate for T where T: ReprValue {}
impl<T> private::Locate for crate::value::Opaque<T>
where
T: ReprValue,
{
fn raw(self) -> VALUE {
unsafe { Ruby::get_unchecked() }
.get_inner(self)
.as_rb_value()
}
fn from_raw(val: VALUE) -> Self {
unsafe { T::from_value_unchecked(Value::new(val)) }.into()
}
}
impl<T> Locate for crate::value::Opaque<T> where T: ReprValue {}
pub struct Compactor(PhantomData<*mut ()>);
impl Compactor {
pub(crate) fn new() -> Self {
Self(PhantomData)
}
pub fn location<T>(&self, value: T) -> T
where
T: Locate,
{
unsafe { T::from_raw(rb_gc_location(value.raw())) }
}
}
pub fn register_mark_object<T>(value: T)
where
T: Mark,
{
unsafe { rb_gc_register_mark_object(value.raw()) }
}
pub fn register_address<T>(valref: &T)
where
T: Mark,
{
unsafe { rb_gc_register_address(valref as *const _ as *mut VALUE) }
}
pub fn unregister_address<T>(valref: &T)
where
T: Mark,
{
unsafe { rb_gc_unregister_address(valref as *const _ as *mut VALUE) }
}
impl Ruby {
pub fn gc_disable(&self) -> bool {
unsafe { Value::new(rb_gc_disable()).to_bool() }
}
pub fn gc_enable(&self) -> bool {
unsafe { Value::new(rb_gc_enable()).to_bool() }
}
pub fn gc_start(&self) {
unsafe { rb_gc_start() };
}
pub fn gc_adjust_memory_usage(&self, diff: isize) {
unsafe { rb_gc_adjust_memory_usage(diff as _) };
}
pub fn gc_count(&self) -> usize {
unsafe { rb_gc_count() as usize }
}
pub fn gc_stat<T>(&self, key: T) -> Result<usize, Error>
where
T: IntoSymbol,
{
let sym = key.into_symbol_with(self);
let mut res = 0;
protect(|| {
res = unsafe { rb_gc_stat(sym.as_rb_value()) as usize };
self.qnil()
})?;
Ok(res)
}
pub fn gc_all_stats(&self) -> RHash {
let res = self.hash_new();
unsafe { rb_gc_stat(res.as_rb_value()) };
res
}
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::gc_disable` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn disable() -> bool {
get_ruby!().gc_disable()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::gc_enable` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn enable() -> bool {
get_ruby!().gc_enable()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::gc_start` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn start() {
get_ruby!().gc_start()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::gc_adjust_memory_usage` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn adjust_memory_usage(diff: isize) {
get_ruby!().gc_adjust_memory_usage(diff)
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::gc_count` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn count() -> usize {
get_ruby!().gc_count()
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::gc_stat` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn stat<T>(key: T) -> Result<usize, Error>
where
T: IntoSymbol,
{
let handle = get_ruby!();
handle.gc_stat(key.into_symbol_with(&handle))
}
#[cfg_attr(
not(feature = "old-api"),
deprecated(note = "please use `Ruby::gc_all_stats` instead")
)]
#[cfg_attr(docsrs, doc(cfg(feature = "old-api")))]
#[inline]
pub fn all_stats() -> RHash {
get_ruby!().gc_all_stats()
}