#![allow(dead_code)]
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct Region {
pub name: String,
pub start: usize,
pub size: usize,
pub used: usize,
}
impl Region {
fn new(name: &str, start: usize, size: usize) -> Self {
Self {
name: name.to_string(),
start,
size,
used: 0,
}
}
fn alloc(&mut self, bytes: usize) -> Option<usize> {
if self.used + bytes > self.size {
return None;
}
let addr = self.start + self.used;
self.used += bytes;
Some(addr)
}
fn reset(&mut self) {
self.used = 0;
}
pub fn free_bytes(&self) -> usize {
self.size.saturating_sub(self.used)
}
}
pub struct RegionAllocator {
regions: HashMap<String, Region>,
total_size: usize,
}
impl RegionAllocator {
pub fn new(total_size: usize) -> Self {
Self {
regions: HashMap::new(),
total_size,
}
}
pub fn add_region(&mut self, name: &str, start: usize, size: usize) -> bool {
if start + size > self.total_size {
return false;
}
self.regions
.insert(name.to_string(), Region::new(name, start, size));
true
}
pub fn alloc_in(&mut self, name: &str, bytes: usize) -> Option<usize> {
self.regions.get_mut(name)?.alloc(bytes)
}
pub fn reset_region(&mut self, name: &str) {
if let Some(r) = self.regions.get_mut(name) {
r.reset();
}
}
pub fn free_in(&self, name: &str) -> usize {
self.regions.get(name).map(|r| r.free_bytes()).unwrap_or(0)
}
pub fn region_count(&self) -> usize {
self.regions.len()
}
pub fn total_size(&self) -> usize {
self.total_size
}
}
pub fn new_region_allocator(total_size: usize) -> RegionAllocator {
RegionAllocator::new(total_size)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add_region() {
let mut ra = RegionAllocator::new(1024);
assert!(ra.add_region("heap", 0, 512));
}
#[test]
fn test_add_region_overflow() {
let mut ra = RegionAllocator::new(256);
assert!(!ra.add_region("bad", 200, 100));
}
#[test]
fn test_alloc_in() {
let mut ra = RegionAllocator::new(1024);
ra.add_region("heap", 0, 512);
let addr = ra.alloc_in("heap", 64);
assert_eq!(addr, Some(0));
}
#[test]
fn test_alloc_sequential() {
let mut ra = RegionAllocator::new(1024);
ra.add_region("heap", 0, 512);
ra.alloc_in("heap", 64);
let addr = ra.alloc_in("heap", 128).expect("should succeed");
assert_eq!(addr, 64);
}
#[test]
fn test_alloc_exhausted() {
let mut ra = RegionAllocator::new(1024);
ra.add_region("tiny", 0, 16);
ra.alloc_in("tiny", 16);
assert!(ra.alloc_in("tiny", 1).is_none());
}
#[test]
fn test_reset_region() {
let mut ra = RegionAllocator::new(1024);
ra.add_region("heap", 0, 512);
ra.alloc_in("heap", 256);
ra.reset_region("heap");
assert_eq!(ra.free_in("heap"), 512);
}
#[test]
fn test_free_in() {
let mut ra = RegionAllocator::new(1024);
ra.add_region("heap", 0, 512);
ra.alloc_in("heap", 100);
assert_eq!(ra.free_in("heap"), 412);
}
#[test]
fn test_region_count() {
let mut ra = RegionAllocator::new(4096);
ra.add_region("a", 0, 1024);
ra.add_region("b", 1024, 1024);
assert_eq!(ra.region_count(), 2);
}
#[test]
fn test_total_size() {
let ra = RegionAllocator::new(8192);
assert_eq!(ra.total_size(), 8192);
}
#[test]
fn test_new_helper() {
let ra = new_region_allocator(2048);
assert_eq!(ra.total_size(), 2048);
}
}