use crate::memory::{MemoryBlock, MemoryStats};
use crate::types::Result;
use crate::RemDbError;
use core::ptr::NonNull;
#[cfg(not(feature = "std"))]
use crate::platform::OnceLock;
#[cfg(not(feature = "std"))]
pub struct Mutex<T> {
data: core::cell::UnsafeCell<T>,
lock: u32,
}
#[cfg(not(feature = "std"))]
impl<T> Mutex<T> {
pub fn new(data: T) -> Self {
Mutex {
data: core::cell::UnsafeCell::new(data),
lock: 0,
}
}
pub fn lock(&self) -> core::result::Result<MutexGuard<'_, T>, ()> {
while unsafe {
core::sync::atomic::AtomicU32::from_ptr(&self.lock as *const u32 as *mut u32)
.compare_exchange(
0,
1,
core::sync::atomic::Ordering::Acquire,
core::sync::atomic::Ordering::Relaxed,
)
.is_err()
} {
core::hint::spin_loop();
}
Ok(MutexGuard { mutex: self })
}
}
#[cfg(not(feature = "std"))]
pub struct MutexGuard<'a, T> {
mutex: &'a Mutex<T>,
}
#[cfg(not(feature = "std"))]
impl<'a, T> core::ops::Deref for MutexGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.mutex.data.get() }
}
}
#[cfg(not(feature = "std"))]
impl<'a, T> core::ops::DerefMut for MutexGuard<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.mutex.data.get() }
}
}
#[cfg(not(feature = "std"))]
impl<'a, T> Drop for MutexGuard<'a, T> {
fn drop(&mut self) {
unsafe {
core::sync::atomic::AtomicU32::from_ptr(&self.mutex.lock as *const u32 as *mut u32)
.store(0, core::sync::atomic::Ordering::Release);
}
}
}
#[cfg(not(feature = "std"))]
unsafe impl<T: Send> Sync for Mutex<T> {}
#[cfg(not(feature = "std"))]
unsafe impl<T: Send> Send for Mutex<T> {}
pub struct StaticAllocator {
start_ptr: NonNull<u8>,
size: usize,
used: usize,
free_list: Option<NonNull<MemoryBlock>>,
alloc_count: usize,
free_count: usize,
}
unsafe impl Send for StaticAllocator {}
unsafe impl Sync for StaticAllocator {}
impl StaticAllocator {
pub fn new(start_ptr: *mut u8, size: usize) -> Option<Self> {
const ALIGNMENT: usize = core::mem::align_of::<MemoryBlock>();
let start_addr = start_ptr as usize;
let aligned_addr = (start_addr + ALIGNMENT - 1) & !(ALIGNMENT - 1);
let aligned_ptr = aligned_addr as *mut u8;
let aligned_size = size - (aligned_addr - start_addr);
if aligned_size < MemoryBlock::SIZE {
return None;
}
let mut allocator = StaticAllocator {
start_ptr: NonNull::new(aligned_ptr)?,
size: aligned_size,
used: 0,
free_list: None,
alloc_count: 0,
free_count: 0,
};
allocator.reset();
Some(allocator)
}
pub fn reset(&mut self) {
unsafe {
let block_ptr = self.start_ptr.as_ptr() as *mut MemoryBlock;
(*block_ptr).next = None;
(*block_ptr).size = self.size - MemoryBlock::SIZE;
(*block_ptr).is_allocated = false;
self.free_list = Some(NonNull::new_unchecked(block_ptr));
self.used = 0;
self.alloc_count = 0;
self.free_count = 0;
}
}
pub fn update_memory_pool(&mut self, start_ptr: *mut u8, size: usize) {
const ALIGNMENT: usize = core::mem::align_of::<MemoryBlock>();
let start_addr = start_ptr as usize;
let aligned_addr = (start_addr + ALIGNMENT - 1) & !(ALIGNMENT - 1);
let aligned_ptr = aligned_addr as *mut u8;
let aligned_size = size - (aligned_addr - start_addr);
if let Some(ptr) = NonNull::new(aligned_ptr) {
self.start_ptr = ptr;
}
self.size = aligned_size;
self.reset();
}
pub fn allocate(&mut self, size: usize) -> Result<NonNull<u8>> {
let aligned_size = (size + 7) & !7;
let total_size = aligned_size + MemoryBlock::SIZE;
let mut current = &mut self.free_list;
while let Some(mut block) = *current {
let block_mut = unsafe { block.as_mut() };
if block_mut.size >= aligned_size {
if block_mut.size >= aligned_size + MemoryBlock::SIZE + 8 {
unsafe {
let new_block_size = block_mut.size - aligned_size - MemoryBlock::SIZE;
let new_block_ptr =
(block.as_ptr() as usize + total_size) as *mut MemoryBlock;
(*new_block_ptr).next = block_mut.next;
(*new_block_ptr).size = new_block_size;
(*new_block_ptr).is_allocated = false;
block_mut.next = Some(NonNull::new_unchecked(new_block_ptr));
block_mut.size = aligned_size;
}
}
let _allocated_block = *current;
*current = unsafe { block.as_mut() }.next;
unsafe {
block.as_mut().is_allocated = true;
}
self.used += unsafe { block.as_mut() }.size + MemoryBlock::SIZE;
self.alloc_count += 1;
let data_ptr = (block.as_ptr() as usize + MemoryBlock::SIZE) as *mut u8;
return Ok(NonNull::new(data_ptr).ok_or(RemDbError::InvalidPointer)?);
}
current = &mut unsafe { block.as_mut() }.next;
}
Err(crate::types::RemDbError::OutOfMemory)
}
pub fn free(&mut self, ptr: NonNull<u8>) {
let block_ptr = (ptr.as_ptr() as usize - MemoryBlock::SIZE) as *mut MemoryBlock;
let block_addr = block_ptr as usize;
let start_addr = self.start_ptr.as_ptr() as usize;
let end_addr = start_addr + self.size;
if block_addr < start_addr || block_addr >= end_addr {
return;
}
let Some(mut block) = NonNull::new(block_ptr) else {
return;
};
unsafe {
block.as_mut().is_allocated = false;
}
let block_size = unsafe { block.as_mut() }.size + MemoryBlock::SIZE;
if self.used >= block_size {
self.used -= block_size;
}
self.free_count += 1;
let mut current = &mut self.free_list;
while let Some(mut current_block) = *current {
if current_block.as_ptr() > block.as_ptr() {
unsafe {
block.as_mut().next = Some(current_block);
}
*current = Some(block);
self.merge_adjacent_blocks();
return;
}
current = &mut unsafe { current_block.as_mut() }.next;
}
unsafe {
block.as_mut().next = None;
}
*current = Some(block);
self.merge_adjacent_blocks();
}
fn merge_adjacent_blocks(&mut self) {
let mut current = &mut self.free_list;
while let Some(mut block) = *current {
let block_mut = unsafe { block.as_mut() };
if let Some(mut next_block) = block_mut.next {
let next_block_mut = unsafe { next_block.as_mut() };
let block_end = block.as_ptr() as usize + MemoryBlock::SIZE + block_mut.size;
let next_block_start = next_block.as_ptr() as usize;
if block_end == next_block_start {
block_mut.size += MemoryBlock::SIZE + next_block_mut.size;
block_mut.next = next_block_mut.next;
continue;
}
}
current = &mut block_mut.next;
}
}
pub fn stats(&self) -> MemoryStats {
let mut free_blocks = 0;
let mut max_free_block = 0;
let mut total_free = 0;
let mut current = self.free_list;
while let Some(block) = current {
free_blocks += 1;
unsafe {
total_free += block.as_ref().size + MemoryBlock::SIZE;
if block.as_ref().size > max_free_block {
max_free_block = block.as_ref().size;
}
current = block.as_ref().next;
}
}
let fragmentation = if free_blocks == 0 {
0.0
} else {
1.0 - (max_free_block as f32 / total_free as f32)
};
MemoryStats {
used: self.used,
total: self.size,
fragmentation,
alloc_count: self.alloc_count,
free_count: self.free_count,
}
}
}
#[cfg(feature = "std")]
static GLOBAL_ALLOCATOR: std::sync::Mutex<Option<StaticAllocator>> = std::sync::Mutex::new(None);
#[cfg(not(feature = "std"))]
static GLOBAL_ALLOCATOR: Mutex<Option<StaticAllocator>> = Mutex::new(None);
pub fn init_global_allocator(start_ptr: *mut u8, size: usize) -> Result<()> {
if size < MemoryBlock::SIZE * 2 {
return Err(crate::types::RemDbError::OutOfMemory);
}
if start_ptr.is_null() {
return Err(crate::types::RemDbError::OutOfMemory);
}
let new_allocator =
StaticAllocator::new(start_ptr, size).ok_or(crate::types::RemDbError::OutOfMemory)?;
let mut allocator_guard = GLOBAL_ALLOCATOR
.lock()
.map_err(|_| crate::types::RemDbError::OutOfMemory)?;
*allocator_guard = Some(new_allocator);
Ok(())
}
pub fn alloc(size: usize) -> Result<NonNull<u8>> {
let mut allocator_guard = GLOBAL_ALLOCATOR
.lock()
.map_err(|_| crate::types::RemDbError::OutOfMemory)?;
let allocator = allocator_guard
.as_mut()
.ok_or(crate::types::RemDbError::OutOfMemory)?;
allocator.allocate(size)
}
pub fn free(ptr: NonNull<u8>) {
if let Ok(mut allocator_guard) = GLOBAL_ALLOCATOR.lock() {
if let Some(allocator) = allocator_guard.as_mut() {
let _ = allocator.free(ptr); }
}
}
pub fn get_memory_stats() -> MemoryStats {
if let Ok(allocator_guard) = GLOBAL_ALLOCATOR.lock() {
if let Some(allocator) = allocator_guard.as_ref() {
return allocator.stats();
}
}
MemoryStats {
used: 0,
total: 0,
fragmentation: 0.0,
alloc_count: 0,
free_count: 0,
}
}
pub fn reset_global_allocator() -> Result<()> {
let mut allocator_guard = GLOBAL_ALLOCATOR
.lock()
.map_err(|_| crate::types::RemDbError::OutOfMemory)?;
if let Some(allocator) = allocator_guard.as_mut() {
allocator.reset();
}
Ok(())
}
#[cfg(not(feature = "std"))]
pub struct GlobalAllocator;
#[cfg(not(feature = "std"))]
unsafe impl core::alloc::GlobalAlloc for GlobalAllocator {
unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
match crate::memory::allocator::alloc(layout.size()) {
Ok(ptr) => ptr.as_ptr(),
Err(_) => core::ptr::null_mut(),
}
}
unsafe fn dealloc(&self, ptr: *mut u8, _layout: core::alloc::Layout) {
if let Some(non_null_ptr) = core::ptr::NonNull::new(ptr) {
crate::memory::allocator::free(non_null_ptr);
}
}
}
#[cfg(not(feature = "std"))]
#[global_allocator]
pub static GLOBAL_ALLOC: GlobalAllocator = GlobalAllocator;