#[cfg(feature = "arena")]
use bumpalo::Bump;
#[cfg(not(feature = "std"))]
use alloc::{string::{String, ToString}, vec::Vec};
#[cfg(feature = "std")]
use std::time::{Duration, Instant};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MemoryStats {
pub total_allocated: usize,
pub memory_in_use: usize,
pub reclaimable_memory: usize,
pub arena_resets: usize,
pub gc_cycles: usize,
pub peak_memory: usize,
}
impl Default for MemoryStats {
fn default() -> Self {
Self {
total_allocated: 0,
memory_in_use: 0,
reclaimable_memory: 0,
arena_resets: 0,
gc_cycles: 0,
peak_memory: 0,
}
}
}
#[derive(Debug, Clone)]
pub struct MemoryPoolConfig {
pub max_arena_size: usize,
pub reset_threshold: usize,
pub pressure_threshold: f64,
pub auto_gc: bool,
#[cfg(feature = "std")]
pub min_gc_interval: Duration,
pub growth_rate_threshold: f64,
}
impl Default for MemoryPoolConfig {
fn default() -> Self {
Self {
max_arena_size: 64 * 1024 * 1024, reset_threshold: 1000,
pressure_threshold: 0.8, auto_gc: true,
#[cfg(feature = "std")]
min_gc_interval: Duration::from_secs(30),
growth_rate_threshold: 2.0, }
}
}
#[derive(Debug)]
pub struct MemoryPool {
config: MemoryPoolConfig,
#[cfg(feature = "arena")]
primary_arena: Bump,
#[cfg(feature = "arena")]
temp_arena: Bump,
stats: MemoryStats,
ops_since_reset: usize,
#[cfg(feature = "std")]
last_gc: Option<Instant>,
memory_at_last_gc: usize,
}
impl MemoryPool {
pub fn new() -> Self {
Self::with_config(MemoryPoolConfig::default())
}
pub fn with_config(config: MemoryPoolConfig) -> Self {
Self {
config,
#[cfg(feature = "arena")]
primary_arena: Bump::new(),
#[cfg(feature = "arena")]
temp_arena: Bump::new(),
stats: MemoryStats::default(),
ops_since_reset: 0,
#[cfg(feature = "std")]
last_gc: None,
memory_at_last_gc: 0,
}
}
#[cfg(feature = "arena")]
#[must_use]
pub fn primary_arena(&self) -> &Bump {
&self.primary_arena
}
#[cfg(feature = "arena")]
pub fn primary_arena_mut(&mut self) -> &mut Bump {
self.ops_since_reset += 1;
self.check_reset_conditions();
&mut self.primary_arena
}
#[cfg(feature = "arena")]
#[must_use]
pub fn temp_arena(&self) -> &Bump {
&self.temp_arena
}
#[cfg(feature = "arena")]
pub fn temp_arena_mut(&mut self) -> &mut Bump {
&mut self.temp_arena
}
#[cfg(feature = "arena")]
pub fn reset_primary_arena(&mut self) {
self.primary_arena.reset();
self.stats.arena_resets += 1;
self.ops_since_reset = 0;
self.update_memory_stats();
}
#[cfg(feature = "arena")]
pub fn reset_temp_arena(&mut self) {
self.temp_arena.reset();
self.update_memory_stats();
}
#[cfg(feature = "arena")]
pub fn reset_all_arenas(&mut self) {
self.reset_primary_arena();
self.reset_temp_arena();
}
fn check_reset_conditions(&mut self) {
let should_reset = self.ops_since_reset >= self.config.reset_threshold
|| self.is_under_memory_pressure()
|| self.should_perform_gc();
if should_reset {
#[cfg(feature = "arena")]
self.reset_primary_arena();
}
}
fn is_under_memory_pressure(&self) -> bool {
if self.config.max_arena_size == 0 {
return false;
}
let pressure_ratio = self.stats.memory_in_use as f64 / self.config.max_arena_size as f64;
pressure_ratio > self.config.pressure_threshold
}
fn should_perform_gc(&self) -> bool {
if !self.config.auto_gc {
return false;
}
#[cfg(feature = "std")]
{
if let Some(last_gc) = self.last_gc {
if last_gc.elapsed() < self.config.min_gc_interval {
return false;
}
}
}
if self.memory_at_last_gc > 0 {
let growth_ratio = self.stats.memory_in_use as f64 / self.memory_at_last_gc as f64;
growth_ratio > self.config.growth_rate_threshold
} else {
self.stats.memory_in_use > self.config.max_arena_size / 2
}
}
pub fn collect_garbage(&mut self) -> usize {
let memory_before = self.stats.memory_in_use;
#[cfg(feature = "arena")]
self.reset_temp_arena();
if self.is_under_memory_pressure() {
#[cfg(feature = "arena")]
self.reset_primary_arena();
}
self.stats.gc_cycles += 1;
#[cfg(feature = "std")]
{
self.last_gc = Some(Instant::now());
}
self.memory_at_last_gc = self.stats.memory_in_use;
memory_before.saturating_sub(self.stats.memory_in_use)
}
fn update_memory_stats(&mut self) {
#[cfg(feature = "arena")]
{
let estimated_primary = self.primary_arena.allocated_bytes();
let estimated_temp = self.temp_arena.allocated_bytes();
self.stats.memory_in_use = estimated_primary + estimated_temp;
self.stats.total_allocated = self.stats.memory_in_use;
if self.stats.memory_in_use > self.stats.peak_memory {
self.stats.peak_memory = self.stats.memory_in_use;
}
}
}
#[must_use]
pub fn stats(&self) -> &MemoryStats {
&self.stats
}
#[must_use]
pub fn memory_usage_string(&self) -> String {
let mb = self.stats.memory_in_use as f64 / (1024.0 * 1024.0);
format!("{mb:.2}MB")
}
#[must_use]
pub fn is_memory_usage_acceptable(&self) -> bool {
self.stats.memory_in_use <= self.config.max_arena_size
}
pub fn force_cleanup(&mut self) {
#[cfg(feature = "arena")]
self.reset_all_arenas();
self.ops_since_reset = 0;
self.memory_at_last_gc = 0;
#[cfg(feature = "std")]
{
self.last_gc = Some(Instant::now());
}
self.update_memory_stats();
}
pub fn set_config(&mut self, config: MemoryPoolConfig) {
self.config = config;
}
#[must_use]
pub fn config(&self) -> &MemoryPoolConfig {
&self.config
}
}
impl Default for MemoryPool {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResetStrategy {
OperationCount(usize),
MemoryThreshold(usize),
#[cfg(feature = "std")]
TimeInterval(Duration),
PressureRatio(f64),
Hybrid,
Manual,
}
impl Default for ResetStrategy {
fn default() -> Self {
Self::Hybrid
}
}
#[derive(Debug)]
pub struct SmartArenaManager {
pool: MemoryPool,
strategy: ResetStrategy,
#[cfg(feature = "std")]
last_evaluation: Instant,
}
impl SmartArenaManager {
pub fn new(strategy: ResetStrategy) -> Self {
Self {
pool: MemoryPool::new(),
strategy,
#[cfg(feature = "std")]
last_evaluation: Instant::now(),
}
}
pub fn evaluate_reset_strategy(&mut self) -> bool {
match self.strategy {
ResetStrategy::OperationCount(threshold) => {
if self.pool.ops_since_reset >= threshold {
self.pool.force_cleanup();
true
} else {
false
}
}
ResetStrategy::MemoryThreshold(threshold) => {
if self.pool.stats.memory_in_use >= threshold {
self.pool.force_cleanup();
true
} else {
false
}
}
#[cfg(feature = "std")]
ResetStrategy::TimeInterval(interval) => {
if self.last_evaluation.elapsed() >= interval {
self.pool.force_cleanup();
self.last_evaluation = Instant::now();
true
} else {
false
}
}
ResetStrategy::PressureRatio(ratio) => {
if self.pool.is_under_memory_pressure() {
let current_ratio = self.pool.stats.memory_in_use as f64
/ self.pool.config.max_arena_size as f64;
if current_ratio >= ratio {
self.pool.force_cleanup();
return true;
}
}
false
}
ResetStrategy::Hybrid => {
let should_reset = self.pool.ops_since_reset >= 1000
|| self.pool.is_under_memory_pressure()
|| self.pool.should_perform_gc();
if should_reset {
self.pool.collect_garbage();
true
} else {
false
}
}
ResetStrategy::Manual => false, }
}
#[must_use]
pub fn pool(&self) -> &MemoryPool {
&self.pool
}
pub fn pool_mut(&mut self) -> &mut MemoryPool {
self.evaluate_reset_strategy();
&mut self.pool
}
pub fn set_strategy(&mut self, strategy: ResetStrategy) {
self.strategy = strategy;
}
#[must_use]
pub const fn strategy(&self) -> ResetStrategy {
self.strategy
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(not(feature = "std"))]
use alloc::{format};
#[test]
fn memory_pool_creation() {
let pool = MemoryPool::new();
assert_eq!(pool.stats().memory_in_use, 0);
assert_eq!(pool.stats().arena_resets, 0);
}
#[test]
fn memory_pool_configuration() {
let config = MemoryPoolConfig {
max_arena_size: 1024,
reset_threshold: 10,
..Default::default()
};
let pool = MemoryPool::with_config(config);
assert_eq!(pool.config().max_arena_size, 1024);
assert_eq!(pool.config().reset_threshold, 10);
}
#[test]
#[cfg(feature = "arena")]
fn arena_reset_functionality() {
let mut pool = MemoryPool::new();
for _ in 0..1001 {
let _ = pool.primary_arena_mut();
}
assert!(pool.stats().arena_resets > 0);
}
#[test]
fn garbage_collection() {
let mut pool = MemoryPool::new();
let reclaimed = pool.collect_garbage();
assert_eq!(pool.stats().gc_cycles, 1);
}
#[test]
fn smart_arena_manager() {
let mut manager = SmartArenaManager::new(ResetStrategy::OperationCount(100));
assert!(!manager.evaluate_reset_strategy());
manager.set_strategy(ResetStrategy::Manual);
assert_eq!(manager.strategy(), ResetStrategy::Manual);
}
#[test]
fn memory_stats_tracking() {
let pool = MemoryPool::new();
let stats = pool.stats();
assert_eq!(stats.total_allocated, 0);
assert_eq!(stats.memory_in_use, 0);
assert_eq!(stats.peak_memory, 0);
}
#[test]
fn memory_pressure_detection() {
let config = MemoryPoolConfig {
max_arena_size: 1024,
pressure_threshold: 0.5, ..Default::default()
};
let pool = MemoryPool::with_config(config);
assert!(!pool.is_under_memory_pressure());
assert!(pool.is_memory_usage_acceptable());
}
}