#[cfg(not(any(feature = "std", feature = "rustc-dep-of-std")))]
use safa_abi::ffi::{option::OptZero, slice::Slice};
use safa_abi::mem::MemMapFlags;
use crate::sync::locks::Mutex;
use super::syscalls;
use core::{alloc::GlobalAlloc, ptr::NonNull};
#[derive(Debug, Default)]
struct Block {
free: bool,
next: Option<NonNull<Block>>,
data_len: usize,
}
fn sys_allocate(size_hint: usize) -> Option<(*mut u8, usize)> {
let page_count = size_hint.next_multiple_of(4096) / 4096;
let (_, s) = syscalls::mem::map(
core::ptr::null(),
page_count,
0,
None,
None,
MemMapFlags::WRITE,
)
.ok()?;
Some((s.as_ptr() as *mut u8, s.len()))
}
impl Block {
pub fn create(data_len: usize) -> Option<(NonNull<Self>, Option<NonNull<Block>>)> {
let data_len = data_len.next_multiple_of(size_of::<Block>());
let size = data_len + size_of::<Block>();
let size = size.next_multiple_of(align_of::<Block>());
assert!(size <= isize::MAX as usize);
let (alloc_ptr, alloc_size) = sys_allocate(size)?;
assert!(alloc_size >= size);
let ptr = alloc_ptr as *mut Block;
unsafe {
*ptr = Self {
free: true,
data_len: size - size_of::<Block>(),
..Default::default()
};
if alloc_size > size {
let extra_block_size = alloc_size - size;
let extra_block_ptr = alloc_ptr.add(size) as *mut Block;
*extra_block_ptr = Self {
free: true,
data_len: extra_block_size - size_of::<Block>(),
..Default::default()
};
(*ptr).next = Some(NonNull::new_unchecked(extra_block_ptr));
}
Some((NonNull::new_unchecked(ptr), (*ptr).next))
}
}
#[inline(always)]
pub unsafe fn block_from_data_ptr(data: NonNull<u8>) -> NonNull<Self> {
unsafe { NonNull::new_unchecked((data.as_ptr() as *mut Block).offset(-1)) }
}
#[inline(always)]
pub unsafe fn data_from_ptr(ptr: *const Self) -> NonNull<[u8]> {
unsafe {
let length = (*ptr).data_len;
let ptr_to_data = ptr.offset(1) as *const u8 as *mut u8;
NonNull::new_unchecked(core::slice::from_raw_parts_mut(ptr_to_data, length) as *mut [u8])
}
}
}
pub struct SystemAllocator {
head: Option<NonNull<Block>>,
}
impl SystemAllocator {
const fn new() -> Self {
Self { head: None }
}
#[inline]
fn try_find_block(&self, data_len: usize) -> Option<NonNull<Block>> {
let size = data_len + size_of::<Block>();
let size = size.next_multiple_of(align_of::<Block>());
let data_len = size - size_of::<Block>();
let mut current = self.head;
let mut best_block: Option<(NonNull<Block>, usize)> = None;
while let Some(block_ptr) = current {
let block = unsafe { &*block_ptr.as_ptr() };
if !block.free {
current = block.next;
continue;
}
if block.data_len == data_len {
return Some(block_ptr);
}
if block.data_len > data_len
&& best_block.is_none_or(|(_, bb_len)| bb_len > block.data_len)
{
best_block = Some((block_ptr, block.data_len));
}
current = block.next;
}
best_block.map(|(ptr, _)| ptr)
}
#[inline]
fn find_block(&mut self, data_len: usize) -> Option<NonNull<Block>> {
let data_len = data_len.next_multiple_of(size_of::<Block>());
if let Some(block) = self.try_find_block(data_len) {
let block_ptr = block.as_ptr();
unsafe {
let block_len = (*block_ptr).data_len;
if block_len > data_len && (block_len - data_len) > size_of::<Block>() {
let left_over = block_len - data_len;
let new_block_len = left_over - size_of::<Block>();
let new_block = block_ptr.add(1).byte_add(data_len);
*new_block = Block {
free: true,
data_len: new_block_len,
next: (*block_ptr).next.take(),
};
(*block_ptr).next = Some(NonNull::new_unchecked(new_block));
(*block_ptr).data_len = data_len;
}
}
Some(block)
} else {
unsafe {
let (new_block, new_allocation_tail) = Block::create(data_len)?;
let set_next_of = new_allocation_tail.unwrap_or(new_block);
let stolen_head = self.head.take();
(*set_next_of.as_ptr()).next = stolen_head;
self.head = Some(new_block);
Some(new_block)
}
}
}
fn merge_blocks(&mut self) {
let mut current = self.head;
while let Some(block_ptr) = current {
unsafe {
let block = block_ptr.as_ptr();
if !(*block).free {
current = (*block).next;
continue;
}
let Some(next) = (*block).next else {
return;
};
let next_ptr = next.as_ptr();
if !(*next_ptr).free {
current = (*block).next;
continue;
}
if block.add(1).byte_add((*block).data_len) == next_ptr {
(*block).next = (*next_ptr).next;
(*block).data_len += (*next_ptr).data_len + size_of::<Block>();
}
current = (*block).next;
}
}
}
fn allocate(&mut self, size: usize) -> Option<NonNull<[u8]>> {
let block = self.find_block(size)?;
unsafe {
let ptr = block.as_ptr();
(*ptr).free = false;
Some(Block::data_from_ptr(ptr))
}
}
unsafe fn deallocate(&mut self, block_data: NonNull<u8>) {
unsafe {
let block_ptr = Block::block_from_data_ptr(block_data).as_ptr();
let block = &mut *block_ptr;
block.free = true;
self.merge_blocks();
}
}
}
unsafe impl Send for SystemAllocator {}
unsafe impl Sync for SystemAllocator {}
pub struct GlobalSystemAllocator {
inner: Mutex<SystemAllocator>,
}
impl GlobalSystemAllocator {
const fn new() -> Self {
Self {
inner: Mutex::new(SystemAllocator::new()),
}
}
#[inline]
pub fn allocate(&self, size: usize) -> Option<NonNull<[u8]>> {
self.inner.lock().allocate(size)
}
#[inline]
pub unsafe fn deallocate(&self, ptr: NonNull<u8>) {
self.inner.lock().deallocate(ptr)
}
}
unsafe impl Sync for GlobalSystemAllocator {}
unsafe impl Send for GlobalSystemAllocator {}
unsafe impl GlobalAlloc for GlobalSystemAllocator {
unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
self.allocate(layout.size())
.map(|x| x.as_ptr() as *mut u8)
.unwrap_or(core::ptr::null_mut())
}
unsafe fn dealloc(&self, ptr: *mut u8, _: core::alloc::Layout) {
self.deallocate(NonNull::new_unchecked(ptr));
}
}
#[cfg_attr(
not(any(feature = "std", feature = "rustc-dep-of-std")),
global_allocator
)]
pub static GLOBAL_SYSTEM_ALLOCATOR: GlobalSystemAllocator = GlobalSystemAllocator::new();
#[cfg(not(any(feature = "std", feature = "rustc-dep-of-std")))]
#[unsafe(no_mangle)]
pub extern "C" fn syscreate(object_size: usize) -> OptZero<Slice<u8>> {
GLOBAL_SYSTEM_ALLOCATOR
.allocate(object_size)
.map(|mut x| unsafe {
let x = x.as_mut();
Slice::from_raw_parts(x.as_mut_ptr(), x.len())
})
.into()
}
#[cfg(not(any(feature = "std", feature = "rustc-dep-of-std")))]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sysdestroy(object_ptr: *mut u8) {
unsafe {
match NonNull::new(object_ptr) {
Some(ptr) => GLOBAL_SYSTEM_ALLOCATOR.deallocate(ptr),
None => (),
}
}
}