use crate::snapshot::types::ActiveAllocation;
#[derive(Debug, Clone)]
struct RangeEntry {
start: usize,
end: usize,
alloc_id: usize,
}
#[derive(Debug, Default)]
pub struct RangeMap {
entries: Vec<RangeEntry>,
}
impl RangeMap {
pub fn new(allocations: &[ActiveAllocation]) -> Self {
let mut entries: Vec<RangeEntry> = allocations
.iter()
.enumerate()
.map(|(id, alloc)| RangeEntry {
start: alloc.ptr,
end: alloc.ptr.saturating_add(alloc.size),
alloc_id: id,
})
.collect();
entries.sort_by_key(|e| e.start);
Self { entries }
}
pub fn find_containing(&self, ptr: usize) -> Option<usize> {
let idx = self.entries.partition_point(|e| e.start <= ptr);
if idx > 0 {
let candidate = &self.entries[idx - 1];
if ptr < candidate.end {
return Some(candidate.alloc_id);
}
}
None
}
pub fn find_exact_start(&self, ptr: usize) -> Option<usize> {
let idx = self.entries.partition_point(|e| e.start < ptr);
if idx < self.entries.len() && self.entries[idx].start == ptr {
Some(self.entries[idx].alloc_id)
} else {
None
}
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_alloc(ptr: usize, size: usize) -> ActiveAllocation {
ActiveAllocation {
ptr,
size,
allocated_at: 0,
var_name: None,
type_name: None,
thread_id: 0,
call_stack_hash: None,
}
}
#[test]
fn test_rangemap_empty() {
let map = RangeMap::new(&[]);
assert!(map.is_empty());
assert_eq!(map.len(), 0);
assert!(map.find_containing(0x1000).is_none());
}
#[test]
fn test_rangemap_single_allocation() {
let allocs = vec![make_alloc(0x1000, 100)];
let map = RangeMap::new(&allocs);
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(0x0FFF), None); }
#[test]
fn test_rangemap_multiple_allocations() {
let allocs = vec![
make_alloc(0x1000, 100),
make_alloc(0x2000, 200),
make_alloc(0x3000, 50),
];
let map = RangeMap::new(&allocs);
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); }
#[test]
fn test_rangemap_adjacent_allocations() {
let allocs = vec![make_alloc(0x1000, 100), make_alloc(0x1064, 100)];
let map = RangeMap::new(&allocs);
assert_eq!(map.find_containing(0x1063), Some(0));
assert_eq!(map.find_containing(0x1064), Some(1));
}
#[test]
fn test_rangemap_find_exact_start() {
let allocs = vec![make_alloc(0x1000, 100), make_alloc(0x2000, 200)];
let map = RangeMap::new(&allocs);
assert_eq!(map.find_exact_start(0x1000), Some(0));
assert_eq!(map.find_exact_start(0x2000), Some(1));
assert_eq!(map.find_exact_start(0x1050), None); assert_eq!(map.find_exact_start(0x3000), None); }
#[test]
fn test_rangemap_unsorted_input() {
let allocs = vec![
make_alloc(0x3000, 10),
make_alloc(0x1000, 10),
make_alloc(0x2000, 10),
];
let map = RangeMap::new(&allocs);
assert_eq!(map.find_containing(0x1000), Some(1));
assert_eq!(map.find_containing(0x2000), Some(2));
assert_eq!(map.find_containing(0x3000), Some(0));
}
#[test]
fn test_rangemap_zero_size_allocation() {
let allocs = vec![make_alloc(0x1000, 0)];
let map = RangeMap::new(&allocs);
assert_eq!(map.find_containing(0x1000), None);
}
#[test]
fn test_rangemap_large_number_of_allocations() {
let allocs: Vec<ActiveAllocation> = (0..1000)
.map(|i| make_alloc(0x10000 + i * 0x1000, 0x100))
.collect();
let map = RangeMap::new(&allocs);
assert_eq!(map.find_containing(0x10000), Some(0));
assert_eq!(map.find_containing(0x11000), Some(1));
assert_eq!(map.find_containing(0x1F000), Some(15));
}
}