#![allow(unsafe_code)]
use std::alloc::{GlobalAlloc, Layout};
use std::cell::Cell;
use std::marker::PhantomData;
#[derive(Clone, Copy)]
pub struct AllocSnapshot {
pub alloc_count: u64,
pub alloc_bytes: u64,
pub free_count: u64,
pub free_bytes: u64,
}
impl AllocSnapshot {
pub const ZERO: Self = Self {
alloc_count: 0,
alloc_bytes: 0,
free_count: 0,
free_bytes: 0,
};
}
thread_local! {
static ALLOC_COUNTERS: Cell<AllocSnapshot> = const { Cell::new(AllocSnapshot::ZERO) };
static REENTRANCY: Cell<u32> = const { Cell::new(0) };
}
pub fn snapshot_alloc_counters() -> AllocSnapshot {
ALLOC_COUNTERS
.try_with(|c| c.get())
.unwrap_or(AllocSnapshot::ZERO)
}
pub fn record_alloc(size: u64) {
let _ = REENTRANCY.try_with(|r| {
if r.get() == 0 {
let _ = ALLOC_COUNTERS.try_with(|c| {
let mut s = c.get();
s.alloc_count += 1;
s.alloc_bytes += size;
c.set(s);
});
}
});
}
fn record_dealloc(size: u64) {
let _ = REENTRANCY.try_with(|r| {
if r.get() == 0 {
let _ = ALLOC_COUNTERS.try_with(|c| {
let mut s = c.get();
s.free_count += 1;
s.free_bytes += size;
c.set(s);
});
}
});
}
#[cfg(feature = "_test_internals")]
pub fn is_reentrant() -> bool {
REENTRANCY.try_with(|r| r.get() > 0).unwrap_or(false)
}
pub struct ReentrancyGuard {
_not_send: PhantomData<*const ()>,
}
impl ReentrancyGuard {
pub fn enter() -> Self {
let _ = REENTRANCY.try_with(|r| r.set(r.get() + 1));
Self {
_not_send: PhantomData,
}
}
}
impl Drop for ReentrancyGuard {
fn drop(&mut self) {
let _ = REENTRANCY.try_with(|r| {
let prev = r.get();
unsafe {
core::ptr::write_volatile(r.as_ptr(), prev.saturating_sub(1));
}
});
}
}
pub struct PianoAllocator<A> {
inner: A,
}
impl<A> PianoAllocator<A> {
pub const fn new(inner: A) -> Self {
Self { inner }
}
}
unsafe impl<A: GlobalAlloc> GlobalAlloc for PianoAllocator<A> {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let ptr = unsafe { self.inner.alloc(layout) };
if !ptr.is_null() {
record_alloc(layout.size() as u64);
}
ptr
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
unsafe { self.inner.dealloc(ptr, layout) };
record_dealloc(layout.size() as u64);
}
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
let result = unsafe { self.inner.realloc(ptr, layout, new_size) };
if !result.is_null() {
record_dealloc(layout.size() as u64);
record_alloc(new_size as u64);
}
result
}
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
let ptr = unsafe { self.inner.alloc_zeroed(layout) };
if !ptr.is_null() {
record_alloc(layout.size() as u64);
}
ptr
}
}