use super::{Config, ValidationError, parse_env_var, parse_env_bool};
use crate::error::{Result, ZiporaError};
use std::path::Path;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum AllocationStrategy {
System,
SecurePool,
LockFree,
ThreadLocal,
FixedCapacity,
MemoryMapped,
}
impl Default for AllocationStrategy {
fn default() -> Self {
Self::SecurePool
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum CacheOptimizationLevel {
None,
Basic,
Advanced,
Maximum,
}
impl Default for CacheOptimizationLevel {
fn default() -> Self {
Self::Advanced
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct NumaConfig {
pub enable_numa_awareness: bool,
pub preferred_node: i32,
pub prefer_local_allocation: bool,
pub enable_balancing: bool,
}
impl Default for NumaConfig {
fn default() -> Self {
Self {
enable_numa_awareness: true,
preferred_node: -1, prefer_local_allocation: true,
enable_balancing: false,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct HugePageConfig {
pub enable_huge_pages: bool,
pub page_size: usize,
pub min_allocation_size: usize,
pub enable_transparent: bool,
}
impl Default for HugePageConfig {
fn default() -> Self {
Self {
enable_huge_pages: false, page_size: 0, min_allocation_size: 2 * 1024 * 1024, enable_transparent: true,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MemoryConfig {
pub allocation_strategy: AllocationStrategy,
pub initial_pool_size: usize,
pub max_pool_size: usize,
pub growth_factor: f64,
pub cache_optimization: CacheOptimizationLevel,
pub numa_config: NumaConfig,
pub huge_page_config: HugePageConfig,
pub enable_debug_tracking: bool,
pub enable_statistics: bool,
pub alignment: usize,
pub cache_line_size: usize,
pub num_pools: usize,
pub enable_prefetching: bool,
pub prefetch_distance: usize,
pub enable_hot_cold_separation: bool,
pub hot_access_threshold: u32,
pub enable_compaction: bool,
pub compaction_threshold: f64,
pub max_fragmentation_ratio: f64,
pub enable_memory_protection: bool,
pub guard_page_size: usize,
pub enable_use_after_free_detection: bool,
pub enable_double_free_detection: bool,
}
impl Default for MemoryConfig {
fn default() -> Self {
Self {
allocation_strategy: AllocationStrategy::default(),
initial_pool_size: 64 * 1024 * 1024, max_pool_size: 0, growth_factor: 1.618, cache_optimization: CacheOptimizationLevel::default(),
numa_config: NumaConfig::default(),
huge_page_config: HugePageConfig::default(),
enable_debug_tracking: false,
enable_statistics: true,
alignment: 64, cache_line_size: 0, num_pools: 8, enable_prefetching: true,
prefetch_distance: 2, enable_hot_cold_separation: true,
hot_access_threshold: 100,
enable_compaction: false, compaction_threshold: 0.8,
max_fragmentation_ratio: 0.3,
enable_memory_protection: true,
guard_page_size: 4096, enable_use_after_free_detection: true,
enable_double_free_detection: true,
}
}
}
impl Config for MemoryConfig {
fn validate(&self) -> Result<()> {
let mut errors = Vec::new();
if self.initial_pool_size == 0 {
errors.push(ValidationError::new(
"initial_pool_size",
&self.initial_pool_size.to_string(),
"initial pool size must be greater than 0"
).with_suggestion("typical values: 16MB-1GB"));
}
if self.max_pool_size != 0 && self.max_pool_size < self.initial_pool_size {
errors.push(ValidationError::new(
"max_pool_size",
&self.max_pool_size.to_string(),
"maximum pool size must be greater than initial pool size"
));
}
if self.growth_factor < 1.0 || self.growth_factor > 4.0 {
errors.push(ValidationError::new(
"growth_factor",
&self.growth_factor.to_string(),
"growth factor must be between 1.0 and 4.0"
).with_suggestion("typical values: 1.5-2.0, golden ratio: 1.618"));
}
if self.alignment == 0 || (self.alignment & (self.alignment - 1)) != 0 {
errors.push(ValidationError::new(
"alignment",
&self.alignment.to_string(),
"alignment must be a power of 2"
).with_suggestion("typical values: 8, 16, 32, 64, 128"));
}
if self.compaction_threshold < 0.0 || self.compaction_threshold > 1.0 {
errors.push(ValidationError::new(
"compaction_threshold",
&self.compaction_threshold.to_string(),
"compaction threshold must be between 0.0 and 1.0"
));
}
if self.max_fragmentation_ratio < 0.0 || self.max_fragmentation_ratio > 1.0 {
errors.push(ValidationError::new(
"max_fragmentation_ratio",
&self.max_fragmentation_ratio.to_string(),
"fragmentation ratio must be between 0.0 and 1.0"
));
}
if self.num_pools == 0 {
errors.push(ValidationError::new(
"num_pools",
&self.num_pools.to_string(),
"number of pools must be at least 1"
).with_suggestion("typical values: 4-16 based on CPU cores"));
}
if !errors.is_empty() {
return Err(ZiporaError::configuration(format!(
"Memory configuration validation failed: {}",
errors.into_iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join("; ")
)));
}
Ok(())
}
fn from_env_with_prefix(prefix: &str) -> Result<Self> {
let mut config = Self::default();
config.initial_pool_size = parse_env_var(&format!("{}MEMORY_INITIAL_POOL_SIZE", prefix), config.initial_pool_size);
config.max_pool_size = parse_env_var(&format!("{}MEMORY_MAX_POOL_SIZE", prefix), config.max_pool_size);
config.growth_factor = parse_env_var(&format!("{}MEMORY_GROWTH_FACTOR", prefix), config.growth_factor);
config.alignment = parse_env_var(&format!("{}MEMORY_ALIGNMENT", prefix), config.alignment);
config.cache_line_size = parse_env_var(&format!("{}MEMORY_CACHE_LINE_SIZE", prefix), config.cache_line_size);
config.num_pools = parse_env_var(&format!("{}MEMORY_NUM_POOLS", prefix), config.num_pools);
config.enable_debug_tracking = parse_env_bool(&format!("{}MEMORY_DEBUG_TRACKING", prefix), config.enable_debug_tracking);
config.enable_statistics = parse_env_bool(&format!("{}MEMORY_STATISTICS", prefix), config.enable_statistics);
config.enable_prefetching = parse_env_bool(&format!("{}MEMORY_PREFETCHING", prefix), config.enable_prefetching);
config.enable_hot_cold_separation = parse_env_bool(&format!("{}MEMORY_HOT_COLD_SEPARATION", prefix), config.enable_hot_cold_separation);
config.enable_compaction = parse_env_bool(&format!("{}MEMORY_COMPACTION", prefix), config.enable_compaction);
config.enable_memory_protection = parse_env_bool(&format!("{}MEMORY_PROTECTION", prefix), config.enable_memory_protection);
config.prefetch_distance = parse_env_var(&format!("{}MEMORY_PREFETCH_DISTANCE", prefix), config.prefetch_distance);
config.hot_access_threshold = parse_env_var(&format!("{}MEMORY_HOT_ACCESS_THRESHOLD", prefix), config.hot_access_threshold);
config.compaction_threshold = parse_env_var(&format!("{}MEMORY_COMPACTION_THRESHOLD", prefix), config.compaction_threshold);
config.max_fragmentation_ratio = parse_env_var(&format!("{}MEMORY_MAX_FRAGMENTATION_RATIO", prefix), config.max_fragmentation_ratio);
config.guard_page_size = parse_env_var(&format!("{}MEMORY_GUARD_PAGE_SIZE", prefix), config.guard_page_size);
config.numa_config.enable_numa_awareness = parse_env_bool(&format!("{}MEMORY_NUMA_ENABLE", prefix), config.numa_config.enable_numa_awareness);
config.numa_config.preferred_node = parse_env_var(&format!("{}MEMORY_NUMA_PREFERRED_NODE", prefix), config.numa_config.preferred_node);
config.numa_config.prefer_local_allocation = parse_env_bool(&format!("{}MEMORY_NUMA_LOCAL_ALLOCATION", prefix), config.numa_config.prefer_local_allocation);
config.numa_config.enable_balancing = parse_env_bool(&format!("{}MEMORY_NUMA_BALANCING", prefix), config.numa_config.enable_balancing);
config.huge_page_config.enable_huge_pages = parse_env_bool(&format!("{}MEMORY_HUGE_PAGES_ENABLE", prefix), config.huge_page_config.enable_huge_pages);
config.huge_page_config.page_size = parse_env_var(&format!("{}MEMORY_HUGE_PAGE_SIZE", prefix), config.huge_page_config.page_size);
config.huge_page_config.min_allocation_size = parse_env_var(&format!("{}MEMORY_HUGE_PAGE_MIN_SIZE", prefix), config.huge_page_config.min_allocation_size);
config.huge_page_config.enable_transparent = parse_env_bool(&format!("{}MEMORY_HUGE_PAGES_TRANSPARENT", prefix), config.huge_page_config.enable_transparent);
config.validate()?;
Ok(config)
}
fn performance_preset() -> Self {
let mut config = Self::default();
config.allocation_strategy = AllocationStrategy::LockFree;
config.initial_pool_size = 256 * 1024 * 1024; config.growth_factor = 2.0; config.cache_optimization = CacheOptimizationLevel::Maximum;
config.alignment = 64; config.num_pools = 16; config.enable_prefetching = true;
config.prefetch_distance = 4; config.enable_hot_cold_separation = true;
config.hot_access_threshold = 50;
config.huge_page_config.enable_huge_pages = true;
config.huge_page_config.min_allocation_size = 1024 * 1024;
config.numa_config.enable_numa_awareness = true;
config.numa_config.prefer_local_allocation = true;
config.enable_memory_protection = false;
config.enable_debug_tracking = false;
config
}
fn memory_preset() -> Self {
let mut config = Self::default();
config.allocation_strategy = AllocationStrategy::FixedCapacity;
config.initial_pool_size = 16 * 1024 * 1024; config.max_pool_size = 128 * 1024 * 1024; config.growth_factor = 1.2; config.cache_optimization = CacheOptimizationLevel::Basic;
config.alignment = 16; config.num_pools = 2; config.enable_prefetching = false; config.enable_hot_cold_separation = false;
config.enable_compaction = true; config.compaction_threshold = 0.6; config.max_fragmentation_ratio = 0.2;
config.huge_page_config.enable_huge_pages = false;
config.numa_config.enable_numa_awareness = false;
config.enable_memory_protection = true;
config.enable_debug_tracking = false;
config
}
fn realtime_preset() -> Self {
let mut config = Self::default();
config.allocation_strategy = AllocationStrategy::FixedCapacity;
config.initial_pool_size = 128 * 1024 * 1024; config.max_pool_size = 128 * 1024 * 1024; config.growth_factor = 1.0; config.cache_optimization = CacheOptimizationLevel::Advanced;
config.alignment = 64; config.num_pools = 4; config.enable_prefetching = true;
config.prefetch_distance = 2; config.enable_hot_cold_separation = false; config.enable_compaction = false;
config.huge_page_config.enable_huge_pages = true;
config.huge_page_config.enable_transparent = false;
config.numa_config.enable_numa_awareness = true;
config.numa_config.prefer_local_allocation = true;
config.numa_config.enable_balancing = false;
config.enable_memory_protection = true;
config.enable_use_after_free_detection = false; config.enable_double_free_detection = true; config.enable_debug_tracking = false;
config
}
fn save_to_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
let serialized = serde_json::to_string_pretty(self)
.map_err(|e| ZiporaError::configuration(format!("Failed to serialize memory config: {}", e)))?;
std::fs::write(path, serialized)
.map_err(|e| ZiporaError::configuration(format!("Failed to write memory config file: {}", e)))?;
Ok(())
}
fn load_from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
let content = std::fs::read_to_string(path)
.map_err(|e| ZiporaError::configuration(format!("Failed to read memory config file: {}", e)))?;
let config: Self = serde_json::from_str(&content)
.map_err(|e| ZiporaError::configuration(format!("Failed to parse memory config file: {}", e)))?;
config.validate()?;
Ok(config)
}
}
impl MemoryConfig {
pub fn builder() -> Self { Self::default() }
pub fn initial_pool_size(mut self, v: usize) -> Self { self.initial_pool_size = v; self }
pub fn max_pool_size(mut self, v: usize) -> Self { self.max_pool_size = v; self }
pub fn enable_numa(mut self, v: bool) -> Self { self.numa_config.enable_numa_awareness = v; self }
pub fn enable_huge_pages(mut self, v: bool) -> Self { self.huge_page_config.enable_huge_pages = v; self }
pub fn alignment(mut self, v: usize) -> Self { self.alignment = v; self }
pub fn allocation_strategy(mut self, v: AllocationStrategy) -> Self { self.allocation_strategy = v; self }
pub fn cache_optimization(mut self, v: CacheOptimizationLevel) -> Self { self.cache_optimization = v; self }
pub fn num_pools(mut self, v: usize) -> Self { self.num_pools = v; self }
pub fn enable_protection(mut self, v: bool) -> Self { self.enable_memory_protection = v; self }
pub fn build(self) -> Result<Self> { self.validate()?; Ok(self) }
pub fn effective_cache_line_size(&self) -> usize {
if self.cache_line_size == 0 {
#[cfg(target_arch = "x86_64")]
{
64 }
#[cfg(target_arch = "aarch64")]
{
128 }
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
{
64 }
} else {
self.cache_line_size
}
}
pub fn effective_num_pools(&self) -> usize {
match self.allocation_strategy {
AllocationStrategy::System => 1,
AllocationStrategy::SecurePool => std::cmp::min(self.num_pools, 8),
AllocationStrategy::LockFree => self.num_pools,
AllocationStrategy::ThreadLocal => std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1),
AllocationStrategy::FixedCapacity => 1,
AllocationStrategy::MemoryMapped => 1,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = MemoryConfig::default();
assert!(config.validate().is_ok());
}
#[test]
fn test_builder_pattern() {
let config = MemoryConfig::builder()
.allocation_strategy(AllocationStrategy::LockFree)
.initial_pool_size(128 * 1024 * 1024)
.enable_numa(true)
.build()
.expect("Failed to build memory config");
assert_eq!(config.allocation_strategy, AllocationStrategy::LockFree);
assert_eq!(config.initial_pool_size, 128 * 1024 * 1024);
assert!(config.numa_config.enable_numa_awareness);
}
#[test]
fn test_presets() {
let perf_config = MemoryConfig::performance_preset();
assert!(perf_config.validate().is_ok());
assert_eq!(perf_config.allocation_strategy, AllocationStrategy::LockFree);
let mem_config = MemoryConfig::memory_preset();
assert!(mem_config.validate().is_ok());
assert_eq!(mem_config.allocation_strategy, AllocationStrategy::FixedCapacity);
let rt_config = MemoryConfig::realtime_preset();
assert!(rt_config.validate().is_ok());
assert_eq!(rt_config.allocation_strategy, AllocationStrategy::FixedCapacity);
assert!(!rt_config.enable_compaction);
}
#[test]
fn test_validation() {
let mut config = MemoryConfig::default();
config.initial_pool_size = 0;
assert!(config.validate().is_err());
config = MemoryConfig::default();
config.growth_factor = 0.5;
assert!(config.validate().is_err());
config.growth_factor = 5.0;
assert!(config.validate().is_err());
config = MemoryConfig::default();
config.alignment = 3; assert!(config.validate().is_err());
}
#[test]
fn test_effective_values() {
let config = MemoryConfig::default();
let cache_line_size = config.effective_cache_line_size();
assert!(cache_line_size >= 32 && cache_line_size <= 128);
let num_pools = config.effective_num_pools();
assert!(num_pools >= 1);
}
#[test]
fn test_serialization() {
let config = MemoryConfig::default();
let json = serde_json::to_string(&config).expect("Failed to serialize");
let deserialized: MemoryConfig = serde_json::from_str(&json).expect("Failed to deserialize");
assert_eq!(config.allocation_strategy, deserialized.allocation_strategy);
assert_eq!(config.initial_pool_size, deserialized.initial_pool_size);
assert_eq!(config.numa_config.enable_numa_awareness, deserialized.numa_config.enable_numa_awareness);
}
}