1use crate::core::types::AllocationInfo;
8use crate::export::binary::error::BinaryExportError;
9use crate::export::binary::selective_reader::AllocationField;
10use crate::export::binary::serializable::primitives;
11use std::collections::{HashMap, HashSet};
12use std::io::{Read, Seek, SeekFrom};
13
14pub struct FieldParser {
16 field_cache: HashMap<String, FieldValue>,
18
19 stats: FieldParserStats,
21
22 #[allow(dead_code)]
24 config: FieldParserConfig,
25}
26
27#[derive(Debug, Clone)]
29pub struct FieldParserConfig {
30 pub enable_caching: bool,
32
33 pub max_cache_size: usize,
35
36 pub validate_field_existence: bool,
38
39 pub enable_optimized_combinations: bool,
41}
42
43impl Default for FieldParserConfig {
44 fn default() -> Self {
45 Self {
46 enable_caching: true,
47 max_cache_size: 1000,
48 validate_field_existence: true,
49 enable_optimized_combinations: true,
50 }
51 }
52}
53
54#[derive(Debug, Clone, Default)]
56pub struct FieldParserStats {
57 pub total_fields_parsed: u64,
59
60 pub fields_skipped: u64,
62
63 pub cache_hits: u64,
65
66 pub cache_misses: u64,
68
69 pub total_parse_time_us: u64,
71
72 pub time_saved_us: u64,
74}
75
76impl FieldParserStats {
77 pub fn cache_hit_rate(&self) -> f64 {
79 let total_requests = self.cache_hits + self.cache_misses;
80 if total_requests == 0 {
81 0.0
82 } else {
83 (self.cache_hits as f64 / total_requests as f64) * 100.0
84 }
85 }
86
87 pub fn parsing_efficiency(&self) -> f64 {
89 let total_fields = self.total_fields_parsed + self.fields_skipped;
90 if total_fields == 0 {
91 0.0
92 } else {
93 (self.fields_skipped as f64 / total_fields as f64) * 100.0
94 }
95 }
96
97 pub fn avg_parse_time_per_field_us(&self) -> f64 {
99 if self.total_fields_parsed == 0 {
100 0.0
101 } else {
102 self.total_parse_time_us as f64 / self.total_fields_parsed as f64
103 }
104 }
105}
106
107#[derive(Debug, Clone)]
109pub struct FieldValue {
110 #[allow(dead_code)]
112 pub value: FieldData,
113
114 #[allow(dead_code)]
116 pub cached_at: std::time::Instant,
117
118 #[allow(dead_code)]
120 pub access_count: u32,
121}
122
123#[derive(Debug, Clone)]
125#[allow(dead_code)]
126pub enum FieldData {
127 Usize(usize),
128 U64(u64),
129 String(String),
130 OptionalString(Option<String>),
131 Bool(bool),
132 StringVec(Vec<String>),
133}
134
135impl FieldParser {
136 pub fn new() -> Self {
138 Self::with_config(FieldParserConfig::default())
139 }
140
141 pub fn with_config(config: FieldParserConfig) -> Self {
143 Self {
144 field_cache: HashMap::new(),
145 stats: FieldParserStats::default(),
146 config,
147 }
148 }
149
150 pub fn parse_selective_fields<R: Read + Seek>(
152 &mut self,
153 reader: &mut R,
154 requested_fields: &HashSet<AllocationField>,
155 ) -> Result<PartialAllocationInfo, BinaryExportError> {
156 let start_time = std::time::Instant::now();
157
158 let mut type_byte = [0u8; 1];
160 reader.read_exact(&mut type_byte)?;
161
162 let mut length_bytes = [0u8; 4];
163 reader.read_exact(&mut length_bytes)?;
164 let record_length = u32::from_le_bytes(length_bytes);
165
166 let record_start_pos = reader.stream_position()?;
167
168 let mut partial_info = PartialAllocationInfo::new();
170
171 self.parse_basic_fields(reader, requested_fields, &mut partial_info)?;
173 self.parse_optional_fields(reader, requested_fields, &mut partial_info)?;
174 self.parse_advanced_fields(
175 reader,
176 requested_fields,
177 &mut partial_info,
178 record_start_pos,
179 record_length,
180 )?;
181
182 self.stats.total_parse_time_us += start_time.elapsed().as_micros() as u64;
183
184 Ok(partial_info)
185 }
186
187 pub fn parse_full_allocation<R: Read + Seek>(
189 &mut self,
190 reader: &mut R,
191 ) -> Result<AllocationInfo, BinaryExportError> {
192 let all_fields = AllocationField::all_fields();
193 let partial = self.parse_selective_fields(reader, &all_fields)?;
194 Ok(partial.to_full_allocation())
195 }
196
197 pub fn get_stats(&self) -> &FieldParserStats {
199 &self.stats
200 }
201
202 pub fn reset_stats(&mut self) {
204 self.stats = FieldParserStats::default();
205 }
206
207 pub fn clear_cache(&mut self) {
209 self.field_cache.clear();
210 }
211
212 pub fn cache_size(&self) -> usize {
214 self.field_cache.len()
215 }
216
217 fn parse_basic_fields<R: Read>(
221 &mut self,
222 reader: &mut R,
223 requested_fields: &HashSet<AllocationField>,
224 partial_info: &mut PartialAllocationInfo,
225 ) -> Result<(), BinaryExportError> {
226 let ptr = primitives::read_u64(reader)? as usize;
228 if requested_fields.contains(&AllocationField::Ptr) {
229 partial_info.ptr = Some(ptr);
230 self.stats.total_fields_parsed += 1;
231 } else {
232 self.stats.fields_skipped += 1;
233 }
234
235 let size = primitives::read_u64(reader)? as usize;
237 if requested_fields.contains(&AllocationField::Size) {
238 partial_info.size = Some(size);
239 self.stats.total_fields_parsed += 1;
240 } else {
241 self.stats.fields_skipped += 1;
242 }
243
244 let timestamp_alloc = primitives::read_u64(reader)?;
246 if requested_fields.contains(&AllocationField::TimestampAlloc) {
247 partial_info.timestamp_alloc = Some(timestamp_alloc);
248 self.stats.total_fields_parsed += 1;
249 } else {
250 self.stats.fields_skipped += 1;
251 }
252
253 Ok(())
254 }
255
256 fn parse_optional_fields<R: Read>(
258 &mut self,
259 reader: &mut R,
260 requested_fields: &HashSet<AllocationField>,
261 partial_info: &mut PartialAllocationInfo,
262 ) -> Result<(), BinaryExportError> {
263 let has_dealloc = primitives::read_u8(reader)? != 0;
265 if has_dealloc {
266 let timestamp_dealloc = primitives::read_u64(reader)?;
267 if requested_fields.contains(&AllocationField::TimestampDealloc) {
268 partial_info.timestamp_dealloc = Some(Some(timestamp_dealloc));
269 self.stats.total_fields_parsed += 1;
270 } else {
271 self.stats.fields_skipped += 1;
272 }
273 } else if requested_fields.contains(&AllocationField::TimestampDealloc) {
274 partial_info.timestamp_dealloc = Some(None);
275 self.stats.total_fields_parsed += 1;
276 } else {
277 self.stats.fields_skipped += 1;
278 }
279
280 let var_name = self.parse_optional_string(reader)?;
282 if requested_fields.contains(&AllocationField::VarName) {
283 partial_info.var_name = Some(var_name);
284 self.stats.total_fields_parsed += 1;
285 } else {
286 self.stats.fields_skipped += 1;
287 }
288
289 let type_name = self.parse_optional_string(reader)?;
291 if requested_fields.contains(&AllocationField::TypeName) {
292 partial_info.type_name = Some(type_name);
293 self.stats.total_fields_parsed += 1;
294 } else {
295 self.stats.fields_skipped += 1;
296 }
297
298 let scope_name = self.parse_optional_string(reader)?;
300 if requested_fields.contains(&AllocationField::ScopeName) {
301 partial_info.scope_name = Some(scope_name);
302 self.stats.total_fields_parsed += 1;
303 } else {
304 self.stats.fields_skipped += 1;
305 }
306
307 let thread_id = primitives::read_string(reader)?;
309 if requested_fields.contains(&AllocationField::ThreadId) {
310 partial_info.thread_id = Some(thread_id);
311 self.stats.total_fields_parsed += 1;
312 } else {
313 self.stats.fields_skipped += 1;
314 }
315
316 let stack_trace = self.parse_optional_string_vec(reader)?;
318 if requested_fields.contains(&AllocationField::StackTrace) {
319 partial_info.stack_trace = Some(stack_trace);
320 self.stats.total_fields_parsed += 1;
321 } else {
322 self.stats.fields_skipped += 1;
323 }
324
325 let borrow_count = primitives::read_u32(reader)? as usize;
327 if requested_fields.contains(&AllocationField::BorrowCount) {
328 partial_info.borrow_count = Some(borrow_count);
329 self.stats.total_fields_parsed += 1;
330 } else {
331 self.stats.fields_skipped += 1;
332 }
333
334 let is_leaked = primitives::read_u8(reader)? != 0;
336 if requested_fields.contains(&AllocationField::IsLeaked) {
337 partial_info.is_leaked = Some(is_leaked);
338 self.stats.total_fields_parsed += 1;
339 } else {
340 self.stats.fields_skipped += 1;
341 }
342
343 Ok(())
344 }
345
346 fn parse_advanced_fields<R: Read + Seek>(
348 &mut self,
349 reader: &mut R,
350 _requested_fields: &HashSet<AllocationField>,
351 _partial_info: &mut PartialAllocationInfo,
352 record_start_pos: u64,
353 record_length: u32,
354 ) -> Result<(), BinaryExportError> {
355 let current_pos = reader.stream_position()?;
357 let bytes_read = current_pos - record_start_pos;
358 let remaining_bytes = record_length as u64 - bytes_read;
359
360 if remaining_bytes == 0 {
361 return Ok(()); }
363
364 if remaining_bytes > 0 {
367 reader.seek(SeekFrom::Current(remaining_bytes as i64))?;
370 self.stats.fields_skipped += remaining_bytes / 4; return Ok(());
372 }
373
374 Ok(())
375 }
376
377 fn parse_optional_string<R: Read>(
379 &mut self,
380 reader: &mut R,
381 ) -> Result<Option<String>, BinaryExportError> {
382 let length = primitives::read_u32(reader)?;
383 if length > 0 {
384 let mut buffer = vec![0u8; length as usize];
385 reader.read_exact(&mut buffer)?;
386 Ok(Some(String::from_utf8(buffer).map_err(|e| {
387 BinaryExportError::SerializationError(format!("Invalid UTF-8: {e}"))
388 })?))
389 } else {
390 Ok(None)
391 }
392 }
393
394 fn parse_optional_string_vec<R: Read>(
396 &mut self,
397 reader: &mut R,
398 ) -> Result<Option<Vec<String>>, BinaryExportError> {
399 let count = primitives::read_u32(reader)? as usize;
400 if count > 0 {
401 let mut vec = Vec::with_capacity(count);
402 for _ in 0..count {
403 vec.push(primitives::read_string(reader)?);
404 }
405 Ok(Some(vec))
406 } else {
407 Ok(None)
408 }
409 }
410
411 #[allow(dead_code)]
413 fn field_exists(&self, _field: &AllocationField) -> bool {
414 true
417 }
418
419 #[allow(dead_code)]
421 fn get_cached_field(&mut self, cache_key: &str) -> Option<&FieldValue> {
422 if !self.config.enable_caching {
423 return None;
424 }
425
426 if let Some(cached) = self.field_cache.get_mut(cache_key) {
427 cached.access_count += 1;
428 self.stats.cache_hits += 1;
429 Some(cached)
430 } else {
431 self.stats.cache_misses += 1;
432 None
433 }
434 }
435
436 #[allow(dead_code)]
438 fn cache_field_value(&mut self, cache_key: String, value: FieldData) {
439 if !self.config.enable_caching {
440 return;
441 }
442
443 if self.field_cache.len() >= self.config.max_cache_size {
445 self.evict_lru_cache_entry();
446 }
447
448 let field_value = FieldValue {
449 value,
450 cached_at: std::time::Instant::now(),
451 access_count: 1,
452 };
453
454 self.field_cache.insert(cache_key, field_value);
455 }
456
457 #[allow(dead_code)]
459 fn evict_lru_cache_entry(&mut self) {
460 if let Some((lru_key, _)) = self
461 .field_cache
462 .iter()
463 .min_by_key(|(_, v)| (v.access_count, v.cached_at))
464 .map(|(k, v)| (k.clone(), v.clone()))
465 {
466 self.field_cache.remove(&lru_key);
467 }
468 }
469}
470
471impl Default for FieldParser {
472 fn default() -> Self {
473 Self::new()
474 }
475}
476
477#[derive(Debug, Clone, Default)]
479pub struct PartialAllocationInfo {
480 pub ptr: Option<usize>,
481 pub size: Option<usize>,
482 pub var_name: Option<Option<String>>,
483 pub type_name: Option<Option<String>>,
484 pub scope_name: Option<Option<String>>,
485 pub timestamp_alloc: Option<u64>,
486 pub timestamp_dealloc: Option<Option<u64>>,
487 pub thread_id: Option<String>,
488 pub borrow_count: Option<usize>,
489 pub stack_trace: Option<Option<Vec<String>>>,
490 pub is_leaked: Option<bool>,
491 pub lifetime_ms: Option<Option<u64>>,
492 pub borrow_info: Option<crate::core::types::BorrowInfo>,
494 pub clone_info: Option<crate::core::types::CloneInfo>,
495 pub ownership_history_available: Option<bool>,
496 }
498
499impl PartialAllocationInfo {
500 pub fn new() -> Self {
502 Self::default()
503 }
504
505 pub fn to_full_allocation(self) -> AllocationInfo {
507 AllocationInfo {
508 ptr: self.ptr.unwrap_or(0),
509 size: self.size.unwrap_or(0),
510 var_name: self.var_name.unwrap_or(None),
511 type_name: self.type_name.unwrap_or(None),
512 scope_name: self.scope_name.unwrap_or(None),
513 timestamp_alloc: self.timestamp_alloc.unwrap_or(0),
514 timestamp_dealloc: self.timestamp_dealloc.unwrap_or(None),
515 thread_id: self.thread_id.unwrap_or_default(),
516 borrow_count: self.borrow_count.unwrap_or(0),
517 stack_trace: self.stack_trace.unwrap_or(None),
518 is_leaked: self.is_leaked.unwrap_or(false),
519 lifetime_ms: self.lifetime_ms.unwrap_or(None),
520 borrow_info: self.borrow_info,
521 clone_info: self.clone_info,
522 ownership_history_available: self.ownership_history_available.unwrap_or(false),
523 smart_pointer_info: None,
524 memory_layout: None,
525 generic_info: None,
526 dynamic_type_info: None,
527 runtime_state: None,
528 stack_allocation: None,
529 temporary_object: None,
530 fragmentation_analysis: None,
531 generic_instantiation: None,
532 type_relationships: None,
533 type_usage: None,
534 function_call_tracking: None,
535 lifecycle_tracking: None,
536 access_tracking: None,
537 drop_chain_analysis: None,
538 }
539 }
540
541 pub fn has_field(&self, field: &AllocationField) -> bool {
543 match field {
544 AllocationField::Ptr => self.ptr.is_some(),
545 AllocationField::Size => self.size.is_some(),
546 AllocationField::VarName => self.var_name.is_some(),
547 AllocationField::TypeName => self.type_name.is_some(),
548 AllocationField::ScopeName => self.scope_name.is_some(),
549 AllocationField::TimestampAlloc => self.timestamp_alloc.is_some(),
550 AllocationField::TimestampDealloc => self.timestamp_dealloc.is_some(),
551 AllocationField::ThreadId => self.thread_id.is_some(),
552 AllocationField::BorrowCount => self.borrow_count.is_some(),
553 AllocationField::StackTrace => self.stack_trace.is_some(),
554 AllocationField::IsLeaked => self.is_leaked.is_some(),
555 AllocationField::LifetimeMs => self.lifetime_ms.is_some(),
556 _ => false, }
558 }
559
560 pub fn field_count(&self) -> usize {
562 let mut count = 0;
563 if self.ptr.is_some() {
564 count += 1;
565 }
566 if self.size.is_some() {
567 count += 1;
568 }
569 if self.var_name.is_some() {
570 count += 1;
571 }
572 if self.type_name.is_some() {
573 count += 1;
574 }
575 if self.scope_name.is_some() {
576 count += 1;
577 }
578 if self.timestamp_alloc.is_some() {
579 count += 1;
580 }
581 if self.timestamp_dealloc.is_some() {
582 count += 1;
583 }
584 if self.thread_id.is_some() {
585 count += 1;
586 }
587 if self.borrow_count.is_some() {
588 count += 1;
589 }
590 if self.stack_trace.is_some() {
591 count += 1;
592 }
593 if self.is_leaked.is_some() {
594 count += 1;
595 }
596 if self.lifetime_ms.is_some() {
597 count += 1;
598 }
599 count
600 }
601}
602
603#[cfg(test)]
604mod tests {
605 use super::*;
606 #[test]
611 fn test_field_parser_creation() {
612 let parser = FieldParser::new();
613 assert_eq!(parser.cache_size(), 0);
614 assert_eq!(parser.get_stats().total_fields_parsed, 0);
615 }
616
617 #[test]
618 fn test_selective_field_parsing() {
619 let mut parser = FieldParser::new();
620
621 let mut partial_info = PartialAllocationInfo::new();
624
625 let requested_fields: HashSet<AllocationField> = [
627 AllocationField::Ptr,
628 AllocationField::Size,
629 AllocationField::ThreadId,
630 ]
631 .into_iter()
632 .collect();
633
634 if requested_fields.contains(&AllocationField::Ptr) {
636 partial_info.ptr = Some(0x1000);
637 parser.stats.total_fields_parsed += 1;
638 } else {
639 parser.stats.fields_skipped += 1;
640 }
641
642 if requested_fields.contains(&AllocationField::Size) {
643 partial_info.size = Some(1024);
644 parser.stats.total_fields_parsed += 1;
645 } else {
646 parser.stats.fields_skipped += 1;
647 }
648
649 if requested_fields.contains(&AllocationField::ThreadId) {
650 partial_info.thread_id = Some("main".to_string());
651 parser.stats.total_fields_parsed += 1;
652 } else {
653 parser.stats.fields_skipped += 1;
654 }
655
656 if !requested_fields.contains(&AllocationField::VarName) {
658 parser.stats.fields_skipped += 1;
659 }
660
661 assert!(partial_info.has_field(&AllocationField::Ptr));
663 assert!(partial_info.has_field(&AllocationField::Size));
664 assert!(partial_info.has_field(&AllocationField::ThreadId));
665 assert!(!partial_info.has_field(&AllocationField::VarName));
666
667 assert_eq!(partial_info.ptr, Some(0x1000));
668 assert_eq!(partial_info.size, Some(1024));
669 assert_eq!(partial_info.thread_id, Some("main".to_string()));
670
671 let stats = parser.get_stats();
673 assert!(stats.fields_skipped > 0);
674 assert!(stats.total_fields_parsed > 0);
675 }
676
677 #[test]
678 fn test_full_allocation_parsing() {
679 let mut parser = FieldParser::new();
680
681 let mut partial_info = PartialAllocationInfo::new();
684
685 partial_info.ptr = Some(0x1000);
687 partial_info.size = Some(1024);
688 partial_info.timestamp_alloc = Some(1234567890);
689 partial_info.var_name = Some(Some("test_var".to_string()));
690 partial_info.type_name = Some(Some("Vec<u8>".to_string()));
691 partial_info.thread_id = Some("main".to_string());
692 partial_info.borrow_count = Some(2);
693 partial_info.is_leaked = Some(false);
694 partial_info.timestamp_dealloc = Some(None);
695 partial_info.scope_name = Some(None);
696 partial_info.stack_trace = Some(None);
697 partial_info.lifetime_ms = Some(None);
698
699 let allocation = partial_info.to_full_allocation();
701
702 assert_eq!(allocation.ptr, 0x1000);
703 assert_eq!(allocation.size, 1024);
704 assert_eq!(allocation.timestamp_alloc, 1234567890);
705 assert_eq!(allocation.var_name, Some("test_var".to_string()));
706 assert_eq!(allocation.type_name, Some("Vec<u8>".to_string()));
707 assert_eq!(allocation.thread_id, "main");
708 assert_eq!(allocation.borrow_count, 2);
709 assert!(!allocation.is_leaked);
710
711 parser.stats.total_fields_parsed = 12; }
714
715 #[test]
716 fn test_partial_allocation_info() {
717 let mut partial = PartialAllocationInfo::new();
718 assert_eq!(partial.field_count(), 0);
719
720 partial.ptr = Some(0x1000);
721 partial.size = Some(1024);
722 partial.thread_id = Some("main".to_string());
723
724 assert_eq!(partial.field_count(), 3);
725 assert!(partial.has_field(&AllocationField::Ptr));
726 assert!(partial.has_field(&AllocationField::Size));
727 assert!(partial.has_field(&AllocationField::ThreadId));
728 assert!(!partial.has_field(&AllocationField::VarName));
729
730 let full = partial.to_full_allocation();
731 assert_eq!(full.ptr, 0x1000);
732 assert_eq!(full.size, 1024);
733 assert_eq!(full.thread_id, "main");
734 assert_eq!(full.var_name, None);
735 }
736
737 #[test]
738 fn test_field_parser_stats() {
739 let mut parser = FieldParser::new();
740
741 parser.stats.total_fields_parsed = 2; parser.stats.fields_skipped = 8; parser.stats.total_parse_time_us = 100; let stats = parser.get_stats();
748 assert_eq!(stats.total_fields_parsed, 2);
749 assert_eq!(stats.fields_skipped, 8);
750 assert!(stats.parsing_efficiency() > 0.0);
751 assert_eq!(stats.parsing_efficiency(), 80.0); assert_eq!(stats.total_parse_time_us, 100);
753 assert_eq!(stats.avg_parse_time_per_field_us(), 50.0); }
755
756 #[test]
757 fn test_field_parser_config() {
758 let config = FieldParserConfig {
759 enable_caching: false,
760 max_cache_size: 500,
761 validate_field_existence: false,
762 enable_optimized_combinations: false,
763 };
764
765 let parser = FieldParser::with_config(config);
766 assert!(!parser.config.enable_caching);
767 assert_eq!(parser.config.max_cache_size, 500);
768 }
769
770 #[test]
771 fn test_cache_operations() {
772 let mut parser = FieldParser::new();
773
774 assert_eq!(parser.cache_size(), 0);
776
777 parser.cache_field_value(
779 "test_key".to_string(),
780 FieldData::String("test_value".to_string()),
781 );
782 assert_eq!(parser.cache_size(), 1);
783
784 parser.clear_cache();
786 assert_eq!(parser.cache_size(), 0);
787 }
788
789 #[test]
790 fn test_allocation_field_enum() {
791 let fields = [
793 AllocationField::Ptr,
794 AllocationField::Size,
795 AllocationField::TimestampAlloc,
796 AllocationField::VarName,
797 AllocationField::TypeName,
798 AllocationField::ThreadId,
799 AllocationField::BorrowCount,
800 AllocationField::IsLeaked,
801 ];
802
803 for (i, field) in fields.iter().enumerate() {
805 for (j, other_field) in fields.iter().enumerate() {
806 if i != j {
807 assert_ne!(field, other_field);
808 }
809 }
810 }
811
812 assert_eq!(format!("{:?}", AllocationField::Ptr), "Ptr");
814 assert_eq!(format!("{:?}", AllocationField::Size), "Size");
815 assert_eq!(format!("{:?}", AllocationField::VarName), "VarName");
816 }
817
818 #[test]
819 fn test_field_data_variants() {
820 let string_data = FieldData::String("test".to_string());
822 let usize_data = FieldData::Usize(1024);
823 let u64_data = FieldData::U64(1234567890);
824 let bool_data = FieldData::Bool(true);
825 match string_data {
827 FieldData::String(s) => assert_eq!(s, "test"),
828 _ => panic!("Expected String variant"),
829 }
830
831 match usize_data {
832 FieldData::Usize(n) => assert_eq!(n, 1024),
833 _ => panic!("Expected Usize variant"),
834 }
835
836 match u64_data {
837 FieldData::U64(n) => assert_eq!(n, 1234567890),
838 _ => panic!("Expected U64 variant"),
839 }
840
841 match bool_data {
842 FieldData::Bool(b) => assert!(b),
843 _ => panic!("Expected Bool variant"),
844 }
845 }
846
847 #[test]
848 fn test_partial_allocation_info_comprehensive() {
849 let mut partial = PartialAllocationInfo::new();
850
851 assert_eq!(partial.field_count(), 0);
853 assert!(!partial.has_field(&AllocationField::Ptr));
854 assert!(!partial.has_field(&AllocationField::Size));
855
856 partial.ptr = Some(0x1000);
858 partial.size = Some(1024);
859 partial.timestamp_alloc = Some(1234567890);
860 partial.thread_id = Some("main".to_string());
864 partial.borrow_count = Some(5);
865 partial.is_leaked = Some(false);
866
867 assert_eq!(partial.field_count(), 6); assert!(partial.has_field(&AllocationField::Ptr));
872 assert!(partial.has_field(&AllocationField::Size));
873 assert!(partial.has_field(&AllocationField::TimestampAlloc));
874 assert!(partial.has_field(&AllocationField::ThreadId));
878 assert!(partial.has_field(&AllocationField::BorrowCount));
879 assert!(partial.has_field(&AllocationField::IsLeaked));
880
881 let full = partial.to_full_allocation();
883 assert_eq!(full.ptr, 0x1000);
884 assert_eq!(full.size, 1024);
885 assert_eq!(full.timestamp_alloc, 1234567890);
886 assert_eq!(full.thread_id, "main");
890 assert_eq!(full.borrow_count, 5);
891 assert!(!full.is_leaked);
892 }
893
894 #[test]
895 fn test_partial_allocation_info_defaults() {
896 let partial = PartialAllocationInfo::new();
897 let full = partial.to_full_allocation();
898
899 assert_eq!(full.ptr, 0);
901 assert_eq!(full.size, 0);
902 assert_eq!(full.timestamp_alloc, 0);
903 assert_eq!(full.var_name, None);
904 assert_eq!(full.type_name, None);
905 assert_eq!(full.thread_id, ""); assert_eq!(full.borrow_count, 0);
907 assert!(!full.is_leaked);
908 }
909
910 #[test]
911 fn test_field_parser_stats_comprehensive() {
912 let mut parser = FieldParser::new();
913
914 let stats = parser.get_stats();
916 assert_eq!(stats.total_fields_parsed, 0);
917 assert_eq!(stats.fields_skipped, 0);
918 assert_eq!(stats.total_parse_time_us, 0);
919 assert_eq!(stats.parsing_efficiency(), 0.0);
920 assert_eq!(stats.avg_parse_time_per_field_us(), 0.0);
921
922 parser.stats.total_fields_parsed = 5;
924 parser.stats.fields_skipped = 3;
925 parser.stats.total_parse_time_us = 200;
926
927 let stats = parser.get_stats();
928 assert_eq!(stats.total_fields_parsed, 5);
929 assert_eq!(stats.fields_skipped, 3);
930 assert_eq!(stats.total_parse_time_us, 200);
931 assert_eq!(stats.parsing_efficiency(), 37.5); assert_eq!(stats.avg_parse_time_per_field_us(), 40.0); parser.stats.total_fields_parsed = 0;
936 parser.stats.total_parse_time_us = 100;
937 let stats = parser.get_stats();
938 assert_eq!(stats.avg_parse_time_per_field_us(), 0.0);
939 }
940
941 #[test]
942 fn test_field_parser_config_variations() {
943 let default_parser = FieldParser::new();
945 assert!(default_parser.config.enable_caching);
946 assert_eq!(default_parser.config.max_cache_size, 1000);
947 assert!(default_parser.config.validate_field_existence);
948 assert!(default_parser.config.enable_optimized_combinations);
949
950 let config1 = FieldParserConfig {
952 enable_caching: false,
953 max_cache_size: 100,
954 validate_field_existence: false,
955 enable_optimized_combinations: false,
956 };
957 let parser1 = FieldParser::with_config(config1);
958 assert!(!parser1.config.enable_caching);
959 assert_eq!(parser1.config.max_cache_size, 100);
960 assert!(!parser1.config.validate_field_existence);
961 assert!(!parser1.config.enable_optimized_combinations);
962
963 let config2 = FieldParserConfig {
965 enable_caching: true,
966 max_cache_size: 5000,
967 validate_field_existence: true,
968 enable_optimized_combinations: true,
969 };
970 let parser2 = FieldParser::with_config(config2);
971 assert!(parser2.config.enable_caching);
972 assert_eq!(parser2.config.max_cache_size, 5000);
973 assert!(parser2.config.validate_field_existence);
974 assert!(parser2.config.enable_optimized_combinations);
975 }
976
977 #[test]
978 fn test_cache_operations_comprehensive() {
979 let mut parser = FieldParser::new();
980
981 parser.cache_field_value(
983 "string_key".to_string(),
984 FieldData::String("string_value".to_string()),
985 );
986 parser.cache_field_value("usize_key".to_string(), FieldData::Usize(1024));
987 parser.cache_field_value("u64_key".to_string(), FieldData::U64(1234567890));
988 parser.cache_field_value("bool_key".to_string(), FieldData::Bool(true));
989 parser.cache_field_value(
990 "vec_key".to_string(),
991 FieldData::String("vec_data".to_string()),
992 );
993
994 assert_eq!(parser.cache_size(), 5);
995
996 parser.clear_cache();
1002 assert_eq!(parser.cache_size(), 0);
1003 }
1004
1005 #[test]
1006 fn test_field_parser_with_empty_fields() {
1007 let _fields: Vec<AllocationField> = vec![];
1008 let parser = FieldParser::new();
1009
1010 assert_eq!(parser.cache_size(), 0);
1012
1013 let stats = parser.get_stats();
1015 assert_eq!(stats.total_fields_parsed, 0);
1016 assert_eq!(stats.fields_skipped, 0);
1017 }
1018
1019 #[test]
1020 fn test_field_parser_with_all_fields() {
1021 let _fields = [
1022 AllocationField::Ptr,
1023 AllocationField::Size,
1024 AllocationField::TimestampAlloc,
1025 AllocationField::VarName,
1026 AllocationField::TypeName,
1027 AllocationField::ThreadId,
1028 AllocationField::BorrowCount,
1029 AllocationField::IsLeaked,
1030 ];
1031 let parser = FieldParser::new();
1032
1033 assert_eq!(parser.cache_size(), 0);
1035 }
1036
1037 #[test]
1038 fn test_field_parser_with_duplicate_fields() {
1039 let _fields = [
1040 AllocationField::Ptr,
1041 AllocationField::Size,
1042 AllocationField::Ptr, AllocationField::Size, ];
1045 let parser = FieldParser::new();
1046
1047 assert_eq!(parser.cache_size(), 0);
1049 }
1050
1051 #[test]
1052 fn test_field_data_memory_usage() {
1053 let small_string = FieldData::String("a".to_string());
1055 let large_string = FieldData::String("a".repeat(1000));
1056
1057 match small_string {
1059 FieldData::String(s) => assert_eq!(s.len(), 1),
1060 _ => panic!("Expected String"),
1061 }
1062
1063 match large_string {
1064 FieldData::String(s) => assert_eq!(s.len(), 1000),
1065 _ => panic!("Expected String"),
1066 }
1067 }
1068
1069 #[test]
1070 fn test_parsing_stats_edge_cases() {
1071 let mut parser = FieldParser::new();
1073
1074 let stats = parser.get_stats();
1076 assert_eq!(stats.parsing_efficiency(), 0.0);
1077 assert_eq!(stats.avg_parse_time_per_field_us(), 0.0);
1078
1079 parser.stats.fields_skipped = 10;
1081 let stats = parser.get_stats();
1082 assert_eq!(stats.parsing_efficiency(), 100.0); parser.stats.total_fields_parsed = 1000000;
1086 parser.stats.total_parse_time_us = 1000000;
1087 let stats = parser.get_stats();
1089 let _efficiency = stats.parsing_efficiency();
1090 let _avg_time = stats.avg_parse_time_per_field_us();
1091 }
1092
1093 #[test]
1094 fn test_allocation_field_coverage() {
1095 let all_fields = vec![
1097 AllocationField::Ptr,
1098 AllocationField::Size,
1099 AllocationField::TimestampAlloc,
1100 AllocationField::VarName,
1101 AllocationField::TypeName,
1102 AllocationField::ThreadId,
1103 AllocationField::BorrowCount,
1104 AllocationField::IsLeaked,
1105 ];
1106
1107 for field in all_fields {
1109 let mut partial = PartialAllocationInfo::new();
1110
1111 match field {
1112 AllocationField::Ptr => {
1113 partial.ptr = Some(0x1000);
1114 assert!(partial.has_field(&AllocationField::Ptr));
1115 }
1116 AllocationField::Size => {
1117 partial.size = Some(1024);
1118 assert!(partial.has_field(&AllocationField::Size));
1119 }
1120 AllocationField::TimestampAlloc => {
1121 partial.timestamp_alloc = Some(1234567890);
1122 assert!(partial.has_field(&AllocationField::TimestampAlloc));
1123 }
1124 AllocationField::VarName => {
1125 assert!(!partial.has_field(&AllocationField::VarName));
1127 }
1128 AllocationField::TypeName => {
1129 assert!(!partial.has_field(&AllocationField::TypeName));
1131 }
1132 AllocationField::ThreadId => {
1133 partial.thread_id = Some("main".to_string());
1134 assert!(partial.has_field(&AllocationField::ThreadId));
1135 }
1136 AllocationField::BorrowCount => {
1137 partial.borrow_count = Some(5);
1138 assert!(partial.has_field(&AllocationField::BorrowCount));
1139 }
1140 AllocationField::IsLeaked => {
1141 partial.is_leaked = Some(true);
1142 assert!(partial.has_field(&AllocationField::IsLeaked));
1143 }
1144 _ => {
1145 }
1148 }
1149 }
1150 }
1151}