pub struct WasmHeap {
young: Vec<u8>,
old: Vec<u8>,
roots: Vec<usize>,
}
impl WasmHeap {
pub fn new() -> Self {
Self {
young: Vec::with_capacity(256 * 1024), old: Vec::with_capacity(2 * 1024 * 1024), roots: Vec::new(),
}
}
pub fn minor_gc(&mut self) {
self.young.clear();
}
pub fn major_gc(&mut self) {
let mut marked = vec![false; self.old.len()];
for &root in &self.roots {
if root < marked.len() {
marked[root] = true;
}
}
let mut compacted = Vec::new();
for (i, &is_marked) in marked.iter().enumerate() {
if is_marked && i < self.old.len() {
compacted.push(self.old[i]);
}
}
self.old = compacted;
}
pub fn young_size(&self) -> usize {
self.young.len()
}
pub fn old_size(&self) -> usize {
self.old.len()
}
pub fn add_root(&mut self, root: usize) {
self.roots.push(root);
}
pub fn clear_roots(&mut self) {
self.roots.clear();
}
pub fn alloc_young(&mut self, data: &[u8]) -> usize {
let offset = self.young.len();
self.young.extend_from_slice(data);
offset
}
pub fn promote(&mut self, offset: usize, len: usize) {
if offset + len <= self.young.len() {
let data: Vec<u8> = self.young[offset..offset + len].to_vec();
self.old.extend(data);
}
}
}
impl Default for WasmHeap {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_heap_new() {
let heap = WasmHeap::new();
assert!(heap.young.is_empty());
assert!(heap.old.is_empty());
assert!(heap.roots.is_empty());
}
#[test]
fn test_heap_default() {
let heap = WasmHeap::default();
assert!(heap.young.is_empty());
}
#[test]
fn test_heap_minor_gc() {
let mut heap = WasmHeap::new();
heap.young.push(1);
heap.young.push(2);
assert_eq!(heap.young.len(), 2);
heap.minor_gc();
assert!(heap.young.is_empty());
}
#[test]
fn test_heap_major_gc_empty() {
let mut heap = WasmHeap::new();
heap.major_gc();
assert!(heap.old.is_empty());
}
#[test]
fn test_heap_major_gc_with_roots() {
let mut heap = WasmHeap::new();
heap.old = vec![1, 2, 3, 4, 5];
heap.roots = vec![0, 2, 4]; heap.major_gc();
assert_eq!(heap.old.len(), 3);
}
#[test]
fn test_heap_major_gc_out_of_bounds_root() {
let mut heap = WasmHeap::new();
heap.old = vec![1, 2, 3];
heap.roots = vec![100]; heap.major_gc();
assert!(heap.old.is_empty());
}
#[test]
fn test_heap_with_data() {
let mut heap = WasmHeap::new();
heap.young.extend_from_slice(&[1, 2, 3, 4, 5]);
assert_eq!(heap.young.len(), 5);
heap.minor_gc();
assert!(heap.young.is_empty());
}
#[test]
fn test_heap_major_gc_all_marked() {
let mut heap = WasmHeap::new();
heap.old = vec![10, 20, 30];
heap.roots = vec![0, 1, 2]; heap.major_gc();
assert_eq!(heap.old.len(), 3);
}
#[test]
fn test_heap_major_gc_none_marked() {
let mut heap = WasmHeap::new();
heap.old = vec![10, 20, 30];
heap.roots = vec![]; heap.major_gc();
assert!(heap.old.is_empty());
}
#[test]
fn test_heap_major_gc_partial_marks() {
let mut heap = WasmHeap::new();
heap.old = vec![10, 20, 30, 40, 50];
heap.roots = vec![1, 3]; heap.major_gc();
assert_eq!(heap.old.len(), 2);
assert!(heap.old.contains(&20));
assert!(heap.old.contains(&40));
}
#[test]
fn test_young_size() {
let mut heap = WasmHeap::new();
assert_eq!(heap.young_size(), 0);
heap.young.push(1);
assert_eq!(heap.young_size(), 1);
}
#[test]
fn test_old_size() {
let mut heap = WasmHeap::new();
assert_eq!(heap.old_size(), 0);
heap.old.push(1);
assert_eq!(heap.old_size(), 1);
}
#[test]
fn test_add_root() {
let mut heap = WasmHeap::new();
assert!(heap.roots.is_empty());
heap.add_root(0);
heap.add_root(5);
assert_eq!(heap.roots.len(), 2);
}
#[test]
fn test_clear_roots() {
let mut heap = WasmHeap::new();
heap.roots = vec![0, 1, 2, 3];
heap.clear_roots();
assert!(heap.roots.is_empty());
}
#[test]
fn test_alloc_young() {
let mut heap = WasmHeap::new();
let offset = heap.alloc_young(&[1, 2, 3]);
assert_eq!(offset, 0);
assert_eq!(heap.young_size(), 3);
let offset2 = heap.alloc_young(&[4, 5]);
assert_eq!(offset2, 3);
assert_eq!(heap.young_size(), 5);
}
#[test]
fn test_promote() {
let mut heap = WasmHeap::new();
heap.alloc_young(&[1, 2, 3, 4, 5]);
heap.promote(1, 3); assert_eq!(heap.old_size(), 3);
assert_eq!(heap.old, vec![2, 3, 4]);
}
#[test]
fn test_promote_out_of_bounds() {
let mut heap = WasmHeap::new();
heap.alloc_young(&[1, 2, 3]);
heap.promote(10, 5); assert_eq!(heap.old_size(), 0);
}
#[cfg(test)]
mod property_tests {
use super::*;
use proptest::prelude::*;
proptest! {
#![proptest_config(ProptestConfig::with_cases(1000))]
#[test]
fn prop_minor_gc_clears_young(data in prop::collection::vec(any::<u8>(), 0..100)) {
let mut heap = WasmHeap::new();
heap.young = data;
heap.minor_gc();
prop_assert!(heap.young.is_empty());
}
#[test]
fn prop_major_gc_no_roots_clears_old(data in prop::collection::vec(any::<u8>(), 0..100)) {
let mut heap = WasmHeap::new();
heap.old = data;
heap.roots.clear();
heap.major_gc();
prop_assert!(heap.old.is_empty());
}
#[test]
fn prop_alloc_increases_size(data in prop::collection::vec(any::<u8>(), 1..50)) {
let mut heap = WasmHeap::new();
let before = heap.young_size();
heap.alloc_young(&data);
prop_assert_eq!(heap.young_size(), before + data.len());
}
#[test]
fn prop_add_root_increases_count(root in any::<usize>()) {
let mut heap = WasmHeap::new();
let before = heap.roots.len();
heap.add_root(root);
prop_assert_eq!(heap.roots.len(), before + 1);
}
#[test]
fn prop_clear_roots_empties(roots in prop::collection::vec(any::<usize>(), 0..20)) {
let mut heap = WasmHeap::new();
heap.roots = roots;
heap.clear_roots();
prop_assert!(heap.roots.is_empty());
}
}
}
}