use crate::debug::JvmDebugger;
use crate::memory::{HeapArray, HeapObject};
use std::collections::HashMap;
#[derive(Debug, Clone)]
enum ArenaSlot {
Occupied(HeapObject),
Free,
}
#[derive(Debug)]
pub struct ArenaAllocator {
slots: Vec<ArenaSlot>,
free_list: Vec<u32>,
next_addr: u32,
debugger: Option<JvmDebugger>,
}
impl ArenaAllocator {
const INITIAL_CAPACITY: usize = 256;
const GROWTH_FACTOR: f64 = 1.5;
pub fn new() -> Self {
Self {
slots: Vec::with_capacity(Self::INITIAL_CAPACITY),
free_list: Vec::new(),
next_addr: 1,
debugger: None,
}
}
pub fn with_debugger(debugger: JvmDebugger) -> Self {
Self {
slots: Vec::with_capacity(Self::INITIAL_CAPACITY),
free_list: Vec::new(),
next_addr: 1,
debugger: Some(debugger),
}
}
pub fn allocate(&mut self, class_name: String) -> u32 {
let addr = if let Some(reused) = self.free_list.pop() {
reused
} else {
let a = self.next_addr;
self.next_addr += 1;
a
};
let idx = self.addr_to_index(addr);
if idx >= self.slots.len() {
self.slots.resize(idx + 1, ArenaSlot::Free);
}
let obj = HeapObject {
fields: HashMap::new(),
class_name: class_name.clone(),
marked: false,
string_data: None,
monitor: None,
};
if let Some(debugger) = &self.debugger {
debugger.log_memory_allocation(addr, 0, &class_name);
}
self.slots[idx] = ArenaSlot::Occupied(obj);
addr
}
fn addr_to_index(&self, addr: u32) -> usize {
(addr - 1) as usize
}
pub fn get_object(&self, addr: u32) -> Option<&HeapObject> {
let idx = self.addr_to_index(addr);
self.slots.get(idx).and_then(|s| match s {
ArenaSlot::Occupied(obj) => Some(obj),
ArenaSlot::Free => None,
})
}
pub fn get_object_mut(&mut self, addr: u32) -> Option<&mut HeapObject> {
let idx = self.addr_to_index(addr);
self.slots.get_mut(idx).and_then(|s| match s {
ArenaSlot::Occupied(obj) => Some(obj),
ArenaSlot::Free => None,
})
}
pub fn free_slot(&mut self, addr: u32) {
let idx = self.addr_to_index(addr);
if idx < self.slots.len() {
self.slots[idx] = ArenaSlot::Free;
self.free_list.push(addr);
}
}
pub fn iter_objects(&self) -> impl Iterator<Item = (u32, &HeapObject)> {
self.slots.iter().enumerate().filter_map(|(i, slot)| {
if let ArenaSlot::Occupied(obj) = slot {
Some(((i + 1) as u32, obj))
} else {
None
}
})
}
pub fn mark(&mut self, addr: u32) {
if let Some(obj) = self.get_object_mut(addr) {
obj.marked = true;
}
}
pub fn object_count(&self) -> usize {
self.slots
.iter()
.filter(|s| matches!(s, ArenaSlot::Occupied(_)))
.count()
}
}
impl Default for ArenaAllocator {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct ArrayArena {
slots: Vec<Option<HeapArray>>,
free_list: Vec<u32>,
next_addr: u32,
}
impl ArrayArena {
const INITIAL_CAPACITY: usize = 128;
pub fn new() -> Self {
Self {
slots: Vec::with_capacity(Self::INITIAL_CAPACITY),
free_list: Vec::new(),
next_addr: 1,
}
}
pub fn allocate(&mut self, array: HeapArray) -> u32 {
let addr = if let Some(idx) = self.free_list.pop() {
idx
} else {
let addr = self.next_addr;
self.next_addr += 1;
addr
};
let idx = (addr - 1) as usize;
if idx >= self.slots.len() {
self.slots.resize(idx + 1, None);
}
self.slots[idx] = Some(array);
addr
}
pub fn get_array(&self, addr: u32) -> Option<&HeapArray> {
let idx = (addr - 1) as usize;
self.slots
.get(idx)
.and_then(|s: &Option<HeapArray>| s.as_ref())
}
pub fn get_array_mut(&mut self, addr: u32) -> Option<&mut HeapArray> {
let idx = (addr - 1) as usize;
self.slots
.get_mut(idx)
.and_then(|s: &mut Option<HeapArray>| s.as_mut())
}
pub fn free_slot(&mut self, addr: u32) {
let idx = (addr - 1) as usize;
if idx < self.slots.len() {
self.slots[idx] = None;
self.free_list.push(addr);
}
}
pub fn iter_arrays(&self) -> impl Iterator<Item = (u32, &HeapArray)> {
self.slots
.iter()
.enumerate()
.filter_map(|(i, slot): (usize, &Option<HeapArray>)| {
slot.as_ref().map(|arr| ((i + 1) as u32, arr))
})
}
pub fn array_count(&self) -> usize {
self.slots.iter().filter(|s| s.is_some()).count()
}
}
impl Default for ArrayArena {
fn default() -> Self {
Self::new()
}
}