1use std::collections::HashMap;
4use std::sync::atomic::{AtomicUsize, Ordering};
5
6#[derive(Debug)]
8pub struct MemoryManager {
9 current_usage: AtomicUsize,
11 max_memory: usize,
13 allocations: std::sync::Mutex<HashMap<String, usize>>,
15}
16
17impl MemoryManager {
18 pub fn new(max_memory: usize) -> Self {
20 MemoryManager {
21 current_usage: AtomicUsize::new(0),
22 max_memory,
23 allocations: std::sync::Mutex::new(HashMap::new()),
24 }
25 }
26
27 pub fn try_allocate(&self, bytes: usize, allocation_type: &str) -> bool {
29 let old_usage = self.current_usage.fetch_add(bytes, Ordering::SeqCst);
30 let new_usage = old_usage + bytes;
31
32 if new_usage > self.max_memory {
33 self.current_usage.fetch_sub(bytes, Ordering::SeqCst);
35 false
36 } else {
37 if let Ok(mut allocs) = self.allocations.lock() {
39 *allocs.entry(allocation_type.to_string()).or_insert(0) += bytes;
40 }
41 true
42 }
43 }
44
45 pub fn free(&self, bytes: usize, allocation_type: &str) {
47 self.current_usage.fetch_sub(bytes, Ordering::SeqCst);
48
49 if let Ok(mut allocs) = self.allocations.lock() {
50 if let Some(current) = allocs.get_mut(allocation_type) {
51 *current = current.saturating_sub(bytes);
52 }
53 }
54 }
55
56 pub fn current_usage(&self) -> usize {
58 self.current_usage.load(Ordering::SeqCst)
59 }
60
61 pub fn max_memory(&self) -> usize {
63 self.max_memory
64 }
65
66 pub fn usage_percentage(&self) -> f64 {
68 (self.current_usage() as f64 / self.max_memory as f64) * 100.0
69 }
70
71 pub fn allocation_breakdown(&self) -> HashMap<String, usize> {
73 self.allocations.lock().unwrap().clone()
74 }
75
76 pub fn has_capacity(&self, bytes: usize) -> bool {
78 self.current_usage() + bytes <= self.max_memory
79 }
80}
81
82pub mod size_estimate {
84 use glyph_types::Value;
85 use std::mem;
86
87 pub fn estimate_value_size(value: &Value) -> usize {
89 match value {
90 Value::None => mem::size_of::<Value>(),
91 Value::Bool(_) => mem::size_of::<Value>(),
92 Value::Int(_) => mem::size_of::<Value>(),
93 Value::Float(_) => mem::size_of::<Value>(),
94 Value::Str(s) => mem::size_of::<Value>() + s.len(),
95 Value::Bytes(b) => mem::size_of::<Value>() + b.len(),
96 Value::List(items) => {
97 mem::size_of::<Value>() + items.iter().map(estimate_value_size).sum::<usize>()
98 }
99 Value::Dict(map) => {
100 mem::size_of::<Value>()
101 + map
102 .iter()
103 .map(|(k, v)| k.len() + estimate_value_size(v))
104 .sum::<usize>()
105 }
106 Value::Optional(opt) => {
107 mem::size_of::<Value>() + opt.as_ref().map_or(0, |v| estimate_value_size(v))
108 }
109 Value::Promise(_) => mem::size_of::<Value>(),
110 Value::Result(res) => {
111 mem::size_of::<Value>()
112 + match res {
113 Ok(v) => estimate_value_size(v),
114 Err(v) => estimate_value_size(v),
115 }
116 }
117 Value::Function { params, body } => {
118 mem::size_of::<Value>() + params.iter().map(|p| p.len()).sum::<usize>() + body.len()
119 }
120 }
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn test_memory_allocation() {
130 let mm = MemoryManager::new(1000);
131
132 assert!(mm.try_allocate(100, "test"));
133 assert_eq!(mm.current_usage(), 100);
134
135 assert!(mm.try_allocate(400, "test"));
136 assert_eq!(mm.current_usage(), 500);
137
138 assert!(!mm.try_allocate(600, "test"));
140 assert_eq!(mm.current_usage(), 500); mm.free(200, "test");
143 assert_eq!(mm.current_usage(), 300);
144 }
145
146 #[test]
147 fn test_allocation_tracking() {
148 let mm = MemoryManager::new(1000);
149
150 mm.try_allocate(100, "strings");
151 mm.try_allocate(200, "lists");
152 mm.try_allocate(150, "strings");
153
154 let breakdown = mm.allocation_breakdown();
155 assert_eq!(breakdown.get("strings"), Some(&250));
156 assert_eq!(breakdown.get("lists"), Some(&200));
157 }
158}