use std::alloc::{GlobalAlloc, Layout, System};
use std::cell::Cell;
thread_local! {
static ARMED: Cell<bool> = const { Cell::new(false) };
static COUNT: Cell<u64> = const { Cell::new(0) };
static INSIDE: Cell<bool> = const { Cell::new(false) };
}
pub struct Counting;
#[allow(unsafe_code)]
unsafe impl GlobalAlloc for Counting {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
count();
#[allow(unsafe_code)]
unsafe {
System.alloc(layout)
}
}
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
count();
#[allow(unsafe_code)]
unsafe {
System.alloc_zeroed(layout)
}
}
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
count();
#[allow(unsafe_code)]
unsafe {
System.realloc(ptr, layout, new_size)
}
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
#[allow(unsafe_code)]
unsafe {
System.dealloc(ptr, layout)
}
}
}
#[inline]
fn count() {
let armed = ARMED.try_with(Cell::get).unwrap_or(false);
if !armed || INSIDE.try_with(Cell::get).unwrap_or(true) {
return;
}
let _ = INSIDE.try_with(|inside| inside.set(true));
let _ = COUNT.try_with(|count| count.set(count.get() + 1));
let _ = INSIDE.try_with(|inside| inside.set(false));
}
pub fn arm() {
let _ = COUNT.try_with(|count| count.set(0));
let _ = ARMED.try_with(|armed| armed.set(true));
}
pub fn disarm() -> u64 {
let _ = ARMED.try_with(|armed| armed.set(false));
COUNT.try_with(Cell::get).unwrap_or(0)
}
pub fn counting<T>(f: impl FnOnce() -> T) -> (T, u64) {
arm();
let out = f();
(out, disarm())
}