#![feature(c_size_t)]
#[cfg(feature = "track_allocations")]
mod tracker;
use core::ffi::c_size_t;
use std::cell::UnsafeCell;
use std::{alloc::GlobalAlloc, num::NonZeroUsize, os::raw::c_void};
use allocations::{allocate, deallocate};
#[cfg(not(target_os = "macos"))]
thread_local! {
static CURRENT_THREAD_ALLOCATOR: UnsafeCell<InternalState<512>> = const {UnsafeCell::new(InternalState::new()) };
}
#[derive(Debug, Copy, Clone)]
struct Block {
size: c_size_t,
ptr: *mut u8,
}
unsafe impl Send for Block {}
unsafe impl Sync for Block {}
struct InternalState<const SIZE: usize> {
size: usize,
free_array: [Option<Block>; SIZE],
}
impl<const SIZE: usize> InternalState<SIZE> {
const fn new() -> Self {
Self {
size: 0,
free_array: [None; SIZE],
}
}
fn insert(&mut self, block: Block) {
self.free_array[self.size] = Some(block);
self.size += 1;
}
}
pub struct BeneAlloc {
#[cfg(feature = "track_allocations")]
pub tracker: UnsafeCell<tracker::Tracker>,
}
unsafe impl Sync for BeneAlloc {}
unsafe impl Send for BeneAlloc {}
impl BeneAlloc {
pub const fn new() -> Self {
Self {
#[cfg(feature = "track_allocations")]
tracker: UnsafeCell::new(tracker::Tracker::new()),
}
}
#[cfg(feature = "track_allocations")]
pub fn print(&self) {
unsafe {
self.tracker.get().as_ref().unwrap().print();
}
}
}
unsafe impl GlobalAlloc for BeneAlloc {
unsafe fn alloc(&self, layout: std::alloc::Layout) -> *mut u8 {
#[cfg(feature = "track_allocations")]
{
let mut tracker = self.tracker.get().as_mut().unwrap();
tracker.track_allocation(layout);
}
let state = CURRENT_THREAD_ALLOCATOR.with(|state| unsafe { &mut *state.get() });
let freeblocks_size = state.size;
let align = NonZeroUsize::new_unchecked(layout.align());
for i in 0..freeblocks_size {
if let Some(block) = state.free_array[i] {
if block.size >= layout.size() && (block.ptr as usize % align) == 0 {
let original_ptr = block.ptr;
if block.size > layout.size() {
let new_block = Block {
size: block.size - layout.size(),
ptr: block.ptr.add(layout.size()),
};
state.free_array[i] = Some(new_block);
} else {
state.free_array[i] = state.free_array[freeblocks_size - 1];
state.free_array[freeblocks_size - 1] = None;
state.size -= 1;
}
debug_assert!(
original_ptr as usize % layout.align() == 0,
"Alignment error. ptr: {:p}, align: {}",
original_ptr,
layout.align()
);
return original_ptr;
}
}
}
let ret = allocate(layout.size());
debug_assert!(ret as usize % layout.align() == 0);
ret as *mut u8
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: std::alloc::Layout) {
#[cfg(feature = "track_allocations")]
{
let mut tracker = self.tracker.get().as_mut().unwrap();
tracker.track_deallocation(layout);
}
let state = CURRENT_THREAD_ALLOCATOR.with(|state| unsafe { &mut *state.get() });
if state.size < state.free_array.len() {
state.insert(Block {
size: layout.size(),
ptr,
});
} else {
deallocate(ptr as *mut c_void, layout.size());
}
}
}