#[allow(dead_code, unused_imports)]
#[allow(clippy::useless_attribute)]
extern crate alloc;
#[allow(dead_code, unused_imports)]
use core::alloc::{GlobalAlloc, Layout};
#[allow(dead_code, unused_imports)]
use core::cell::UnsafeCell;
use core::cmp::max;
use core::ptr;
use core::ptr::NonNull;
pub(crate) struct Hole {
next: Option<NonNull<Hole>>,
size: usize,
prev: Option<NonNull<Hole>>,
}
impl Hole {
pub fn header_size() -> usize {
core::mem::size_of::<Self>()
}
pub fn min_size() -> usize {
Self::header_size()
}
pub fn align() -> usize {
core::mem::align_of::<Self>()
}
pub fn round_to_align(size: usize) -> usize {
align_up(size, Self::align())
}
}
pub(crate) struct HoleList {
pub front: Option<NonNull<Hole>>,
}
impl HoleList {
pub const fn new() -> Self {
HoleList { front: None }
}
pub unsafe fn init(&mut self, addr: *mut u8, size: usize) -> bool {
if size < Hole::min_size() {
self.front = None;
return false;
}
ptr::write(
addr as *mut Hole,
Hole { next: None, size, prev: None },
);
self.front = Some(NonNull::new_unchecked(addr as *mut Hole));
true
}
pub unsafe fn allocate_first_fit(&mut self, layout: Layout) -> *mut u8 {
let size = Hole::round_to_align(max(layout.size(), Hole::min_size()));
let effective_align = max(layout.align(), Hole::align());
let mut current = self.front;
while let Some(hole_ptr) = current {
let hole = &*hole_ptr.as_ptr();
let hole_addr = hole_ptr.as_ptr() as usize;
let hole_end_addr = hole_addr.wrapping_add(hole.size);
let aligned = align_up(hole_addr, effective_align) as *mut u8;
let aligned_addr = aligned as usize;
let alloc_end = aligned_addr.wrapping_add(size);
if aligned_addr < hole_addr
|| aligned_addr >= hole_end_addr
|| alloc_end > hole_end_addr
{
current = hole.next;
continue;
}
self.remove(hole_ptr);
let front = aligned_addr.wrapping_sub(hole_addr);
if front >= Hole::min_size() {
let front_hole = hole_addr as *mut Hole;
ptr::write(
front_hole,
Hole { next: None, size: front, prev: None },
);
self.insert(NonNull::new_unchecked(front_hole));
}
let tail = hole_end_addr.wrapping_sub(alloc_end);
if tail >= Hole::min_size() {
let tail_hole = aligned.add(size) as *mut Hole;
ptr::write(
tail_hole,
Hole { next: None, size: tail, prev: None },
);
self.insert(NonNull::new_unchecked(tail_hole));
}
return aligned;
}
ptr::null_mut()
}
pub unsafe fn deallocate(&mut self, ptr: *mut u8, layout: Layout) {
let size = Hole::round_to_align(max(layout.size(), Hole::min_size()));
ptr::write(
ptr as *mut Hole,
Hole { next: None, size, prev: None },
);
self.insert(NonNull::new_unchecked(ptr as *mut Hole));
}
pub unsafe fn remove(&mut self, hole: NonNull<Hole>) {
let prev = (*hole.as_ptr()).prev;
let next = (*hole.as_ptr()).next;
if let Some(p) = prev {
(*p.as_ptr()).next = next;
} else {
self.front = next;
}
if let Some(n) = next {
(*n.as_ptr()).prev = prev;
}
}
pub unsafe fn insert(&mut self, mut hole: NonNull<Hole>) {
let hole_addr = hole.as_ptr() as usize;
let mut current = self.front;
let mut prev: Option<NonNull<Hole>> = None;
while let Some(curr) = current {
if curr.as_ptr() as usize > hole_addr {
break;
}
prev = current;
current = (*curr.as_ptr()).next;
}
(*hole.as_ptr()).prev = prev;
(*hole.as_ptr()).next = current;
if let Some(p) = prev {
(*p.as_ptr()).next = Some(hole);
} else {
self.front = Some(hole);
}
if let Some(c) = current {
(*c.as_ptr()).prev = Some(hole);
}
if let Some(p) = prev {
let p_off = p.as_ptr() as usize;
let p_sz = (*p.as_ptr()).size;
let h_off = hole.as_ptr() as usize;
let h_sz = (*hole.as_ptr()).size;
if p_off.wrapping_add(p_sz).wrapping_add(Hole::min_size()) > h_off {
let new_sz = max(p_off.wrapping_add(p_sz), h_off.wrapping_add(h_sz)) - p_off;
(*p.as_ptr()).size = new_sz;
(*p.as_ptr()).next = (*hole.as_ptr()).next;
if let Some(n) = (*hole.as_ptr()).next {
(*n.as_ptr()).prev = Some(p);
}
hole = p;
}
}
if let Some(n) = (*hole.as_ptr()).next {
let h_off = hole.as_ptr() as usize;
let h_sz = (*hole.as_ptr()).size;
let n_off = n.as_ptr() as usize;
let n_sz = (*n.as_ptr()).size;
if h_off.wrapping_add(h_sz).wrapping_add(Hole::min_size()) > n_off {
let new_sz = max(h_off.wrapping_add(h_sz), n_off.wrapping_add(n_sz)) - h_off;
(*hole.as_ptr()).size = new_sz;
(*hole.as_ptr()).next = (*n.as_ptr()).next;
if let Some(nn) = (*n.as_ptr()).next {
(*nn.as_ptr()).prev = Some(hole);
}
}
}
}
pub fn hole_count(&self) -> usize {
let mut count = 0;
let mut cur = self.front;
while let Some(c) = cur {
count += 1;
cur = unsafe { (*c.as_ptr()).next };
}
count
}
pub fn total_free(&self) -> usize {
let mut total = 0;
let mut cur = self.front;
while let Some(c) = cur {
total += unsafe { (*c.as_ptr()).size };
cur = unsafe { (*c.as_ptr()).next };
}
total
}
}
#[cfg(not(test))]
struct GlobalHeap(UnsafeCell<HoleList>);
#[cfg(not(test))]
unsafe impl Sync for GlobalHeap {}
#[cfg(not(test))]
static mut HEAP_INITIALIZED: bool = false;
#[cfg(not(test))]
pub unsafe fn init_heap(base: usize, size: usize) -> bool {
let holes = &mut *ALLOC.0.get();
let ok = holes.init(base as *mut u8, size);
HEAP_INITIALIZED = ok;
ok
}
#[cfg(not(test))]
unsafe fn ensure_heap_initialized() -> bool {
HEAP_INITIALIZED
}
#[cfg(not(test))]
unsafe impl GlobalAlloc for GlobalHeap {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
if !ensure_heap_initialized() {
return ptr::null_mut();
}
let holes = &mut *self.0.get();
holes.allocate_first_fit(layout)
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
if !ensure_heap_initialized() {
return;
}
let holes = &mut *self.0.get();
holes.deallocate(ptr, layout);
}
}
#[cfg(not(test))]
#[global_allocator]
static ALLOC: GlobalHeap = GlobalHeap(UnsafeCell::new(HoleList::new()));
pub fn align_up(addr: usize, align: usize) -> usize {
(addr + align - 1) & !(align - 1)
}