use std::cell::Cell;
thread_local! {
static OP_COUNTER: Cell<Option<u64>> = const { Cell::new(None) };
}
#[inline]
pub(crate) fn record_op() {
OP_COUNTER.with(|counter| {
if let Some(count) = counter.get() {
counter.set(Some(count.saturating_add(1)));
}
});
}
pub fn count_ops<R>(f: impl FnOnce() -> R) -> (R, u64) {
let saved = OP_COUNTER.with(|counter| counter.replace(Some(0)));
let result = f();
let count = OP_COUNTER
.with(|counter| counter.replace(saved))
.unwrap_or(0);
if saved.is_some() {
OP_COUNTER.with(|counter| {
if let Some(outer) = counter.get() {
counter.set(Some(outer.saturating_add(count)));
}
});
}
(result, count)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn count_ops_is_zero_when_no_ops_run() {
let (value, ops) = count_ops(|| 42u32);
assert_eq!(value, 42);
assert_eq!(ops, 0, "a closure that runs no interpreter ops counts zero");
}
#[test]
fn record_op_is_inert_outside_a_count_scope() {
record_op();
record_op();
let (_, ops) = count_ops(|| {
record_op();
record_op();
record_op();
});
assert_eq!(ops, 3, "only ops inside the scope are counted");
}
#[test]
fn nested_scopes_are_additive() {
let ((_, inner_ops), outer_ops) = count_ops(|| {
record_op(); let inner = count_ops(|| {
record_op();
record_op(); });
record_op(); inner
});
assert_eq!(inner_ops, 2, "inner scope counts only its own ops");
assert_eq!(
outer_ops, 4,
"outer scope counts its own 2 ops plus the 2 propagated from the inner scope"
);
}
}