use crate::runtime::vm::RawVal;
use slotmap::{DefaultKey, SlotMap};
#[derive(Debug, Clone)]
pub struct HeapObject {
pub refcount: u64,
pub size: usize,
pub data: Vec<RawVal>,
}
impl HeapObject {
pub fn new(size: usize) -> Self {
Self {
refcount: 1,
size,
data: vec![0; size],
}
}
pub fn with_data(data: Vec<RawVal>) -> Self {
let size = data.len();
Self {
refcount: 1,
size,
data,
}
}
}
pub type HeapStorage = SlotMap<DefaultKey, HeapObject>;
pub type HeapIdx = DefaultKey;
pub fn heap_retain(storage: &mut HeapStorage, idx: HeapIdx) {
if let Some(obj) = storage.get_mut(idx) {
obj.refcount += 1;
log::trace!("heap_retain: {:?} refcount -> {}", idx, obj.refcount);
} else {
log::warn!("heap_retain: invalid HeapIdx {idx:?}");
}
}
pub fn heap_release(storage: &mut HeapStorage, idx: HeapIdx) {
if let Some(obj) = storage.get_mut(idx) {
obj.refcount -= 1;
log::trace!("heap_release: {:?} refcount -> {}", idx, obj.refcount);
if obj.refcount == 0 {
log::trace!("heap_release: freeing {idx:?}");
storage.remove(idx);
}
} else {
log::warn!("heap_release: invalid HeapIdx {idx:?}");
}
}
pub fn heap_release_closure(storage: &mut HeapStorage, idx: HeapIdx) {
let should_free = if let Some(obj) = storage.get_mut(idx) {
obj.refcount -= 1;
log::trace!(
"heap_release_closure: {:?} refcount -> {}",
idx,
obj.refcount
);
obj.refcount == 0
} else {
log::warn!("heap_release_closure: invalid HeapIdx {idx:?}");
return;
};
if should_free {
log::trace!("heap_release_closure: freeing {idx:?}");
storage.remove(idx);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_heap_object_creation() {
let obj = HeapObject::new(5);
assert_eq!(obj.refcount, 1);
assert_eq!(obj.size, 5);
assert_eq!(obj.data.len(), 5);
assert!(obj.data.iter().all(|&v| v == 0));
}
#[test]
fn test_heap_object_with_data() {
let data = vec![1, 2, 3, 4, 5];
let obj = HeapObject::with_data(data.clone());
assert_eq!(obj.refcount, 1);
assert_eq!(obj.size, 5);
assert_eq!(obj.data, data);
}
#[test]
fn test_heap_retain_and_release() {
let mut storage = HeapStorage::default();
let idx = storage.insert(HeapObject::new(3));
assert_eq!(storage.get(idx).unwrap().refcount, 1);
heap_retain(&mut storage, idx);
assert_eq!(storage.get(idx).unwrap().refcount, 2);
heap_retain(&mut storage, idx);
assert_eq!(storage.get(idx).unwrap().refcount, 3);
heap_release(&mut storage, idx);
assert_eq!(storage.get(idx).unwrap().refcount, 2);
heap_release(&mut storage, idx);
assert_eq!(storage.get(idx).unwrap().refcount, 1);
heap_release(&mut storage, idx);
assert!(storage.get(idx).is_none());
}
#[test]
fn test_heap_release_already_freed() {
let mut storage = HeapStorage::default();
let idx = storage.insert(HeapObject::new(3));
heap_release(&mut storage, idx);
assert!(storage.get(idx).is_none());
heap_release(&mut storage, idx);
}
}