ranty 1.0.0

The Ranty procedural templating language
Documentation
use std::cell::Cell;

pub(crate) use rust_cc::weak::Weak;
pub(crate) use rust_cc::{collect_cycles, Cc, Context, Finalize, Trace};

pub const DEFAULT_ALLOCATION_THRESHOLD: usize = 1024;

thread_local! {
    static ALLOCATION_DEBT: Cell<usize> = const { Cell::new(0) };
    static ALLOCATION_THRESHOLD: Cell<usize> = const { Cell::new(DEFAULT_ALLOCATION_THRESHOLD) };
}

#[inline]
pub(crate) fn alloc<T: Trace>(value: T) -> Cc<T> {
    let gc_value = Cc::new(value);
    charge_allocation();
    gc_value
}

#[inline]
pub(crate) fn alloc_cyclic<T: Trace, F>(f: F) -> Cc<T>
where
    F: FnOnce(&Weak<T>) -> T,
{
    let gc_value = Cc::new_cyclic(f);
    charge_allocation();
    gc_value
}

#[inline]
fn charge_allocation() {
    ALLOCATION_DEBT.with(|debt| {
        let next = debt.get().saturating_add(1);
        debt.set(next);
        if next >= allocation_threshold() {
            collect();
        }
    });
}

#[inline]
pub fn collect() {
    collect_cycles();
    ALLOCATION_DEBT.with(|debt| debt.set(0));
}

#[inline]
pub(crate) fn allocation_threshold() -> usize {
    ALLOCATION_THRESHOLD.with(|threshold| threshold.get().max(1))
}

pub(crate) struct AllocationThresholdGuard {
    previous_threshold: usize,
}

impl AllocationThresholdGuard {
    #[inline]
    pub(crate) fn new(threshold: usize) -> Self {
        let threshold = threshold.max(1);
        let previous_threshold = ALLOCATION_THRESHOLD.with(|cell| cell.replace(threshold));
        Self { previous_threshold }
    }
}

impl Drop for AllocationThresholdGuard {
    fn drop(&mut self) {
        ALLOCATION_THRESHOLD.with(|cell| {
            cell.set(self.previous_threshold);
        });
    }
}