use crate::core::{MemScopeError, MemScopeResult};
use std::time::Duration;
#[derive(Debug, Clone)]
pub struct MemoryConfig {
pub max_allocations: usize,
pub max_history_age: Duration,
pub memory_limit_mb: usize,
pub enable_warnings: bool,
pub cleanup_threshold: f64,
pub batch_cleanup_size: usize,
pub enable_auto_compaction: bool,
pub compaction_interval: Duration,
}
impl Default for MemoryConfig {
fn default() -> Self {
Self {
max_allocations: 100_000,
max_history_age: Duration::from_secs(3600), memory_limit_mb: 512, enable_warnings: true,
cleanup_threshold: 0.8, batch_cleanup_size: 1000, enable_auto_compaction: true,
compaction_interval: Duration::from_secs(300), }
}
}
impl MemoryConfig {
pub fn development() -> Self {
Self {
max_allocations: 1_000_000,
max_history_age: Duration::from_secs(7200), memory_limit_mb: 1024, enable_warnings: true,
cleanup_threshold: 0.9,
batch_cleanup_size: 10000,
enable_auto_compaction: true,
compaction_interval: Duration::from_secs(600), }
}
pub fn production() -> Self {
Self {
max_allocations: 50_000,
max_history_age: Duration::from_secs(1800), memory_limit_mb: 256, enable_warnings: true,
cleanup_threshold: 0.7,
batch_cleanup_size: 500,
enable_auto_compaction: true,
compaction_interval: Duration::from_secs(120), }
}
pub fn testing() -> Self {
Self {
max_allocations: 1000,
max_history_age: Duration::from_secs(60), memory_limit_mb: 32, enable_warnings: false, cleanup_threshold: 0.8,
batch_cleanup_size: 100,
enable_auto_compaction: false, compaction_interval: Duration::from_secs(30),
}
}
pub fn high_performance() -> Self {
Self {
max_allocations: 200_000,
max_history_age: Duration::from_secs(900), memory_limit_mb: 512,
enable_warnings: false, cleanup_threshold: 0.85,
batch_cleanup_size: 2000, enable_auto_compaction: false, compaction_interval: Duration::from_secs(3600),
}
}
pub fn validate(&self) -> MemScopeResult<()> {
if self.max_allocations == 0 {
return Err(MemScopeError::error(
"config",
"validate",
"max_allocations must be greater than 0",
));
}
if self.memory_limit_mb == 0 {
return Err(MemScopeError::error(
"config",
"validate",
"memory_limit_mb must be greater than 0",
));
}
if self.cleanup_threshold <= 0.0 || self.cleanup_threshold >= 1.0 {
return Err(MemScopeError::error(
"config",
"validate",
"cleanup_threshold must be between 0.0 and 1.0",
));
}
if self.batch_cleanup_size == 0 {
return Err(MemScopeError::error(
"config",
"validate",
"batch_cleanup_size must be greater than 0",
));
}
if self.batch_cleanup_size > self.max_allocations {
return Err(MemScopeError::error(
"config",
"validate",
"batch_cleanup_size should not exceed max_allocations",
));
}
Ok(())
}
pub fn auto_adjust_for_system(&mut self) -> MemScopeResult<()> {
let system_memory_mb = self.get_system_memory_mb()?;
let max_allowed_mb = (system_memory_mb as f64 * 0.1) as usize;
if self.memory_limit_mb > max_allowed_mb {
self.memory_limit_mb = max_allowed_mb.max(64); }
self.max_allocations = (self.memory_limit_mb * 1024 * 1024 / 512).min(self.max_allocations);
self.batch_cleanup_size = (self.max_allocations / 100).max(100);
Ok(())
}
fn get_system_memory_mb(&self) -> MemScopeResult<usize> {
#[cfg(target_os = "linux")]
{
use std::fs;
let meminfo = fs::read_to_string("/proc/meminfo").map_err(|_| {
MemScopeError::error("config", "get_system_memory_mb", "System info unavailable")
})?;
for line in meminfo.lines() {
if line.starts_with("MemTotal:") {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 2 {
let kb: usize = parts[1].parse().map_err(|_| {
MemScopeError::error(
"config",
"get_system_memory_mb",
"Failed to parse memory info",
)
})?;
return Ok(kb / 1024); }
}
}
}
#[cfg(target_os = "windows")]
{
Ok(8192)
}
#[cfg(target_os = "macos")]
{
Ok(8192)
}
#[cfg(not(any(target_os = "windows", target_os = "macos")))]
{
Ok(4096)
}
}
pub fn for_current_system() -> MemScopeResult<Self> {
let mut config = Self::default();
config.auto_adjust_for_system()?;
config.validate()?;
Ok(config)
}
pub fn estimate_memory_usage(&self) -> MemoryEstimate {
let avg_allocation_size = 128; let max_memory_usage = self.max_allocations * avg_allocation_size;
let configured_limit = self.memory_limit_mb * 1024 * 1024;
MemoryEstimate {
max_entries: self.max_allocations,
estimated_max_usage_mb: (max_memory_usage / (1024 * 1024)) as f64,
configured_limit_mb: self.memory_limit_mb as f64,
effective_limit_mb: (configured_limit.min(max_memory_usage) / (1024 * 1024)) as f64,
cleanup_trigger_mb: (configured_limit as f64 * self.cleanup_threshold)
/ (1024.0 * 1024.0),
}
}
}
#[derive(Debug, Clone)]
pub struct MemoryEstimate {
pub max_entries: usize,
pub estimated_max_usage_mb: f64,
pub configured_limit_mb: f64,
pub effective_limit_mb: f64,
pub cleanup_trigger_mb: f64,
}
impl MemoryEstimate {
pub fn is_reasonable(&self) -> bool {
self.effective_limit_mb >= 1.0 && self.cleanup_trigger_mb < self.effective_limit_mb && self.max_entries >= 10 }
pub fn get_recommendations(&self) -> Vec<String> {
let mut recommendations = Vec::new();
if self.effective_limit_mb < 64.0 {
recommendations.push(
"Consider increasing memory limit to at least 64MB for better performance".into(),
);
}
if self.max_entries < 10000 {
recommendations
.push("Low max_entries may cause frequent cleanup, consider increasing".into());
}
if self.cleanup_trigger_mb / self.effective_limit_mb > 0.9 {
recommendations.push("Cleanup threshold is too high, may cause memory pressure".into());
}
if self.estimated_max_usage_mb > self.configured_limit_mb * 2.0 {
recommendations.push("Estimated usage significantly exceeds configured limit".into());
}
recommendations
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config_validation() {
let config = MemoryConfig::default();
assert!(config.validate().is_ok());
}
#[test]
fn test_preset_configs() {
let configs = vec![
MemoryConfig::development(),
MemoryConfig::production(),
MemoryConfig::testing(),
MemoryConfig::high_performance(),
];
for config in configs {
assert!(config.validate().is_ok(), "Preset config should be valid");
}
}
#[test]
fn test_invalid_configs() {
let config = MemoryConfig {
max_allocations: 0,
..Default::default()
};
assert!(config.validate().is_err());
let config2 = MemoryConfig {
cleanup_threshold: 1.5,
..Default::default()
};
assert!(config2.validate().is_err());
let config3 = MemoryConfig {
cleanup_threshold: -0.1,
..Default::default()
};
assert!(config3.validate().is_err());
}
#[test]
fn test_memory_estimation() {
let config = MemoryConfig::default();
let estimate = config.estimate_memory_usage();
let _ = estimate.is_reasonable(); assert!(estimate.effective_limit_mb > 0.0);
let _ = estimate.cleanup_trigger_mb; }
#[test]
fn test_recommendations() {
let config = MemoryConfig {
max_allocations: 500, memory_limit_mb: 16, cleanup_threshold: 0.95, ..Default::default()
};
let estimate = config.estimate_memory_usage();
let recommendations = estimate.get_recommendations();
assert!(!recommendations.is_empty());
assert!(recommendations.len() >= 2); }
#[test]
fn test_system_config_creation() {
match MemoryConfig::for_current_system() {
Ok(config) => {
assert!(config.validate().is_ok());
assert!(config.memory_limit_mb >= 64);
}
Err(_) => {
}
}
}
}