use std::cell::{Cell, RefCell};
use rustc_hash::FxHashSet;
macro_rules! active_set {
($tl:ident, $enter:ident, $guard:ident, $doc:literal) => {
thread_local! {
static $tl: RefCell<FxHashSet<usize>> = RefCell::new(FxHashSet::default());
}
#[doc = $doc]
pub(crate) fn $enter(ptr: usize) -> Option<$guard> {
let newly = $tl.with(|s| s.borrow_mut().insert(ptr));
newly.then(|| $guard(ptr))
}
pub(crate) struct $guard(usize);
impl Drop for $guard {
fn drop(&mut self) {
$tl.with(|s| {
s.borrow_mut().remove(&self.0);
});
}
}
};
}
active_set!(
REPR_ACTIVE,
repr_enter,
ReprGuard,
"Enter a container during `repr`/`str`. `None` means it is already being formatted (a cycle) — the caller emits the ellipsis form (`[...]` / `{...}`)."
);
active_set!(
JSON_ACTIVE,
json_enter,
JsonGuard,
"Enter a container during JSON serialization. `None` means it is already being serialized (a cycle) — the caller raises `Circular reference detected`."
);
thread_local! {
static EQ_DEPTH: Cell<u32> = const { Cell::new(0) };
static EQ_OVERFLOW: Cell<bool> = const { Cell::new(false) };
}
pub(crate) const EQ_RECURSION_LIMIT: u32 = 1000;
pub(crate) fn eq_depth_enter() -> Option<EqDepthGuard> {
EQ_DEPTH.with(|d| {
let cur = d.get();
if cur == 0 {
EQ_OVERFLOW.with(|o| o.set(false));
}
if cur >= EQ_RECURSION_LIMIT {
EQ_OVERFLOW.with(|o| o.set(true));
None
} else {
d.set(cur + 1);
Some(EqDepthGuard(()))
}
})
}
pub(crate) fn take_eq_overflow() -> bool {
EQ_OVERFLOW.with(std::cell::Cell::take)
}
pub(crate) struct EqDepthGuard(());
impl Drop for EqDepthGuard {
fn drop(&mut self) {
EQ_DEPTH.with(|d| d.set(d.get().saturating_sub(1)));
}
}