use std::marker::PhantomData;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct RegionId(pub u32);
impl RegionId {
pub fn new(id: u32) -> Self {
RegionId(id)
}
pub fn raw(self) -> u32 {
self.0
}
}
impl std::fmt::Display for RegionId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "region#{}", self.0)
}
}
#[derive(Debug)]
pub struct Region {
pub id: RegionId,
pub data: Vec<u8>,
pub offset: usize,
pub capacity: usize,
}
impl Region {
pub fn new(id: RegionId, capacity: usize) -> Self {
let capacity = capacity.max(1);
Region {
id,
data: vec![0u8; capacity],
offset: 0,
capacity,
}
}
pub fn remaining(&self) -> usize {
self.capacity.saturating_sub(self.offset)
}
pub fn used(&self) -> usize {
self.offset
}
pub fn is_empty(&self) -> bool {
self.offset == 0
}
pub fn reset(&mut self) {
self.offset = 0;
for b in &mut self.data {
*b = 0;
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct RegionHandle<'a> {
pub id: RegionId,
pub phantom: PhantomData<&'a ()>,
}
impl<'a> RegionHandle<'a> {
pub fn new(id: RegionId) -> Self {
RegionHandle {
id,
phantom: PhantomData,
}
}
pub fn region_id(self) -> RegionId {
self.id
}
}
impl<'a> std::fmt::Display for RegionHandle<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "handle({})", self.id)
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct AllocStats {
pub regions_created: usize,
pub regions_freed: usize,
pub total_allocated: usize,
pub total_freed: usize,
pub peak_usage: usize,
}
impl AllocStats {
pub fn active_regions(&self) -> usize {
self.regions_created.saturating_sub(self.regions_freed)
}
pub fn utilization(&self, total_capacity: usize) -> f64 {
if total_capacity == 0 {
return 0.0;
}
self.total_allocated as f64 / total_capacity as f64
}
}
impl std::fmt::Display for AllocStats {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"AllocStats {{ created: {}, freed: {}, allocated: {}, peak: {} }}",
self.regions_created, self.regions_freed, self.total_allocated, self.peak_usage
)
}
}
#[derive(Clone, Debug)]
pub struct RegionConfig {
pub initial_capacity: usize,
pub growth_factor: f64,
pub max_regions: usize,
}
impl RegionConfig {
pub fn new(initial_capacity: usize, growth_factor: f64, max_regions: usize) -> Self {
let growth_factor = growth_factor.clamp(1.001, 16.0);
RegionConfig {
initial_capacity: initial_capacity.max(64),
growth_factor,
max_regions: max_regions.max(1),
}
}
pub fn grow(&self, current: usize) -> usize {
((current as f64 * self.growth_factor) as usize).max(current + 64)
}
}
impl Default for RegionConfig {
fn default() -> Self {
RegionConfig::new(4096, 2.0, 256)
}
}
pub struct RegionAllocator {
pub regions: Vec<Region>,
pub free_list: Vec<RegionId>,
pub default_capacity: usize,
pub(super) config: RegionConfig,
pub(super) stats: AllocStats,
pub(super) next_id: u32,
pub(super) current_usage: usize,
}