use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
thread_local! {
static LIVE_BYTES_IN_THREAD: AtomicUsize = const { AtomicUsize::new(0) };
}
pub struct TrackingAllocator {
allocator: std::alloc::System,
}
impl TrackingAllocator {
pub const fn system() -> Self {
Self {
allocator: std::alloc::System,
}
}
pub fn live_bytes() -> usize {
LIVE_BYTES_IN_THREAD.with(|bytes| bytes.load(Relaxed))
}
pub fn memory_use<R>(run: impl Fn() -> R) -> (R, usize) {
let used_bytes_start = Self::live_bytes();
let ret = run();
let bytes_used = Self::live_bytes() - used_bytes_start;
(ret, bytes_used)
}
}
#[expect(unsafe_code)]
unsafe impl std::alloc::GlobalAlloc for TrackingAllocator {
unsafe fn alloc(&self, layout: std::alloc::Layout) -> *mut u8 {
LIVE_BYTES_IN_THREAD.with(|bytes| bytes.fetch_add(layout.size(), Relaxed));
unsafe { self.allocator.alloc(layout) }
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: std::alloc::Layout) {
LIVE_BYTES_IN_THREAD.with(|bytes| bytes.fetch_sub(layout.size(), Relaxed));
unsafe { self.allocator.dealloc(ptr, layout) };
}
}