feat(domain): add memory module with page pool, per-process tracking, and region table

This commit is contained in:
2026-10-17 23:45:17 +02:00
parent 021b2d83b9
commit ed80fc43a6
8 changed files with 437 additions and 13 deletions

View File

@@ -0,0 +1,207 @@
use super::page_pool::PagePool;
use super::process_memory::ProcessMemory;
use super::*;
use types::address::PhysicalAddress;
use types::bounded::PageCount;
use types::generation::Generation;
use types::id::{ProcessId, RegionId};
fn base() -> PhysicalAddress {
PhysicalAddress::from_raw(0x8000_0000)
}
fn process(id: u16) -> ProcessId {
ProcessId::from_raw(id)
}
fn pages(n: u16) -> PageCount {
PageCount::new(n).unwrap()
}
// --- PagePool tests ---
#[test]
fn new_pool_has_all_pages_free() {
let pool = PagePool::new(base(), 100);
assert_eq!(pool.free_count(), 100);
}
#[test]
fn allocate_reduces_free_count() {
let mut pool = PagePool::new(base(), 100);
pool.allocate(10).unwrap();
assert_eq!(pool.free_count(), 90);
}
#[test]
fn allocate_returns_base_aligned_address() {
let mut pool = PagePool::new(base(), 100);
let addr = pool.allocate(1).unwrap();
assert_eq!(addr, base());
}
#[test]
fn allocate_sequential_returns_contiguous() {
let mut pool = PagePool::new(base(), 100);
let a = pool.allocate(4).unwrap();
let b = pool.allocate(4).unwrap();
assert_eq!(a, base());
assert_eq!(b, base().offset(4));
}
#[test]
fn allocate_too_many_returns_none() {
let mut pool = PagePool::new(base(), 10);
assert!(pool.allocate(11).is_none());
}
#[test]
fn free_then_reallocate() {
let mut pool = PagePool::new(base(), 10);
let addr = pool.allocate(5).unwrap();
assert_eq!(pool.free_count(), 5);
assert!(pool.free(addr, 5));
assert_eq!(pool.free_count(), 10);
let addr2 = pool.allocate(5).unwrap();
assert_eq!(addr2, base());
}
#[test]
fn free_out_of_range_returns_false() {
let mut pool = PagePool::new(base(), 10);
assert!(!pool.free(PhysicalAddress::from_raw(0), 1));
}
#[test]
fn allocate_fragmented_finds_gap() {
let mut pool = PagePool::new(base(), 10);
let a = pool.allocate(3).unwrap();
let _b = pool.allocate(3).unwrap();
let _c = pool.allocate(3).unwrap();
pool.free(a, 3);
// now pages 0-2 are free, 3-8 allocated, 9 free
// asking for 3 contiguous should find 0-2
let d = pool.allocate(3).unwrap();
assert_eq!(d, base());
}
#[test]
fn allocate_exact_remaining() {
let mut pool = PagePool::new(base(), 5);
let addr = pool.allocate(5).unwrap();
assert_eq!(addr, base());
assert_eq!(pool.free_count(), 0);
assert!(pool.allocate(1).is_none());
}
// --- ProcessMemory tests ---
#[test]
fn new_process_memory_has_zero_pages() {
let pm = ProcessMemory::new(process(0), 100);
assert_eq!(pm.page_count(), 0);
assert_eq!(pm.limit(), 100);
}
#[test]
fn grant_pages_within_limit() {
let mut pm = ProcessMemory::new(process(0), 100);
assert!(pm.grant(10).is_ok());
assert_eq!(pm.page_count(), 10);
}
#[test]
fn grant_pages_exceeding_limit_fails() {
let mut pm = ProcessMemory::new(process(0), 10);
assert!(pm.grant(11).is_err());
assert_eq!(pm.page_count(), 0);
}
#[test]
fn release_pages_reduces_count() {
let mut pm = ProcessMemory::new(process(0), 100);
pm.grant(20).unwrap();
assert!(pm.release(5).is_ok());
assert_eq!(pm.page_count(), 15);
}
#[test]
fn release_more_than_owned_fails() {
let mut pm = ProcessMemory::new(process(0), 100);
pm.grant(5).unwrap();
assert!(pm.release(10).is_err());
assert_eq!(pm.page_count(), 5);
}
#[test]
fn grant_up_to_exact_limit() {
let mut pm = ProcessMemory::new(process(0), 10);
pm.grant(10).unwrap();
assert!(pm.grant(1).is_err());
}
// --- MemoryRegion tests ---
#[test]
fn memory_region_construction_and_accessors() {
let region = MemoryRegion::new(
RegionId::from_raw(1),
process(0),
base(),
pages(4),
Generation::initial(),
);
assert_eq!(region.id(), RegionId::from_raw(1));
assert_eq!(region.owner(), process(0));
assert_eq!(region.base(), base());
assert_eq!(region.size(), pages(4));
assert_eq!(region.generation(), &Generation::initial());
}
// --- RegionTable tests ---
#[test]
fn region_table_insert_and_get() {
let mut table = RegionTable::new();
let id = table.insert(MemoryRegion::new(
RegionId::from_raw(0),
process(0),
base(),
pages(4),
Generation::initial(),
));
assert!(id.is_some());
let rid = id.unwrap();
assert!(table.get(rid).is_some());
}
#[test]
fn region_table_remove() {
let mut table = RegionTable::new();
let id = table.insert(MemoryRegion::new(
RegionId::from_raw(0),
process(0),
base(),
pages(4),
Generation::initial(),
)).unwrap();
let removed = table.remove(id);
assert!(removed.is_some());
assert!(table.get(id).is_none());
}
#[test]
fn region_table_regions_for_process() {
let mut table = RegionTable::new();
table.insert(MemoryRegion::new(
RegionId::from_raw(0), process(0), base(), pages(4), Generation::initial(),
));
table.insert(MemoryRegion::new(
RegionId::from_raw(1), process(1), base(), pages(4), Generation::initial(),
));
table.insert(MemoryRegion::new(
RegionId::from_raw(2), process(0), base(), pages(2), Generation::initial(),
));
assert_eq!(table.regions_for_process(process(0)).count(), 2);
assert_eq!(table.regions_for_process(process(1)).count(), 1);
}