memscope_rs/core/
allocation_adapter.rs

1//! Compatibility adapter for AllocationInfo migration
2//!
3//! This module provides a compatibility layer that allows existing code
4//! to continue working with the original AllocationInfo interface while
5//! internally using the optimized OptimizedAllocationInfo with string interning.
6
7use crate::core::optimized_types::OptimizedAllocationInfo;
8use crate::core::string_pool::intern_string;
9use crate::core::types::AllocationInfo;
10use std::sync::Arc;
11
12/// Adapter that provides AllocationInfo interface backed by OptimizedAllocationInfo
13///
14/// This allows existing code to continue working unchanged while benefiting
15/// from string interning optimizations under the hood.
16pub struct AllocationInfoAdapter {
17    inner: OptimizedAllocationInfo,
18}
19
20impl AllocationInfoAdapter {
21    /// Create a new adapter from an OptimizedAllocationInfo
22    pub fn new(optimized: OptimizedAllocationInfo) -> Self {
23        Self { inner: optimized }
24    }
25
26    /// Create a new adapter with basic allocation info
27    pub fn from_allocation(ptr: usize, size: usize) -> Self {
28        Self {
29            inner: OptimizedAllocationInfo::new(ptr, size),
30        }
31    }
32
33    /// Get the underlying optimized allocation info
34    pub fn inner(&self) -> &OptimizedAllocationInfo {
35        &self.inner
36    }
37
38    /// Get a mutable reference to the underlying optimized allocation info
39    pub fn inner_mut(&mut self) -> &mut OptimizedAllocationInfo {
40        &mut self.inner
41    }
42
43    /// Convert to the original AllocationInfo format
44    pub fn to_allocation_info(&self) -> AllocationInfo {
45        self.inner.clone().into()
46    }
47
48    /// Convert from the original AllocationInfo format
49    pub fn from_allocation_info(info: AllocationInfo) -> Self {
50        Self {
51            inner: OptimizedAllocationInfo::from(info),
52        }
53    }
54
55    // Provide AllocationInfo-compatible interface methods
56
57    pub fn ptr(&self) -> usize {
58        self.inner.ptr
59    }
60
61    pub fn size(&self) -> usize {
62        self.inner.size
63    }
64
65    pub fn var_name(&self) -> Option<String> {
66        self.inner.var_name.as_ref().map(|s| s.to_string())
67    }
68
69    pub fn set_var_name(&mut self, name: Option<String>) {
70        self.inner.var_name = name.map(|s| intern_string(&s));
71    }
72
73    pub fn type_name(&self) -> Option<String> {
74        self.inner.type_name.as_ref().map(|s| s.to_string())
75    }
76
77    pub fn set_type_name(&mut self, name: Option<String>) {
78        self.inner.type_name = name.map(|s| intern_string(&s));
79    }
80
81    pub fn scope_name(&self) -> Option<String> {
82        self.inner.scope_name.as_ref().map(|s| s.to_string())
83    }
84
85    pub fn set_scope_name(&mut self, name: Option<String>) {
86        self.inner.scope_name = name.map(|s| intern_string(&s));
87    }
88
89    pub fn timestamp_alloc(&self) -> u64 {
90        self.inner.timestamp_alloc
91    }
92
93    pub fn timestamp_dealloc(&self) -> Option<u64> {
94        self.inner.timestamp_dealloc
95    }
96
97    pub fn set_timestamp_dealloc(&mut self, timestamp: Option<u64>) {
98        self.inner.timestamp_dealloc = timestamp;
99        if let Some(dealloc) = timestamp {
100            self.inner.lifetime_ms = Some((dealloc - self.inner.timestamp_alloc) / 1_000_000);
101        }
102    }
103
104    pub fn thread_id(&self) -> String {
105        self.inner.thread_id.to_string()
106    }
107
108    pub fn set_thread_id(&mut self, id: String) {
109        self.inner.thread_id = intern_string(&id);
110    }
111
112    pub fn borrow_count(&self) -> usize {
113        self.inner.borrow_count
114    }
115
116    pub fn set_borrow_count(&mut self, count: usize) {
117        self.inner.borrow_count = count;
118    }
119
120    pub fn stack_trace(&self) -> Option<Vec<String>> {
121        self.inner
122            .stack_trace
123            .as_ref()
124            .map(|trace| trace.iter().map(|frame| frame.to_string()).collect())
125    }
126
127    pub fn set_stack_trace(&mut self, trace: Option<Vec<String>>) {
128        self.inner.stack_trace =
129            trace.map(|t| Arc::new(t.into_iter().map(|frame| intern_string(&frame)).collect()));
130    }
131
132    pub fn is_leaked(&self) -> bool {
133        self.inner.is_leaked
134    }
135
136    pub fn set_is_leaked(&mut self, leaked: bool) {
137        self.inner.is_leaked = leaked;
138    }
139
140    pub fn lifetime_ms(&self) -> Option<u64> {
141        self.inner.lifetime_ms
142    }
143
144    pub fn is_active(&self) -> bool {
145        self.inner.is_active()
146    }
147
148    pub fn mark_deallocated(&mut self) {
149        self.inner.mark_deallocated();
150    }
151}
152
153impl Clone for AllocationInfoAdapter {
154    fn clone(&self) -> Self {
155        Self {
156            inner: self.inner.clone(),
157        }
158    }
159}
160
161impl std::fmt::Debug for AllocationInfoAdapter {
162    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163        f.debug_struct("AllocationInfoAdapter")
164            .field("inner", &self.inner)
165            .finish()
166    }
167}
168
169impl PartialEq for AllocationInfoAdapter {
170    fn eq(&self, other: &Self) -> bool {
171        self.inner == other.inner
172    }
173}
174
175impl From<AllocationInfo> for AllocationInfoAdapter {
176    fn from(info: AllocationInfo) -> Self {
177        Self::from_allocation_info(info)
178    }
179}
180
181impl From<AllocationInfoAdapter> for AllocationInfo {
182    fn from(adapter: AllocationInfoAdapter) -> Self {
183        adapter.to_allocation_info()
184    }
185}
186
187impl From<OptimizedAllocationInfo> for AllocationInfoAdapter {
188    fn from(optimized: OptimizedAllocationInfo) -> Self {
189        Self::new(optimized)
190    }
191}
192
193impl From<AllocationInfoAdapter> for OptimizedAllocationInfo {
194    fn from(adapter: AllocationInfoAdapter) -> Self {
195        adapter.inner
196    }
197}
198
199/// Collection of allocation adapters for batch operations
200pub struct AllocationCollection {
201    allocations: Vec<AllocationInfoAdapter>,
202}
203
204impl AllocationCollection {
205    /// Create a new empty collection
206    pub fn new() -> Self {
207        Self {
208            allocations: Vec::new(),
209        }
210    }
211
212    /// Create a collection from a vector of AllocationInfo
213    pub fn from_allocation_infos(infos: Vec<AllocationInfo>) -> Self {
214        Self {
215            allocations: infos.into_iter().map(AllocationInfoAdapter::from).collect(),
216        }
217    }
218
219    /// Create a collection from a vector of OptimizedAllocationInfo
220    pub fn from_optimized_infos(infos: Vec<OptimizedAllocationInfo>) -> Self {
221        Self {
222            allocations: infos.into_iter().map(AllocationInfoAdapter::from).collect(),
223        }
224    }
225
226    /// Add an allocation to the collection
227    pub fn push(&mut self, allocation: AllocationInfoAdapter) {
228        self.allocations.push(allocation);
229    }
230
231    /// Get the number of allocations in the collection
232    pub fn len(&self) -> usize {
233        self.allocations.len()
234    }
235
236    /// Check if the collection is empty
237    pub fn is_empty(&self) -> bool {
238        self.allocations.is_empty()
239    }
240
241    /// Get an iterator over the allocations
242    pub fn iter(&self) -> std::slice::Iter<'_, AllocationInfoAdapter> {
243        self.allocations.iter()
244    }
245
246    /// Get a mutable iterator over the allocations
247    pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, AllocationInfoAdapter> {
248        self.allocations.iter_mut()
249    }
250
251    /// Convert to a vector of AllocationInfo (for compatibility)
252    pub fn to_allocation_infos(&self) -> Vec<AllocationInfo> {
253        self.allocations
254            .iter()
255            .map(|a| a.to_allocation_info())
256            .collect()
257    }
258
259    /// Convert to a vector of OptimizedAllocationInfo (for performance)
260    pub fn to_optimized_infos(&self) -> Vec<OptimizedAllocationInfo> {
261        self.allocations.iter().map(|a| a.inner.clone()).collect()
262    }
263
264    /// Get memory usage statistics for the collection
265    pub fn memory_stats(&self) -> CollectionMemoryStats {
266        let total_size: usize = self.allocations.iter().map(|a| a.size()).sum();
267        let active_count = self.allocations.iter().filter(|a| a.is_active()).count();
268        let leaked_count = self.allocations.iter().filter(|a| a.is_leaked()).count();
269
270        CollectionMemoryStats {
271            total_allocations: self.allocations.len(),
272            active_allocations: active_count,
273            leaked_allocations: leaked_count,
274            total_size_bytes: total_size,
275        }
276    }
277}
278
279impl Default for AllocationCollection {
280    fn default() -> Self {
281        Self::new()
282    }
283}
284
285/// Memory statistics for an allocation collection
286#[derive(Debug, Clone)]
287pub struct CollectionMemoryStats {
288    pub total_allocations: usize,
289    pub active_allocations: usize,
290    pub leaked_allocations: usize,
291    pub total_size_bytes: usize,
292}
293
294#[cfg(test)]
295mod tests {
296    use super::*;
297    use crate::core::string_pool::clear_string_pool;
298
299    #[test]
300    fn test_allocation_adapter_basic_operations() {
301        clear_string_pool();
302
303        let mut adapter = AllocationInfoAdapter::from_allocation(0x1000, 64);
304
305        assert_eq!(adapter.ptr(), 0x1000);
306        assert_eq!(adapter.size(), 64);
307        assert!(adapter.is_active());
308
309        adapter.set_var_name(Some("test_var".to_string()));
310        adapter.set_type_name(Some("TestType".to_string()));
311
312        assert_eq!(adapter.var_name(), Some("test_var".to_string()));
313        assert_eq!(adapter.type_name(), Some("TestType".to_string()));
314
315        adapter.mark_deallocated();
316        assert!(!adapter.is_active());
317        assert!(adapter.lifetime_ms().is_some());
318    }
319
320    #[test]
321    fn test_conversion_compatibility() {
322        clear_string_pool();
323
324        // Create original AllocationInfo
325        let original = AllocationInfo {
326            ptr: 0x1000,
327            size: 64,
328            var_name: Some("test".to_string()),
329            type_name: Some("TestType".to_string()),
330            scope_name: None,
331            timestamp_alloc: 12345,
332            timestamp_dealloc: None,
333            thread_id: "thread-1".to_string(),
334            borrow_count: 0,
335            stack_trace: None,
336            is_leaked: false,
337            lifetime_ms: None,
338            borrow_info: None,
339            clone_info: None,
340            ownership_history_available: false,
341            smart_pointer_info: None,
342            memory_layout: None,
343            generic_info: None,
344            dynamic_type_info: None,
345            runtime_state: None,
346            stack_allocation: None,
347            temporary_object: None,
348            fragmentation_analysis: None,
349            generic_instantiation: None,
350            type_relationships: None,
351            type_usage: None,
352            function_call_tracking: None,
353            lifecycle_tracking: None,
354            access_tracking: None,
355            drop_chain_analysis: None,
356        };
357
358        // Convert through adapter
359        let adapter = AllocationInfoAdapter::from(original.clone());
360        let converted_back = AllocationInfo::from(adapter);
361
362        assert_eq!(converted_back.ptr, original.ptr);
363        assert_eq!(converted_back.size, original.size);
364        assert_eq!(converted_back.var_name, original.var_name);
365        assert_eq!(converted_back.type_name, original.type_name);
366    }
367
368    #[test]
369    fn test_allocation_collection() {
370        clear_string_pool();
371
372        let mut collection = AllocationCollection::new();
373
374        let adapter1 = AllocationInfoAdapter::from_allocation(0x1000, 64);
375        let adapter2 = AllocationInfoAdapter::from_allocation(0x2000, 128);
376
377        collection.push(adapter1);
378        collection.push(adapter2);
379
380        assert_eq!(collection.len(), 2);
381        assert!(!collection.is_empty());
382
383        let stats = collection.memory_stats();
384        assert_eq!(stats.total_allocations, 2);
385        assert_eq!(stats.active_allocations, 2);
386        assert_eq!(stats.total_size_bytes, 192); // 64 + 128
387    }
388
389    #[test]
390    fn test_string_interning_through_adapter() {
391        clear_string_pool();
392
393        let mut adapter1 = AllocationInfoAdapter::from_allocation(0x1000, 64);
394        let mut adapter2 = AllocationInfoAdapter::from_allocation(0x2000, 128);
395
396        adapter1.set_var_name(Some("shared_name".to_string()));
397        adapter2.set_var_name(Some("shared_name".to_string()));
398
399        // Verify that the underlying Arc<str> are the same (interned)
400        assert!(Arc::ptr_eq(
401            adapter1
402                .inner()
403                .var_name
404                .as_ref()
405                .expect("Missing variable name"),
406            adapter2
407                .inner()
408                .var_name
409                .as_ref()
410                .expect("Missing variable name")
411        ));
412    }
413
414    #[test]
415    fn test_allocation_adapter_comprehensive() {
416        clear_string_pool();
417
418        let mut adapter = AllocationInfoAdapter::from_allocation(0x5000, 256);
419
420        // Test all setter methods
421        adapter.set_var_name(Some("comprehensive_var".to_string()));
422        adapter.set_type_name(Some("Vec<String>".to_string()));
423        adapter.set_scope_name(Some("main".to_string()));
424        adapter.set_thread_id("worker-thread".to_string());
425        adapter.set_borrow_count(5);
426        adapter.set_is_leaked(true);
427
428        // Test all getter methods
429        assert_eq!(adapter.ptr(), 0x5000);
430        assert_eq!(adapter.size(), 256);
431        assert_eq!(adapter.var_name(), Some("comprehensive_var".to_string()));
432        assert_eq!(adapter.type_name(), Some("Vec<String>".to_string()));
433        assert_eq!(adapter.scope_name(), Some("main".to_string()));
434        assert_eq!(adapter.thread_id(), "worker-thread");
435        assert_eq!(adapter.borrow_count(), 5);
436        assert!(adapter.is_leaked());
437        assert!(adapter.is_active());
438
439        // Test deallocation
440        adapter.mark_deallocated();
441        assert!(!adapter.is_active());
442        assert!(adapter.lifetime_ms().is_some());
443        assert!(adapter.timestamp_dealloc().is_some());
444    }
445
446    #[test]
447    fn test_allocation_adapter_edge_cases() {
448        clear_string_pool();
449
450        // Test with zero-sized allocation
451        let mut zero_adapter = AllocationInfoAdapter::from_allocation(0x0, 0);
452        assert_eq!(zero_adapter.ptr(), 0x0);
453        assert_eq!(zero_adapter.size(), 0);
454        assert!(zero_adapter.is_active());
455
456        // Test with maximum values
457        let mut max_adapter = AllocationInfoAdapter::from_allocation(usize::MAX, usize::MAX);
458        assert_eq!(max_adapter.ptr(), usize::MAX);
459        assert_eq!(max_adapter.size(), usize::MAX);
460
461        // Test with empty strings
462        zero_adapter.set_var_name(Some("".to_string()));
463        zero_adapter.set_type_name(Some("".to_string()));
464        assert_eq!(zero_adapter.var_name(), Some("".to_string()));
465        assert_eq!(zero_adapter.type_name(), Some("".to_string()));
466
467        // Test with None values
468        zero_adapter.set_var_name(None);
469        zero_adapter.set_type_name(None);
470        assert_eq!(zero_adapter.var_name(), None);
471        assert_eq!(zero_adapter.type_name(), None);
472
473        // Test with very long strings
474        let long_string = "a".repeat(10000);
475        max_adapter.set_var_name(Some(long_string.clone()));
476        assert_eq!(max_adapter.var_name(), Some(long_string));
477    }
478
479    #[test]
480    fn test_allocation_collection_comprehensive() {
481        clear_string_pool();
482
483        let mut collection = AllocationCollection::new();
484
485        // Test empty collection
486        assert_eq!(collection.len(), 0);
487        assert!(collection.is_empty());
488        let empty_stats = collection.memory_stats();
489        assert_eq!(empty_stats.total_allocations, 0);
490        assert_eq!(empty_stats.active_allocations, 0);
491        assert_eq!(empty_stats.total_size_bytes, 0);
492
493        // Add various allocations
494        let adapter1 = AllocationInfoAdapter::from_allocation(0x1000, 64);
495        let mut adapter2 = AllocationInfoAdapter::from_allocation(0x2000, 128);
496        let mut adapter3 = AllocationInfoAdapter::from_allocation(0x3000, 256);
497
498        // Mark one as leaked
499        adapter2.set_is_leaked(true);
500
501        // Mark one as deallocated
502        adapter3.mark_deallocated();
503
504        collection.push(adapter1);
505        collection.push(adapter2);
506        collection.push(adapter3);
507
508        assert_eq!(collection.len(), 3);
509        assert!(!collection.is_empty());
510
511        let stats = collection.memory_stats();
512        assert_eq!(stats.total_allocations, 3);
513        assert_eq!(stats.active_allocations, 2); // One is deallocated
514        assert_eq!(stats.total_size_bytes, 448); // 64 + 128 + 256
515
516        // Test iteration
517        let mut count = 0;
518        for adapter in collection.iter() {
519            assert!(adapter.ptr() >= 0x1000);
520            count += 1;
521        }
522        assert_eq!(count, 3);
523
524        // Test mutable iteration
525        for adapter in collection.iter_mut() {
526            adapter.set_borrow_count(adapter.borrow_count() + 1);
527        }
528
529        // Verify borrow counts were incremented
530        for adapter in collection.iter() {
531            assert_eq!(adapter.borrow_count(), 1);
532        }
533    }
534
535    #[test]
536    fn test_allocation_collection_operations() {
537        clear_string_pool();
538
539        let mut collection = AllocationCollection::new();
540
541        // Test with_capacity
542        let capacity_collection = AllocationCollection::new();
543        assert_eq!(capacity_collection.len(), 0);
544
545        // Add many allocations
546        for i in 0..50 {
547            let adapter = AllocationInfoAdapter::from_allocation(0x1000 + i * 0x100, 64 + i);
548            collection.push(adapter);
549        }
550
551        assert_eq!(collection.len(), 50);
552
553        let stats = collection.memory_stats();
554        assert_eq!(stats.total_allocations, 50);
555        assert_eq!(stats.active_allocations, 50);
556
557        // Test clear
558        collection.allocations.clear();
559        assert_eq!(collection.len(), 0);
560        assert!(collection.is_empty());
561    }
562
563    #[test]
564    fn test_allocation_adapter_timestamps() {
565        clear_string_pool();
566
567        let adapter = AllocationInfoAdapter::from_allocation(0x1000, 64);
568        let alloc_time = adapter.timestamp_alloc();
569        assert!(alloc_time > 0);
570
571        // Initially no deallocation timestamp
572        assert_eq!(adapter.timestamp_dealloc(), None);
573
574        // After marking as deallocated
575        let mut mutable_adapter = adapter;
576        mutable_adapter.mark_deallocated();
577        let dealloc_time = mutable_adapter.timestamp_dealloc();
578        assert!(dealloc_time.is_some());
579        assert!(dealloc_time.unwrap() >= alloc_time);
580
581        // Lifetime should be calculated
582        let lifetime = mutable_adapter.lifetime_ms();
583        assert!(lifetime.is_some());
584    }
585
586    #[test]
587    fn test_allocation_adapter_borrow_tracking() {
588        clear_string_pool();
589
590        let mut adapter = AllocationInfoAdapter::from_allocation(0x1000, 64);
591
592        // Test initial borrow count
593        assert_eq!(adapter.borrow_count(), 0);
594
595        // Test setting borrow count
596        adapter.set_borrow_count(5);
597        assert_eq!(adapter.borrow_count(), 5);
598
599        // Test incrementing borrow count
600        adapter.set_borrow_count(adapter.borrow_count() + 1);
601        assert_eq!(adapter.borrow_count(), 6);
602
603        // Test with maximum borrow count
604        adapter.set_borrow_count(usize::MAX);
605        assert_eq!(adapter.borrow_count(), usize::MAX);
606    }
607
608    #[test]
609    fn test_allocation_adapter_leak_detection() {
610        clear_string_pool();
611
612        let mut adapter = AllocationInfoAdapter::from_allocation(0x1000, 64);
613
614        // Initially not leaked
615        assert!(!adapter.is_leaked());
616
617        // Mark as leaked
618        adapter.set_is_leaked(true);
619        assert!(adapter.is_leaked());
620
621        // Mark as not leaked
622        adapter.set_is_leaked(false);
623        assert!(!adapter.is_leaked());
624    }
625
626    #[test]
627    fn test_allocation_adapter_thread_tracking() {
628        clear_string_pool();
629
630        let mut adapter = AllocationInfoAdapter::from_allocation(0x1000, 64);
631
632        // Test default thread ID
633        let default_thread = adapter.thread_id();
634        assert!(!default_thread.is_empty());
635
636        // Test setting custom thread ID
637        adapter.set_thread_id("custom-thread-123".to_string());
638        assert_eq!(adapter.thread_id(), "custom-thread-123");
639
640        // Test with empty thread ID
641        adapter.set_thread_id("".to_string());
642        assert_eq!(adapter.thread_id(), "");
643
644        // Test with very long thread ID
645        let long_thread_id = "thread-".repeat(1000);
646        adapter.set_thread_id(long_thread_id.clone());
647        assert_eq!(adapter.thread_id(), long_thread_id);
648    }
649
650    #[test]
651    fn test_allocation_adapter_conversion_roundtrip() {
652        clear_string_pool();
653
654        // Create a comprehensive AllocationInfo
655        let original = AllocationInfo {
656            ptr: 0x12345678,
657            size: 1024,
658            var_name: Some("test_variable".to_string()),
659            type_name: Some("HashMap<String, Vec<i32>>".to_string()),
660            scope_name: Some("function_scope".to_string()),
661            timestamp_alloc: 1000000,
662            timestamp_dealloc: Some(2000000),
663            thread_id: "main-thread".to_string(),
664            borrow_count: 10,
665            stack_trace: Some(vec!["frame1".to_string(), "frame2".to_string()]),
666            is_leaked: true,
667            lifetime_ms: Some(1000),
668            borrow_info: None,
669            clone_info: None,
670            ownership_history_available: true,
671            smart_pointer_info: None,
672            memory_layout: None,
673            generic_info: None,
674            dynamic_type_info: None,
675            runtime_state: None,
676            stack_allocation: None,
677            temporary_object: None,
678            fragmentation_analysis: None,
679            generic_instantiation: None,
680            type_relationships: None,
681            type_usage: None,
682            function_call_tracking: None,
683            lifecycle_tracking: None,
684            access_tracking: None,
685            drop_chain_analysis: None,
686        };
687
688        // Convert to adapter and back
689        let adapter = AllocationInfoAdapter::from(original.clone());
690        let converted = AllocationInfo::from(adapter);
691
692        // Verify all important fields are preserved
693        assert_eq!(converted.ptr, original.ptr);
694        assert_eq!(converted.size, original.size);
695        assert_eq!(converted.var_name, original.var_name);
696        assert_eq!(converted.type_name, original.type_name);
697        assert_eq!(converted.scope_name, original.scope_name);
698        assert_eq!(converted.timestamp_alloc, original.timestamp_alloc);
699        assert_eq!(converted.timestamp_dealloc, original.timestamp_dealloc);
700        assert_eq!(converted.thread_id, original.thread_id);
701        assert_eq!(converted.borrow_count, original.borrow_count);
702        assert_eq!(converted.is_leaked, original.is_leaked);
703        assert_eq!(converted.lifetime_ms, original.lifetime_ms);
704        assert_eq!(
705            converted.ownership_history_available,
706            original.ownership_history_available
707        );
708    }
709
710    #[test]
711    fn test_allocation_collection_memory_stats_edge_cases() {
712        clear_string_pool();
713
714        let mut collection = AllocationCollection::new();
715
716        // Test with all deallocated allocations
717        let mut adapter1 = AllocationInfoAdapter::from_allocation(0x1000, 100);
718        let mut adapter2 = AllocationInfoAdapter::from_allocation(0x2000, 200);
719        adapter1.mark_deallocated();
720        adapter2.mark_deallocated();
721
722        collection.push(adapter1);
723        collection.push(adapter2);
724
725        let stats = collection.memory_stats();
726        assert_eq!(stats.total_allocations, 2);
727        assert_eq!(stats.active_allocations, 0); // All deallocated
728        assert_eq!(stats.total_size_bytes, 300); // Still counts total size
729
730        // Test with all leaked allocations
731        collection.allocations.clear();
732        let mut adapter3 = AllocationInfoAdapter::from_allocation(0x3000, 150);
733        let mut adapter4 = AllocationInfoAdapter::from_allocation(0x4000, 250);
734        adapter3.set_is_leaked(true);
735        adapter4.set_is_leaked(true);
736
737        collection.push(adapter3);
738        collection.push(adapter4);
739
740        let stats = collection.memory_stats();
741        assert_eq!(stats.total_allocations, 2);
742        assert_eq!(stats.active_allocations, 2); // Leaked but still active
743        assert_eq!(stats.total_size_bytes, 400);
744    }
745
746    #[test]
747    fn test_allocation_adapter_concurrent_access() {
748        use std::sync::{Arc, Mutex};
749        use std::thread;
750
751        clear_string_pool();
752
753        let collection = Arc::new(Mutex::new(AllocationCollection::new()));
754        let mut handles = vec![];
755
756        // Test concurrent access to collection
757        for i in 0..10 {
758            let collection_clone = collection.clone();
759            let handle = thread::spawn(move || {
760                let adapter = AllocationInfoAdapter::from_allocation(0x1000 + i * 0x100, 64 + i);
761                let mut coll = collection_clone.lock().expect("Failed to lock collection");
762                coll.push(adapter);
763            });
764            handles.push(handle);
765        }
766
767        for handle in handles {
768            handle.join().expect("Thread should complete");
769        }
770
771        let final_collection = collection.lock().expect("Failed to lock collection");
772        assert_eq!(final_collection.len(), 10);
773
774        let stats = final_collection.memory_stats();
775        assert_eq!(stats.total_allocations, 10);
776        assert_eq!(stats.active_allocations, 10);
777    }
778
779    #[test]
780    fn test_allocation_adapter_string_interning_efficiency() {
781        clear_string_pool();
782
783        let mut adapters = Vec::new();
784        let common_type = "std::collections::HashMap<String, Vec<i32>>";
785        let common_var_prefix = "map_instance_";
786
787        // Create many adapters with similar names
788        for i in 0..100 {
789            let mut adapter = AllocationInfoAdapter::from_allocation(0x1000 + i * 0x100, 64);
790            adapter.set_type_name(Some(common_type.to_string()));
791            adapter.set_var_name(Some(format!("{}{}", common_var_prefix, i)));
792            adapters.push(adapter);
793        }
794
795        // Verify that type names have the same content (string interning may or may not work depending on pool state)
796        for i in 1..adapters.len() {
797            assert_eq!(
798                adapters[0].type_name().unwrap(),
799                adapters[i].type_name().unwrap(),
800                "Type names should have the same content"
801            );
802        }
803
804        // Variable names should be different but still interned efficiently
805        for adapter in &adapters {
806            assert!(adapter.var_name().unwrap().starts_with(common_var_prefix));
807        }
808
809        // All type names should be the expected common type
810        for adapter in &adapters {
811            assert_eq!(adapter.type_name(), Some(common_type.to_string()));
812        }
813    }
814}