diff --git a/agb/src/agb_alloc/block_allocator.rs b/agb/src/agb_alloc/block_allocator.rs new file mode 100644 index 0000000..9ca55fa --- /dev/null +++ b/agb/src/agb_alloc/block_allocator.rs @@ -0,0 +1,66 @@ +use core::alloc::{GlobalAlloc, Layout}; +use core::ptr::NonNull; + +use crate::interrupt::Mutex; + +use super::bump_allocator::BumpAllocator; + +struct Block { + used: bool, + next: Option>, +} + +impl Block { + pub unsafe fn from_data_ptr(data_ptr: *mut u8, layout: Layout) -> *mut Block { + let block_layout = Layout::new::(); + let (_, offset) = block_layout.extend(layout).expect("Overflow on allocation"); + + data_ptr.sub(offset).cast() + } +} + +struct BlockAllocatorState { + first_block: Option>, + last_block: Option>, +} + +pub(crate) struct BlockAllocator { + inner_allocator: BumpAllocator, + state: Mutex, +} + +unsafe impl GlobalAlloc for BlockAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let block_layout = Layout::new::(); + let (overall_layout, offset) = block_layout.extend(layout).expect("Overflow on allocation"); + + let block_ptr = self.inner_allocator.alloc(overall_layout); + + if block_ptr.is_null() { + return core::ptr::null_mut(); + } + + let block_ptr = NonNull::new_unchecked(block_ptr).cast(); + + let mut state = self.state.lock(); + + *block_ptr.cast::().as_mut() = Block { + used: true, + next: None, + }; + + state.first_block.get_or_insert(block_ptr); + + if let Some(last_block) = state.last_block { + last_block.cast::().as_mut().next = Some(block_ptr); + } + state.last_block = Some(block_ptr); + + block_ptr.as_ptr().cast::().add(offset) + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + let block = Block::from_data_ptr(ptr, layout); + (&mut *block).used = false; + } +} diff --git a/agb/src/agb_alloc/mod.rs b/agb/src/agb_alloc/mod.rs index 6d26da3..45220b1 100644 --- a/agb/src/agb_alloc/mod.rs +++ b/agb/src/agb_alloc/mod.rs @@ -1,3 +1,4 @@ +mod block_allocator; mod bump_allocator; use bump_allocator::BumpAllocator;