memscope_rs/analysis/relation_inference/
range_map.rs1use crate::analysis::is_virtual_pointer;
7use crate::snapshot::types::ActiveAllocation;
8
9#[derive(Debug, Clone)]
11struct RangeEntry {
12 start: usize,
14 end: usize,
16 alloc_id: usize,
18}
19
20#[derive(Debug, Default)]
25pub struct RangeMap {
26 entries: Vec<RangeEntry>,
28}
29
30impl RangeMap {
31 pub fn new(allocations: &[ActiveAllocation]) -> Self {
38 let mut entries: Vec<RangeEntry> = allocations
39 .iter()
40 .enumerate()
41 .filter_map(|(id, alloc)| {
42 alloc.ptr.and_then(|ptr| {
45 if !is_virtual_pointer(ptr) {
46 Some(RangeEntry {
47 start: ptr,
48 end: ptr.saturating_add(alloc.size),
49 alloc_id: id,
50 })
51 } else {
52 None
53 }
54 })
55 })
56 .collect();
57
58 entries.sort_by_key(|e| e.start);
60
61 Self { entries }
62 }
63 pub fn find_containing(&self, ptr: usize) -> Option<usize> {
72 let idx = self.entries.partition_point(|e| e.start <= ptr);
76
77 if idx > 0 {
78 let candidate = &self.entries[idx - 1];
79 if ptr < candidate.end {
80 return Some(candidate.alloc_id);
81 }
82 }
83 None
84 }
85
86 pub fn find_exact_start(&self, ptr: usize) -> Option<usize> {
90 let idx = self.entries.partition_point(|e| e.start < ptr);
91 if idx < self.entries.len() && self.entries[idx].start == ptr {
92 Some(self.entries[idx].alloc_id)
93 } else {
94 None
95 }
96 }
97
98 pub fn len(&self) -> usize {
100 self.entries.len()
101 }
102
103 pub fn is_empty(&self) -> bool {
105 self.entries.is_empty()
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112 use crate::core::types::TrackKind::HeapOwner;
113
114 fn make_alloc(ptr: usize, size: usize) -> ActiveAllocation {
115 ActiveAllocation {
116 kind: HeapOwner { ptr, size },
117 ptr: Some(ptr),
118 size,
119 allocated_at: 0,
120 var_name: None,
121 type_name: None,
122 thread_id: 0,
123 call_stack_hash: None,
124 module_path: None,
125 stack_ptr: None,
126 }
127 }
128
129 #[test]
130 fn test_rangemap_empty() {
131 let map = RangeMap::new(&[]);
132 assert!(map.is_empty());
133 assert_eq!(map.len(), 0);
134 assert!(map.find_containing(0x1000).is_none());
135 }
136
137 #[test]
138 fn test_rangemap_single_allocation() {
139 let allocs = vec![make_alloc(0x1000, 100)];
140 let map = RangeMap::new(&allocs);
141
142 assert_eq!(map.find_containing(0x1000), Some(0));
143 assert_eq!(map.find_containing(0x1050), Some(0));
144 assert_eq!(map.find_containing(0x1063), Some(0)); assert_eq!(map.find_containing(0x1064), None); assert_eq!(map.find_containing(0x0FFF), None); }
148
149 #[test]
150 fn test_rangemap_multiple_allocations() {
151 let allocs = vec![
156 make_alloc(0x1000, 100),
157 make_alloc(0x2000, 200),
158 make_alloc(0x3000, 50),
159 ];
160 let map = RangeMap::new(&allocs);
161
162 assert_eq!(map.find_containing(0x1000), Some(0)); assert_eq!(map.find_containing(0x1050), Some(0)); assert_eq!(map.find_containing(0x1063), Some(0)); assert_eq!(map.find_containing(0x1064), None); assert_eq!(map.find_containing(0x2000), Some(1)); assert_eq!(map.find_containing(0x2050), Some(1)); assert_eq!(map.find_containing(0x20C7), Some(1)); assert_eq!(map.find_containing(0x20C8), None); assert_eq!(map.find_containing(0x3000), Some(2)); assert_eq!(map.find_containing(0x3031), Some(2)); assert_eq!(map.find_containing(0x3032), None); assert_eq!(map.find_containing(0x1500), None); assert_eq!(map.find_containing(0x2500), None); }
181
182 #[test]
183 fn test_rangemap_adjacent_allocations() {
184 let allocs = vec![make_alloc(0x1000, 100), make_alloc(0x1064, 100)];
185 let map = RangeMap::new(&allocs);
186
187 assert_eq!(map.find_containing(0x1063), Some(0));
188 assert_eq!(map.find_containing(0x1064), Some(1));
189 }
190
191 #[test]
192 fn test_rangemap_find_exact_start() {
193 let allocs = vec![make_alloc(0x1000, 100), make_alloc(0x2000, 200)];
194 let map = RangeMap::new(&allocs);
195
196 assert_eq!(map.find_exact_start(0x1000), Some(0));
197 assert_eq!(map.find_exact_start(0x2000), Some(1));
198 assert_eq!(map.find_exact_start(0x1050), None); assert_eq!(map.find_exact_start(0x3000), None); }
201
202 #[test]
203 fn test_rangemap_unsorted_input() {
204 let allocs = vec![
205 make_alloc(0x3000, 10),
206 make_alloc(0x1000, 10),
207 make_alloc(0x2000, 10),
208 ];
209 let map = RangeMap::new(&allocs);
210
211 assert_eq!(map.find_containing(0x1000), Some(1));
213 assert_eq!(map.find_containing(0x2000), Some(2));
214 assert_eq!(map.find_containing(0x3000), Some(0));
215 }
216
217 #[test]
218 fn test_rangemap_zero_size_allocation() {
219 let allocs = vec![make_alloc(0x1000, 0)];
220 let map = RangeMap::new(&allocs);
221
222 assert_eq!(map.find_containing(0x1000), None);
224 }
225
226 #[test]
227 fn test_rangemap_large_number_of_allocations() {
228 let allocs: Vec<ActiveAllocation> = (0..1000)
229 .map(|i| make_alloc(0x10000 + i * 0x1000, 0x100))
230 .collect();
231 let map = RangeMap::new(&allocs);
232
233 assert_eq!(map.find_containing(0x10000), Some(0));
235 assert_eq!(map.find_containing(0x11000), Some(1));
236 assert_eq!(map.find_containing(0x1F000), Some(15));
237 }
238}