#![deny(
missing_debug_implementations,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unused_import_braces,
unused_imports,
unused_qualifications,
missing_docs
)]
#![cfg_attr(feature = "nightly", feature(const_fn))]
#![cfg_attr(feature = "docs-rs", feature(allocator_api))]
use std::{
alloc::{GlobalAlloc, Layout, System},
ops,
sync::atomic::{AtomicIsize, AtomicUsize, Ordering},
};
#[derive(Default, Debug)]
pub struct StatsAlloc<T: GlobalAlloc> {
allocations: AtomicUsize,
deallocations: AtomicUsize,
reallocations: AtomicUsize,
bytes_allocated: AtomicUsize,
bytes_deallocated: AtomicUsize,
bytes_reallocated: AtomicIsize,
inner: T,
}
#[derive(Clone, Copy, Default, Debug, Hash, PartialEq, Eq)]
pub struct Stats {
pub allocations: usize,
pub deallocations: usize,
pub reallocations: usize,
pub bytes_allocated: usize,
pub bytes_deallocated: usize,
pub bytes_reallocated: isize,
}
pub static INSTRUMENTED_SYSTEM: StatsAlloc<System> = StatsAlloc {
allocations: AtomicUsize::new(0),
deallocations: AtomicUsize::new(0),
reallocations: AtomicUsize::new(0),
bytes_allocated: AtomicUsize::new(0),
bytes_deallocated: AtomicUsize::new(0),
bytes_reallocated: AtomicIsize::new(0),
inner: System,
};
impl StatsAlloc<System> {
#[cfg(feature = "nightly")]
pub const fn system() -> Self {
Self::new(System)
}
#[cfg(not(feature = "nightly"))]
pub fn system() -> Self {
Self::new(System)
}
}
impl<T: GlobalAlloc> StatsAlloc<T> {
#[cfg(feature = "nightly")]
pub const fn new(inner: T) -> Self {
StatsAlloc {
allocations: AtomicUsize::new(0),
deallocations: AtomicUsize::new(0),
reallocations: AtomicUsize::new(0),
bytes_allocated: AtomicUsize::new(0),
bytes_deallocated: AtomicUsize::new(0),
bytes_reallocated: AtomicIsize::new(0),
inner,
}
}
#[cfg(not(feature = "nightly"))]
pub fn new(inner: T) -> Self {
StatsAlloc {
allocations: AtomicUsize::new(0),
deallocations: AtomicUsize::new(0),
reallocations: AtomicUsize::new(0),
bytes_allocated: AtomicUsize::new(0),
bytes_deallocated: AtomicUsize::new(0),
bytes_reallocated: AtomicIsize::new(0),
inner,
}
}
pub fn stats(&self) -> Stats {
Stats {
allocations: self.allocations.load(Ordering::SeqCst),
deallocations: self.deallocations.load(Ordering::SeqCst),
reallocations: self.reallocations.load(Ordering::SeqCst),
bytes_allocated: self.bytes_allocated.load(Ordering::SeqCst),
bytes_deallocated: self.bytes_deallocated.load(Ordering::SeqCst),
bytes_reallocated: self.bytes_reallocated.load(Ordering::SeqCst),
}
}
}
impl ops::Sub for Stats {
type Output = Stats;
fn sub(mut self, rhs: Self) -> Self::Output {
self -= rhs;
self
}
}
impl ops::SubAssign for Stats {
fn sub_assign(&mut self, rhs: Self) {
self.allocations -= rhs.allocations;
self.deallocations -= rhs.deallocations;
self.reallocations -= rhs.reallocations;
self.bytes_allocated -= rhs.bytes_allocated;
self.bytes_deallocated -= rhs.bytes_deallocated;
self.bytes_reallocated -= rhs.bytes_reallocated;
}
}
#[derive(Debug)]
pub struct Region<'a, T: GlobalAlloc + 'a> {
alloc: &'a StatsAlloc<T>,
initial_stats: Stats,
}
impl<'a, T: GlobalAlloc + 'a> Region<'a, T> {
#[inline]
pub fn new(alloc: &'a StatsAlloc<T>) -> Self {
Region {
alloc,
initial_stats: alloc.stats(),
}
}
#[inline]
pub fn initial(&self) -> Stats {
self.initial_stats
}
#[inline]
pub fn change(&self) -> Stats {
self.alloc.stats() - self.initial_stats
}
#[inline]
pub fn change_and_reset(&mut self) -> Stats {
let latest = self.alloc.stats();
let diff = latest - self.initial_stats;
self.initial_stats = latest;
diff
}
#[inline]
pub fn reset(&mut self) {
self.initial_stats = self.alloc.stats();
}
}
unsafe impl<'a, T: GlobalAlloc + 'a> GlobalAlloc for &'a StatsAlloc<T> {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
(*self).alloc(layout)
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
(*self).dealloc(ptr, layout)
}
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
(*self).alloc_zeroed(layout)
}
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
(*self).realloc(ptr, layout, new_size)
}
}
unsafe impl<T: GlobalAlloc> GlobalAlloc for StatsAlloc<T> {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
self.allocations.fetch_add(1, Ordering::SeqCst);
self.bytes_allocated.fetch_add(layout.size(), Ordering::SeqCst);
self.inner.alloc(layout)
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
self.deallocations.fetch_add(1, Ordering::SeqCst);
self.bytes_deallocated.fetch_add(layout.size(), Ordering::SeqCst);
self.inner.dealloc(ptr, layout)
}
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
self.allocations.fetch_add(1, Ordering::SeqCst);
self.bytes_allocated.fetch_add(layout.size(), Ordering::SeqCst);
self.inner.alloc_zeroed(layout)
}
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
self.reallocations.fetch_add(1, Ordering::SeqCst);
if new_size > layout.size() {
let difference = new_size - layout.size();
self.bytes_allocated.fetch_add(difference, Ordering::SeqCst);
} else if new_size < layout.size() {
let difference = layout.size() - new_size;
self.bytes_deallocated.fetch_add(difference, Ordering::SeqCst);
}
self.bytes_reallocated
.fetch_add(new_size.wrapping_sub(layout.size()) as isize, Ordering::SeqCst);
self.inner.realloc(ptr, layout, new_size)
}
}