use crate::error::MemoryError;
#[derive(Debug, Clone)]
pub enum MemoryType {
Object(String),
PrimitiveArray(PrimitiveArrayType),
ObjectArray(String),
MultiArray(String, Vec<i32>),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PrimitiveArrayType {
Int, Byte, Short, Char, Long, Float, Double, Boolean,
}
#[derive(Debug, Clone)]
pub struct MemoryStats {
pub total_allocated: usize,
pub allocation_count: u64,
pub deallocation_count: u64,
pub current_usage: usize,
}
pub trait MemoryAllocator {
fn allocate_object(&mut self, class_name: &str) -> Result<u32, MemoryError>;
fn allocate_array(&mut self, array_type: MemoryType, length: i32) -> Result<u32, MemoryError>;
fn get_stats(&self) -> MemoryStats;
fn can_allocate(&self, size: usize) -> bool;
fn cleanup(&mut self) -> Result<(), MemoryError>;
}
pub struct ArenaAllocator {
arenas: Vec<MemoryArena>,
current_arena: usize,
stats: MemoryStats,
}
pub struct MemoryArena {
base: *mut u8,
size: usize,
used: usize,
objects: Vec<u32>,
}
impl ArenaAllocator {
pub fn new(arena_size: usize) -> Self {
let arenas = vec![
MemoryArena {
base: std::ptr::null_mut(), size: arena_size,
used: 0,
objects: vec![],
}
];
Self {
arenas,
current_arena: 0,
stats: MemoryStats {
total_allocated: arena_size,
allocation_count: 0,
deallocation_count: 0,
current_usage: 0,
},
}
}
fn add_arena(&mut self, size: usize) -> Result<(), MemoryError> {
let layout = std::alloc::Layout::from_size_align(size, 8)
.map_err(|_| MemoryError::InvalidArrayLength(size))?;
let base = unsafe { std::alloc::alloc(layout) };
if base.is_null() {
return Err(MemoryError::OutOfMemory);
}
self.arenas.push(MemoryArena {
base,
size,
used: 0,
objects: vec![],
});
self.stats.total_allocated += size;
self.current_arena = self.arenas.len() - 1;
Ok(())
}
}
impl MemoryAllocator for ArenaAllocator {
fn allocate_object(&mut self, class_name: &str) -> Result<u32, MemoryError> {
let object_size = 16; if !self.can_allocate(object_size) {
self.cleanup()?;
}
let base_address = self.arenas.len() * 1024;
let arena = &mut self.arenas[self.current_arena];
if arena.used + object_size > arena.size {
let new_size = arena.size * 2;
self.add_arena(new_size)?;
return self.allocate_object(class_name);
}
let address = (base_address + arena.used) as u32;
arena.used += object_size;
arena.objects.push(address);
self.stats.allocation_count += 1;
self.stats.current_usage += object_size;
Ok(address)
}
fn allocate_array(&mut self, array_type: MemoryType, length: i32) -> Result<u32, MemoryError> {
if length < 0 {
return Err(MemoryError::InvalidArrayLength(length as usize));
}
let element_size = match array_type {
MemoryType::PrimitiveArray(PrimitiveArrayType::Int) => 4,
MemoryType::PrimitiveArray(PrimitiveArrayType::Byte) => 1,
MemoryType::PrimitiveArray(PrimitiveArrayType::Short) => 2,
MemoryType::PrimitiveArray(PrimitiveArrayType::Char) => 2,
MemoryType::PrimitiveArray(PrimitiveArrayType::Long) => 8,
MemoryType::PrimitiveArray(PrimitiveArrayType::Float) => 4,
MemoryType::PrimitiveArray(PrimitiveArrayType::Double) => 8,
MemoryType::PrimitiveArray(PrimitiveArrayType::Boolean) => 1,
_ => return Err(MemoryError::InvalidArrayOperation("Unsupported array type".to_string())),
};
let total_size = element_size * length as usize + 16;
if !self.can_allocate(total_size) {
self.cleanup()?;
}
let base_address = self.arenas.len() * 1024;
let arena = &mut self.arenas[self.current_arena];
if arena.used + total_size > arena.size {
let new_size = arena.size * 2;
self.add_arena(new_size)?;
return self.allocate_array(array_type, length);
}
let address = (base_address + arena.used) as u32;
arena.used += total_size;
arena.objects.push(address);
self.stats.allocation_count += 1;
self.stats.current_usage += total_size;
Ok(address)
}
fn get_stats(&self) -> MemoryStats {
self.stats.clone()
}
fn can_allocate(&self, size: usize) -> bool {
if let Some(arena) = self.arenas.get(self.current_arena) {
arena.used + size <= arena.size
} else {
false
}
}
fn cleanup(&mut self) -> Result<(), MemoryError> {
if let Some(arena) = self.arenas.get_mut(self.current_arena) {
arena.used = 0;
arena.objects.clear();
self.stats.deallocation_count += 1;
self.stats.current_usage = 0;
}
Ok(())
}
}
impl Drop for MemoryArena {
fn drop(&mut self) {
if !self.base.is_null() {
unsafe {
std::alloc::dealloc(
self.base,
std::alloc::Layout::from_size_align(self.size, 8).unwrap_or_else(|_| {
std::alloc::Layout::from_size_align_unchecked(self.size, 8)
}),
);
}
}
}
}
pub struct TlabAllocator {
tlab_size: usize,
tlab: Option<Tlab>,
stats: MemoryStats,
}
impl TlabAllocator {
pub fn new(tlab_size: usize) -> Self {
Self {
tlab_size,
tlab: None,
stats: MemoryStats {
total_allocated: 0,
allocation_count: 0,
deallocation_count: 0,
current_usage: 0,
},
}
}
fn get_or_create_tlab(&mut self) -> &mut Tlab {
if self.tlab.is_none() {
let layout = std::alloc::Layout::from_size_align(self.tlab_size, 8)
.unwrap_or_else(|_| unsafe { std::alloc::Layout::from_size_align_unchecked(self.tlab_size, 8) });
let base = unsafe { std::alloc::alloc(layout) };
if !base.is_null() {
self.tlab = Some(Tlab {
base,
size: self.tlab_size,
used: 0,
});
self.stats.total_allocated += self.tlab_size;
}
}
self.tlab.as_mut().unwrap_or_else(|| {
panic!("Critical: Failed to allocate TLAB");
})
}
}
impl MemoryAllocator for TlabAllocator {
fn allocate_object(&mut self, _class_name: &str) -> Result<u32, MemoryError> {
let object_size = 16;
loop {
let tlab = self.get_or_create_tlab();
if tlab.used + object_size <= tlab.size {
let address = tlab.base as u32 + tlab.used as u32;
tlab.used += object_size;
self.stats.allocation_count += 1;
self.stats.current_usage += object_size;
return Ok(address);
}
self.cleanup()?;
}
}
fn allocate_array(&mut self, _array_type: MemoryType, length: i32) -> Result<u32, MemoryError> {
if length < 0 {
return Err(MemoryError::InvalidArrayLength(length as usize));
}
let element_size = match _array_type {
MemoryType::PrimitiveArray(PrimitiveArrayType::Int) => 4,
MemoryType::PrimitiveArray(PrimitiveArrayType::Byte) => 1,
MemoryType::PrimitiveArray(PrimitiveArrayType::Short) => 2,
MemoryType::PrimitiveArray(PrimitiveArrayType::Char) => 2,
MemoryType::PrimitiveArray(PrimitiveArrayType::Long) => 8,
MemoryType::PrimitiveArray(PrimitiveArrayType::Float) => 4,
MemoryType::PrimitiveArray(PrimitiveArrayType::Double) => 8,
MemoryType::PrimitiveArray(PrimitiveArrayType::Boolean) => 1,
_ => return Err(MemoryError::InvalidArrayOperation("Unsupported array type".to_string())),
};
let total_size = element_size * length as usize + 16;
loop {
let tlab = self.get_or_create_tlab();
if tlab.used + total_size <= tlab.size {
let address = tlab.base as u32 + tlab.used as u32;
tlab.used += total_size;
self.stats.allocation_count += 1;
self.stats.current_usage += total_size;
return Ok(address);
}
self.cleanup()?;
}
}
fn get_stats(&self) -> MemoryStats {
self.stats.clone()
}
fn can_allocate(&self, size: usize) -> bool {
if let Some(tlab) = &self.tlab {
tlab.used + size <= tlab.size
} else {
true }
}
fn cleanup(&mut self) -> Result<(), MemoryError> {
if let Some(tlab) = &mut self.tlab {
tlab.used = 0;
self.stats.deallocation_count += 1;
self.stats.current_usage = 0;
}
Ok(())
}
}
struct Tlab {
base: *mut u8,
size: usize,
used: usize,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_arena_allocator() {
let mut allocator = ArenaAllocator::new(1024);
let obj1 = allocator.allocate_object("Test").unwrap();
let obj2 = allocator.allocate_object("Test").unwrap();
assert_ne!(obj1, obj2);
assert_eq!(allocator.get_stats().allocation_count, 2);
}
#[test]
fn test_tlab_allocator() {
let mut allocator = TlabAllocator::new(512);
let obj1 = allocator.allocate_object("Test").unwrap();
let obj2 = allocator.allocate_object("Test").unwrap();
assert_ne!(obj1, obj2);
assert_eq!(allocator.get_stats().allocation_count, 2);
}
#[test]
fn test_array_allocation() {
let mut allocator = ArenaAllocator::new(1024);
let arr = allocator.allocate_array(
MemoryType::PrimitiveArray(PrimitiveArrayType::Int),
10
).unwrap();
assert_ne!(arr, 0);
assert_eq!(allocator.get_stats().allocation_count, 1);
}
}