use std::collections::HashMap;
use std::sync::atomic::{AtomicUsize, Ordering};
#[derive(Debug)]
pub struct MemoryManager {
current_usage: AtomicUsize,
max_memory: usize,
allocations: std::sync::Mutex<HashMap<String, usize>>,
}
impl MemoryManager {
pub fn new(max_memory: usize) -> Self {
MemoryManager {
current_usage: AtomicUsize::new(0),
max_memory,
allocations: std::sync::Mutex::new(HashMap::new()),
}
}
pub fn try_allocate(&self, bytes: usize, allocation_type: &str) -> bool {
let old_usage = self.current_usage.fetch_add(bytes, Ordering::SeqCst);
let new_usage = old_usage + bytes;
if new_usage > self.max_memory {
self.current_usage.fetch_sub(bytes, Ordering::SeqCst);
false
} else {
if let Ok(mut allocs) = self.allocations.lock() {
*allocs.entry(allocation_type.to_string()).or_insert(0) += bytes;
}
true
}
}
pub fn free(&self, bytes: usize, allocation_type: &str) {
self.current_usage.fetch_sub(bytes, Ordering::SeqCst);
if let Ok(mut allocs) = self.allocations.lock() {
if let Some(current) = allocs.get_mut(allocation_type) {
*current = current.saturating_sub(bytes);
}
}
}
pub fn current_usage(&self) -> usize {
self.current_usage.load(Ordering::SeqCst)
}
pub fn max_memory(&self) -> usize {
self.max_memory
}
pub fn usage_percentage(&self) -> f64 {
(self.current_usage() as f64 / self.max_memory as f64) * 100.0
}
pub fn allocation_breakdown(&self) -> HashMap<String, usize> {
self.allocations.lock().unwrap().clone()
}
pub fn has_capacity(&self, bytes: usize) -> bool {
self.current_usage() + bytes <= self.max_memory
}
}
pub mod size_estimate {
use glyph_types::Value;
use std::mem;
pub fn estimate_value_size(value: &Value) -> usize {
match value {
Value::None => mem::size_of::<Value>(),
Value::Bool(_) => mem::size_of::<Value>(),
Value::Int(_) => mem::size_of::<Value>(),
Value::Float(_) => mem::size_of::<Value>(),
Value::Str(s) => mem::size_of::<Value>() + s.len(),
Value::Bytes(b) => mem::size_of::<Value>() + b.len(),
Value::List(items) => {
mem::size_of::<Value>() + items.iter().map(estimate_value_size).sum::<usize>()
}
Value::Dict(map) => {
mem::size_of::<Value>()
+ map
.iter()
.map(|(k, v)| k.len() + estimate_value_size(v))
.sum::<usize>()
}
Value::Optional(opt) => {
mem::size_of::<Value>() + opt.as_ref().map_or(0, |v| estimate_value_size(v))
}
Value::Promise(_) => mem::size_of::<Value>(),
Value::Result(res) => {
mem::size_of::<Value>()
+ match res {
Ok(v) => estimate_value_size(v),
Err(v) => estimate_value_size(v),
}
}
Value::Function { params, body } => {
mem::size_of::<Value>() + params.iter().map(|p| p.len()).sum::<usize>() + body.len()
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_memory_allocation() {
let mm = MemoryManager::new(1000);
assert!(mm.try_allocate(100, "test"));
assert_eq!(mm.current_usage(), 100);
assert!(mm.try_allocate(400, "test"));
assert_eq!(mm.current_usage(), 500);
assert!(!mm.try_allocate(600, "test"));
assert_eq!(mm.current_usage(), 500);
mm.free(200, "test");
assert_eq!(mm.current_usage(), 300);
}
#[test]
fn test_allocation_tracking() {
let mm = MemoryManager::new(1000);
mm.try_allocate(100, "strings");
mm.try_allocate(200, "lists");
mm.try_allocate(150, "strings");
let breakdown = mm.allocation_breakdown();
assert_eq!(breakdown.get("strings"), Some(&250));
assert_eq!(breakdown.get("lists"), Some(&200));
}
}