use core::alloc::{GlobalAlloc, Layout};
use core::cell::UnsafeCell;
use core::mem::ManuallyDrop;
use core::ptr::NonNull;
use crate::class;
use crate::heap::Heap;
thread_local! {
static HEAP: UnsafeCell<ManuallyDrop<Heap>> =
const { UnsafeCell::new(ManuallyDrop::new(Heap::new(0))) };
}
fn with_heap<R>(f: impl FnOnce(&mut Heap) -> R) -> Option<R> {
HEAP.try_with(|cell| {
let heap = unsafe { &mut *cell.get() };
heap.ensure_identity();
f(heap)
})
.ok()
}
pub struct KevyAlloc;
const BASE_SLOT: usize = core::mem::size_of::<usize>();
fn over_aligned_total(layout: Layout) -> Option<usize> {
layout.size().checked_add(layout.align())?.checked_add(BASE_SLOT)
}
fn is_over_aligned(layout: Layout) -> bool {
layout.align() > class::MAX_NATIVE_ALIGN
&& !(layout.size() > class::MAX_SMALL && layout.align() <= crate::os::PAGE)
}
unsafe impl GlobalAlloc for KevyAlloc {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
if is_over_aligned(layout) {
return alloc_over_aligned(layout);
}
match with_heap(|h| h.alloc(layout.size(), layout.align())) {
Some(Some(p)) => p.as_ptr(),
_ => core::ptr::null_mut(),
}
}
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
if let Some(p) = NonNull::new(ptr)
&& !is_over_aligned(layout)
&& new_size <= class::MAX_SMALL
&& with_heap(|h| {
unsafe { h.try_resize_in_place(p, layout.size(), new_size, layout.align()) }
}) == Some(true)
{
return ptr;
}
unsafe {
let Ok(new_layout) = Layout::from_size_align(new_size, layout.align()) else {
return core::ptr::null_mut();
};
let fresh = self.alloc(new_layout);
if !fresh.is_null() {
core::ptr::copy_nonoverlapping(ptr, fresh, layout.size().min(new_size));
self.dealloc(ptr, layout);
}
fresh
}
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
let Some(p) = NonNull::new(ptr) else { return };
if is_over_aligned(layout) {
unsafe { dealloc_over_aligned(p, layout) };
return;
}
with_heap(|h| {
unsafe { h.dealloc(p, layout.size(), layout.align()) };
});
}
}
fn alloc_over_aligned(layout: Layout) -> *mut u8 {
let Some(total) = over_aligned_total(layout) else {
return core::ptr::null_mut();
};
let Some(Some(base)) = with_heap(|h| h.alloc(total, class::MIN_ALIGN)) else {
return core::ptr::null_mut();
};
let raw = base.as_ptr() as usize;
let aligned = (raw + BASE_SLOT + layout.align() - 1) & !(layout.align() - 1);
unsafe { ((aligned - BASE_SLOT) as *mut usize).write(raw) };
aligned as *mut u8
}
unsafe fn dealloc_over_aligned(ptr: NonNull<u8>, layout: Layout) {
let Some(total) = over_aligned_total(layout) else {
return;
};
let raw = unsafe { ((ptr.as_ptr() as usize - BASE_SLOT) as *const usize).read() };
let Some(base) = NonNull::new(raw as *mut u8) else {
return;
};
with_heap(|h| {
unsafe { h.dealloc(base, total, class::MIN_ALIGN) };
});
}
#[must_use]
pub fn thread_stats() -> Option<crate::Stats> {
with_heap(|h| h.snapshot())
}
pub fn thread_reclaim() {
with_heap(Heap::reclaim);
}