1use std::collections::{BTreeMap, HashMap, VecDeque};
7use std::sync::{Arc, Mutex};
8use std::time::{Duration, Instant};
9
10pub struct DefragmentationEngine {
12 config: DefragConfig,
14 stats: DefragStats,
16 active_tasks: Vec<DefragTask>,
18 memory_layout: MemoryLayoutTracker,
20 strategies: Vec<Box<dyn CompactionStrategy>>,
22 performance_history: VecDeque<DefragPerformance>,
24}
25
26#[derive(Debug, Clone)]
28pub struct DefragConfig {
29 pub auto_defrag: bool,
31 pub fragmentation_threshold: f64,
33 pub max_defrag_time: Duration,
35 pub min_free_space: usize,
37 pub incremental_defrag: bool,
39 pub incremental_chunk_size: usize,
41 pub parallel_defrag: bool,
43 pub worker_threads: usize,
45 pub preferred_algorithm: CompactionAlgorithm,
47 pub enable_stats: bool,
49}
50
51impl Default for DefragConfig {
52 fn default() -> Self {
53 Self {
54 auto_defrag: true,
55 fragmentation_threshold: 0.3,
56 max_defrag_time: Duration::from_millis(100),
57 min_free_space: 1024 * 1024, incremental_defrag: true,
59 incremental_chunk_size: 64 * 1024, parallel_defrag: false,
61 worker_threads: 2,
62 preferred_algorithm: CompactionAlgorithm::SlidingCompaction,
63 enable_stats: true,
64 }
65 }
66}
67
68#[derive(Debug, Clone, PartialEq)]
70pub enum CompactionAlgorithm {
71 SlidingCompaction,
73 TwoPointer,
75 MarkSweepCompact,
77 CopyingGC,
79 Generational,
81 Adaptive,
83}
84
85#[derive(Debug, Clone, Default)]
87pub struct DefragStats {
88 pub total_cycles: u64,
90 pub total_bytes_moved: u64,
92 pub total_time_spent: Duration,
94 pub average_fragmentation_reduction: f64,
96 pub successful_cycles: u64,
98 pub failed_cycles: u64,
100 pub average_cycle_time: Duration,
102 pub peak_fragmentation: f64,
104 pub current_fragmentation: f64,
106 pub objects_relocated: u64,
108 pub compaction_efficiency: f64,
110}
111
112#[derive(Debug, Clone)]
114pub struct DefragTask {
115 pub id: u64,
117 pub start_addr: usize,
119 pub size: usize,
121 pub algorithm: CompactionAlgorithm,
123 pub status: TaskStatus,
125 pub created_at: Instant,
127 pub estimated_completion: Option<Duration>,
129 pub priority: TaskPriority,
131}
132
133#[derive(Debug, Clone, PartialEq)]
135pub enum TaskStatus {
136 Pending,
137 Running,
138 Paused,
139 Completed,
140 Failed(String),
141 Cancelled,
142}
143
144#[derive(Debug, Clone, PartialEq, Ord, PartialOrd, Eq)]
146pub enum TaskPriority {
147 Low,
148 Normal,
149 High,
150 Critical,
151}
152
153pub struct MemoryLayoutTracker {
155 free_regions: BTreeMap<usize, FreeRegion>,
157 allocated_blocks: HashMap<usize, AllocatedBlock>,
159 fragmentation_cache: Option<(f64, Instant)>,
161 cache_validity: Duration,
163}
164
165#[derive(Debug, Clone)]
167pub struct FreeRegion {
168 pub address: usize,
169 pub size: usize,
170 pub age: Duration,
171 pub access_frequency: u32,
172 pub adjacent_to_allocated: bool,
173}
174
175#[derive(Debug, Clone)]
177pub struct AllocatedBlock {
178 pub address: usize,
179 pub size: usize,
180 pub allocation_time: Instant,
181 pub last_access: Option<Instant>,
182 pub access_count: u32,
183 pub is_movable: bool,
184 pub reference_count: u32,
185}
186
187pub trait CompactionStrategy: Send + Sync {
189 fn name(&self) -> &str;
190 fn can_handle(&self, layout: &MemoryLayoutTracker) -> bool;
191 fn estimate_benefit(&self, layout: &MemoryLayoutTracker) -> f64;
192 fn execute(
193 &mut self,
194 layout: &mut MemoryLayoutTracker,
195 ) -> Result<CompactionResult, DefragError>;
196 fn get_statistics(&self) -> CompactionStats;
197 fn reset(&mut self);
200}
201
202#[derive(Debug, Clone)]
204pub struct CompactionResult {
205 pub bytes_moved: usize,
206 pub objects_relocated: u32,
207 pub fragmentation_reduction: f64,
208 pub time_taken: Duration,
209 pub algorithm_used: CompactionAlgorithm,
210 pub efficiency_score: f64,
211}
212
213#[derive(Debug, Clone, Default)]
215pub struct CompactionStats {
216 pub executions: u64,
217 pub total_bytes_moved: u64,
218 pub total_objects_relocated: u64,
219 pub total_time: Duration,
220 pub average_efficiency: f64,
221 pub success_rate: f64,
222}
223
224#[derive(Debug, Clone)]
226pub struct DefragPerformance {
227 pub timestamp: Instant,
228 pub fragmentation_before: f64,
229 pub fragmentation_after: f64,
230 pub time_taken: Duration,
231 pub bytes_moved: usize,
232 pub algorithm_used: CompactionAlgorithm,
233 pub success: bool,
234}
235
236impl Default for MemoryLayoutTracker {
237 fn default() -> Self {
238 Self::new()
239 }
240}
241
242impl MemoryLayoutTracker {
243 pub fn new() -> Self {
244 Self {
245 free_regions: BTreeMap::new(),
246 allocated_blocks: HashMap::new(),
247 fragmentation_cache: None,
248 cache_validity: Duration::from_millis(100),
249 }
250 }
251
252 pub fn calculate_fragmentation(&mut self) -> f64 {
254 let now = Instant::now();
255
256 if let Some((cached_frag, cache_time)) = self.fragmentation_cache {
258 if now.duration_since(cache_time) < self.cache_validity {
259 return cached_frag;
260 }
261 }
262
263 let fragmentation = if self.free_regions.is_empty() {
264 0.0
265 } else {
266 let total_free_space: usize = self.free_regions.values().map(|r| r.size).sum();
267 let largest_free_block = self
268 .free_regions
269 .values()
270 .map(|r| r.size)
271 .max()
272 .unwrap_or(0);
273
274 if total_free_space == 0 {
275 0.0
276 } else {
277 1.0 - (largest_free_block as f64 / total_free_space as f64)
278 }
279 };
280
281 self.fragmentation_cache = Some((fragmentation, now));
283 fragmentation
284 }
285
286 pub fn add_free_region(&mut self, address: usize, size: usize) {
288 let region = FreeRegion {
289 address,
290 size,
291 age: Duration::from_secs(0),
292 access_frequency: 0,
293 adjacent_to_allocated: self.is_adjacent_to_allocated(address, size),
294 };
295 self.free_regions.insert(address, region);
296 self.invalidate_cache();
297 }
298
299 pub fn add_allocated_block(&mut self, address: usize, size: usize, is_movable: bool) {
301 let block = AllocatedBlock {
302 address,
303 size,
304 allocation_time: Instant::now(),
305 last_access: None,
306 access_count: 0,
307 is_movable,
308 reference_count: 1,
309 };
310 self.allocated_blocks.insert(address, block);
311 self.invalidate_cache();
312 }
313
314 pub fn remove_free_region(&mut self, address: usize) -> Option<FreeRegion> {
316 self.invalidate_cache();
317 self.free_regions.remove(&address)
318 }
319
320 pub fn remove_allocated_block(&mut self, address: usize) -> Option<AllocatedBlock> {
322 self.invalidate_cache();
323 self.allocated_blocks.remove(&address)
324 }
325
326 pub fn get_total_free_space(&self) -> usize {
328 self.free_regions.values().map(|r| r.size).sum()
329 }
330
331 pub fn get_largest_free_block(&self) -> usize {
333 self.free_regions
334 .values()
335 .map(|r| r.size)
336 .max()
337 .unwrap_or(0)
338 }
339
340 pub fn get_movable_blocks(&self) -> Vec<&AllocatedBlock> {
342 self.allocated_blocks
343 .values()
344 .filter(|b| b.is_movable)
345 .collect()
346 }
347
348 fn is_adjacent_to_allocated(&self, address: usize, size: usize) -> bool {
350 let end_address = address + size;
351
352 for block in self.allocated_blocks.values() {
353 let block_end = block.address + block.size;
354
355 if block_end == address || block.address == end_address {
357 return true;
358 }
359 }
360
361 false
362 }
363
364 fn invalidate_cache(&mut self) {
366 self.fragmentation_cache = None;
367 }
368
369 pub fn coalesce_free_regions(&mut self) -> usize {
371 let mut coalesced_count = 0;
372 let mut regions_to_remove = Vec::new();
373 let mut regions_to_add = Vec::new();
374
375 let addresses: Vec<usize> = self.free_regions.keys().cloned().collect();
376
377 for &addr in &addresses {
378 if regions_to_remove.contains(&addr) {
379 continue;
380 }
381
382 if let Some(region) = self.free_regions.get(&addr) {
383 let end_addr = addr + region.size;
384
385 if let Some(next_region) = self.free_regions.get(&end_addr) {
387 let coalesced_region = FreeRegion {
389 address: addr,
390 size: region.size + next_region.size,
391 age: region.age.min(next_region.age),
392 access_frequency: region.access_frequency + next_region.access_frequency,
393 adjacent_to_allocated: region.adjacent_to_allocated
394 || next_region.adjacent_to_allocated,
395 };
396
397 regions_to_remove.push(addr);
398 regions_to_remove.push(end_addr);
399 regions_to_add.push((addr, coalesced_region));
400 coalesced_count += 1;
401 }
402 }
403 }
404
405 for addr in regions_to_remove {
407 self.free_regions.remove(&addr);
408 }
409
410 for (addr, region) in regions_to_add {
411 self.free_regions.insert(addr, region);
412 }
413
414 self.invalidate_cache();
415 coalesced_count
416 }
417}
418
419pub struct SlidingCompactionStrategy {
421 stats: CompactionStats,
422}
423
424impl Default for SlidingCompactionStrategy {
425 fn default() -> Self {
426 Self::new()
427 }
428}
429
430impl SlidingCompactionStrategy {
431 pub fn new() -> Self {
432 Self {
433 stats: CompactionStats::default(),
434 }
435 }
436}
437
438impl CompactionStrategy for SlidingCompactionStrategy {
439 fn name(&self) -> &str {
440 "SlidingCompaction"
441 }
442
443 fn can_handle(&self, layout: &MemoryLayoutTracker) -> bool {
444 !layout.get_movable_blocks().is_empty() && layout.get_total_free_space() > 0
445 }
446
447 fn estimate_benefit(&self, layout: &MemoryLayoutTracker) -> f64 {
448 let movable_blocks = layout.get_movable_blocks();
449 let total_free = layout.get_total_free_space();
450 let largest_free = layout.get_largest_free_block();
451
452 if total_free == 0 {
453 return 0.0;
454 }
455
456 let fragmentation_reduction = (total_free - largest_free) as f64 / total_free as f64;
458 let mobility_factor =
459 movable_blocks.len() as f64 / (layout.allocated_blocks.len() as f64 + 1.0);
460
461 fragmentation_reduction * mobility_factor
462 }
463
464 fn execute(
465 &mut self,
466 layout: &mut MemoryLayoutTracker,
467 ) -> Result<CompactionResult, DefragError> {
468 let start_time = Instant::now();
469 let initial_fragmentation = layout.calculate_fragmentation();
470
471 let movable_blocks: Vec<AllocatedBlock> =
472 layout.get_movable_blocks().into_iter().cloned().collect();
473
474 if movable_blocks.is_empty() {
475 return Err(DefragError::NoMovableBlocks);
476 }
477
478 let mut bytes_moved = 0;
479 let mut objects_relocated = 0;
480 let mut compaction_address = 0;
481
482 if let Some((&first_free_addr, _)) = layout.free_regions.iter().next() {
484 compaction_address = first_free_addr;
485 }
486
487 let mut sorted_blocks = movable_blocks;
489 sorted_blocks.sort_by_key(|b| b.address);
490
491 for block in sorted_blocks {
493 if block.address > compaction_address {
494 layout.remove_allocated_block(block.address);
496 layout.add_allocated_block(compaction_address, block.size, block.is_movable);
497
498 layout.add_free_region(block.address, block.size);
500
501 bytes_moved += block.size;
502 objects_relocated += 1;
503
504 compaction_address += block.size;
505 } else {
506 compaction_address = block.address + block.size;
507 }
508 }
509
510 layout.coalesce_free_regions();
512
513 let final_fragmentation = layout.calculate_fragmentation();
514 let fragmentation_reduction = initial_fragmentation - final_fragmentation;
515 let time_taken = start_time.elapsed();
516
517 self.stats.executions += 1;
519 self.stats.total_bytes_moved += bytes_moved as u64;
520 self.stats.total_objects_relocated += objects_relocated as u64;
521 self.stats.total_time += time_taken;
522
523 let efficiency = if bytes_moved > 0 {
524 fragmentation_reduction / (bytes_moved as f64 / 1024.0 / 1024.0) } else {
526 0.0
527 };
528
529 self.stats.average_efficiency =
530 (self.stats.average_efficiency * (self.stats.executions - 1) as f64 + efficiency)
531 / self.stats.executions as f64;
532 self.stats.success_rate = 1.0; Ok(CompactionResult {
535 bytes_moved,
536 objects_relocated,
537 fragmentation_reduction,
538 time_taken,
539 algorithm_used: CompactionAlgorithm::SlidingCompaction,
540 efficiency_score: efficiency,
541 })
542 }
543
544 fn get_statistics(&self) -> CompactionStats {
545 self.stats.clone()
546 }
547
548 fn reset(&mut self) {
549 self.stats = CompactionStats::default();
550 }
551}
552
553pub struct TwoPointerCompactionStrategy {
555 stats: CompactionStats,
556}
557
558impl Default for TwoPointerCompactionStrategy {
559 fn default() -> Self {
560 Self::new()
561 }
562}
563
564impl TwoPointerCompactionStrategy {
565 pub fn new() -> Self {
566 Self {
567 stats: CompactionStats::default(),
568 }
569 }
570}
571
572impl CompactionStrategy for TwoPointerCompactionStrategy {
573 fn name(&self) -> &str {
574 "TwoPointer"
575 }
576
577 fn can_handle(&self, layout: &MemoryLayoutTracker) -> bool {
578 layout.get_movable_blocks().len() >= 2 && layout.get_total_free_space() > 0
579 }
580
581 fn estimate_benefit(&self, layout: &MemoryLayoutTracker) -> f64 {
582 let movable_blocks = layout.get_movable_blocks();
583 let free_space = layout.get_total_free_space();
584
585 if movable_blocks.len() < 2 || free_space == 0 {
586 return 0.0;
587 }
588
589 let mut addresses: Vec<usize> = movable_blocks.iter().map(|b| b.address).collect();
591 addresses.sort();
592
593 let mut total_gaps = 0;
594 for i in 1..addresses.len() {
595 let gap = addresses[i] - addresses[i - 1];
596 if gap > movable_blocks[i - 1].size {
597 total_gaps += gap - movable_blocks[i - 1].size;
598 }
599 }
600
601 total_gaps as f64 / free_space as f64
602 }
603
604 fn execute(
605 &mut self,
606 layout: &mut MemoryLayoutTracker,
607 ) -> Result<CompactionResult, DefragError> {
608 let start_time = Instant::now();
609 let initial_fragmentation = layout.calculate_fragmentation();
610
611 let movable_blocks: Vec<AllocatedBlock> =
612 layout.get_movable_blocks().into_iter().cloned().collect();
613
614 if movable_blocks.len() < 2 {
615 return Err(DefragError::InsufficientBlocks);
616 }
617
618 let mut bytes_moved = 0;
619 let mut objects_relocated = 0;
620
621 let mut sorted_blocks = movable_blocks;
623 sorted_blocks.sort_by_key(|b| b.address);
624
625 let mut compact_addr = sorted_blocks[0].address;
626
627 for block in &sorted_blocks {
629 if block.address != compact_addr {
630 layout.remove_allocated_block(block.address);
632 layout.add_allocated_block(compact_addr, block.size, block.is_movable);
633
634 layout.add_free_region(block.address, block.size);
636
637 bytes_moved += block.size;
638 objects_relocated += 1;
639 }
640
641 compact_addr += block.size;
642 }
643
644 layout.coalesce_free_regions();
646
647 let final_fragmentation = layout.calculate_fragmentation();
648 let fragmentation_reduction = initial_fragmentation - final_fragmentation;
649 let time_taken = start_time.elapsed();
650
651 self.stats.executions += 1;
653 self.stats.total_bytes_moved += bytes_moved as u64;
654 self.stats.total_objects_relocated += objects_relocated as u64;
655 self.stats.total_time += time_taken;
656
657 let efficiency = if bytes_moved > 0 {
658 fragmentation_reduction / (bytes_moved as f64 / 1024.0 / 1024.0)
659 } else {
660 0.0
661 };
662
663 self.stats.average_efficiency =
664 (self.stats.average_efficiency * (self.stats.executions - 1) as f64 + efficiency)
665 / self.stats.executions as f64;
666
667 Ok(CompactionResult {
668 bytes_moved,
669 objects_relocated,
670 fragmentation_reduction,
671 time_taken,
672 algorithm_used: CompactionAlgorithm::TwoPointer,
673 efficiency_score: efficiency,
674 })
675 }
676
677 fn get_statistics(&self) -> CompactionStats {
678 self.stats.clone()
679 }
680
681 fn reset(&mut self) {
682 self.stats = CompactionStats::default();
683 }
684}
685
686impl DefragmentationEngine {
687 pub fn new(config: DefragConfig) -> Self {
688 let strategies: Vec<Box<dyn CompactionStrategy>> = vec![
689 Box::new(SlidingCompactionStrategy::new()),
690 Box::new(TwoPointerCompactionStrategy::new()),
691 ];
692
693 Self {
694 config,
695 stats: DefragStats::default(),
696 active_tasks: Vec::new(),
697 memory_layout: MemoryLayoutTracker::new(),
698 strategies: strategies
699 .into_iter()
700 .map(|s| s as Box<dyn CompactionStrategy>)
701 .collect(),
702 performance_history: VecDeque::with_capacity(1000),
703 }
704 }
705
706 pub fn should_defragment(&mut self) -> bool {
708 if !self.config.auto_defrag {
709 return false;
710 }
711
712 let current_fragmentation = self.memory_layout.calculate_fragmentation();
713 self.stats.current_fragmentation = current_fragmentation;
714
715 current_fragmentation > self.config.fragmentation_threshold
716 && self.memory_layout.get_total_free_space() >= self.config.min_free_space
717 }
718
719 pub fn defragment(&mut self) -> Result<CompactionResult, DefragError> {
721 let start_time = Instant::now();
722
723 if self
724 .active_tasks
725 .iter()
726 .any(|t| t.status == TaskStatus::Running)
727 {
728 return Err(DefragError::DefragmentationInProgress);
729 }
730
731 let strategy_index = self.select_best_strategy()?;
733 let strategy = &mut self.strategies[strategy_index];
734
735 let result = strategy.execute(&mut self.memory_layout)?;
737
738 self.stats.total_cycles += 1;
740 self.stats.total_bytes_moved += result.bytes_moved as u64;
741 self.stats.total_time_spent += result.time_taken;
742 self.stats.successful_cycles += 1;
743 self.stats.objects_relocated += result.objects_relocated as u64;
744 self.stats.average_fragmentation_reduction = (self.stats.average_fragmentation_reduction
745 * (self.stats.total_cycles - 1) as f64
746 + result.fragmentation_reduction)
747 / self.stats.total_cycles as f64;
748
749 let cycle_time = start_time.elapsed();
750 self.stats.average_cycle_time = Duration::from_nanos(
751 (self.stats.average_cycle_time.as_nanos() as u64 * (self.stats.total_cycles - 1)
752 + cycle_time.as_nanos() as u64)
753 / self.stats.total_cycles,
754 );
755
756 let performance = DefragPerformance {
758 timestamp: start_time,
759 fragmentation_before: self.stats.current_fragmentation,
760 fragmentation_after: self.memory_layout.calculate_fragmentation(),
761 time_taken: cycle_time,
762 bytes_moved: result.bytes_moved,
763 algorithm_used: result.algorithm_used.clone(),
764 success: true,
765 };
766
767 self.performance_history.push_back(performance);
768 if self.performance_history.len() > 1000 {
769 self.performance_history.pop_front();
770 }
771
772 Ok(result)
773 }
774
775 fn select_best_strategy(&mut self) -> Result<usize, DefragError> {
777 let mut best_index = 0;
778 let mut best_benefit = 0.0;
779
780 for (i, strategy) in self.strategies.iter().enumerate() {
781 if strategy.can_handle(&self.memory_layout) {
782 let benefit = strategy.estimate_benefit(&self.memory_layout);
783 if benefit > best_benefit {
784 best_benefit = benefit;
785 best_index = i;
786 }
787 }
788 }
789
790 if best_benefit == 0.0 {
791 return Err(DefragError::NoSuitableStrategy);
792 }
793
794 Ok(best_index)
795 }
796
797 pub fn create_task(
799 &mut self,
800 start_addr: usize,
801 size: usize,
802 algorithm: CompactionAlgorithm,
803 priority: TaskPriority,
804 ) -> u64 {
805 let task_id = self.active_tasks.len() as u64;
806 let task = DefragTask {
807 id: task_id,
808 start_addr,
809 size,
810 algorithm,
811 status: TaskStatus::Pending,
812 created_at: Instant::now(),
813 estimated_completion: None,
814 priority,
815 };
816
817 self.active_tasks.push(task);
818 task_id
819 }
820
821 pub fn get_stats(&self) -> &DefragStats {
823 &self.stats
824 }
825
826 pub fn get_performance_history(&self) -> &VecDeque<DefragPerformance> {
828 &self.performance_history
829 }
830
831 pub fn update_layout(
833 &mut self,
834 allocated_blocks: HashMap<usize, AllocatedBlock>,
835 free_regions: BTreeMap<usize, FreeRegion>,
836 ) {
837 self.memory_layout.allocated_blocks = allocated_blocks;
838 self.memory_layout.free_regions = free_regions;
839 self.memory_layout.invalidate_cache();
840 }
841
842 pub fn get_layout(&self) -> &MemoryLayoutTracker {
844 &self.memory_layout
845 }
846
847 pub fn reset(&mut self) {
849 self.stats = DefragStats::default();
850 self.active_tasks.clear();
851 self.memory_layout = MemoryLayoutTracker::new();
852 self.performance_history.clear();
853
854 for strategy in &mut self.strategies {
856 strategy.reset();
857 }
858 }
859}
860
861unsafe impl Send for DefragmentationEngine {}
868unsafe impl Sync for DefragmentationEngine {}
869
870#[derive(Debug, Clone)]
872pub enum DefragError {
873 DefragmentationInProgress,
874 NoMovableBlocks,
875 InsufficientBlocks,
876 NoSuitableStrategy,
877 MemoryLayoutCorrupted,
878 TimeoutExceeded,
879 InternalError(String),
880}
881
882impl std::fmt::Display for DefragError {
883 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
884 match self {
885 DefragError::DefragmentationInProgress => {
886 write!(f, "Defragmentation already in progress")
887 }
888 DefragError::NoMovableBlocks => write!(f, "No movable blocks available for compaction"),
889 DefragError::InsufficientBlocks => {
890 write!(f, "Insufficient blocks for compaction strategy")
891 }
892 DefragError::NoSuitableStrategy => {
893 write!(f, "No suitable compaction strategy available")
894 }
895 DefragError::MemoryLayoutCorrupted => write!(f, "Memory layout is corrupted"),
896 DefragError::TimeoutExceeded => write!(f, "Defragmentation timeout exceeded"),
897 DefragError::InternalError(msg) => write!(f, "Internal error: {}", msg),
898 }
899 }
900}
901
902impl std::error::Error for DefragError {}
903
904pub struct ThreadSafeDefragmentationEngine {
906 engine: Arc<Mutex<DefragmentationEngine>>,
907}
908
909impl ThreadSafeDefragmentationEngine {
910 pub fn new(config: DefragConfig) -> Self {
911 Self {
912 engine: Arc::new(Mutex::new(DefragmentationEngine::new(config))),
913 }
914 }
915
916 pub fn should_defragment(&self) -> bool {
917 let mut engine = self.engine.lock().unwrap_or_else(|e| e.into_inner());
918 engine.should_defragment()
919 }
920
921 pub fn defragment(&self) -> Result<CompactionResult, DefragError> {
922 let mut engine = self.engine.lock().unwrap_or_else(|e| e.into_inner());
923 engine.defragment()
924 }
925
926 pub fn get_stats(&self) -> DefragStats {
927 let engine = self.engine.lock().unwrap_or_else(|e| e.into_inner());
928 engine.get_stats().clone()
929 }
930
931 pub fn get_performance_history(&self) -> Vec<DefragPerformance> {
932 let engine = self.engine.lock().unwrap_or_else(|e| e.into_inner());
933 engine.get_performance_history().iter().cloned().collect()
934 }
935}
936
937#[cfg(test)]
938mod tests {
939 use super::*;
940
941 #[test]
942 fn test_memory_layout_tracker() {
943 let mut tracker = MemoryLayoutTracker::new();
944
945 tracker.add_allocated_block(1000, 500, true);
947 tracker.add_allocated_block(2000, 300, false);
948 tracker.add_free_region(1500, 200);
949 tracker.add_free_region(2500, 800);
950
951 let fragmentation = tracker.calculate_fragmentation();
952 assert!((0.0..=1.0).contains(&fragmentation));
953
954 let total_free = tracker.get_total_free_space();
955 assert_eq!(total_free, 1000);
956
957 let largest_free = tracker.get_largest_free_block();
958 assert_eq!(largest_free, 800);
959 }
960
961 #[test]
962 fn test_sliding_compaction_strategy() {
963 let mut strategy = SlidingCompactionStrategy::new();
964 let mut layout = MemoryLayoutTracker::new();
965
966 layout.add_allocated_block(1000, 500, true);
968 layout.add_free_region(1500, 200);
969 layout.add_allocated_block(2000, 300, true);
970 layout.add_free_region(2300, 500);
971
972 assert!(strategy.can_handle(&layout));
973
974 let benefit = strategy.estimate_benefit(&layout);
975 assert!(benefit > 0.0);
976
977 let result = strategy.execute(&mut layout);
978 assert!(result.is_ok());
979
980 let compaction_result = result.expect("unwrap failed");
981 assert!(compaction_result.bytes_moved > 0);
982 assert!(compaction_result.objects_relocated > 0);
983
984 assert!(strategy.get_statistics().executions > 0);
986 strategy.reset();
989 assert_eq!(strategy.get_statistics().executions, 0);
990 assert_eq!(strategy.get_statistics().total_bytes_moved, 0);
991 }
992
993 #[test]
994 fn test_two_pointer_compaction_strategy_reset_clears_statistics() {
995 let mut strategy = TwoPointerCompactionStrategy::new();
996 let mut layout = MemoryLayoutTracker::new();
997 layout.add_allocated_block(1000, 500, true);
998 layout.add_allocated_block(2000, 300, true);
999 layout.add_free_region(1500, 200);
1000
1001 strategy
1002 .execute(&mut layout)
1003 .expect("two-pointer compaction should succeed with movable blocks");
1004 assert!(strategy.get_statistics().executions > 0);
1005
1006 strategy.reset();
1007 assert_eq!(strategy.get_statistics().executions, 0);
1008 assert_eq!(strategy.get_statistics().total_bytes_moved, 0);
1009 }
1010
1011 #[test]
1012 fn test_defragmentation_engine_reset_clears_strategy_statistics() {
1013 let config = DefragConfig::default();
1014 let mut engine = DefragmentationEngine::new(config);
1015
1016 let mut allocated_blocks = HashMap::new();
1017 allocated_blocks.insert(
1018 1000,
1019 AllocatedBlock {
1020 address: 1000,
1021 size: 500,
1022 allocation_time: Instant::now(),
1023 last_access: None,
1024 access_count: 0,
1025 is_movable: true,
1026 reference_count: 1,
1027 },
1028 );
1029 allocated_blocks.insert(
1030 2000,
1031 AllocatedBlock {
1032 address: 2000,
1033 size: 300,
1034 allocation_time: Instant::now(),
1035 last_access: None,
1036 access_count: 0,
1037 is_movable: true,
1038 reference_count: 1,
1039 },
1040 );
1041 let mut free_regions = BTreeMap::new();
1042 free_regions.insert(
1043 1500,
1044 FreeRegion {
1045 address: 1500,
1046 size: 300,
1047 age: Duration::from_secs(10),
1048 access_frequency: 0,
1049 adjacent_to_allocated: true,
1050 },
1051 );
1052 engine.update_layout(allocated_blocks, free_regions);
1053 engine
1054 .defragment()
1055 .expect("defragmentation should find a suitable strategy for this layout");
1056 assert!(
1057 engine
1058 .strategies
1059 .iter()
1060 .any(|s| s.get_statistics().executions > 0),
1061 "defragment() should have driven at least one strategy's statistics"
1062 );
1063
1064 engine.reset();
1065 assert!(
1066 engine
1067 .strategies
1068 .iter()
1069 .all(|s| s.get_statistics().executions == 0),
1070 "reset() must clear every strategy's accumulated statistics"
1071 );
1072 }
1073
1074 #[test]
1075 fn test_defragmentation_engine() {
1076 let config = DefragConfig::default();
1077 let mut engine = DefragmentationEngine::new(config);
1078
1079 let mut allocated_blocks = HashMap::new();
1081 allocated_blocks.insert(
1082 1000,
1083 AllocatedBlock {
1084 address: 1000,
1085 size: 500,
1086 allocation_time: Instant::now(),
1087 last_access: None,
1088 access_count: 0,
1089 is_movable: true,
1090 reference_count: 1,
1091 },
1092 );
1093
1094 let mut free_regions = BTreeMap::new();
1095 free_regions.insert(
1096 1500,
1097 FreeRegion {
1098 address: 1500,
1099 size: 300,
1100 age: Duration::from_secs(10),
1101 access_frequency: 0,
1102 adjacent_to_allocated: true,
1103 },
1104 );
1105
1106 engine.update_layout(allocated_blocks, free_regions);
1107
1108 let should_defrag = engine.should_defragment();
1112 assert!(!should_defrag);
1113
1114 let stats = engine.get_stats();
1115 assert_eq!(stats.total_cycles, 0); }
1117
1118 #[test]
1119 fn test_coalescing() {
1120 let mut tracker = MemoryLayoutTracker::new();
1121
1122 tracker.add_free_region(1000, 500);
1124 tracker.add_free_region(1500, 300);
1125 tracker.add_free_region(2000, 200); let coalesced = tracker.coalesce_free_regions();
1128 assert_eq!(coalesced, 1); assert_eq!(tracker.free_regions.len(), 2);
1132 }
1133
1134 #[test]
1135 fn test_thread_safe_engine() {
1136 let config = DefragConfig::default();
1137 let engine = ThreadSafeDefragmentationEngine::new(config);
1138
1139 let should_defrag = engine.should_defragment();
1140 assert!(!should_defrag, "should not trigger defrag on empty layout");
1141
1142 let stats = engine.get_stats();
1143 assert_eq!(stats.total_cycles, 0);
1144 }
1145}