use super::{Config, ValidationError, parse_env_bool, parse_env_var};
use crate::error::{Result, ZiporaError};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::path::Path;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Default)]
pub enum AllocationStrategy {
System,
#[default]
SecurePool,
LockFree,
ThreadLocal,
FixedCapacity,
MemoryMapped,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Default)]
pub enum CacheOptimizationLevel {
None,
Basic,
#[default]
Advanced,
Maximum,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::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)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::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)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::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<()> {
#[cfg(feature = "serde")]
{
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(())
}
#[cfg(not(feature = "serde"))]
Err(crate::error::ZiporaError::invalid_operation(
"Requires serde feature",
))
}
fn load_from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
#[cfg(feature = "serde")]
{
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)
}
#[cfg(not(feature = "serde"))]
Err(crate::error::ZiporaError::invalid_operation(
"Requires serde feature",
))
}
}
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
);
}
}