use std::cell::Cell;
use std::collections::HashMap;
use super::functions::{
ARENA_ALIGN, DEFAULT_CHUNK_SIZE, MAX_CHUNK_SIZE, MIN_CHUNK_SIZE, PAGE_SIZE,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ArenaOffset {
pub chunk: usize,
pub offset: usize,
}
impl ArenaOffset {
pub fn new(chunk: usize, offset: usize) -> Self {
ArenaOffset { chunk, offset }
}
}
#[allow(dead_code)]
pub struct LinearAllocator {
buf: Vec<u8>,
top: usize,
alloc_count: u64,
overflow_count: u64,
}
#[allow(dead_code)]
impl LinearAllocator {
pub fn new(size: usize) -> Self {
Self {
buf: vec![0u8; size.max(16)],
top: 0,
alloc_count: 0,
overflow_count: 0,
}
}
pub fn alloc_offset(&mut self, size: usize, align: usize) -> Option<usize> {
let align = align.next_power_of_two().max(1);
let aligned = (self.top + align - 1) & !(align - 1);
if aligned + size > self.buf.len() {
self.overflow_count += 1;
return None;
}
self.top = aligned + size;
self.alloc_count += 1;
Some(aligned)
}
pub fn get_bytes(&self, offset: usize, size: usize) -> Option<&[u8]> {
self.buf.get(offset..offset + size)
}
pub fn get_bytes_mut(&mut self, offset: usize, size: usize) -> Option<&mut [u8]> {
self.buf.get_mut(offset..offset + size)
}
pub fn reset(&mut self) {
self.top = 0;
}
pub fn top(&self) -> usize {
self.top
}
pub fn capacity(&self) -> usize {
self.buf.len()
}
pub fn remaining(&self) -> usize {
self.buf.len().saturating_sub(self.top)
}
pub fn utilization(&self) -> f64 {
if self.buf.is_empty() {
0.0
} else {
self.top as f64 / self.buf.len() as f64
}
}
pub fn alloc_count(&self) -> u64 {
self.alloc_count
}
pub fn overflow_count(&self) -> u64 {
self.overflow_count
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct ArenaBenchResult {
pub iterations: u64,
pub total_bytes: u64,
pub allocs_per_iter: usize,
pub description: String,
}
#[allow(dead_code)]
impl ArenaBenchResult {
pub fn new(iterations: u64, total_bytes: u64, allocs_per_iter: usize, desc: &str) -> Self {
Self {
iterations,
total_bytes,
allocs_per_iter,
description: desc.to_string(),
}
}
pub fn bytes_per_iter(&self) -> f64 {
if self.iterations == 0 {
0.0
} else {
self.total_bytes as f64 / self.iterations as f64
}
}
}
#[derive(Clone, Debug, Default)]
pub struct RegionStats {
pub allocations: u64,
pub bytes_allocated: u64,
pub resets: u64,
}
pub struct GenerationalArena<T> {
pub(super) entries: Vec<GenerationalEntry<T>>,
free_list: Vec<usize>,
pub(super) generation: u32,
}
impl<T> GenerationalArena<T> {
pub fn new() -> Self {
GenerationalArena {
entries: Vec::new(),
free_list: Vec::new(),
generation: 0,
}
}
pub fn with_capacity(cap: usize) -> Self {
GenerationalArena {
entries: Vec::with_capacity(cap),
free_list: Vec::new(),
generation: 0,
}
}
pub fn insert(&mut self, value: T) -> GenIdx {
self.generation = self.generation.wrapping_add(1);
if let Some(slot) = self.free_list.pop() {
self.entries[slot] = GenerationalEntry {
value: Some(value),
generation: self.generation,
};
GenIdx {
index: slot as u32,
generation: self.generation,
}
} else {
let index = self.entries.len() as u32;
self.entries.push(GenerationalEntry {
value: Some(value),
generation: self.generation,
});
GenIdx {
index,
generation: self.generation,
}
}
}
pub fn get(&self, idx: GenIdx) -> Option<&T> {
let entry = self.entries.get(idx.index as usize)?;
if entry.generation == idx.generation {
entry.value.as_ref()
} else {
None
}
}
pub fn get_mut(&mut self, idx: GenIdx) -> Option<&mut T> {
let entry = self.entries.get_mut(idx.index as usize)?;
if entry.generation == idx.generation {
entry.value.as_mut()
} else {
None
}
}
pub fn remove(&mut self, idx: GenIdx) -> Option<T> {
let entry = self.entries.get_mut(idx.index as usize)?;
if entry.generation == idx.generation {
let value = entry.value.take();
self.free_list.push(idx.index as usize);
value
} else {
None
}
}
pub fn contains(&self, idx: GenIdx) -> bool {
self.entries
.get(idx.index as usize)
.map(|e| e.generation == idx.generation && e.value.is_some())
.unwrap_or(false)
}
pub fn len(&self) -> usize {
self.entries.len() - self.free_list.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn clear(&mut self) {
self.entries.clear();
self.free_list.clear();
}
pub fn iter(&self) -> impl Iterator<Item = (GenIdx, &T)> {
self.entries.iter().enumerate().filter_map(|(i, e)| {
e.value.as_ref().map(|v| {
(
GenIdx {
index: i as u32,
generation: e.generation,
},
v,
)
})
})
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct GenIdx {
pub index: u32,
pub generation: u32,
}
pub struct ArenaPool {
pub(super) available: Vec<BumpArena>,
pub(super) max_pool_size: usize,
pub(super) chunk_size: usize,
stats: ArenaPoolStats,
}
impl ArenaPool {
pub fn new() -> Self {
ArenaPool {
available: Vec::new(),
max_pool_size: 8,
chunk_size: DEFAULT_CHUNK_SIZE,
stats: ArenaPoolStats::default(),
}
}
pub fn with_config(max_pool_size: usize, chunk_size: usize) -> Self {
ArenaPool {
available: Vec::new(),
max_pool_size,
chunk_size,
stats: ArenaPoolStats::default(),
}
}
pub fn acquire(&mut self) -> BumpArena {
self.stats.acquired += 1;
if let Some(mut arena) = self.available.pop() {
arena.reset();
arena
} else {
self.stats.created += 1;
BumpArena::with_chunk_size(self.chunk_size)
}
}
pub fn release(&mut self, arena: BumpArena) {
self.stats.returned += 1;
if self.available.len() < self.max_pool_size {
self.available.push(arena);
} else {
self.stats.discarded += 1;
}
}
pub fn available_count(&self) -> usize {
self.available.len()
}
pub fn stats(&self) -> &ArenaPoolStats {
&self.stats
}
pub fn set_max_pool_size(&mut self, size: usize) {
self.max_pool_size = size;
while self.available.len() > self.max_pool_size {
self.available.pop();
}
}
pub fn clear(&mut self) {
self.available.clear();
}
}
#[allow(dead_code)]
pub struct ArenaPageManager {
pages: Vec<Box<[u8; PAGE_SIZE]>>,
free_list: Vec<usize>,
alloc_count: u64,
free_count: u64,
}
#[allow(dead_code)]
impl ArenaPageManager {
pub fn new() -> Self {
Self {
pages: Vec::new(),
free_list: Vec::new(),
alloc_count: 0,
free_count: 0,
}
}
pub fn alloc_page(&mut self) -> usize {
self.alloc_count += 1;
if let Some(idx) = self.free_list.pop() {
idx
} else {
let idx = self.pages.len();
self.pages.push(Box::new([0u8; PAGE_SIZE]));
idx
}
}
pub fn free_page(&mut self, idx: usize) {
if idx < self.pages.len() {
for b in self.pages[idx].iter_mut() {
*b = 0;
}
self.free_list.push(idx);
self.free_count += 1;
}
}
pub fn page(&self, idx: usize) -> Option<&[u8; PAGE_SIZE]> {
self.pages.get(idx).map(|p| p.as_ref())
}
pub fn page_mut(&mut self, idx: usize) -> Option<&mut [u8; PAGE_SIZE]> {
self.pages.get_mut(idx).map(|p| p.as_mut())
}
pub fn total_pages(&self) -> usize {
self.pages.len()
}
pub fn free_pages(&self) -> usize {
self.free_list.len()
}
pub fn live_pages(&self) -> usize {
self.pages.len().saturating_sub(self.free_list.len())
}
pub fn total_bytes(&self) -> usize {
self.pages.len() * PAGE_SIZE
}
pub fn alloc_count(&self) -> u64 {
self.alloc_count
}
pub fn free_count(&self) -> u64 {
self.free_count
}
}
pub struct BumpArena {
pub(super) chunks: Vec<Chunk>,
current_chunk: usize,
chunk_size: usize,
stats: ArenaStats,
}
impl BumpArena {
pub fn new() -> Self {
BumpArena {
chunks: vec![Chunk::new(DEFAULT_CHUNK_SIZE)],
current_chunk: 0,
chunk_size: DEFAULT_CHUNK_SIZE,
stats: ArenaStats::new(),
}
}
pub fn with_chunk_size(size: usize) -> Self {
let size = size.clamp(MIN_CHUNK_SIZE, MAX_CHUNK_SIZE);
BumpArena {
chunks: vec![Chunk::new(size)],
current_chunk: 0,
chunk_size: size,
stats: ArenaStats::new(),
}
}
pub fn alloc(&mut self, size: usize) -> ArenaOffset {
self.alloc_aligned(size, ARENA_ALIGN)
}
pub fn alloc_aligned(&mut self, size: usize, align: usize) -> ArenaOffset {
self.stats.total_allocations += 1;
self.stats.total_bytes_allocated += size as u64;
if let Some(offset) = self.chunks[self.current_chunk].try_alloc(size, align) {
return ArenaOffset {
chunk: self.current_chunk,
offset,
};
}
for i in (self.current_chunk + 1)..self.chunks.len() {
if let Some(offset) = self.chunks[i].try_alloc(size, align) {
self.current_chunk = i;
return ArenaOffset {
chunk: self.current_chunk,
offset,
};
}
}
let new_chunk_size = if size > self.chunk_size {
(size + align).max(self.chunk_size)
} else {
self.chunk_size.min(MAX_CHUNK_SIZE)
};
let mut chunk = Chunk::new(new_chunk_size);
let offset = chunk
.try_alloc(size, align)
.expect("freshly allocated chunk must have enough space for the requested allocation");
self.chunks.push(chunk);
self.current_chunk = self.chunks.len() - 1;
self.stats.total_chunks_allocated += 1;
ArenaOffset {
chunk: self.current_chunk,
offset,
}
}
pub fn get_bytes(&self, loc: &ArenaOffset, size: usize) -> Option<&[u8]> {
let chunk = self.chunks.get(loc.chunk)?;
if loc.offset + size > chunk.data.len() {
return None;
}
Some(&chunk.data[loc.offset..loc.offset + size])
}
pub fn get_bytes_mut(&mut self, loc: &ArenaOffset, size: usize) -> Option<&mut [u8]> {
let chunk = self.chunks.get_mut(loc.chunk)?;
if loc.offset + size > chunk.data.len() {
return None;
}
Some(&mut chunk.data[loc.offset..loc.offset + size])
}
pub fn reset(&mut self) {
for chunk in &mut self.chunks {
chunk.reset();
}
self.current_chunk = 0;
self.stats.total_resets += 1;
}
pub fn bytes_used(&self) -> usize {
self.chunks.iter().map(|c| c.used).sum()
}
pub fn total_capacity(&self) -> usize {
self.chunks.iter().map(|c| c.capacity()).sum()
}
pub fn num_chunks(&self) -> usize {
self.chunks.len()
}
pub fn stats(&self) -> &ArenaStats {
&self.stats
}
pub fn shrink(&mut self) {
let keep = self
.chunks
.iter()
.position(|c| c.used == 0)
.unwrap_or(self.chunks.len());
self.chunks.truncate(keep.max(1));
self.current_chunk = self.current_chunk.min(self.chunks.len() - 1);
}
}
#[allow(dead_code)]
pub struct MarkArena {
buf: Vec<u8>,
top: usize,
marks: Vec<usize>,
}
#[allow(dead_code)]
impl MarkArena {
pub fn new(capacity: usize) -> Self {
Self {
buf: vec![0u8; capacity.max(64)],
top: 0,
marks: Vec::new(),
}
}
pub fn alloc(&mut self, size: usize) -> Option<usize> {
if self.top + size > self.buf.len() {
return None;
}
let offset = self.top;
self.top += size;
Some(offset)
}
pub fn mark(&mut self) -> usize {
let mark = self.top;
self.marks.push(mark);
mark
}
pub fn release(&mut self) {
if let Some(mark) = self.marks.pop() {
self.top = mark;
}
}
pub fn release_to(&mut self, mark: usize) {
self.marks.retain(|&m| m < mark);
self.top = mark.min(self.top);
}
pub fn reset(&mut self) {
self.top = 0;
self.marks.clear();
}
pub fn top(&self) -> usize {
self.top
}
pub fn mark_depth(&self) -> usize {
self.marks.len()
}
pub fn capacity(&self) -> usize {
self.buf.len()
}
}
#[derive(Debug)]
pub(super) struct GenerationalEntry<T> {
value: Option<T>,
generation: u32,
}
pub struct ThreadLocalArena {
arena: BumpArena,
_high_water_mark: Cell<usize>,
_allocs_since_reset: Cell<u64>,
}
impl ThreadLocalArena {
pub fn new() -> Self {
ThreadLocalArena {
arena: BumpArena::new(),
_high_water_mark: Cell::new(DEFAULT_CHUNK_SIZE),
_allocs_since_reset: Cell::new(0),
}
}
pub fn alloc(&mut self, size: usize) -> ArenaOffset {
self._allocs_since_reset
.set(self._allocs_since_reset.get() + 1);
self.arena.alloc(size)
}
pub fn reset(&mut self) {
self.arena.reset();
self._allocs_since_reset.set(0);
}
pub fn arena(&self) -> &BumpArena {
&self.arena
}
pub fn bytes_used(&self) -> usize {
self.arena.bytes_used()
}
}
#[allow(dead_code)]
pub struct AdaptiveArena {
inner: BumpArena,
pressure_samples: Vec<f64>,
target_utilization: f64,
sample_window: usize,
}
#[allow(dead_code)]
impl AdaptiveArena {
pub fn new(target_utilization: f64, sample_window: usize) -> Self {
Self {
inner: BumpArena::new(),
pressure_samples: Vec::new(),
target_utilization: target_utilization.clamp(0.1, 0.99),
sample_window: sample_window.max(3),
}
}
pub fn alloc(&mut self, size: usize) -> ArenaOffset {
let result = self.inner.alloc(size);
let pressure = self.inner.bytes_used() as f64
/ (self.inner.num_chunks() as f64 * DEFAULT_CHUNK_SIZE as f64 + 1.0);
self.pressure_samples.push(pressure);
if self.pressure_samples.len() > self.sample_window {
self.pressure_samples.remove(0);
}
result
}
pub fn avg_pressure(&self) -> f64 {
if self.pressure_samples.is_empty() {
return 0.0;
}
self.pressure_samples.iter().sum::<f64>() / self.pressure_samples.len() as f64
}
pub fn is_over_utilized(&self) -> bool {
self.avg_pressure() > self.target_utilization
}
pub fn reset(&mut self) {
self.inner.reset();
self.pressure_samples.clear();
}
pub fn allocated_bytes(&self) -> usize {
self.inner.bytes_used()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ArenaIdx(pub u32);
impl ArenaIdx {
pub fn new(index: u32) -> Self {
ArenaIdx(index)
}
pub fn raw(self) -> u32 {
self.0
}
}
#[derive(Clone, Debug, Default)]
pub struct ArenaStats {
pub total_allocations: u64,
pub total_bytes_allocated: u64,
pub total_resets: u64,
pub total_chunks_allocated: u64,
}
impl ArenaStats {
pub fn new() -> Self {
Self::default()
}
pub fn avg_alloc_size(&self) -> f64 {
if self.total_allocations == 0 {
return 0.0;
}
self.total_bytes_allocated as f64 / self.total_allocations as f64
}
pub fn reset(&mut self) {
*self = Self::default();
}
}
#[derive(Clone, Debug, Default)]
pub struct TypedArenaStats {
pub total_allocations: u64,
pub total_deallocations: u64,
pub live_count: u64,
pub peak_count: u64,
}
#[allow(dead_code)]
#[derive(Debug, Default)]
pub struct ArenaChunkPool {
chunks: Vec<Vec<u8>>,
chunk_size: usize,
max_pooled: usize,
reused: u64,
created: u64,
}
#[allow(dead_code)]
impl ArenaChunkPool {
pub fn new(chunk_size: usize, max_pooled: usize) -> Self {
Self {
chunks: Vec::new(),
chunk_size,
max_pooled,
reused: 0,
created: 0,
}
}
pub fn acquire(&mut self) -> Vec<u8> {
if let Some(mut chunk) = self.chunks.pop() {
for b in chunk.iter_mut() {
*b = 0;
}
self.reused += 1;
chunk
} else {
self.created += 1;
vec![0u8; self.chunk_size]
}
}
pub fn release(&mut self, chunk: Vec<u8>) {
if chunk.len() == self.chunk_size && self.chunks.len() < self.max_pooled {
self.chunks.push(chunk);
}
}
pub fn pooled_count(&self) -> usize {
self.chunks.len()
}
pub fn reused_count(&self) -> u64 {
self.reused
}
pub fn created_count(&self) -> u64 {
self.created
}
pub fn hit_rate(&self) -> f64 {
let total = self.reused + self.created;
if total == 0 {
0.0
} else {
self.reused as f64 / total as f64
}
}
}
#[allow(dead_code)]
pub struct SlabArena {
slot_size: usize,
buf: Vec<u8>,
free_slots: Vec<usize>,
alloc_count: u64,
}
#[allow(dead_code)]
impl SlabArena {
pub fn new(slot_size: usize, initial_slots: usize) -> Self {
let slot_size = slot_size.max(8);
Self {
slot_size,
buf: vec![0u8; slot_size * initial_slots],
free_slots: (0..initial_slots).rev().collect(),
alloc_count: 0,
}
}
pub fn alloc(&mut self) -> Option<usize> {
if let Some(slot) = self.free_slots.pop() {
self.alloc_count += 1;
Some(slot * self.slot_size)
} else {
let new_slot = self.buf.len() / self.slot_size;
self.buf.extend(vec![0u8; self.slot_size]);
self.alloc_count += 1;
Some(new_slot * self.slot_size)
}
}
pub fn free(&mut self, offset: usize) {
let slot = offset / self.slot_size;
if !self.free_slots.contains(&slot) {
self.free_slots.push(slot);
}
}
pub fn live_count(&self) -> usize {
let total = self.buf.len() / self.slot_size;
total.saturating_sub(self.free_slots.len())
}
pub fn total_slots(&self) -> usize {
self.buf.len() / self.slot_size
}
pub fn slot_size(&self) -> usize {
self.slot_size
}
pub fn alloc_count(&self) -> u64 {
self.alloc_count
}
}
#[allow(dead_code)]
#[derive(Clone, Debug, Default)]
pub struct ArenaExtStats {
pub alloc_calls: u64,
pub total_bytes_allocated: u64,
pub peak_bytes: u64,
pub reset_count: u64,
pub overflow_count: u64,
pub chunk_alloc_count: u64,
}
#[allow(dead_code)]
impl ArenaExtStats {
pub fn new() -> Self {
Self::default()
}
pub fn merge(&mut self, other: &ArenaExtStats) {
self.alloc_calls += other.alloc_calls;
self.total_bytes_allocated += other.total_bytes_allocated;
self.peak_bytes = self.peak_bytes.max(other.peak_bytes);
self.reset_count += other.reset_count;
self.overflow_count += other.overflow_count;
self.chunk_alloc_count += other.chunk_alloc_count;
}
pub fn record_alloc(&mut self, bytes: u64) {
self.alloc_calls += 1;
self.total_bytes_allocated += bytes;
}
pub fn record_reset(&mut self) {
self.reset_count += 1;
}
pub fn record_overflow(&mut self) {
self.overflow_count += 1;
}
pub fn record_chunk_alloc(&mut self) {
self.chunk_alloc_count += 1;
}
pub fn update_peak(&mut self, current_bytes: u64) {
if current_bytes > self.peak_bytes {
self.peak_bytes = current_bytes;
}
}
pub fn avg_alloc_size(&self) -> f64 {
if self.alloc_calls == 0 {
0.0
} else {
self.total_bytes_allocated as f64 / self.alloc_calls as f64
}
}
}
pub struct ScopedArena<'pool> {
pub(super) arena: Option<BumpArena>,
pub(super) pool: &'pool mut ArenaPool,
}
impl<'pool> ScopedArena<'pool> {
pub fn new(pool: &'pool mut ArenaPool) -> Self {
let arena = pool.acquire();
ScopedArena {
arena: Some(arena),
pool,
}
}
pub fn alloc(&mut self, size: usize) -> ArenaOffset {
self.arena
.as_mut()
.expect("ScopedArena is valid during its lifetime; arena is always Some before drop")
.alloc(size)
}
pub fn arena(&self) -> &BumpArena {
self.arena
.as_ref()
.expect("ScopedArena is valid during its lifetime; arena is always Some before drop")
}
pub fn arena_mut(&mut self) -> &mut BumpArena {
self.arena
.as_mut()
.expect("ScopedArena is valid during its lifetime; arena is always Some before drop")
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ArenaCheckpoint {
bytes_used: usize,
chunk_count: usize,
}
#[allow(dead_code)]
impl ArenaCheckpoint {
pub fn capture(arena: &BumpArena) -> Self {
Self {
bytes_used: arena.bytes_used(),
chunk_count: arena.num_chunks(),
}
}
pub fn bytes_used(&self) -> usize {
self.bytes_used
}
pub fn chunk_count(&self) -> usize {
self.chunk_count
}
pub fn bytes_since(&self, later_bytes_used: usize) -> usize {
later_bytes_used.saturating_sub(self.bytes_used)
}
}
#[allow(dead_code)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ArenaSnapshot {
pub offset: usize,
pub chunk_count: usize,
pub allocated_bytes: usize,
}
#[allow(dead_code)]
impl ArenaSnapshot {
pub fn capture(arena: &BumpArena) -> Self {
Self {
offset: arena.bytes_used(),
chunk_count: arena.num_chunks(),
allocated_bytes: arena.bytes_used(),
}
}
pub fn bytes_since(&self, later: &ArenaSnapshot) -> usize {
later.allocated_bytes.saturating_sub(self.allocated_bytes)
}
pub fn new_chunks_since(&self, later: &ArenaSnapshot) -> usize {
later.chunk_count.saturating_sub(self.chunk_count)
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct AllocRecord {
pub size: usize,
pub align: usize,
pub offset: usize,
pub label: String,
}
#[derive(Debug)]
pub(super) struct Chunk {
data: Vec<u8>,
used: usize,
}
impl Chunk {
fn new(capacity: usize) -> Self {
Chunk {
data: vec![0u8; capacity],
used: 0,
}
}
fn capacity(&self) -> usize {
self.data.len()
}
fn remaining(&self) -> usize {
self.data.len() - self.used
}
fn try_alloc(&mut self, size: usize, align: usize) -> Option<usize> {
let aligned_used = (self.used + align - 1) & !(align - 1);
let new_used = aligned_used + size;
if new_used > self.data.len() {
return None;
}
let offset = aligned_used;
self.used = new_used;
Some(offset)
}
fn reset(&mut self) {
self.used = 0;
}
}
pub struct RegionManager {
pub(super) regions: HashMap<u64, Region>,
next_id: u64,
pub(super) scope_stack: Vec<u64>,
}
impl RegionManager {
pub fn new() -> Self {
let root = Region::new(0);
let mut regions = HashMap::new();
regions.insert(0, root);
RegionManager {
regions,
next_id: 1,
scope_stack: vec![0],
}
}
pub fn current_region_id(&self) -> u64 {
*self.scope_stack.last().unwrap_or(&0)
}
pub fn push_region(&mut self) -> u64 {
let id = self.next_id;
self.next_id += 1;
let parent_id = self.current_region_id();
let region = Region::child(id, parent_id);
self.regions.insert(id, region);
if let Some(parent) = self.regions.get_mut(&parent_id) {
parent.add_child(id);
}
self.scope_stack.push(id);
id
}
pub fn push_region_with_size(&mut self, chunk_size: usize) -> u64 {
let id = self.next_id;
self.next_id += 1;
let parent_id = self.current_region_id();
let mut region = Region::with_size(id, chunk_size);
region.parent_id = Some(parent_id);
self.regions.insert(id, region);
if let Some(parent) = self.regions.get_mut(&parent_id) {
parent.add_child(id);
}
self.scope_stack.push(id);
id
}
pub fn pop_region(&mut self) -> Option<u64> {
if self.scope_stack.len() <= 1 {
return None;
}
let id = self.scope_stack.pop()?;
if let Some(region) = self.regions.get_mut(&id) {
region.deactivate();
}
Some(id)
}
pub fn alloc(&mut self, size: usize) -> Option<(u64, ArenaOffset)> {
let id = self.current_region_id();
let offset = self.regions.get_mut(&id)?.alloc(size)?;
Some((id, offset))
}
pub fn get_bytes(&self, region_id: u64, loc: &ArenaOffset, size: usize) -> Option<&[u8]> {
self.regions.get(®ion_id)?.get_bytes(loc, size)
}
pub fn reset_region(&mut self, region_id: u64) {
let children: Vec<u64> = self
.regions
.get(®ion_id)
.map(|r| r.children().to_vec())
.unwrap_or_default();
for child_id in children {
self.reset_region(child_id);
}
if let Some(region) = self.regions.get_mut(®ion_id) {
region.reset();
}
}
pub fn get_region(&self, id: u64) -> Option<&Region> {
self.regions.get(&id)
}
pub fn get_region_mut(&mut self, id: u64) -> Option<&mut Region> {
self.regions.get_mut(&id)
}
pub fn num_regions(&self) -> usize {
self.regions.len()
}
pub fn total_bytes_used(&self) -> usize {
self.regions.values().map(|r| r.bytes_used()).sum()
}
pub fn total_capacity(&self) -> usize {
self.regions.values().map(|r| r.total_capacity()).sum()
}
pub fn scope_depth(&self) -> usize {
self.scope_stack.len()
}
pub fn remove_region(&mut self, region_id: u64) {
let children: Vec<u64> = self
.regions
.get(®ion_id)
.map(|r| r.children().to_vec())
.unwrap_or_default();
for child_id in children {
self.remove_region(child_id);
}
self.regions.remove(®ion_id);
}
}
pub struct Region {
pub(super) id: u64,
pub(super) arena: BumpArena,
pub(super) parent_id: Option<u64>,
pub(super) children: Vec<u64>,
pub(super) active: bool,
stats: RegionStats,
}
impl Region {
pub fn new(id: u64) -> Self {
Region {
id,
arena: BumpArena::new(),
parent_id: None,
children: Vec::new(),
active: true,
stats: RegionStats::default(),
}
}
pub fn with_size(id: u64, chunk_size: usize) -> Self {
Region {
id,
arena: BumpArena::with_chunk_size(chunk_size),
parent_id: None,
children: Vec::new(),
active: true,
stats: RegionStats::default(),
}
}
pub fn child(id: u64, parent_id: u64) -> Self {
Region {
id,
arena: BumpArena::new(),
parent_id: Some(parent_id),
children: Vec::new(),
active: true,
stats: RegionStats::default(),
}
}
pub fn id(&self) -> u64 {
self.id
}
pub fn parent_id(&self) -> Option<u64> {
self.parent_id
}
pub fn is_active(&self) -> bool {
self.active
}
pub fn alloc(&mut self, size: usize) -> Option<ArenaOffset> {
if !self.active {
return None;
}
self.stats.allocations += 1;
self.stats.bytes_allocated += size as u64;
Some(self.arena.alloc(size))
}
pub fn get_bytes(&self, loc: &ArenaOffset, size: usize) -> Option<&[u8]> {
self.arena.get_bytes(loc, size)
}
pub fn get_bytes_mut(&mut self, loc: &ArenaOffset, size: usize) -> Option<&mut [u8]> {
self.arena.get_bytes_mut(loc, size)
}
pub fn reset(&mut self) {
self.arena.reset();
self.stats.resets += 1;
}
pub fn deactivate(&mut self) {
self.active = false;
}
pub fn reactivate(&mut self) {
self.active = true;
}
pub fn add_child(&mut self, child_id: u64) {
self.children.push(child_id);
}
pub fn children(&self) -> &[u64] {
&self.children
}
pub fn stats(&self) -> &RegionStats {
&self.stats
}
pub fn arena(&self) -> &BumpArena {
&self.arena
}
pub fn bytes_used(&self) -> usize {
self.arena.bytes_used()
}
pub fn total_capacity(&self) -> usize {
self.arena.total_capacity()
}
}
pub struct TypedArena<T> {
pub(super) values: Vec<T>,
free_list: Vec<usize>,
stats: TypedArenaStats,
}
impl<T> TypedArena<T> {
pub fn new() -> Self {
TypedArena {
values: Vec::new(),
free_list: Vec::new(),
stats: TypedArenaStats::default(),
}
}
pub fn with_capacity(cap: usize) -> Self {
TypedArena {
values: Vec::with_capacity(cap),
free_list: Vec::new(),
stats: TypedArenaStats::default(),
}
}
pub fn alloc(&mut self, value: T) -> ArenaIdx {
self.stats.total_allocations += 1;
self.stats.live_count += 1;
if self.stats.live_count > self.stats.peak_count {
self.stats.peak_count = self.stats.live_count;
}
let idx = self.values.len();
self.values.push(value);
ArenaIdx(idx as u32)
}
pub fn get(&self, idx: ArenaIdx) -> Option<&T> {
self.values.get(idx.0 as usize)
}
pub fn get_mut(&mut self, idx: ArenaIdx) -> Option<&mut T> {
self.values.get_mut(idx.0 as usize)
}
pub fn len(&self) -> usize {
self.values.len()
}
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = (ArenaIdx, &T)> {
self.values
.iter()
.enumerate()
.map(|(i, v)| (ArenaIdx(i as u32), v))
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = (ArenaIdx, &mut T)> {
self.values
.iter_mut()
.enumerate()
.map(|(i, v)| (ArenaIdx(i as u32), v))
}
pub fn stats(&self) -> &TypedArenaStats {
&self.stats
}
pub fn clear(&mut self) {
self.values.clear();
self.free_list.clear();
self.stats.live_count = 0;
}
pub fn capacity(&self) -> usize {
self.values.capacity()
}
}
#[derive(Clone, Debug, Default)]
pub struct ArenaPoolStats {
pub acquired: u64,
pub returned: u64,
pub created: u64,
pub discarded: u64,
}
#[allow(dead_code)]
pub struct ArenaAllocHistory {
inner: LinearAllocator,
history: Vec<AllocRecord>,
max_history: usize,
}
#[allow(dead_code)]
impl ArenaAllocHistory {
pub fn new(capacity: usize, max_history: usize) -> Self {
Self {
inner: LinearAllocator::new(capacity),
history: Vec::new(),
max_history,
}
}
pub fn alloc_labeled(&mut self, size: usize, align: usize, label: &str) -> Option<usize> {
let offset = self.inner.alloc_offset(size, align)?;
if self.history.len() < self.max_history {
self.history.push(AllocRecord {
size,
align,
offset,
label: label.to_string(),
});
}
Some(offset)
}
pub fn history(&self) -> &[AllocRecord] {
&self.history
}
pub fn top(&self) -> usize {
self.inner.top()
}
pub fn reset(&mut self) {
self.inner.reset();
self.history.clear();
}
pub fn alloc_count(&self) -> u64 {
self.inner.alloc_count()
}
pub fn largest_alloc(&self) -> Option<&AllocRecord> {
self.history.iter().max_by_key(|r| r.size)
}
}
#[allow(dead_code)]
#[derive(Debug)]
pub struct RingArena {
buf: Vec<u8>,
head: usize,
wrap_count: u64,
}
#[allow(dead_code)]
impl RingArena {
pub fn new(capacity: usize) -> Self {
Self {
buf: vec![0u8; capacity],
head: 0,
wrap_count: 0,
}
}
pub fn alloc(&mut self, size: usize) -> usize {
let start = self.head;
let cap = self.buf.len();
if cap == 0 {
return 0;
}
if start + size > cap {
self.wrap_count += 1;
self.head = size % cap;
0
} else {
self.head = (self.head + size) % cap;
if self.head == 0 && size > 0 {
self.wrap_count += 1;
}
start
}
}
pub fn capacity(&self) -> usize {
self.buf.len()
}
pub fn head(&self) -> usize {
self.head
}
pub fn wrap_count(&self) -> u64 {
self.wrap_count
}
pub fn get(&self, offset: usize, len: usize) -> Option<&[u8]> {
if offset + len <= self.buf.len() {
Some(&self.buf[offset..offset + len])
} else {
None
}
}
}
#[derive(Clone, Debug, Default)]
pub struct ArenaWatermark {
current: u64,
peak: u64,
}
#[allow(dead_code)]
impl ArenaWatermark {
pub fn new() -> Self {
Self::default()
}
pub fn record_alloc(&mut self, bytes: u64) {
self.current += bytes;
if self.current > self.peak {
self.peak = self.current;
}
}
pub fn record_free(&mut self, bytes: u64) {
self.current = self.current.saturating_sub(bytes);
}
pub fn current(&self) -> u64 {
self.current
}
pub fn peak(&self) -> u64 {
self.peak
}
pub fn reset(&mut self) {
self.current = 0;
self.peak = 0;
}
}