use core::alloc::{GlobalAlloc, Layout};
use crate::counters::Counters;
pub(crate) static COUNTERS: Counters = Counters::new();
pub struct TrackingAlloc<A> {
inner: A,
}
impl<A> TrackingAlloc<A> {
pub const fn new(inner: A) -> Self {
Self { inner }
}
}
unsafe impl<A: GlobalAlloc> GlobalAlloc for TrackingAlloc<A> {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let ptr = unsafe { self.inner.alloc(layout) };
if !ptr.is_null() {
COUNTERS.record_alloc(layout.size());
crate::detail::record_alloc(layout.size());
}
ptr
}
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
let ptr = unsafe { self.inner.alloc_zeroed(layout) };
if !ptr.is_null() {
COUNTERS.record_alloc(layout.size());
crate::detail::record_alloc(layout.size());
}
ptr
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
unsafe { self.inner.dealloc(ptr, layout) };
COUNTERS.record_free(layout.size());
crate::detail::record_free(layout.size());
}
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
let new_ptr = unsafe { self.inner.realloc(ptr, layout, new_size) };
if !new_ptr.is_null() {
COUNTERS.record_realloc(layout.size(), new_size);
crate::detail::record_realloc(layout.size(), new_size);
}
new_ptr
}
}
#[macro_export]
macro_rules! install_global_allocator {
() => {
#[global_allocator]
static CN_GLOBAL_ALLOC: $crate::TrackingAlloc<std::alloc::System> =
$crate::TrackingAlloc::new(std::alloc::System);
};
}
#[cfg(test)]
mod tests {
use super::*;
use std::alloc::System;
#[test]
fn tracking_alloc_forwards_and_counts() {
let alloc = TrackingAlloc::new(System);
let layout = Layout::from_size_align(4096, 8).expect("valid layout");
let before = crate::stats().map(|s| s.live_bytes).unwrap_or(0);
let ptr = unsafe { alloc.alloc(layout) };
assert!(!ptr.is_null(), "system allocator returned null for 4 KiB");
let during = crate::stats()
.expect("the wrapper just allocated")
.live_bytes;
assert!(
during >= before + 4096,
"live bytes {during} did not rise by the allocation size from {before}"
);
unsafe { alloc.dealloc(ptr, layout) };
assert!(crate::stats().expect("counters are live").live_bytes >= before);
}
}