use core::sync::atomic::{AtomicU64, Ordering};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum DeallocPath {
HugeClassifier = 0,
InPlaceSmall = 1,
ActiveFreeLastBlock = 2,
FullToActive = 3,
ColdOrRecursing = 4,
}
impl DeallocPath {
pub const COUNT: usize = 5;
pub const ALL: [Self; Self::COUNT] = [
Self::HugeClassifier,
Self::InPlaceSmall,
Self::ActiveFreeLastBlock,
Self::FullToActive,
Self::ColdOrRecursing,
];
#[inline]
pub const fn index(self) -> usize {
self as usize
}
pub const fn name(self) -> &'static str {
match self {
Self::HugeClassifier => "huge_classifier",
Self::InPlaceSmall => "in_place_small",
Self::ActiveFreeLastBlock => "active_free_last_block",
Self::FullToActive => "full_to_active",
Self::ColdOrRecursing => "cold_or_recursing",
}
}
}
static COUNTS: [AtomicU64; DeallocPath::COUNT] = [const { AtomicU64::new(0) }; DeallocPath::COUNT];
#[inline(always)]
pub fn record(path: DeallocPath) {
COUNTS[path.index()].fetch_add(1, Ordering::Relaxed);
}
pub fn snapshot() -> [(DeallocPath, &'static str, u64); DeallocPath::COUNT] {
DeallocPath::ALL.map(|path| {
(
path,
path.name(),
COUNTS[path.index()].load(Ordering::Relaxed),
)
})
}
pub fn reset() {
for c in &COUNTS {
c.store(0, Ordering::Relaxed);
}
}
pub fn total() -> u64 {
COUNTS.iter().map(|c| c.load(Ordering::Relaxed)).sum()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn each_path_has_a_distinct_stable_index() {
let mut seen = [false; DeallocPath::COUNT];
for path in DeallocPath::ALL {
let index = path.index();
assert!(
index < DeallocPath::COUNT,
"{path:?} index {index} exceeds counter array length"
);
assert!(!seen[index], "duplicate index for {path:?}");
seen[index] = true;
}
assert_eq!(
seen,
[true; DeallocPath::COUNT],
"every counter slot should be covered by exactly one path"
);
}
#[test]
fn reset_clears_every_counter() {
record(DeallocPath::InPlaceSmall);
record(DeallocPath::FullToActive);
assert!(total() >= 2);
reset();
for (_, _, count) in snapshot() {
assert_eq!(count, 0, "counter not zeroed after reset()");
}
assert_eq!(total(), 0);
}
#[test]
fn record_increments_only_the_targeted_path() {
reset();
for _ in 0..7 {
record(DeallocPath::InPlaceSmall);
}
record(DeallocPath::ColdOrRecursing);
let snap = snapshot();
for (path, _name, count) in snap {
let expected = match path {
DeallocPath::InPlaceSmall => 7,
DeallocPath::ColdOrRecursing => 1,
_ => 0,
};
assert_eq!(count, expected, "{path:?} counter mismatch");
}
}
}