use std::alloc::{GlobalAlloc, Layout, System};
use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
pub static ALLOCATED_TOTAL: AtomicUsize = AtomicUsize::new(0);
pub static FREED_TOTAL: AtomicUsize = AtomicUsize::new(0);
pub static ALLOC_COUNT: AtomicUsize = AtomicUsize::new(0);
pub struct CountingAllocator;
unsafe impl GlobalAlloc for CountingAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let ptr = unsafe { System.alloc(layout) };
if !ptr.is_null() {
ALLOCATED_TOTAL.fetch_add(layout.size(), Relaxed);
ALLOC_COUNT.fetch_add(1, Relaxed);
}
ptr
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
unsafe { System.dealloc(ptr, layout) };
FREED_TOTAL.fetch_add(layout.size(), Relaxed);
}
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
let new_ptr = unsafe { System.realloc(ptr, layout, new_size) };
if !new_ptr.is_null() {
if new_size > layout.size() {
ALLOCATED_TOTAL.fetch_add(new_size - layout.size(), Relaxed);
} else {
FREED_TOTAL.fetch_add(layout.size() - new_size, Relaxed);
}
}
new_ptr
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct AllocSnapshot {
pub allocated: usize,
pub freed: usize,
pub count: usize,
}
impl AllocSnapshot {
pub fn now() -> Self {
Self {
allocated: ALLOCATED_TOTAL.load(Relaxed),
freed: FREED_TOTAL.load(Relaxed),
count: ALLOC_COUNT.load(Relaxed),
}
}
pub fn delta_since(&self, baseline: &Self) -> AllocDelta {
AllocDelta {
allocated_bytes: self.allocated.saturating_sub(baseline.allocated),
freed_bytes: self.freed.saturating_sub(baseline.freed),
alloc_count: self.count.saturating_sub(baseline.count),
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct AllocDelta {
pub allocated_bytes: usize,
pub freed_bytes: usize,
pub alloc_count: usize,
}
impl AllocDelta {
pub fn live_bytes(&self) -> usize {
self.allocated_bytes.saturating_sub(self.freed_bytes)
}
}