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 }
125 }
126
127 #[test]
128 fn test_rangemap_empty() {
129 let map = RangeMap::new(&[]);
130 assert!(map.is_empty());
131 assert_eq!(map.len(), 0);
132 assert!(map.find_containing(0x1000).is_none());
133 }
134
135 #[test]
136 fn test_rangemap_single_allocation() {
137 let allocs = vec![make_alloc(0x1000, 100)];
138 let map = RangeMap::new(&allocs);
139
140 assert_eq!(map.find_containing(0x1000), Some(0));
141 assert_eq!(map.find_containing(0x1050), Some(0));
142 assert_eq!(map.find_containing(0x1063), Some(0)); assert_eq!(map.find_containing(0x1064), None); assert_eq!(map.find_containing(0x0FFF), None); }
146
147 #[test]
148 fn test_rangemap_multiple_allocations() {
149 let allocs = vec![
154 make_alloc(0x1000, 100),
155 make_alloc(0x2000, 200),
156 make_alloc(0x3000, 50),
157 ];
158 let map = RangeMap::new(&allocs);
159
160 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); }
179
180 #[test]
181 fn test_rangemap_adjacent_allocations() {
182 let allocs = vec![make_alloc(0x1000, 100), make_alloc(0x1064, 100)];
183 let map = RangeMap::new(&allocs);
184
185 assert_eq!(map.find_containing(0x1063), Some(0));
186 assert_eq!(map.find_containing(0x1064), Some(1));
187 }
188
189 #[test]
190 fn test_rangemap_find_exact_start() {
191 let allocs = vec![make_alloc(0x1000, 100), make_alloc(0x2000, 200)];
192 let map = RangeMap::new(&allocs);
193
194 assert_eq!(map.find_exact_start(0x1000), Some(0));
195 assert_eq!(map.find_exact_start(0x2000), Some(1));
196 assert_eq!(map.find_exact_start(0x1050), None); assert_eq!(map.find_exact_start(0x3000), None); }
199
200 #[test]
201 fn test_rangemap_unsorted_input() {
202 let allocs = vec![
203 make_alloc(0x3000, 10),
204 make_alloc(0x1000, 10),
205 make_alloc(0x2000, 10),
206 ];
207 let map = RangeMap::new(&allocs);
208
209 assert_eq!(map.find_containing(0x1000), Some(1));
211 assert_eq!(map.find_containing(0x2000), Some(2));
212 assert_eq!(map.find_containing(0x3000), Some(0));
213 }
214
215 #[test]
216 fn test_rangemap_zero_size_allocation() {
217 let allocs = vec![make_alloc(0x1000, 0)];
218 let map = RangeMap::new(&allocs);
219
220 assert_eq!(map.find_containing(0x1000), None);
222 }
223
224 #[test]
225 fn test_rangemap_large_number_of_allocations() {
226 let allocs: Vec<ActiveAllocation> = (0..1000)
227 .map(|i| make_alloc(0x10000 + i * 0x1000, 0x100))
228 .collect();
229 let map = RangeMap::new(&allocs);
230
231 assert_eq!(map.find_containing(0x10000), Some(0));
233 assert_eq!(map.find_containing(0x11000), Some(1));
234 assert_eq!(map.find_containing(0x1F000), Some(15));
235 }
236}