1#![allow(dead_code)]
11
12use std::collections::{HashMap, HashSet};
13use std::sync::{Arc, Mutex, RwLock};
14use std::time::{Duration, Instant};
15
16use serde::{Deserialize, Serialize};
17
18use crate::{Result, ShaclError};
19
20#[derive(Debug)]
22pub struct MemoryOptimizer {
23 string_interner: Arc<RwLock<StringInterner>>,
25
26 memory_pool: Arc<Mutex<MemoryPool>>,
28
29 memory_monitor: MemoryMonitor,
31
32 config: MemoryOptimizationConfig,
34
35 stats: MemoryOptimizationStats,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct MemoryOptimizationConfig {
42 pub enable_string_interning: bool,
44
45 pub enable_memory_pooling: bool,
47
48 pub enable_monitoring: bool,
50
51 pub max_memory_threshold: usize,
53
54 pub pressure_warning_threshold: f64,
56
57 pub pressure_critical_threshold: f64,
59
60 pub enable_gc_hints: bool,
62
63 pub compaction_interval: Duration,
65
66 pub pool_limits: PoolLimits,
68}
69
70impl Default for MemoryOptimizationConfig {
71 fn default() -> Self {
72 Self {
73 enable_string_interning: true,
74 enable_memory_pooling: true,
75 enable_monitoring: true,
76 max_memory_threshold: 1024 * 1024 * 1024, pressure_warning_threshold: 0.7,
78 pressure_critical_threshold: 0.9,
79 enable_gc_hints: true,
80 compaction_interval: Duration::from_secs(300), pool_limits: PoolLimits::default(),
82 }
83 }
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize)]
88pub struct PoolLimits {
89 pub max_small_objects: usize,
91
92 pub max_medium_objects: usize,
94
95 pub max_large_objects: usize,
97
98 pub small_size_threshold: usize,
100
101 pub medium_size_threshold: usize,
103}
104
105impl Default for PoolLimits {
106 fn default() -> Self {
107 Self {
108 max_small_objects: 10000,
109 max_medium_objects: 1000,
110 max_large_objects: 100,
111 small_size_threshold: 256,
112 medium_size_threshold: 4096,
113 }
114 }
115}
116
117#[derive(Debug)]
119pub struct StringInterner {
120 strings: HashMap<String, InternedString>,
122
123 reverse_map: HashMap<InternedString, String>,
125
126 next_id: u32,
128
129 stats: InternerStats,
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
135pub struct InternedString(u32);
136
137#[derive(Debug, Clone, Default, Serialize, Deserialize)]
139pub struct InternerStats {
140 pub total_strings: usize,
142
143 pub memory_saved: usize,
145
146 pub lookups: usize,
148
149 pub hits: usize,
151
152 pub avg_string_length: f64,
154}
155
156impl StringInterner {
157 pub fn new() -> Self {
159 Self {
160 strings: HashMap::new(),
161 reverse_map: HashMap::new(),
162 next_id: 0,
163 stats: InternerStats::default(),
164 }
165 }
166
167 pub fn intern(&mut self, s: &str) -> InternedString {
169 self.stats.lookups += 1;
170
171 if let Some(&id) = self.strings.get(s) {
172 self.stats.hits += 1;
173 return id;
174 }
175
176 let id = InternedString(self.next_id);
177 self.next_id += 1;
178
179 let owned_string = s.to_string();
180 self.strings.insert(owned_string.clone(), id);
181 self.reverse_map.insert(id, owned_string);
182
183 self.stats.total_strings += 1;
185 self.stats.memory_saved += s.len(); self.update_avg_length(s.len());
187
188 id
189 }
190
191 pub fn get(&self, id: InternedString) -> Option<&String> {
193 self.reverse_map.get(&id)
194 }
195
196 pub fn stats(&self) -> &InternerStats {
198 &self.stats
199 }
200
201 pub fn clear(&mut self) {
203 self.strings.clear();
204 self.reverse_map.clear();
205 self.next_id = 0;
206 self.stats = InternerStats::default();
207 }
208
209 pub fn compact(&mut self, used_ids: &HashSet<InternedString>) {
211 let old_size = self.strings.len();
212
213 self.strings.retain(|_, id| used_ids.contains(id));
215 self.reverse_map.retain(|&id, _| used_ids.contains(&id));
216
217 let removed = old_size - self.strings.len();
218 self.stats.memory_saved += removed * 50; }
220
221 fn update_avg_length(&mut self, new_length: usize) {
223 let total_length = self.stats.avg_string_length * (self.stats.total_strings - 1) as f64
224 + new_length as f64;
225 self.stats.avg_string_length = total_length / self.stats.total_strings as f64;
226 }
227}
228
229impl Default for StringInterner {
230 fn default() -> Self {
231 Self::new()
232 }
233}
234
235#[derive(Debug)]
237pub struct MemoryPool {
238 small_pool: Vec<PooledObject>,
240
241 medium_pool: Vec<PooledObject>,
243
244 large_pool: Vec<PooledObject>,
246
247 stats: PoolStats,
249
250 limits: PoolLimits,
252}
253
254#[derive(Debug)]
256struct PooledObject {
257 data: Vec<u8>,
259
260 allocated_at: Instant,
262
263 in_use: bool,
265}
266
267#[derive(Debug, Clone, Default, Serialize, Deserialize)]
269pub struct PoolStats {
270 pub total_allocations: usize,
272
273 pub pool_hits: usize,
275
276 pub pool_misses: usize,
278
279 pub active_objects: usize,
281
282 pub peak_objects: usize,
284
285 pub total_memory_managed: usize,
287}
288
289impl MemoryPool {
290 pub fn new(limits: PoolLimits) -> Self {
292 Self {
293 small_pool: Vec::with_capacity(limits.max_small_objects),
294 medium_pool: Vec::with_capacity(limits.max_medium_objects),
295 large_pool: Vec::with_capacity(limits.max_large_objects),
296 stats: PoolStats::default(),
297 limits,
298 }
299 }
300
301 pub fn allocate(&mut self, size: usize) -> Result<*mut u8> {
303 self.stats.total_allocations += 1;
304
305 let pool_capacity = self.pool_capacity(size);
307
308 let pool_index = if size <= self.limits.small_size_threshold {
310 0
311 } else if size <= self.limits.medium_size_threshold {
312 1
313 } else {
314 2
315 };
316
317 let pool = match pool_index {
318 0 => &mut self.small_pool,
319 1 => &mut self.medium_pool,
320 _ => &mut self.large_pool,
321 };
322
323 for obj in pool.iter_mut() {
325 if !obj.in_use && obj.data.capacity() >= size {
326 obj.in_use = true;
327 obj.allocated_at = Instant::now();
328 self.stats.pool_hits += 1;
329 self.stats.active_objects += 1;
330
331 if self.stats.active_objects > self.stats.peak_objects {
332 self.stats.peak_objects = self.stats.active_objects;
333 }
334
335 return Ok(obj.data.as_mut_ptr());
336 }
337 }
338
339 if pool.len() < pool_capacity {
341 let mut data = vec![0; size];
342
343 let ptr = data.as_mut_ptr();
344
345 pool.push(PooledObject {
346 data,
347 allocated_at: Instant::now(),
348 in_use: true,
349 });
350
351 self.stats.pool_misses += 1;
352 self.stats.active_objects += 1;
353 self.stats.total_memory_managed += size;
354
355 if self.stats.active_objects > self.stats.peak_objects {
356 self.stats.peak_objects = self.stats.active_objects;
357 }
358
359 Ok(ptr)
360 } else {
361 self.stats.pool_misses += 1;
363 Err(ShaclError::MemoryPool("Pool capacity exceeded".to_string()))
364 }
365 }
366
367 pub fn deallocate(&mut self, ptr: *mut u8) {
369 for pool in [
371 &mut self.small_pool,
372 &mut self.medium_pool,
373 &mut self.large_pool,
374 ] {
375 for obj in pool.iter_mut() {
376 if obj.data.as_mut_ptr() == ptr && obj.in_use {
377 obj.in_use = false;
378 self.stats.active_objects -= 1;
379 return;
380 }
381 }
382 }
383 }
384
385 pub fn compact(&mut self, max_age: Duration) {
387 let cutoff = Instant::now() - max_age;
388
389 let compact_pool = |pool: &mut Vec<PooledObject>| {
390 let old_len = pool.len();
391 pool.retain(|obj| obj.in_use || obj.allocated_at > cutoff);
392 old_len - pool.len()
393 };
394
395 let removed = compact_pool(&mut self.small_pool)
396 + compact_pool(&mut self.medium_pool)
397 + compact_pool(&mut self.large_pool);
398
399 if removed > 0 {
400 self.stats.total_memory_managed = self
401 .small_pool
402 .iter()
403 .chain(self.medium_pool.iter())
404 .chain(self.large_pool.iter())
405 .map(|obj| obj.data.capacity())
406 .sum();
407 }
408 }
409
410 pub fn stats(&self) -> &PoolStats {
412 &self.stats
413 }
414
415 fn select_pool(&mut self, size: usize) -> &mut Vec<PooledObject> {
417 if size <= self.limits.small_size_threshold {
418 &mut self.small_pool
419 } else if size <= self.limits.medium_size_threshold {
420 &mut self.medium_pool
421 } else {
422 &mut self.large_pool
423 }
424 }
425
426 fn pool_capacity(&self, size: usize) -> usize {
428 if size <= self.limits.small_size_threshold {
429 self.limits.max_small_objects
430 } else if size <= self.limits.medium_size_threshold {
431 self.limits.max_medium_objects
432 } else {
433 self.limits.max_large_objects
434 }
435 }
436}
437
438#[derive(Debug)]
440pub struct MemoryMonitor {
441 samples: Vec<MemorySample>,
443
444 pressure_level: MemoryPressureLevel,
446
447 stats: MonitoringStats,
449
450 last_collection: Instant,
452}
453
454#[derive(Debug, Clone, Serialize)]
456pub struct MemorySample {
457 #[serde(skip)]
459 pub timestamp: Instant,
460
461 pub memory_usage: usize,
463
464 pub pressure: f64,
466}
467
468impl Default for MemorySample {
469 fn default() -> Self {
470 Self {
471 timestamp: Instant::now(),
472 memory_usage: 0,
473 pressure: 0.0,
474 }
475 }
476}
477
478#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
480pub enum MemoryPressureLevel {
481 Normal,
483
484 Warning,
486
487 Critical,
489
490 Emergency,
492}
493
494#[derive(Debug, Clone, Default, Serialize, Deserialize)]
496pub struct MonitoringStats {
497 pub total_samples: usize,
499
500 pub avg_memory_usage: f64,
502
503 pub peak_memory_usage: usize,
505
506 pub pressure_warnings: usize,
508
509 pub critical_pressure_events: usize,
511
512 pub high_pressure_duration: Duration,
514}
515
516impl MemoryMonitor {
517 pub fn new() -> Self {
519 Self {
520 samples: Vec::new(),
521 pressure_level: MemoryPressureLevel::Normal,
522 stats: MonitoringStats::default(),
523 last_collection: Instant::now(),
524 }
525 }
526
527 pub fn record_sample(&mut self, memory_usage: usize, max_memory: usize) {
529 let pressure = memory_usage as f64 / max_memory as f64;
530 let timestamp = Instant::now();
531
532 self.samples.push(MemorySample {
533 timestamp,
534 memory_usage,
535 pressure,
536 });
537
538 if self.samples.len() > 1000 {
540 self.samples.remove(0);
541 }
542
543 let old_level = self.pressure_level.clone();
545 self.pressure_level = if pressure >= 0.95 {
546 MemoryPressureLevel::Emergency
547 } else if pressure >= 0.9 {
548 MemoryPressureLevel::Critical
549 } else if pressure >= 0.7 {
550 MemoryPressureLevel::Warning
551 } else {
552 MemoryPressureLevel::Normal
553 };
554
555 self.stats.total_samples += 1;
557 self.stats.avg_memory_usage = (self.stats.avg_memory_usage
558 * (self.stats.total_samples - 1) as f64
559 + memory_usage as f64)
560 / self.stats.total_samples as f64;
561
562 if memory_usage > self.stats.peak_memory_usage {
563 self.stats.peak_memory_usage = memory_usage;
564 }
565
566 if matches!(self.pressure_level, MemoryPressureLevel::Warning)
567 && !matches!(old_level, MemoryPressureLevel::Warning)
568 {
569 self.stats.pressure_warnings += 1;
570 }
571
572 if matches!(
573 self.pressure_level,
574 MemoryPressureLevel::Critical | MemoryPressureLevel::Emergency
575 ) && !matches!(
576 old_level,
577 MemoryPressureLevel::Critical | MemoryPressureLevel::Emergency
578 ) {
579 self.stats.critical_pressure_events += 1;
580 }
581
582 if matches!(
584 self.pressure_level,
585 MemoryPressureLevel::Critical | MemoryPressureLevel::Emergency
586 ) {
587 self.stats.high_pressure_duration += timestamp.duration_since(self.last_collection);
588 }
589
590 self.last_collection = timestamp;
591 }
592
593 pub fn pressure_level(&self) -> &MemoryPressureLevel {
595 &self.pressure_level
596 }
597
598 pub fn stats(&self) -> &MonitoringStats {
600 &self.stats
601 }
602
603 pub fn memory_trend(&self) -> MemoryTrend {
605 if self.samples.len() < 10 {
606 return MemoryTrend::Stable;
607 }
608
609 let recent_avg = self
610 .samples
611 .iter()
612 .rev()
613 .take(5)
614 .map(|s| s.memory_usage)
615 .sum::<usize>() as f64
616 / 5.0;
617
618 let older_avg = self
619 .samples
620 .iter()
621 .rev()
622 .skip(5)
623 .take(5)
624 .map(|s| s.memory_usage)
625 .sum::<usize>() as f64
626 / 5.0;
627
628 let change_ratio = (recent_avg - older_avg) / older_avg;
629
630 if change_ratio > 0.1 {
631 MemoryTrend::Increasing
632 } else if change_ratio < -0.1 {
633 MemoryTrend::Decreasing
634 } else {
635 MemoryTrend::Stable
636 }
637 }
638
639 pub fn clear_old_samples(&mut self, max_age: Duration) {
641 let cutoff = Instant::now() - max_age;
642 self.samples.retain(|sample| sample.timestamp > cutoff);
643 }
644}
645
646impl Default for MemoryMonitor {
647 fn default() -> Self {
648 Self::new()
649 }
650}
651
652#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
654pub enum MemoryTrend {
655 Increasing,
657
658 Decreasing,
660
661 Stable,
663}
664
665#[derive(Debug, Clone, Default, Serialize, Deserialize)]
667pub struct MemoryOptimizationStats {
668 pub interning_stats: InternerStats,
670
671 pub pool_stats: PoolStats,
673
674 pub monitoring_stats: MonitoringStats,
676
677 pub total_memory_saved: usize,
679
680 pub optimization_operations: usize,
682
683 pub optimization_time: Duration,
685}
686
687impl MemoryOptimizer {
688 pub fn new(config: MemoryOptimizationConfig) -> Self {
690 Self {
691 string_interner: Arc::new(RwLock::new(StringInterner::new())),
692 memory_pool: Arc::new(Mutex::new(MemoryPool::new(config.pool_limits.clone()))),
693 memory_monitor: MemoryMonitor::new(),
694 config,
695 stats: MemoryOptimizationStats::default(),
696 }
697 }
698
699 pub fn intern_string(&mut self, s: &str) -> Result<InternedString> {
701 if !self.config.enable_string_interning {
702 return Err(ShaclError::MemoryOptimization(
703 "String interning is disabled".to_string(),
704 ));
705 }
706
707 let start = Instant::now();
708 let mut interner = self
709 .string_interner
710 .write()
711 .expect("rwlock should not be poisoned");
712 let result = interner.intern(s);
713
714 self.stats.optimization_time += start.elapsed();
715 self.stats.optimization_operations += 1;
716
717 Ok(result)
718 }
719
720 pub fn get_interned_string(&self, id: InternedString) -> Option<String> {
722 let interner = self
723 .string_interner
724 .read()
725 .expect("rwlock should not be poisoned");
726 interner.get(id).cloned()
727 }
728
729 pub fn pool_allocate(&mut self, size: usize) -> Result<*mut u8> {
731 if !self.config.enable_memory_pooling {
732 return Err(ShaclError::MemoryOptimization(
733 "Memory pooling is disabled".to_string(),
734 ));
735 }
736
737 let start = Instant::now();
738 let mut pool = self
739 .memory_pool
740 .lock()
741 .expect("mutex lock should not be poisoned");
742 let result = pool.allocate(size);
743
744 self.stats.optimization_time += start.elapsed();
745 self.stats.optimization_operations += 1;
746
747 result
748 }
749
750 pub fn pool_deallocate(&mut self, ptr: *mut u8) {
752 if self.config.enable_memory_pooling {
753 let mut pool = self
754 .memory_pool
755 .lock()
756 .expect("mutex lock should not be poisoned");
757 pool.deallocate(ptr);
758 }
759 }
760
761 pub fn record_memory_usage(&mut self, memory_usage: usize) {
763 if self.config.enable_monitoring {
764 self.memory_monitor
765 .record_sample(memory_usage, self.config.max_memory_threshold);
766 }
767 }
768
769 pub fn memory_pressure_level(&self) -> &MemoryPressureLevel {
771 self.memory_monitor.pressure_level()
772 }
773
774 pub fn is_memory_pressure_high(&self) -> bool {
776 matches!(
777 self.memory_monitor.pressure_level(),
778 MemoryPressureLevel::Critical | MemoryPressureLevel::Emergency
779 )
780 }
781
782 pub fn optimize(&mut self) -> Result<OptimizationResult> {
784 let start = Instant::now();
785 let mut result = OptimizationResult::default();
786
787 if self.config.enable_string_interning {
789 let used_strings = self.collect_used_strings()?;
790 let mut interner = self
791 .string_interner
792 .write()
793 .expect("rwlock should not be poisoned");
794 let old_size = interner.strings.len();
795 interner.compact(&used_strings);
796 let new_size = interner.strings.len();
797 result.strings_removed = old_size - new_size;
798 }
799
800 if self.config.enable_memory_pooling {
802 let mut pool = self
803 .memory_pool
804 .lock()
805 .expect("mutex lock should not be poisoned");
806 pool.compact(self.config.compaction_interval);
807 result.pool_objects_freed =
808 pool.stats().total_allocations - pool.stats().active_objects;
809 }
810
811 if self.config.enable_gc_hints && self.is_memory_pressure_high() {
813 #[cfg(not(target_family = "wasm"))]
814 {
815 self.clear_caches();
817 result.gc_triggered = true;
818 }
819 }
820
821 self.update_statistics();
823
824 result.optimization_duration = start.elapsed();
825 self.stats.optimization_time += result.optimization_duration;
826 self.stats.optimization_operations += 1;
827
828 Ok(result)
829 }
830
831 pub fn clear_caches(&mut self) {
833 if self.config.enable_string_interning {
834 let mut interner = self
835 .string_interner
836 .write()
837 .expect("rwlock should not be poisoned");
838 interner.clear();
839 }
840 }
841
842 pub fn get_statistics(&self) -> MemoryOptimizationStats {
844 let mut stats = self.stats.clone();
845
846 if self.config.enable_string_interning {
847 let interner = self
848 .string_interner
849 .read()
850 .expect("rwlock should not be poisoned");
851 stats.interning_stats = interner.stats().clone();
852 }
853
854 if self.config.enable_memory_pooling {
855 let pool = self
856 .memory_pool
857 .lock()
858 .expect("mutex lock should not be poisoned");
859 stats.pool_stats = pool.stats().clone();
860 }
861
862 if self.config.enable_monitoring {
863 stats.monitoring_stats = self.memory_monitor.stats().clone();
864 }
865
866 stats.total_memory_saved = stats.interning_stats.memory_saved;
867
868 stats
869 }
870
871 fn collect_used_strings(&self) -> Result<HashSet<InternedString>> {
873 Ok(HashSet::new())
876 }
877
878 fn update_statistics(&mut self) {
880 if self.config.enable_string_interning {
882 let interner = self
883 .string_interner
884 .read()
885 .expect("rwlock should not be poisoned");
886 self.stats.interning_stats = interner.stats().clone();
887 }
888
889 if self.config.enable_memory_pooling {
890 let pool = self
891 .memory_pool
892 .lock()
893 .expect("mutex lock should not be poisoned");
894 self.stats.pool_stats = pool.stats().clone();
895 }
896
897 if self.config.enable_monitoring {
898 self.stats.monitoring_stats = self.memory_monitor.stats().clone();
899 }
900 }
901}
902
903#[derive(Debug, Clone, Default, Serialize, Deserialize)]
905pub struct OptimizationResult {
906 pub strings_removed: usize,
908
909 pub pool_objects_freed: usize,
911
912 pub gc_triggered: bool,
914
915 pub optimization_duration: Duration,
917
918 pub memory_freed: usize,
920}
921
922pub mod compact {
924 use super::*;
925
926 #[derive(Debug, Clone, Serialize, Deserialize)]
928 pub struct CompactViolation {
929 pub focus_node: InternedString,
931
932 pub shape_id: InternedString,
934
935 pub constraint_component: InternedString,
937
938 pub severity: u8,
940
941 pub message: Option<InternedString>,
943 }
944
945 #[derive(Debug, Clone, Serialize, Deserialize)]
947 pub struct CompactShape {
948 pub id: InternedString,
950
951 pub shape_type: u8,
953
954 pub targets: Vec<CompactTarget>,
956
957 pub constraints: Vec<CompactConstraint>,
959 }
960
961 #[derive(Debug, Clone, Serialize, Deserialize)]
963 pub struct CompactTarget {
964 pub target_type: u8,
966
967 pub value: InternedString,
969 }
970
971 #[derive(Debug, Clone, Serialize, Deserialize)]
973 pub struct CompactConstraint {
974 pub constraint_type: u8,
976
977 pub parameters: Vec<u8>,
979 }
980
981 #[derive(Debug)]
983 pub struct CompactConverter {
984 interner: Arc<RwLock<StringInterner>>,
986 }
987
988 impl CompactConverter {
989 pub fn new(interner: Arc<RwLock<StringInterner>>) -> Self {
991 Self { interner }
992 }
993
994 pub fn compact_violation(
996 &self,
997 violation: &crate::validation::ValidationViolation,
998 ) -> Result<CompactViolation> {
999 let mut interner = self
1000 .interner
1001 .write()
1002 .expect("rwlock should not be poisoned");
1003
1004 let focus_node = interner.intern(&format!("{:?}", violation.focus_node));
1005 let shape_id = interner.intern(&violation.source_shape.to_string());
1006 let constraint_component =
1007 interner.intern(&violation.source_constraint_component.to_string());
1008
1009 let severity = match violation.result_severity {
1010 crate::Severity::Info => 0,
1011 crate::Severity::Warning => 1,
1012 crate::Severity::Violation => 2,
1013 };
1014
1015 let message = violation
1016 .result_message
1017 .as_ref()
1018 .map(|msg| interner.intern(msg));
1019
1020 Ok(CompactViolation {
1021 focus_node,
1022 shape_id,
1023 constraint_component,
1024 severity,
1025 message,
1026 })
1027 }
1028
1029 pub fn expand_violation(
1031 &self,
1032 compact: &CompactViolation,
1033 ) -> Result<crate::validation::ValidationViolation> {
1034 let interner = self.interner.read().expect("rwlock should not be poisoned");
1035
1036 let _focus_node_str = interner.get(compact.focus_node).ok_or_else(|| {
1037 ShaclError::MemoryOptimization("Invalid focus node reference".to_string())
1038 })?;
1039
1040 let _shape_id_str = interner.get(compact.shape_id).ok_or_else(|| {
1041 ShaclError::MemoryOptimization("Invalid shape ID reference".to_string())
1042 })?;
1043
1044 let _constraint_component_str =
1045 interner.get(compact.constraint_component).ok_or_else(|| {
1046 ShaclError::MemoryOptimization(
1047 "Invalid constraint component reference".to_string(),
1048 )
1049 })?;
1050
1051 Err(ShaclError::MemoryOptimization(
1054 "Compact violation expansion not fully implemented".to_string(),
1055 ))
1056 }
1057 }
1058}
1059
1060#[cfg(test)]
1063mod tests {
1064 use super::*;
1065
1066 #[test]
1067 fn test_string_interner() {
1068 let mut interner = StringInterner::new();
1069
1070 let id1 = interner.intern("test_string");
1071 let id2 = interner.intern("test_string");
1072 let id3 = interner.intern("different_string");
1073
1074 assert_eq!(id1, id2); assert_ne!(id1, id3); assert_eq!(interner.get(id1), Some(&"test_string".to_string()));
1078 assert_eq!(interner.get(id3), Some(&"different_string".to_string()));
1079
1080 let stats = interner.stats();
1081 assert_eq!(stats.total_strings, 2); assert_eq!(stats.lookups, 3); assert_eq!(stats.hits, 1); }
1085
1086 #[test]
1087 fn test_memory_monitor() {
1088 let mut monitor = MemoryMonitor::new();
1089
1090 monitor.record_sample(1000, 10000); assert_eq!(monitor.pressure_level(), &MemoryPressureLevel::Normal);
1093
1094 monitor.record_sample(7500, 10000); assert_eq!(monitor.pressure_level(), &MemoryPressureLevel::Warning);
1097
1098 monitor.record_sample(9500, 10000); assert_eq!(monitor.pressure_level(), &MemoryPressureLevel::Emergency);
1101
1102 let stats = monitor.stats();
1103 assert_eq!(stats.total_samples, 3);
1104 assert!(stats.peak_memory_usage == 9500);
1105 }
1106
1107 #[test]
1108 fn test_memory_optimizer_config() {
1109 let config = MemoryOptimizationConfig::default();
1110
1111 assert!(config.enable_string_interning);
1112 assert!(config.enable_memory_pooling);
1113 assert!(config.enable_monitoring);
1114 assert_eq!(config.max_memory_threshold, 1024 * 1024 * 1024);
1115 assert_eq!(config.pressure_warning_threshold, 0.7);
1116 assert_eq!(config.pressure_critical_threshold, 0.9);
1117 }
1118
1119 #[test]
1120 fn test_memory_optimizer_creation() {
1121 let config = MemoryOptimizationConfig::default();
1122 let optimizer = MemoryOptimizer::new(config);
1123
1124 assert_eq!(
1125 optimizer.memory_pressure_level(),
1126 &MemoryPressureLevel::Normal
1127 );
1128 assert!(!optimizer.is_memory_pressure_high());
1129 }
1130}