#![no_std]
#![doc = include_str!("../README.md")]
#[cfg(feature = "std")]
extern crate std;
use core::alloc::GlobalAlloc;
use derive_more::{Add, AddAssign, Sub, SubAssign, Neg};
#[cfg(test)]
mod test;
#[cfg(feature = "thread")]
std::thread_local! {
static THREAD_METRICS: core::cell::Cell<Metrics> = core::cell::Cell::new(Metrics { allocated_bytes: 0, allocations: 0 });
}
#[cfg(feature = "global")]
static GLOBAL_METRICS: spin::Mutex<Metrics> = spin::Mutex::new(Metrics { allocated_bytes: 0, allocations: 0 });
fn add_assign_metrics(_metrics: Metrics) {
#[cfg(feature = "thread")]
{ THREAD_METRICS.set(THREAD_METRICS.get() + _metrics) }
#[cfg(feature = "global")]
{ *GLOBAL_METRICS.lock() += _metrics }
}
#[cfg_attr(feature = "thread", doc = r#"
```rust
let before = alloc_metrics::thread_metrics();
// ... do some work ...
let after = alloc_metrics::thread_metrics();
let delta = after - before;
```
"#)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Add, AddAssign, Sub, SubAssign, Neg)]
pub struct Metrics {
pub allocated_bytes: isize,
pub allocations: isize,
}
pub struct MetricAlloc<A: GlobalAlloc> {
wrapped: A,
}
impl<A: GlobalAlloc> MetricAlloc<A> {
pub const fn new(wrapped: A) -> Self {
Self { wrapped }
}
}
unsafe impl<A: GlobalAlloc> GlobalAlloc for MetricAlloc<A> {
unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
let res = self.wrapped.alloc(layout);
if !res.is_null() {
add_assign_metrics(Metrics {
allocated_bytes: layout.size() as isize,
allocations: 1,
});
}
res
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) {
self.wrapped.dealloc(ptr, layout);
add_assign_metrics(Metrics {
allocated_bytes: -(layout.size() as isize),
allocations: -1,
});
}
}
#[cfg(feature = "thread")]
pub fn thread_metrics() -> Metrics {
THREAD_METRICS.get()
}
#[cfg(feature = "global")]
pub fn global_metrics() -> Metrics {
*GLOBAL_METRICS.lock()
}