use std::{
alloc::{Allocator, Global},
panic::Location,
sync::{
atomic::{AtomicIsize, Ordering},
Arc,
},
};
pub type UnkaiGlobal = Unkai<Global>;
pub struct Unkai<A>
where
A: Allocator,
{
caller: &'static Location<'static>,
usage: Arc<AtomicIsize>,
alloc: A,
}
impl<A> Unkai<A>
where
A: Allocator,
{
#[track_caller]
pub fn new(alloc: A) -> Self {
Self {
caller: Location::caller(),
usage: Arc::new(AtomicIsize::new(0)),
alloc,
}
}
pub fn report_usage(&self) -> isize {
self.usage.load(Ordering::Relaxed)
}
pub fn report_caller(&self) -> &'static Location<'static> {
self.caller
}
}
unsafe impl<A> Allocator for Unkai<A>
where
A: Allocator,
{
fn allocate(
&self,
layout: std::alloc::Layout,
) -> Result<std::ptr::NonNull<[u8]>, std::alloc::AllocError> {
let size = layout.size();
self.usage.fetch_add(size as isize, Ordering::Relaxed);
self.alloc.allocate(layout)
}
unsafe fn deallocate(&self, ptr: std::ptr::NonNull<u8>, layout: std::alloc::Layout) {
let size = layout.size();
self.usage.fetch_sub(size as isize, Ordering::Relaxed);
self.alloc.deallocate(ptr, layout)
}
}
impl<A: Allocator + Clone> Clone for Unkai<A> {
fn clone(&self) -> Self {
Self {
caller: self.caller,
usage: self.usage.clone(),
alloc: self.alloc.clone(),
}
}
}
impl Default for UnkaiGlobal {
#[track_caller]
fn default() -> Self {
Self {
caller: Location::caller(),
usage: Default::default(),
alloc: Global,
}
}
}