#[derive(Debug)]
pub struct BumpAllocator {
buffer: [u8; 4096], offset: usize,
}
impl BumpAllocator {
pub const fn new() -> Self {
Self {
buffer: [0; 4096],
offset: 0,
}
}
pub fn alloc(&mut self, size: usize, align: usize) -> Option<&mut [u8]> {
let align_offset = (self.offset + align - 1) & !(align - 1);
let end = align_offset.checked_add(size)?;
if end > self.buffer.len() {
return None;
}
self.offset = end;
Some(&mut self.buffer[align_offset..end])
}
pub fn reset(&mut self) {
self.offset = 0;
}
pub fn used(&self) -> usize {
self.offset
}
pub fn available(&self) -> usize {
self.buffer.len() - self.offset
}
pub const fn capacity(&self) -> usize {
self.buffer.len()
}
}
impl Default for BumpAllocator {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct FixedSizePool<const SIZE: usize, const COUNT: usize> {
blocks: [[u8; SIZE]; COUNT],
free_list: [bool; COUNT],
}
impl<const SIZE: usize, const COUNT: usize> FixedSizePool<SIZE, COUNT> {
pub const fn new() -> Self {
Self {
blocks: [[0; SIZE]; COUNT],
free_list: [true; COUNT],
}
}
pub fn alloc(&mut self) -> Option<&mut [u8; SIZE]> {
for i in 0..COUNT {
if self.free_list[i] {
self.free_list[i] = false;
return Some(&mut self.blocks[i]);
}
}
None
}
pub unsafe fn dealloc(&mut self, block: *mut [u8; SIZE]) {
let pool_start = self.blocks.as_ptr() as usize;
let pool_end = pool_start + (SIZE * COUNT);
let block_addr = block as usize;
if block_addr >= pool_start && block_addr < pool_end {
let index = (block_addr - pool_start) / SIZE;
if index < COUNT {
self.free_list[index] = true;
}
}
}
pub fn free_count(&self) -> usize {
self.free_list.iter().filter(|&&free| free).count()
}
pub fn allocated_count(&self) -> usize {
COUNT - self.free_count()
}
}
impl<const SIZE: usize, const COUNT: usize> Default for FixedSizePool<SIZE, COUNT> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bump_allocator_creation() {
let allocator = BumpAllocator::new();
assert_eq!(allocator.used(), 0);
assert_eq!(allocator.available(), 4096);
assert_eq!(allocator.capacity(), 4096);
}
#[test]
fn test_bump_allocator_alloc() {
let mut allocator = BumpAllocator::new();
let slice1 = allocator.alloc(100, 1);
assert!(slice1.is_some());
assert_eq!(slice1.unwrap().len(), 100);
assert_eq!(allocator.used(), 100);
let slice2 = allocator.alloc(200, 1);
assert!(slice2.is_some());
assert_eq!(slice2.unwrap().len(), 200);
assert_eq!(allocator.used(), 300);
}
#[test]
fn test_bump_allocator_alignment() {
let mut allocator = BumpAllocator::new();
let slice1 = allocator.alloc(5, 8);
assert!(slice1.is_some());
let slice2 = allocator.alloc(10, 8);
assert!(slice2.is_some());
}
#[test]
fn test_bump_allocator_out_of_memory() {
let mut allocator = BumpAllocator::new();
let slice = allocator.alloc(5000, 1);
assert!(slice.is_none());
}
#[test]
fn test_bump_allocator_reset() {
let mut allocator = BumpAllocator::new();
allocator.alloc(100, 1);
assert_eq!(allocator.used(), 100);
allocator.reset();
assert_eq!(allocator.used(), 0);
assert_eq!(allocator.available(), 4096);
}
#[test]
fn test_bump_allocator_full_reset_and_reuse() {
let mut allocator = BumpAllocator::new();
let slice1 = allocator.alloc(4096, 1);
assert!(slice1.is_some());
allocator.reset();
let slice2 = allocator.alloc(4096, 1);
assert!(slice2.is_some());
}
#[test]
fn test_fixed_size_pool_creation() {
let pool: FixedSizePool<64, 10> = FixedSizePool::new();
assert_eq!(pool.free_count(), 10);
assert_eq!(pool.allocated_count(), 0);
}
#[test]
fn test_fixed_size_pool_alloc() {
let mut pool: FixedSizePool<64, 10> = FixedSizePool::new();
let block1 = pool.alloc();
assert!(block1.is_some());
assert_eq!(pool.free_count(), 9);
assert_eq!(pool.allocated_count(), 1);
let block2 = pool.alloc();
assert!(block2.is_some());
assert_eq!(pool.free_count(), 8);
assert_eq!(pool.allocated_count(), 2);
}
#[test]
fn test_fixed_size_pool_exhaustion() {
let mut pool: FixedSizePool<64, 3> = FixedSizePool::new();
assert!(pool.alloc().is_some());
assert!(pool.alloc().is_some());
assert!(pool.alloc().is_some());
assert!(pool.alloc().is_none());
assert_eq!(pool.free_count(), 0);
assert_eq!(pool.allocated_count(), 3);
}
#[test]
fn test_fixed_size_pool_alloc_dealloc() {
let mut pool: FixedSizePool<32, 2> = FixedSizePool::new();
let block1_ptr;
let block2_ptr;
{
let block1 = pool.alloc().unwrap();
block1_ptr = block1 as *mut [u8; 32];
let block2 = pool.alloc().unwrap();
block2_ptr = block2 as *mut [u8; 32];
}
assert_eq!(pool.free_count(), 0);
unsafe {
pool.dealloc(block1_ptr);
pool.dealloc(block2_ptr);
}
assert_eq!(pool.free_count(), 2);
}
#[test]
fn test_fixed_size_pool_alloc_dealloc_reuse() {
let mut pool: FixedSizePool<16, 1> = FixedSizePool::new();
let block_ptr;
{
let block = pool.alloc().unwrap();
block_ptr = block as *mut [u8; 16];
}
unsafe {
pool.dealloc(block_ptr);
}
let block2 = pool.alloc();
assert!(block2.is_some());
}
#[test]
fn test_bump_allocator_multiple_allocs() {
let mut allocator = BumpAllocator::new();
for i in 1..=10 {
let slice = allocator.alloc(100, 1);
assert!(slice.is_some());
assert_eq!(allocator.used(), i * 100);
}
}
#[test]
fn test_bump_allocator_capacity_boundary() {
let mut allocator = BumpAllocator::new();
let slice1 = allocator.alloc(4000, 1);
assert!(slice1.is_some());
let slice2 = allocator.alloc(96, 1);
assert!(slice2.is_some());
let slice3 = allocator.alloc(1, 1);
assert!(slice3.is_none());
}
}