1use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use std::collections::HashMap;
9use std::error::Error;
10use std::fmt;
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct UnifiedMemoryData {
15 pub stats: MemoryStatistics,
17
18 pub allocations: Vec<AllocationInfo>,
20
21 pub performance: PerformanceMetrics,
23
24 pub lifecycle: LifecycleAnalysis,
26
27 pub security: SecurityAnalysis,
29
30 pub complex_types: ComplexTypeAnalysis,
32
33 pub variable_relationships: VariableRelationships,
35
36 pub metadata: AnalysisMetadata,
38
39 #[serde(rename = "_multiSource")]
41 pub multi_source: HashMap<String, Value>,
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct MemoryStatistics {
47 pub active_memory: usize,
49 pub active_allocations: usize,
51 pub peak_memory: usize,
53 pub total_allocations: usize,
55 pub total_allocated: usize,
57 pub memory_efficiency: f64,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct BorrowInfo {
64 pub immutable_borrows: u32,
66 pub mutable_borrows: u32,
68 pub max_concurrent_borrows: u32,
70 pub last_borrow_timestamp: u64,
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct CloneInfo {
77 pub clone_count: u32,
79 pub is_clone: bool,
81 pub original_ptr: Option<String>,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct AllocationInfo {
88 pub ptr: String,
90 pub size: usize,
92 pub var_name: Option<String>,
94 pub type_name: Option<String>,
96 pub scope_name: Option<String>,
98 pub timestamp_alloc: u64,
100 pub timestamp_dealloc: Option<u64>,
102 pub thread_id: Option<String>,
104 pub borrow_count: Option<u32>,
106 pub stack_trace: Option<Vec<String>>,
108 pub is_leaked: bool,
110 pub lifetime_ms: Option<u64>,
112 pub borrow_info: Option<BorrowInfo>,
114 pub clone_info: Option<CloneInfo>,
116 pub ownership_history_available: Option<bool>,
118 pub ffi_tracked: Option<bool>,
120 pub safety_violations: Option<Vec<String>>,
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct PerformanceMetrics {
127 pub processing_time_ms: u64,
129 pub allocations_per_second: f64,
131 pub memory_efficiency: f64,
133 pub optimization_status: OptimizationStatus,
135 pub allocation_distribution: AllocationDistribution,
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct OptimizationStatus {
142 pub parallel_processing: bool,
144 pub schema_validation: bool,
146 pub streaming_enabled: bool,
148 pub batch_size: Option<usize>,
150 pub buffer_size_kb: Option<usize>,
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct AllocationDistribution {
157 pub tiny: usize,
159 pub small: usize,
161 pub medium: usize,
163 pub large: usize,
165 pub massive: usize,
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize)]
171pub struct LifecycleAnalysis {
172 pub lifecycle_events: Vec<Value>,
174 pub scope_analysis: HashMap<String, Value>,
176 pub variable_lifetimes: HashMap<String, Value>,
178}
179
180#[derive(Debug, Clone, Serialize, Deserialize)]
182pub struct SecurityAnalysis {
183 pub total_violations: usize,
185 pub risk_level: String,
187 pub severity_breakdown: SeverityBreakdown,
189 pub violation_reports: Vec<Value>,
191 pub recommendations: Vec<String>,
193}
194
195#[derive(Debug, Clone, Serialize, Deserialize)]
197pub struct SeverityBreakdown {
198 pub critical: usize,
200 pub high: usize,
202 pub medium: usize,
204 pub low: usize,
206 pub info: usize,
208}
209
210#[derive(Debug, Clone, Serialize, Deserialize)]
212pub struct ComplexTypeAnalysis {
213 pub categorized_types: CategorizedTypes,
215 pub complex_type_analysis: Vec<Value>,
217 pub summary: ComplexTypeSummary,
219}
220
221#[derive(Debug, Clone, Serialize, Deserialize)]
223pub struct CategorizedTypes {
224 pub collections: Vec<Value>,
226 pub generic_types: Vec<Value>,
228 pub smart_pointers: Vec<Value>,
230 pub trait_objects: Vec<Value>,
232}
233
234#[derive(Debug, Clone, Serialize, Deserialize)]
236pub struct ComplexTypeSummary {
237 pub total_complex_types: usize,
239 pub complexity_distribution: ComplexityDistribution,
241}
242
243#[derive(Debug, Clone, Serialize, Deserialize)]
245pub struct ComplexityDistribution {
246 pub low_complexity: usize,
248 pub medium_complexity: usize,
250 pub high_complexity: usize,
252 pub very_high_complexity: usize,
254}
255
256#[derive(Debug, Clone, Serialize, Deserialize)]
258pub struct VariableRelationships {
259 pub relationships: Vec<Value>,
261 pub registry: HashMap<String, Value>,
263 pub dependency_graph: HashMap<String, Value>,
265 pub scope_hierarchy: HashMap<String, Value>,
267}
268
269#[derive(Debug, Clone, Serialize, Deserialize)]
271pub struct AnalysisMetadata {
272 pub timestamp: u64,
274 pub export_version: String,
276 pub analysis_type: String,
278 pub data_integrity_hash: Option<String>,
280}
281
282#[derive(Debug)]
284pub enum NormalizationError {
285 MissingField(String),
287 InvalidType(String),
289 ValidationError(String),
291 JsonError(serde_json::Error),
293}
294
295impl fmt::Display for NormalizationError {
296 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
297 match self {
298 NormalizationError::MissingField(field) => {
299 write!(f, "Missing required field: {field}")
300 }
301 NormalizationError::InvalidType(msg) => {
302 write!(f, "Invalid data type: {msg}")
303 }
304 NormalizationError::ValidationError(msg) => {
305 write!(f, "Data validation error: {msg}")
306 }
307 NormalizationError::JsonError(err) => {
308 write!(f, "JSON error: {err}")
309 }
310 }
311 }
312}
313
314impl Error for NormalizationError {}
315
316impl From<serde_json::Error> for NormalizationError {
317 fn from(err: serde_json::Error) -> Self {
318 NormalizationError::JsonError(err)
319 }
320}
321
322pub struct DataNormalizer {
324 validation_enabled: bool,
326 #[allow(dead_code)]
328 default_values: HashMap<String, Value>,
329}
330
331impl DataNormalizer {
332 pub fn new() -> Self {
334 Self {
335 validation_enabled: true,
336 default_values: Self::create_default_values(),
337 }
338 }
339
340 pub fn without_validation() -> Self {
342 Self {
343 validation_enabled: false,
344 default_values: Self::create_default_values(),
345 }
346 }
347
348 pub fn normalize(
350 &self,
351 multi_source: &HashMap<String, Value>,
352 ) -> Result<UnifiedMemoryData, NormalizationError> {
353 tracing::info!("🔄 Starting data normalization...");
354
355 let stats = self.normalize_memory_stats(multi_source)?;
357 let allocations = self.normalize_allocations(multi_source)?;
358 let performance = self.normalize_performance(multi_source)?;
359 let lifecycle = self.normalize_lifecycle(multi_source)?;
360 let security = self.normalize_security(multi_source)?;
361 let complex_types = self.normalize_complex_types(multi_source)?;
362 let variable_relationships = self.normalize_variable_relationships(multi_source)?;
363 let metadata = self.normalize_metadata(multi_source)?;
364
365 let unified = UnifiedMemoryData {
366 stats,
367 allocations,
368 performance,
369 lifecycle,
370 security,
371 complex_types,
372 variable_relationships,
373 metadata,
374 multi_source: multi_source.clone(),
375 };
376
377 if self.validation_enabled {
379 self.validate_unified_data(&unified)?;
380 }
381
382 tracing::info!("✅ Data normalization completed successfully");
383 Ok(unified)
384 }
385
386 fn create_default_values() -> HashMap<String, Value> {
388 let mut defaults = HashMap::new();
389 defaults.insert("active_memory".to_string(), Value::Number(0.into()));
390 defaults.insert("active_allocations".to_string(), Value::Number(0.into()));
391 defaults.insert("peak_memory".to_string(), Value::Number(0.into()));
392 defaults.insert("total_allocations".to_string(), Value::Number(0.into()));
393 defaults.insert("total_allocated".to_string(), Value::Number(0.into()));
394 defaults.insert(
395 "memory_efficiency".to_string(),
396 Value::Number(serde_json::Number::from_f64(0.0).expect("Failed to create JSON number")),
397 );
398 defaults
399 }
400
401 fn normalize_memory_stats(
403 &self,
404 multi_source: &HashMap<String, Value>,
405 ) -> Result<MemoryStatistics, NormalizationError> {
406 let memory_data = multi_source.get("memory_analysis");
408 let performance_data = multi_source.get("performance");
409
410 let memory_stats = memory_data
411 .and_then(|data| data.get("memory_stats"))
412 .or_else(|| memory_data.and_then(|data| data.get("stats")));
413
414 let perf_memory = performance_data.and_then(|data| data.get("memory_performance"));
415
416 let metadata = memory_data.and_then(|data| data.get("metadata"));
417
418 Ok(MemoryStatistics {
419 active_memory: self
420 .extract_usize(memory_stats, "active_memory")
421 .or_else(|| self.extract_usize(perf_memory, "active_memory"))
422 .unwrap_or(0),
423 active_allocations: self.count_active_allocations(memory_data),
424 peak_memory: self
425 .extract_usize(memory_stats, "peak_memory")
426 .or_else(|| self.extract_usize(perf_memory, "peak_memory"))
427 .unwrap_or(0),
428 total_allocations: self
429 .extract_usize(memory_stats, "total_allocations")
430 .or_else(|| self.extract_usize(perf_memory, "total_allocated"))
431 .or_else(|| self.extract_usize(metadata, "total_allocations"))
432 .unwrap_or(0),
433 total_allocated: self
434 .extract_usize(memory_stats, "total_allocated")
435 .or_else(|| self.extract_usize(perf_memory, "total_allocated"))
436 .unwrap_or(0),
437 memory_efficiency: self
438 .extract_f64(perf_memory, "memory_efficiency")
439 .unwrap_or(0.0),
440 })
441 }
442
443 fn count_active_allocations(&self, memory_data: Option<&Value>) -> usize {
445 memory_data
446 .and_then(|data| data.get("allocations"))
447 .and_then(|allocs| allocs.as_array())
448 .map(|arr| arr.len())
449 .unwrap_or(0)
450 }
451
452 fn normalize_allocations(
454 &self,
455 multi_source: &HashMap<String, Value>,
456 ) -> Result<Vec<AllocationInfo>, NormalizationError> {
457 let memory_data = multi_source.get("memory_analysis");
459 let unsafe_ffi_data = multi_source.get("unsafe_ffi");
460
461 let empty_vec = vec![];
462 let allocations_array = memory_data
463 .and_then(|data| data.get("allocations"))
464 .and_then(|allocs| allocs.as_array())
465 .or_else(|| {
466 unsafe_ffi_data
467 .and_then(|data| data.get("allocations"))
468 .and_then(|allocs| allocs.as_array())
469 })
470 .unwrap_or(&empty_vec);
471
472 let mut normalized_allocations = Vec::new();
473
474 for (index, alloc) in allocations_array.iter().enumerate() {
475 if let Some(_alloc_obj) = alloc.as_object() {
476 let borrow_info = alloc.get("borrow_info").map(|bi| BorrowInfo {
478 immutable_borrows: self.extract_u32(Some(bi), "immutable_borrows").unwrap_or(0),
479 mutable_borrows: self.extract_u32(Some(bi), "mutable_borrows").unwrap_or(0),
480 max_concurrent_borrows: self
481 .extract_u32(Some(bi), "max_concurrent_borrows")
482 .unwrap_or(0),
483 last_borrow_timestamp: self
484 .extract_u64(Some(bi), "last_borrow_timestamp")
485 .unwrap_or(0),
486 });
487
488 let clone_info = alloc.get("clone_info").map(|ci| CloneInfo {
490 clone_count: self.extract_u32(Some(ci), "clone_count").unwrap_or(0),
491 is_clone: self.extract_bool(Some(ci), "is_clone").unwrap_or(false),
492 original_ptr: self.extract_string(Some(ci), "original_ptr"),
493 });
494
495 let safety_violations = alloc
497 .get("safety_violations")
498 .and_then(|sv| sv.as_array())
499 .map(|arr| {
500 arr.iter()
501 .filter_map(|v| v.as_str().map(|s| s.to_string()))
502 .collect()
503 });
504
505 let allocation_info = AllocationInfo {
506 ptr: self
507 .extract_string(Some(alloc), "ptr")
508 .unwrap_or_else(|| format!("0x{index:x}")),
509 size: self.extract_usize(Some(alloc), "size").unwrap_or(0),
510 var_name: self.extract_string(Some(alloc), "var_name"),
511 type_name: self.extract_string(Some(alloc), "type_name"),
512 scope_name: self
513 .extract_string(Some(alloc), "scope_name")
514 .or_else(|| self.extract_string(Some(alloc), "scope")),
515 timestamp_alloc: self
516 .extract_u64(Some(alloc), "timestamp_alloc")
517 .or_else(|| self.extract_u64(Some(alloc), "timestamp"))
518 .unwrap_or(0),
519 timestamp_dealloc: self.extract_u64(Some(alloc), "timestamp_dealloc"),
520 thread_id: self.extract_string(Some(alloc), "thread_id"),
521 borrow_count: self.extract_u32(Some(alloc), "borrow_count"),
522 stack_trace: self.extract_string_array(Some(alloc), "stack_trace"),
523 is_leaked: self.extract_bool(Some(alloc), "is_leaked").unwrap_or(false),
524 lifetime_ms: self.extract_u64(Some(alloc), "lifetime_ms"),
525 borrow_info,
526 clone_info,
527 ownership_history_available: self
528 .extract_bool(Some(alloc), "ownership_history_available"),
529 ffi_tracked: self.extract_bool(Some(alloc), "ffi_tracked"),
530 safety_violations,
531 };
532 normalized_allocations.push(allocation_info);
533 }
534 }
535
536 tracing::info!("📊 Normalized {} allocations", normalized_allocations.len());
537 Ok(normalized_allocations)
538 }
539
540 fn normalize_performance(
542 &self,
543 multi_source: &HashMap<String, Value>,
544 ) -> Result<PerformanceMetrics, NormalizationError> {
545 let performance_data = multi_source.get("performance");
546 let export_perf = performance_data.and_then(|data| data.get("export_performance"));
547 let memory_perf = performance_data.and_then(|data| data.get("memory_performance"));
548 let alloc_dist = performance_data.and_then(|data| data.get("allocation_distribution"));
549 let opt_status = performance_data.and_then(|data| data.get("optimization_status"));
550
551 Ok(PerformanceMetrics {
552 processing_time_ms: self
553 .extract_u64(export_perf, "total_processing_time_ms")
554 .unwrap_or(0),
555 allocations_per_second: export_perf
556 .and_then(|data| data.get("processing_rate"))
557 .and_then(|rate| self.extract_f64(Some(rate), "allocations_per_second"))
558 .unwrap_or(0.0),
559 memory_efficiency: self
560 .extract_f64(memory_perf, "memory_efficiency")
561 .unwrap_or(0.0),
562 optimization_status: OptimizationStatus {
563 parallel_processing: self
564 .extract_bool(opt_status, "parallel_processing")
565 .unwrap_or(false),
566 schema_validation: self
567 .extract_bool(opt_status, "schema_validation")
568 .unwrap_or(false),
569 streaming_enabled: self
570 .extract_bool(opt_status, "streaming_enabled")
571 .unwrap_or(false),
572 batch_size: self.extract_usize(opt_status, "batch_size"),
573 buffer_size_kb: self.extract_usize(opt_status, "buffer_size_kb"),
574 },
575 allocation_distribution: AllocationDistribution {
576 tiny: self.extract_usize(alloc_dist, "tiny").unwrap_or(0),
577 small: self.extract_usize(alloc_dist, "small").unwrap_or(0),
578 medium: self.extract_usize(alloc_dist, "medium").unwrap_or(0),
579 large: self.extract_usize(alloc_dist, "large").unwrap_or(0),
580 massive: self.extract_usize(alloc_dist, "massive").unwrap_or(0),
581 },
582 })
583 }
584
585 fn normalize_lifecycle(
587 &self,
588 multi_source: &HashMap<String, Value>,
589 ) -> Result<LifecycleAnalysis, NormalizationError> {
590 let empty_object = Value::Object(serde_json::Map::new());
591 let lifecycle_data = multi_source.get("lifetime").unwrap_or(&empty_object);
592
593 Ok(LifecycleAnalysis {
594 lifecycle_events: lifecycle_data
595 .get("lifecycle_events")
596 .and_then(|events| events.as_array())
597 .cloned()
598 .unwrap_or_default(),
599 scope_analysis: lifecycle_data
600 .get("scope_analysis")
601 .and_then(|scope| scope.as_object())
602 .map(|obj| obj.iter().map(|(k, v)| (k.clone(), v.clone())).collect())
603 .unwrap_or_default(),
604 variable_lifetimes: lifecycle_data
605 .get("variable_lifetimes")
606 .and_then(|lifetimes| lifetimes.as_object())
607 .map(|obj| obj.iter().map(|(k, v)| (k.clone(), v.clone())).collect())
608 .unwrap_or_default(),
609 })
610 }
611
612 fn normalize_security(
614 &self,
615 multi_source: &HashMap<String, Value>,
616 ) -> Result<SecurityAnalysis, NormalizationError> {
617 let security_data = multi_source.get("security_violations");
618 let security_summary = security_data
619 .and_then(|data| data.get("security_summary"))
620 .and_then(|summary| summary.get("security_analysis_summary"));
621 let severity = security_summary.and_then(|summary| summary.get("severity_breakdown"));
622
623 Ok(SecurityAnalysis {
624 total_violations: self
625 .extract_usize(security_summary, "total_violations")
626 .unwrap_or(0),
627 risk_level: security_summary
628 .and_then(|summary| summary.get("risk_assessment"))
629 .and_then(|risk| self.extract_string(Some(risk), "risk_level"))
630 .unwrap_or_else(|| "Unknown".to_string()),
631 severity_breakdown: SeverityBreakdown {
632 critical: self.extract_usize(severity, "critical").unwrap_or(0),
633 high: self.extract_usize(severity, "high").unwrap_or(0),
634 medium: self.extract_usize(severity, "medium").unwrap_or(0),
635 low: self.extract_usize(severity, "low").unwrap_or(0),
636 info: self.extract_usize(severity, "info").unwrap_or(0),
637 },
638 violation_reports: security_data
639 .and_then(|data| data.get("violation_reports"))
640 .and_then(|reports| reports.as_array())
641 .cloned()
642 .unwrap_or_default(),
643 recommendations: security_data
644 .and_then(|data| data.get("analysis_recommendations"))
645 .and_then(|recs| recs.as_array())
646 .map(|arr| {
647 arr.iter()
648 .filter_map(|v| v.as_str().map(|s| s.to_string()))
649 .collect()
650 })
651 .unwrap_or_default(),
652 })
653 }
654
655 fn normalize_complex_types(
657 &self,
658 multi_source: &HashMap<String, Value>,
659 ) -> Result<ComplexTypeAnalysis, NormalizationError> {
660 let empty_object = Value::Object(serde_json::Map::new());
661 let complex_data = multi_source.get("complex_types").unwrap_or(&empty_object);
662 let categorized = complex_data.get("categorized_types");
663 let summary = complex_data.get("summary");
664 let complexity_dist = summary.and_then(|s| s.get("complexity_distribution"));
665
666 Ok(ComplexTypeAnalysis {
667 categorized_types: CategorizedTypes {
668 collections: categorized
669 .and_then(|cat| cat.get("collections"))
670 .and_then(|coll| coll.as_array())
671 .cloned()
672 .unwrap_or_default(),
673 generic_types: categorized
674 .and_then(|cat| cat.get("generic_types"))
675 .and_then(|gen| gen.as_array())
676 .cloned()
677 .unwrap_or_default(),
678 smart_pointers: categorized
679 .and_then(|cat| cat.get("smart_pointers"))
680 .and_then(|smart| smart.as_array())
681 .cloned()
682 .unwrap_or_default(),
683 trait_objects: categorized
684 .and_then(|cat| cat.get("trait_objects"))
685 .and_then(|traits| traits.as_array())
686 .cloned()
687 .unwrap_or_default(),
688 },
689 complex_type_analysis: complex_data
690 .get("complex_type_analysis")
691 .and_then(|analysis| analysis.as_array())
692 .cloned()
693 .unwrap_or_default(),
694 summary: ComplexTypeSummary {
695 total_complex_types: self
696 .extract_usize(summary, "total_complex_types")
697 .unwrap_or(0),
698 complexity_distribution: ComplexityDistribution {
699 low_complexity: self
700 .extract_usize(complexity_dist, "low_complexity")
701 .unwrap_or(0),
702 medium_complexity: self
703 .extract_usize(complexity_dist, "medium_complexity")
704 .unwrap_or(0),
705 high_complexity: self
706 .extract_usize(complexity_dist, "high_complexity")
707 .unwrap_or(0),
708 very_high_complexity: self
709 .extract_usize(complexity_dist, "very_high_complexity")
710 .unwrap_or(0),
711 },
712 },
713 })
714 }
715
716 fn normalize_variable_relationships(
718 &self,
719 multi_source: &HashMap<String, Value>,
720 ) -> Result<VariableRelationships, NormalizationError> {
721 let empty_object = Value::Object(serde_json::Map::new());
722 let var_data = multi_source
723 .get("variable_relationships")
724 .unwrap_or(&empty_object);
725
726 Ok(VariableRelationships {
727 relationships: var_data
728 .get("variable_relationships")
729 .and_then(|rels| rels.as_array())
730 .cloned()
731 .unwrap_or_default(),
732 registry: var_data
733 .get("variable_registry")
734 .and_then(|reg| reg.as_object())
735 .map(|obj| obj.iter().map(|(k, v)| (k.clone(), v.clone())).collect())
736 .unwrap_or_default(),
737 dependency_graph: var_data
738 .get("dependency_graph")
739 .and_then(|graph| graph.as_object())
740 .map(|obj| obj.iter().map(|(k, v)| (k.clone(), v.clone())).collect())
741 .unwrap_or_default(),
742 scope_hierarchy: var_data
743 .get("scope_hierarchy")
744 .and_then(|hierarchy| hierarchy.as_object())
745 .map(|obj| obj.iter().map(|(k, v)| (k.clone(), v.clone())).collect())
746 .unwrap_or_default(),
747 })
748 }
749
750 fn normalize_metadata(
752 &self,
753 multi_source: &HashMap<String, Value>,
754 ) -> Result<AnalysisMetadata, NormalizationError> {
755 let memory_data = multi_source.get("memory_analysis");
756 let metadata = memory_data.and_then(|data| data.get("metadata"));
757
758 Ok(AnalysisMetadata {
759 timestamp: self.extract_u64(metadata, "timestamp").unwrap_or_else(|| {
760 std::time::SystemTime::now()
761 .duration_since(std::time::UNIX_EPOCH)
762 .unwrap_or_default()
763 .as_secs()
764 }),
765 export_version: self
766 .extract_string(metadata, "export_version")
767 .unwrap_or_else(|| "2.0".to_string()),
768 analysis_type: self
769 .extract_string(metadata, "analysis_type")
770 .unwrap_or_else(|| "integrated_analysis".to_string()),
771 data_integrity_hash: self.extract_string(metadata, "data_integrity_hash"),
772 })
773 }
774
775 fn validate_unified_data(&self, data: &UnifiedMemoryData) -> Result<(), NormalizationError> {
777 if data.stats.active_memory > data.stats.peak_memory && data.stats.peak_memory > 0 {
779 return Err(NormalizationError::ValidationError(
780 "Active memory cannot exceed peak memory".to_string(),
781 ));
782 }
783
784 if data.stats.active_allocations > data.stats.total_allocations
785 && data.stats.total_allocations > 0
786 {
787 return Err(NormalizationError::ValidationError(
788 "Active allocations cannot exceed total allocations".to_string(),
789 ));
790 }
791
792 let actual_active_count = data
794 .allocations
795 .iter()
796 .filter(|alloc| alloc.timestamp_dealloc.is_none())
797 .count();
798
799 if actual_active_count != data.stats.active_allocations && data.stats.active_allocations > 0
800 {
801 tracing::info!(
802 "⚠️ Warning: Active allocation count mismatch (stats: {}, actual: {})",
803 data.stats.active_allocations,
804 actual_active_count
805 );
806 }
807
808 tracing::info!("✅ Data validation passed");
809 Ok(())
810 }
811
812 fn extract_usize(&self, data: Option<&Value>, field: &str) -> Option<usize> {
815 data?.get(field)?.as_u64().map(|v| v as usize)
816 }
817
818 fn extract_u64(&self, data: Option<&Value>, field: &str) -> Option<u64> {
819 data?.get(field)?.as_u64()
820 }
821
822 fn extract_f64(&self, data: Option<&Value>, field: &str) -> Option<f64> {
823 data?.get(field)?.as_f64()
824 }
825
826 fn extract_bool(&self, data: Option<&Value>, field: &str) -> Option<bool> {
827 data?.get(field)?.as_bool()
828 }
829
830 fn extract_string(&self, data: Option<&Value>, field: &str) -> Option<String> {
831 data?.get(field)?.as_str().map(|s| s.to_string())
832 }
833
834 fn extract_string_array(&self, data: Option<&Value>, field: &str) -> Option<Vec<String>> {
835 data?
836 .get(field)?
837 .as_array()?
838 .iter()
839 .map(|v| v.as_str().map(|s| s.to_string()))
840 .collect()
841 }
842
843 fn extract_u32(&self, data: Option<&Value>, field: &str) -> Option<u32> {
844 data?.get(field)?.as_u64().map(|v| v as u32)
845 }
846}
847
848impl Default for DataNormalizer {
849 fn default() -> Self {
850 Self::new()
851 }
852}
853
854#[cfg(test)]
855mod tests {
856 use super::*;
857 use serde_json::json;
858 use std::collections::HashMap;
859
860 fn create_test_allocations() -> HashMap<String, serde_json::Value> {
861 let mut data = HashMap::new();
862 data.insert(
863 "memory_analysis".to_string(),
864 json!({
865 "allocations": [
866 {
867 "ptr": "0x1000",
868 "size": 1024,
869 "var_name": "test_var",
870 "type_name": "TestType",
871 "timestamp_alloc": 1000,
872 "timestamp_dealloc": 0,
873 "scope_name": "test_scope"
874 },
875 {
876 "ptr": "0x2000",
877 "size": 2048,
878 "var_name": "test_var2",
879 "type_name": "TestType2",
880 "timestamp_alloc": 2000,
881 "timestamp_dealloc": 3000,
882 "scope_name": "test_scope2"
883 }
884 ]
885 }),
886 );
887 data
888 }
889
890 fn create_test_data() -> HashMap<String, serde_json::Value> {
891 let mut data = create_test_allocations();
892
893 if let Some(memory_analysis) = data
895 .get_mut("memory_analysis")
896 .and_then(|v| v.as_object_mut())
897 {
898 memory_analysis.insert(
899 "memory_stats".to_string(),
900 json!({
901 "active_memory": 0, "active_allocations": 0, "peak_memory": 3072,
904 "total_allocations": 2,
905 "total_allocated": 3072,
906 "memory_efficiency": 0.95
907 }),
908 );
909 }
910
911 data.insert(
913 "performance_metrics".to_string(),
914 json!({
915 "average_allocation_size": 256.5,
916 "allocation_rate": 10.5,
917 "deallocation_rate": 9.5,
918 "average_lifetime": 1000.0,
919 "optimization_status": {
920 "enabled": true,
921 "optimized_allocations": 5,
922 "total_allocations": 10
923 },
924 "allocation_distribution": {
925 "small_allocations": 5,
926 "medium_allocations": 3,
927 "large_allocations": 2
928 }
929 }),
930 );
931
932 data
933 }
934
935 #[test]
936 fn test_data_normalizer_creation() {
937 let normalizer = DataNormalizer::default();
939 assert!(normalizer.validation_enabled);
940
941 let normalizer = DataNormalizer::without_validation();
943 assert!(!normalizer.validation_enabled);
944 }
945
946 #[test]
947 fn test_normalize_empty_data() {
948 let normalizer = DataNormalizer::without_validation();
949 let empty_data = HashMap::new();
950
951 let result = normalizer.normalize(&empty_data);
952 assert!(result.is_ok());
953
954 let unified = result.unwrap();
955 assert_eq!(unified.allocations.len(), 0);
956 }
957
958 #[test]
959 fn test_normalize_with_allocations() {
960 let normalizer = DataNormalizer::without_validation();
961 let test_data = create_test_data();
962
963 let result = normalizer.normalize(&test_data);
964 assert!(result.is_ok());
965
966 let unified = result.unwrap();
967 assert_eq!(unified.allocations.len(), 2);
969
970 let _expected_active_allocations = 1;
972 let _expected_active_memory = 1024; println!("Active allocations: {}", unified.stats.active_allocations);
977 println!("Active memory: {}", unified.stats.active_memory);
978
979 let alloc1 = &unified.allocations[0];
983 assert_eq!(alloc1.ptr, "0x1000");
984 assert_eq!(alloc1.size, 1024);
985 assert_eq!(alloc1.var_name.as_deref(), Some("test_var"));
986 assert_eq!(alloc1.type_name.as_deref(), Some("TestType"));
987 assert_eq!(alloc1.timestamp_alloc, 1000);
988 assert_eq!(alloc1.timestamp_dealloc, Some(0));
990
991 let alloc2 = &unified.allocations[1];
993 assert_eq!(alloc2.ptr, "0x2000");
994 assert_eq!(alloc2.timestamp_alloc, 2000);
995 assert_eq!(alloc2.timestamp_dealloc, Some(3000));
996 }
997
998 #[test]
999 fn test_extraction_helpers() {
1000 let normalizer = DataNormalizer::default();
1001 let test_data = json!({
1002 "int_field": 42,
1003 "float_field": std::f64::consts::PI,
1004 "bool_field": true,
1005 "string_field": "test",
1006 "array_field": ["a", "b", "c"],
1007 "nested": {"field": "value"}
1008 });
1009
1010 assert_eq!(
1012 normalizer.extract_usize(Some(&test_data), "int_field"),
1013 Some(42)
1014 );
1015 assert_eq!(
1016 normalizer.extract_f64(Some(&test_data), "float_field"),
1017 Some(std::f64::consts::PI)
1018 );
1019 assert_eq!(
1020 normalizer.extract_bool(Some(&test_data), "bool_field"),
1021 Some(true)
1022 );
1023 assert_eq!(
1024 normalizer.extract_string(Some(&test_data), "string_field"),
1025 Some("test".to_string())
1026 );
1027 assert_eq!(
1028 normalizer.extract_string_array(Some(&test_data), "array_field"),
1029 Some(vec!["a".to_string(), "b".to_string(), "c".to_string()])
1030 );
1031
1032 assert_eq!(normalizer.extract_usize(Some(&test_data), "missing"), None);
1034 assert_eq!(normalizer.extract_string(Some(&test_data), "missing"), None);
1035 }
1036
1037 #[test]
1038 fn test_validation() {
1039 let normalizer = DataNormalizer::default();
1041 let test_data = HashMap::new();
1042
1043 let result = normalizer.normalize(&test_data);
1044 assert!(result.is_ok(), "Should pass with empty data");
1045
1046 let test_data = create_test_data();
1048 let result = normalizer.normalize(&test_data);
1049 assert!(result.is_ok(), "Should pass with test data");
1050 }
1051}