use std::alloc::{Layout, alloc, dealloc};
use std::ptr::NonNull;
struct Chunk {
data: NonNull<u8>,
size: usize,
}
impl Chunk {
fn new(size: usize) -> Self {
let layout = Layout::from_size_align(size, 64).expect("Invalid layout");
let data = unsafe {
let ptr = alloc(layout);
NonNull::new(ptr).expect("Allocation failed")
};
Self { data, size }
}
}
impl Drop for Chunk {
fn drop(&mut self) {
let layout = Layout::from_size_align(self.size, 64).expect("Invalid layout");
unsafe {
dealloc(self.data.as_ptr(), layout);
}
}
}
pub struct Arena {
chunks: Vec<Chunk>,
current_chunk: usize,
current_offset: usize,
chunk_size: usize,
}
impl Arena {
pub const DEFAULT_CHUNK_SIZE: usize = 1024 * 1024;
pub fn new() -> Self {
Self::with_chunk_size(Self::DEFAULT_CHUNK_SIZE)
}
pub fn with_chunk_size(chunk_size: usize) -> Self {
Self {
chunks: Vec::new(),
current_chunk: 0,
current_offset: 0,
chunk_size,
}
}
pub fn allocate(&mut self, layout: Layout) -> *mut u8 {
let size = layout.size();
let align = layout.align();
if self.chunks.is_empty() {
self.allocate_chunk();
}
let offset = (self.current_offset + align - 1) & !(align - 1);
if offset + size > self.chunk_size {
self.allocate_chunk();
return self.allocate(layout);
}
let ptr = unsafe {
self.chunks[self.current_chunk]
.data
.as_ptr()
.add(offset)
};
self.current_offset = offset + size;
ptr
}
fn allocate_chunk(&mut self) {
let chunk = Chunk::new(self.chunk_size);
self.chunks.push(chunk);
self.current_chunk = self.chunks.len() - 1;
self.current_offset = 0;
}
pub fn reset(&mut self) {
self.current_chunk = 0;
self.current_offset = 0;
}
pub fn capacity(&self) -> usize {
self.chunks.len() * self.chunk_size
}
pub fn used(&self) -> usize {
if self.chunks.is_empty() {
0
} else {
self.current_chunk * self.chunk_size + self.current_offset
}
}
}
impl Default for Arena {
fn default() -> Self {
Self::new()
}
}
impl Drop for Arena {
fn drop(&mut self) {
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_arena_basic() {
let mut arena = Arena::with_chunk_size(1024);
let layout = Layout::from_size_align(64, 8).unwrap();
let ptr = arena.allocate(layout);
assert!(!ptr.is_null());
assert_eq!(arena.used(), 64);
}
#[test]
fn test_arena_multiple_allocations() {
let mut arena = Arena::with_chunk_size(1024);
let layout = Layout::from_size_align(32, 8).unwrap();
for _ in 0..10 {
let ptr = arena.allocate(layout);
assert!(!ptr.is_null());
}
assert!(arena.used() >= 320);
}
#[test]
fn test_arena_chunk_overflow() {
let mut arena = Arena::with_chunk_size(128);
let layout = Layout::from_size_align(64, 8).unwrap();
let ptr1 = arena.allocate(layout);
let ptr2 = arena.allocate(layout);
let ptr3 = arena.allocate(layout);
assert!(!ptr1.is_null());
assert!(!ptr2.is_null());
assert!(!ptr3.is_null());
assert!(arena.capacity() >= 256); }
#[test]
fn test_arena_reset() {
let mut arena = Arena::with_chunk_size(1024);
let layout = Layout::from_size_align(64, 8).unwrap();
arena.allocate(layout);
assert!(arena.used() > 0);
arena.reset();
assert_eq!(arena.used(), 0);
}
}