use std::collections::{BTreeMap, HashMap, VecDeque};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
pub struct DefragmentationEngine {
config: DefragConfig,
stats: DefragStats,
active_tasks: Vec<DefragTask>,
memory_layout: MemoryLayoutTracker,
strategies: Vec<Box<dyn CompactionStrategy>>,
performance_history: VecDeque<DefragPerformance>,
}
#[derive(Debug, Clone)]
pub struct DefragConfig {
pub auto_defrag: bool,
pub fragmentation_threshold: f64,
pub max_defrag_time: Duration,
pub min_free_space: usize,
pub incremental_defrag: bool,
pub incremental_chunk_size: usize,
pub parallel_defrag: bool,
pub worker_threads: usize,
pub preferred_algorithm: CompactionAlgorithm,
pub enable_stats: bool,
}
impl Default for DefragConfig {
fn default() -> Self {
Self {
auto_defrag: true,
fragmentation_threshold: 0.3,
max_defrag_time: Duration::from_millis(100),
min_free_space: 1024 * 1024, incremental_defrag: true,
incremental_chunk_size: 64 * 1024, parallel_defrag: false,
worker_threads: 2,
preferred_algorithm: CompactionAlgorithm::SlidingCompaction,
enable_stats: true,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum CompactionAlgorithm {
SlidingCompaction,
TwoPointer,
MarkSweepCompact,
CopyingGC,
Generational,
Adaptive,
}
#[derive(Debug, Clone, Default)]
pub struct DefragStats {
pub total_cycles: u64,
pub total_bytes_moved: u64,
pub total_time_spent: Duration,
pub average_fragmentation_reduction: f64,
pub successful_cycles: u64,
pub failed_cycles: u64,
pub average_cycle_time: Duration,
pub peak_fragmentation: f64,
pub current_fragmentation: f64,
pub objects_relocated: u64,
pub compaction_efficiency: f64,
}
#[derive(Debug, Clone)]
pub struct DefragTask {
pub id: u64,
pub start_addr: usize,
pub size: usize,
pub algorithm: CompactionAlgorithm,
pub status: TaskStatus,
pub created_at: Instant,
pub estimated_completion: Option<Duration>,
pub priority: TaskPriority,
}
#[derive(Debug, Clone, PartialEq)]
pub enum TaskStatus {
Pending,
Running,
Paused,
Completed,
Failed(String),
Cancelled,
}
#[derive(Debug, Clone, PartialEq, Ord, PartialOrd, Eq)]
pub enum TaskPriority {
Low,
Normal,
High,
Critical,
}
pub struct MemoryLayoutTracker {
free_regions: BTreeMap<usize, FreeRegion>,
allocated_blocks: HashMap<usize, AllocatedBlock>,
fragmentation_cache: Option<(f64, Instant)>,
cache_validity: Duration,
}
#[derive(Debug, Clone)]
pub struct FreeRegion {
pub address: usize,
pub size: usize,
pub age: Duration,
pub access_frequency: u32,
pub adjacent_to_allocated: bool,
}
#[derive(Debug, Clone)]
pub struct AllocatedBlock {
pub address: usize,
pub size: usize,
pub allocation_time: Instant,
pub last_access: Option<Instant>,
pub access_count: u32,
pub is_movable: bool,
pub reference_count: u32,
}
pub trait CompactionStrategy: Send + Sync {
fn name(&self) -> &str;
fn can_handle(&self, layout: &MemoryLayoutTracker) -> bool;
fn estimate_benefit(&self, layout: &MemoryLayoutTracker) -> f64;
fn execute(
&mut self,
layout: &mut MemoryLayoutTracker,
) -> Result<CompactionResult, DefragError>;
fn get_statistics(&self) -> CompactionStats;
fn reset(&mut self);
}
#[derive(Debug, Clone)]
pub struct CompactionResult {
pub bytes_moved: usize,
pub objects_relocated: u32,
pub fragmentation_reduction: f64,
pub time_taken: Duration,
pub algorithm_used: CompactionAlgorithm,
pub efficiency_score: f64,
}
#[derive(Debug, Clone, Default)]
pub struct CompactionStats {
pub executions: u64,
pub total_bytes_moved: u64,
pub total_objects_relocated: u64,
pub total_time: Duration,
pub average_efficiency: f64,
pub success_rate: f64,
}
#[derive(Debug, Clone)]
pub struct DefragPerformance {
pub timestamp: Instant,
pub fragmentation_before: f64,
pub fragmentation_after: f64,
pub time_taken: Duration,
pub bytes_moved: usize,
pub algorithm_used: CompactionAlgorithm,
pub success: bool,
}
impl Default for MemoryLayoutTracker {
fn default() -> Self {
Self::new()
}
}
impl MemoryLayoutTracker {
pub fn new() -> Self {
Self {
free_regions: BTreeMap::new(),
allocated_blocks: HashMap::new(),
fragmentation_cache: None,
cache_validity: Duration::from_millis(100),
}
}
pub fn calculate_fragmentation(&mut self) -> f64 {
let now = Instant::now();
if let Some((cached_frag, cache_time)) = self.fragmentation_cache {
if now.duration_since(cache_time) < self.cache_validity {
return cached_frag;
}
}
let fragmentation = if self.free_regions.is_empty() {
0.0
} else {
let total_free_space: usize = self.free_regions.values().map(|r| r.size).sum();
let largest_free_block = self
.free_regions
.values()
.map(|r| r.size)
.max()
.unwrap_or(0);
if total_free_space == 0 {
0.0
} else {
1.0 - (largest_free_block as f64 / total_free_space as f64)
}
};
self.fragmentation_cache = Some((fragmentation, now));
fragmentation
}
pub fn add_free_region(&mut self, address: usize, size: usize) {
let region = FreeRegion {
address,
size,
age: Duration::from_secs(0),
access_frequency: 0,
adjacent_to_allocated: self.is_adjacent_to_allocated(address, size),
};
self.free_regions.insert(address, region);
self.invalidate_cache();
}
pub fn add_allocated_block(&mut self, address: usize, size: usize, is_movable: bool) {
let block = AllocatedBlock {
address,
size,
allocation_time: Instant::now(),
last_access: None,
access_count: 0,
is_movable,
reference_count: 1,
};
self.allocated_blocks.insert(address, block);
self.invalidate_cache();
}
pub fn remove_free_region(&mut self, address: usize) -> Option<FreeRegion> {
self.invalidate_cache();
self.free_regions.remove(&address)
}
pub fn remove_allocated_block(&mut self, address: usize) -> Option<AllocatedBlock> {
self.invalidate_cache();
self.allocated_blocks.remove(&address)
}
pub fn get_total_free_space(&self) -> usize {
self.free_regions.values().map(|r| r.size).sum()
}
pub fn get_largest_free_block(&self) -> usize {
self.free_regions
.values()
.map(|r| r.size)
.max()
.unwrap_or(0)
}
pub fn get_movable_blocks(&self) -> Vec<&AllocatedBlock> {
self.allocated_blocks
.values()
.filter(|b| b.is_movable)
.collect()
}
fn is_adjacent_to_allocated(&self, address: usize, size: usize) -> bool {
let end_address = address + size;
for block in self.allocated_blocks.values() {
let block_end = block.address + block.size;
if block_end == address || block.address == end_address {
return true;
}
}
false
}
fn invalidate_cache(&mut self) {
self.fragmentation_cache = None;
}
pub fn coalesce_free_regions(&mut self) -> usize {
let mut coalesced_count = 0;
let mut regions_to_remove = Vec::new();
let mut regions_to_add = Vec::new();
let addresses: Vec<usize> = self.free_regions.keys().cloned().collect();
for &addr in &addresses {
if regions_to_remove.contains(&addr) {
continue;
}
if let Some(region) = self.free_regions.get(&addr) {
let end_addr = addr + region.size;
if let Some(next_region) = self.free_regions.get(&end_addr) {
let coalesced_region = FreeRegion {
address: addr,
size: region.size + next_region.size,
age: region.age.min(next_region.age),
access_frequency: region.access_frequency + next_region.access_frequency,
adjacent_to_allocated: region.adjacent_to_allocated
|| next_region.adjacent_to_allocated,
};
regions_to_remove.push(addr);
regions_to_remove.push(end_addr);
regions_to_add.push((addr, coalesced_region));
coalesced_count += 1;
}
}
}
for addr in regions_to_remove {
self.free_regions.remove(&addr);
}
for (addr, region) in regions_to_add {
self.free_regions.insert(addr, region);
}
self.invalidate_cache();
coalesced_count
}
}
pub struct SlidingCompactionStrategy {
stats: CompactionStats,
}
impl Default for SlidingCompactionStrategy {
fn default() -> Self {
Self::new()
}
}
impl SlidingCompactionStrategy {
pub fn new() -> Self {
Self {
stats: CompactionStats::default(),
}
}
}
impl CompactionStrategy for SlidingCompactionStrategy {
fn name(&self) -> &str {
"SlidingCompaction"
}
fn can_handle(&self, layout: &MemoryLayoutTracker) -> bool {
!layout.get_movable_blocks().is_empty() && layout.get_total_free_space() > 0
}
fn estimate_benefit(&self, layout: &MemoryLayoutTracker) -> f64 {
let movable_blocks = layout.get_movable_blocks();
let total_free = layout.get_total_free_space();
let largest_free = layout.get_largest_free_block();
if total_free == 0 {
return 0.0;
}
let fragmentation_reduction = (total_free - largest_free) as f64 / total_free as f64;
let mobility_factor =
movable_blocks.len() as f64 / (layout.allocated_blocks.len() as f64 + 1.0);
fragmentation_reduction * mobility_factor
}
fn execute(
&mut self,
layout: &mut MemoryLayoutTracker,
) -> Result<CompactionResult, DefragError> {
let start_time = Instant::now();
let initial_fragmentation = layout.calculate_fragmentation();
let movable_blocks: Vec<AllocatedBlock> =
layout.get_movable_blocks().into_iter().cloned().collect();
if movable_blocks.is_empty() {
return Err(DefragError::NoMovableBlocks);
}
let mut bytes_moved = 0;
let mut objects_relocated = 0;
let mut compaction_address = 0;
if let Some((&first_free_addr, _)) = layout.free_regions.iter().next() {
compaction_address = first_free_addr;
}
let mut sorted_blocks = movable_blocks;
sorted_blocks.sort_by_key(|b| b.address);
for block in sorted_blocks {
if block.address > compaction_address {
layout.remove_allocated_block(block.address);
layout.add_allocated_block(compaction_address, block.size, block.is_movable);
layout.add_free_region(block.address, block.size);
bytes_moved += block.size;
objects_relocated += 1;
compaction_address += block.size;
} else {
compaction_address = block.address + block.size;
}
}
layout.coalesce_free_regions();
let final_fragmentation = layout.calculate_fragmentation();
let fragmentation_reduction = initial_fragmentation - final_fragmentation;
let time_taken = start_time.elapsed();
self.stats.executions += 1;
self.stats.total_bytes_moved += bytes_moved as u64;
self.stats.total_objects_relocated += objects_relocated as u64;
self.stats.total_time += time_taken;
let efficiency = if bytes_moved > 0 {
fragmentation_reduction / (bytes_moved as f64 / 1024.0 / 1024.0) } else {
0.0
};
self.stats.average_efficiency =
(self.stats.average_efficiency * (self.stats.executions - 1) as f64 + efficiency)
/ self.stats.executions as f64;
self.stats.success_rate = 1.0;
Ok(CompactionResult {
bytes_moved,
objects_relocated,
fragmentation_reduction,
time_taken,
algorithm_used: CompactionAlgorithm::SlidingCompaction,
efficiency_score: efficiency,
})
}
fn get_statistics(&self) -> CompactionStats {
self.stats.clone()
}
fn reset(&mut self) {
self.stats = CompactionStats::default();
}
}
pub struct TwoPointerCompactionStrategy {
stats: CompactionStats,
}
impl Default for TwoPointerCompactionStrategy {
fn default() -> Self {
Self::new()
}
}
impl TwoPointerCompactionStrategy {
pub fn new() -> Self {
Self {
stats: CompactionStats::default(),
}
}
}
impl CompactionStrategy for TwoPointerCompactionStrategy {
fn name(&self) -> &str {
"TwoPointer"
}
fn can_handle(&self, layout: &MemoryLayoutTracker) -> bool {
layout.get_movable_blocks().len() >= 2 && layout.get_total_free_space() > 0
}
fn estimate_benefit(&self, layout: &MemoryLayoutTracker) -> f64 {
let movable_blocks = layout.get_movable_blocks();
let free_space = layout.get_total_free_space();
if movable_blocks.len() < 2 || free_space == 0 {
return 0.0;
}
let mut addresses: Vec<usize> = movable_blocks.iter().map(|b| b.address).collect();
addresses.sort();
let mut total_gaps = 0;
for i in 1..addresses.len() {
let gap = addresses[i] - addresses[i - 1];
if gap > movable_blocks[i - 1].size {
total_gaps += gap - movable_blocks[i - 1].size;
}
}
total_gaps as f64 / free_space as f64
}
fn execute(
&mut self,
layout: &mut MemoryLayoutTracker,
) -> Result<CompactionResult, DefragError> {
let start_time = Instant::now();
let initial_fragmentation = layout.calculate_fragmentation();
let movable_blocks: Vec<AllocatedBlock> =
layout.get_movable_blocks().into_iter().cloned().collect();
if movable_blocks.len() < 2 {
return Err(DefragError::InsufficientBlocks);
}
let mut bytes_moved = 0;
let mut objects_relocated = 0;
let mut sorted_blocks = movable_blocks;
sorted_blocks.sort_by_key(|b| b.address);
let mut compact_addr = sorted_blocks[0].address;
for block in &sorted_blocks {
if block.address != compact_addr {
layout.remove_allocated_block(block.address);
layout.add_allocated_block(compact_addr, block.size, block.is_movable);
layout.add_free_region(block.address, block.size);
bytes_moved += block.size;
objects_relocated += 1;
}
compact_addr += block.size;
}
layout.coalesce_free_regions();
let final_fragmentation = layout.calculate_fragmentation();
let fragmentation_reduction = initial_fragmentation - final_fragmentation;
let time_taken = start_time.elapsed();
self.stats.executions += 1;
self.stats.total_bytes_moved += bytes_moved as u64;
self.stats.total_objects_relocated += objects_relocated as u64;
self.stats.total_time += time_taken;
let efficiency = if bytes_moved > 0 {
fragmentation_reduction / (bytes_moved as f64 / 1024.0 / 1024.0)
} else {
0.0
};
self.stats.average_efficiency =
(self.stats.average_efficiency * (self.stats.executions - 1) as f64 + efficiency)
/ self.stats.executions as f64;
Ok(CompactionResult {
bytes_moved,
objects_relocated,
fragmentation_reduction,
time_taken,
algorithm_used: CompactionAlgorithm::TwoPointer,
efficiency_score: efficiency,
})
}
fn get_statistics(&self) -> CompactionStats {
self.stats.clone()
}
fn reset(&mut self) {
self.stats = CompactionStats::default();
}
}
impl DefragmentationEngine {
pub fn new(config: DefragConfig) -> Self {
let strategies: Vec<Box<dyn CompactionStrategy>> = vec![
Box::new(SlidingCompactionStrategy::new()),
Box::new(TwoPointerCompactionStrategy::new()),
];
Self {
config,
stats: DefragStats::default(),
active_tasks: Vec::new(),
memory_layout: MemoryLayoutTracker::new(),
strategies: strategies
.into_iter()
.map(|s| s as Box<dyn CompactionStrategy>)
.collect(),
performance_history: VecDeque::with_capacity(1000),
}
}
pub fn should_defragment(&mut self) -> bool {
if !self.config.auto_defrag {
return false;
}
let current_fragmentation = self.memory_layout.calculate_fragmentation();
self.stats.current_fragmentation = current_fragmentation;
current_fragmentation > self.config.fragmentation_threshold
&& self.memory_layout.get_total_free_space() >= self.config.min_free_space
}
pub fn defragment(&mut self) -> Result<CompactionResult, DefragError> {
let start_time = Instant::now();
if self
.active_tasks
.iter()
.any(|t| t.status == TaskStatus::Running)
{
return Err(DefragError::DefragmentationInProgress);
}
let strategy_index = self.select_best_strategy()?;
let strategy = &mut self.strategies[strategy_index];
let result = strategy.execute(&mut self.memory_layout)?;
self.stats.total_cycles += 1;
self.stats.total_bytes_moved += result.bytes_moved as u64;
self.stats.total_time_spent += result.time_taken;
self.stats.successful_cycles += 1;
self.stats.objects_relocated += result.objects_relocated as u64;
self.stats.average_fragmentation_reduction = (self.stats.average_fragmentation_reduction
* (self.stats.total_cycles - 1) as f64
+ result.fragmentation_reduction)
/ self.stats.total_cycles as f64;
let cycle_time = start_time.elapsed();
self.stats.average_cycle_time = Duration::from_nanos(
(self.stats.average_cycle_time.as_nanos() as u64 * (self.stats.total_cycles - 1)
+ cycle_time.as_nanos() as u64)
/ self.stats.total_cycles,
);
let performance = DefragPerformance {
timestamp: start_time,
fragmentation_before: self.stats.current_fragmentation,
fragmentation_after: self.memory_layout.calculate_fragmentation(),
time_taken: cycle_time,
bytes_moved: result.bytes_moved,
algorithm_used: result.algorithm_used.clone(),
success: true,
};
self.performance_history.push_back(performance);
if self.performance_history.len() > 1000 {
self.performance_history.pop_front();
}
Ok(result)
}
fn select_best_strategy(&mut self) -> Result<usize, DefragError> {
let mut best_index = 0;
let mut best_benefit = 0.0;
for (i, strategy) in self.strategies.iter().enumerate() {
if strategy.can_handle(&self.memory_layout) {
let benefit = strategy.estimate_benefit(&self.memory_layout);
if benefit > best_benefit {
best_benefit = benefit;
best_index = i;
}
}
}
if best_benefit == 0.0 {
return Err(DefragError::NoSuitableStrategy);
}
Ok(best_index)
}
pub fn create_task(
&mut self,
start_addr: usize,
size: usize,
algorithm: CompactionAlgorithm,
priority: TaskPriority,
) -> u64 {
let task_id = self.active_tasks.len() as u64;
let task = DefragTask {
id: task_id,
start_addr,
size,
algorithm,
status: TaskStatus::Pending,
created_at: Instant::now(),
estimated_completion: None,
priority,
};
self.active_tasks.push(task);
task_id
}
pub fn get_stats(&self) -> &DefragStats {
&self.stats
}
pub fn get_performance_history(&self) -> &VecDeque<DefragPerformance> {
&self.performance_history
}
pub fn update_layout(
&mut self,
allocated_blocks: HashMap<usize, AllocatedBlock>,
free_regions: BTreeMap<usize, FreeRegion>,
) {
self.memory_layout.allocated_blocks = allocated_blocks;
self.memory_layout.free_regions = free_regions;
self.memory_layout.invalidate_cache();
}
pub fn get_layout(&self) -> &MemoryLayoutTracker {
&self.memory_layout
}
pub fn reset(&mut self) {
self.stats = DefragStats::default();
self.active_tasks.clear();
self.memory_layout = MemoryLayoutTracker::new();
self.performance_history.clear();
for strategy in &mut self.strategies {
strategy.reset();
}
}
}
unsafe impl Send for DefragmentationEngine {}
unsafe impl Sync for DefragmentationEngine {}
#[derive(Debug, Clone)]
pub enum DefragError {
DefragmentationInProgress,
NoMovableBlocks,
InsufficientBlocks,
NoSuitableStrategy,
MemoryLayoutCorrupted,
TimeoutExceeded,
InternalError(String),
}
impl std::fmt::Display for DefragError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DefragError::DefragmentationInProgress => {
write!(f, "Defragmentation already in progress")
}
DefragError::NoMovableBlocks => write!(f, "No movable blocks available for compaction"),
DefragError::InsufficientBlocks => {
write!(f, "Insufficient blocks for compaction strategy")
}
DefragError::NoSuitableStrategy => {
write!(f, "No suitable compaction strategy available")
}
DefragError::MemoryLayoutCorrupted => write!(f, "Memory layout is corrupted"),
DefragError::TimeoutExceeded => write!(f, "Defragmentation timeout exceeded"),
DefragError::InternalError(msg) => write!(f, "Internal error: {}", msg),
}
}
}
impl std::error::Error for DefragError {}
pub struct ThreadSafeDefragmentationEngine {
engine: Arc<Mutex<DefragmentationEngine>>,
}
impl ThreadSafeDefragmentationEngine {
pub fn new(config: DefragConfig) -> Self {
Self {
engine: Arc::new(Mutex::new(DefragmentationEngine::new(config))),
}
}
pub fn should_defragment(&self) -> bool {
let mut engine = self.engine.lock().unwrap_or_else(|e| e.into_inner());
engine.should_defragment()
}
pub fn defragment(&self) -> Result<CompactionResult, DefragError> {
let mut engine = self.engine.lock().unwrap_or_else(|e| e.into_inner());
engine.defragment()
}
pub fn get_stats(&self) -> DefragStats {
let engine = self.engine.lock().unwrap_or_else(|e| e.into_inner());
engine.get_stats().clone()
}
pub fn get_performance_history(&self) -> Vec<DefragPerformance> {
let engine = self.engine.lock().unwrap_or_else(|e| e.into_inner());
engine.get_performance_history().iter().cloned().collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_memory_layout_tracker() {
let mut tracker = MemoryLayoutTracker::new();
tracker.add_allocated_block(1000, 500, true);
tracker.add_allocated_block(2000, 300, false);
tracker.add_free_region(1500, 200);
tracker.add_free_region(2500, 800);
let fragmentation = tracker.calculate_fragmentation();
assert!((0.0..=1.0).contains(&fragmentation));
let total_free = tracker.get_total_free_space();
assert_eq!(total_free, 1000);
let largest_free = tracker.get_largest_free_block();
assert_eq!(largest_free, 800);
}
#[test]
fn test_sliding_compaction_strategy() {
let mut strategy = SlidingCompactionStrategy::new();
let mut layout = MemoryLayoutTracker::new();
layout.add_allocated_block(1000, 500, true);
layout.add_free_region(1500, 200);
layout.add_allocated_block(2000, 300, true);
layout.add_free_region(2300, 500);
assert!(strategy.can_handle(&layout));
let benefit = strategy.estimate_benefit(&layout);
assert!(benefit > 0.0);
let result = strategy.execute(&mut layout);
assert!(result.is_ok());
let compaction_result = result.expect("unwrap failed");
assert!(compaction_result.bytes_moved > 0);
assert!(compaction_result.objects_relocated > 0);
assert!(strategy.get_statistics().executions > 0);
strategy.reset();
assert_eq!(strategy.get_statistics().executions, 0);
assert_eq!(strategy.get_statistics().total_bytes_moved, 0);
}
#[test]
fn test_two_pointer_compaction_strategy_reset_clears_statistics() {
let mut strategy = TwoPointerCompactionStrategy::new();
let mut layout = MemoryLayoutTracker::new();
layout.add_allocated_block(1000, 500, true);
layout.add_allocated_block(2000, 300, true);
layout.add_free_region(1500, 200);
strategy
.execute(&mut layout)
.expect("two-pointer compaction should succeed with movable blocks");
assert!(strategy.get_statistics().executions > 0);
strategy.reset();
assert_eq!(strategy.get_statistics().executions, 0);
assert_eq!(strategy.get_statistics().total_bytes_moved, 0);
}
#[test]
fn test_defragmentation_engine_reset_clears_strategy_statistics() {
let config = DefragConfig::default();
let mut engine = DefragmentationEngine::new(config);
let mut allocated_blocks = HashMap::new();
allocated_blocks.insert(
1000,
AllocatedBlock {
address: 1000,
size: 500,
allocation_time: Instant::now(),
last_access: None,
access_count: 0,
is_movable: true,
reference_count: 1,
},
);
allocated_blocks.insert(
2000,
AllocatedBlock {
address: 2000,
size: 300,
allocation_time: Instant::now(),
last_access: None,
access_count: 0,
is_movable: true,
reference_count: 1,
},
);
let mut free_regions = BTreeMap::new();
free_regions.insert(
1500,
FreeRegion {
address: 1500,
size: 300,
age: Duration::from_secs(10),
access_frequency: 0,
adjacent_to_allocated: true,
},
);
engine.update_layout(allocated_blocks, free_regions);
engine
.defragment()
.expect("defragmentation should find a suitable strategy for this layout");
assert!(
engine
.strategies
.iter()
.any(|s| s.get_statistics().executions > 0),
"defragment() should have driven at least one strategy's statistics"
);
engine.reset();
assert!(
engine
.strategies
.iter()
.all(|s| s.get_statistics().executions == 0),
"reset() must clear every strategy's accumulated statistics"
);
}
#[test]
fn test_defragmentation_engine() {
let config = DefragConfig::default();
let mut engine = DefragmentationEngine::new(config);
let mut allocated_blocks = HashMap::new();
allocated_blocks.insert(
1000,
AllocatedBlock {
address: 1000,
size: 500,
allocation_time: Instant::now(),
last_access: None,
access_count: 0,
is_movable: true,
reference_count: 1,
},
);
let mut free_regions = BTreeMap::new();
free_regions.insert(
1500,
FreeRegion {
address: 1500,
size: 300,
age: Duration::from_secs(10),
access_frequency: 0,
adjacent_to_allocated: true,
},
);
engine.update_layout(allocated_blocks, free_regions);
let should_defrag = engine.should_defragment();
assert!(!should_defrag);
let stats = engine.get_stats();
assert_eq!(stats.total_cycles, 0); }
#[test]
fn test_coalescing() {
let mut tracker = MemoryLayoutTracker::new();
tracker.add_free_region(1000, 500);
tracker.add_free_region(1500, 300);
tracker.add_free_region(2000, 200);
let coalesced = tracker.coalesce_free_regions();
assert_eq!(coalesced, 1);
assert_eq!(tracker.free_regions.len(), 2);
}
#[test]
fn test_thread_safe_engine() {
let config = DefragConfig::default();
let engine = ThreadSafeDefragmentationEngine::new(config);
let should_defrag = engine.should_defragment();
assert!(!should_defrag, "should not trigger defrag on empty layout");
let stats = engine.get_stats();
assert_eq!(stats.total_cycles, 0);
}
}