1use std::time::Duration;
2
3#[derive(Debug, Clone)]
8pub struct MemoryConfig {
9 pub max_allocations: usize,
11 pub max_history_age: Duration,
13 pub memory_limit_mb: usize,
15 pub enable_warnings: bool,
17 pub cleanup_threshold: f64,
19 pub batch_cleanup_size: usize,
21 pub enable_auto_compaction: bool,
23 pub compaction_interval: Duration,
25}
26
27impl Default for MemoryConfig {
28 fn default() -> Self {
29 Self {
30 max_allocations: 100_000,
31 max_history_age: Duration::from_secs(3600), memory_limit_mb: 512, enable_warnings: true,
34 cleanup_threshold: 0.8, batch_cleanup_size: 1000, enable_auto_compaction: true,
37 compaction_interval: Duration::from_secs(300), }
39 }
40}
41
42impl MemoryConfig {
43 pub fn development() -> Self {
45 Self {
46 max_allocations: 1_000_000,
47 max_history_age: Duration::from_secs(7200), memory_limit_mb: 1024, enable_warnings: true,
50 cleanup_threshold: 0.9,
51 batch_cleanup_size: 10000,
52 enable_auto_compaction: true,
53 compaction_interval: Duration::from_secs(600), }
55 }
56
57 pub fn production() -> Self {
59 Self {
60 max_allocations: 50_000,
61 max_history_age: Duration::from_secs(1800), memory_limit_mb: 256, enable_warnings: true,
64 cleanup_threshold: 0.7,
65 batch_cleanup_size: 500,
66 enable_auto_compaction: true,
67 compaction_interval: Duration::from_secs(120), }
69 }
70
71 pub fn testing() -> Self {
73 Self {
74 max_allocations: 1000,
75 max_history_age: Duration::from_secs(60), memory_limit_mb: 32, enable_warnings: false, cleanup_threshold: 0.8,
79 batch_cleanup_size: 100,
80 enable_auto_compaction: false, compaction_interval: Duration::from_secs(30),
82 }
83 }
84
85 pub fn high_performance() -> Self {
87 Self {
88 max_allocations: 200_000,
89 max_history_age: Duration::from_secs(900), memory_limit_mb: 512,
91 enable_warnings: false, cleanup_threshold: 0.85,
93 batch_cleanup_size: 2000, enable_auto_compaction: false, compaction_interval: Duration::from_secs(3600),
96 }
97 }
98
99 pub fn validate(&self) -> Result<(), ConfigError> {
101 if self.max_allocations == 0 {
102 return Err(ConfigError::InvalidValue(
103 "max_allocations must be greater than 0".into(),
104 ));
105 }
106
107 if self.memory_limit_mb == 0 {
108 return Err(ConfigError::InvalidValue(
109 "memory_limit_mb must be greater than 0".into(),
110 ));
111 }
112
113 if self.cleanup_threshold <= 0.0 || self.cleanup_threshold >= 1.0 {
114 return Err(ConfigError::InvalidValue(
115 "cleanup_threshold must be between 0.0 and 1.0".into(),
116 ));
117 }
118
119 if self.batch_cleanup_size == 0 {
120 return Err(ConfigError::InvalidValue(
121 "batch_cleanup_size must be greater than 0".into(),
122 ));
123 }
124
125 if self.batch_cleanup_size > self.max_allocations {
126 return Err(ConfigError::InvalidValue(
127 "batch_cleanup_size should not exceed max_allocations".into(),
128 ));
129 }
130
131 Ok(())
132 }
133
134 pub fn auto_adjust_for_system(&mut self) -> Result<(), ConfigError> {
136 let system_memory_mb = self.get_system_memory_mb()?;
138
139 let max_allowed_mb = (system_memory_mb as f64 * 0.1) as usize;
141 if self.memory_limit_mb > max_allowed_mb {
142 self.memory_limit_mb = max_allowed_mb.max(64); }
144
145 self.max_allocations = (self.memory_limit_mb * 1024 * 1024 / 512).min(self.max_allocations);
147 self.batch_cleanup_size = (self.max_allocations / 100).max(100);
148
149 Ok(())
150 }
151
152 fn get_system_memory_mb(&self) -> Result<usize, ConfigError> {
154 #[cfg(target_os = "linux")]
155 {
156 use std::fs;
157 let meminfo = fs::read_to_string("/proc/meminfo")
158 .map_err(|_| ConfigError::SystemInfoUnavailable)?;
159
160 for line in meminfo.lines() {
161 if line.starts_with("MemTotal:") {
162 let parts: Vec<&str> = line.split_whitespace().collect();
163 if parts.len() >= 2 {
164 let kb: usize = parts[1]
165 .parse()
166 .map_err(|_| ConfigError::SystemInfoUnavailable)?;
167 return Ok(kb / 1024); }
169 }
170 }
171 }
172
173 #[cfg(target_os = "windows")]
174 {
175 Ok(8192)
176 }
177 #[cfg(target_os = "macos")]
178 {
179 Ok(8192)
180 }
181 #[cfg(not(any(target_os = "windows", target_os = "macos")))]
182 {
183 Ok(4096)
184 }
185 }
186
187 pub fn for_current_system() -> Result<Self, ConfigError> {
189 let mut config = Self::default();
190 config.auto_adjust_for_system()?;
191 config.validate()?;
192 Ok(config)
193 }
194
195 pub fn estimate_memory_usage(&self) -> MemoryEstimate {
197 let avg_allocation_size = 128; let max_memory_usage = self.max_allocations * avg_allocation_size;
200 let configured_limit = self.memory_limit_mb * 1024 * 1024;
201
202 MemoryEstimate {
203 max_entries: self.max_allocations,
204 estimated_max_usage_mb: (max_memory_usage / (1024 * 1024)) as f64,
205 configured_limit_mb: self.memory_limit_mb as f64,
206 effective_limit_mb: (configured_limit.min(max_memory_usage) / (1024 * 1024)) as f64,
207 cleanup_trigger_mb: (configured_limit as f64 * self.cleanup_threshold)
208 / (1024.0 * 1024.0),
209 }
210 }
211}
212
213#[derive(Debug, thiserror::Error)]
215pub enum ConfigError {
216 #[error("Invalid configuration value: {0}")]
217 InvalidValue(String),
218
219 #[error("System information unavailable")]
220 SystemInfoUnavailable,
221
222 #[error("Insufficient system resources")]
223 InsufficientResources,
224}
225
226#[derive(Debug, Clone)]
228pub struct MemoryEstimate {
229 pub max_entries: usize,
231 pub estimated_max_usage_mb: f64,
233 pub configured_limit_mb: f64,
235 pub effective_limit_mb: f64,
237 pub cleanup_trigger_mb: f64,
239}
240
241impl MemoryEstimate {
242 pub fn is_reasonable(&self) -> bool {
244 self.effective_limit_mb >= 1.0 && self.cleanup_trigger_mb < self.effective_limit_mb && self.max_entries >= 10 }
248
249 pub fn get_recommendations(&self) -> Vec<String> {
251 let mut recommendations = Vec::new();
252
253 if self.effective_limit_mb < 64.0 {
254 recommendations.push(
255 "Consider increasing memory limit to at least 64MB for better performance".into(),
256 );
257 }
258
259 if self.max_entries < 10000 {
260 recommendations
261 .push("Low max_entries may cause frequent cleanup, consider increasing".into());
262 }
263
264 if self.cleanup_trigger_mb / self.effective_limit_mb > 0.9 {
265 recommendations.push("Cleanup threshold is too high, may cause memory pressure".into());
266 }
267
268 if self.estimated_max_usage_mb > self.configured_limit_mb * 2.0 {
269 recommendations.push("Estimated usage significantly exceeds configured limit".into());
270 }
271
272 recommendations
273 }
274}
275
276#[cfg(test)]
277mod tests {
278 use super::*;
279
280 #[test]
281 fn test_default_config_validation() {
282 let config = MemoryConfig::default();
283 assert!(config.validate().is_ok());
284 }
285
286 #[test]
287 fn test_preset_configs() {
288 let configs = vec![
289 MemoryConfig::development(),
290 MemoryConfig::production(),
291 MemoryConfig::testing(),
292 MemoryConfig::high_performance(),
293 ];
294
295 for config in configs {
296 assert!(config.validate().is_ok(), "Preset config should be valid");
297 }
298 }
299
300 #[test]
301 fn test_invalid_configs() {
302 let config = MemoryConfig {
304 max_allocations: 0,
305 ..Default::default()
306 };
307 assert!(config.validate().is_err());
308
309 let config2 = MemoryConfig {
311 cleanup_threshold: 1.5,
312 ..Default::default()
313 };
314 assert!(config2.validate().is_err());
315
316 let config3 = MemoryConfig {
317 cleanup_threshold: -0.1,
318 ..Default::default()
319 };
320 assert!(config3.validate().is_err());
321 }
322
323 #[test]
324 fn test_memory_estimation() {
325 let config = MemoryConfig::default();
326 let estimate = config.estimate_memory_usage();
327
328 let _ = estimate.is_reasonable(); assert!(estimate.effective_limit_mb > 0.0);
331 let _ = estimate.cleanup_trigger_mb; }
334
335 #[test]
336 fn test_recommendations() {
337 let config = MemoryConfig {
338 max_allocations: 500, memory_limit_mb: 16, cleanup_threshold: 0.95, ..Default::default()
342 };
343
344 let estimate = config.estimate_memory_usage();
345 let recommendations = estimate.get_recommendations();
346
347 assert!(!recommendations.is_empty());
348 assert!(recommendations.len() >= 2); }
350
351 #[test]
352 fn test_system_config_creation() {
353 match MemoryConfig::for_current_system() {
355 Ok(config) => {
356 assert!(config.validate().is_ok());
357 assert!(config.memory_limit_mb >= 64);
358 }
359 Err(_) => {
360 }
362 }
363 }
364}