use crate::analysis::relation_inference::{InferenceRecord, RangeMap, Relation, RelationEdge};
use crate::analysis::unsafe_inference::TypeKind;
const MIN_VALID_POINTER: usize = 0x1000;
const MAX_SLICE_SIZE: usize = 256;
pub fn detect_slice(
records: &[InferenceRecord],
allocations: &[crate::snapshot::types::ActiveAllocation],
range_map: &RangeMap,
) -> Vec<RelationEdge> {
let mut relations = Vec::new();
for record in records {
if record.type_kind != TypeKind::FatPtr {
continue;
}
if record.size > MAX_SLICE_SIZE {
continue;
}
let ptr = record.ptr;
if ptr == 0 || ptr < MIN_VALID_POINTER {
continue;
}
let Some(target_id) = range_map.find_containing(ptr) else {
continue;
};
if target_id == record.id {
continue;
}
let target = &allocations[target_id];
let target_start = match target.ptr {
Some(p) => p,
None => continue, };
let target_end = target_start.saturating_add(target.size);
if ptr == target_start {
continue;
}
let slice_end = ptr.saturating_add(record.size);
if slice_end > target_end {
continue;
}
relations.push(RelationEdge {
from: record.id,
to: target_id,
relation: Relation::Slice,
});
}
relations
}
#[cfg(test)]
mod tests {
use super::*;
use crate::analysis::unsafe_inference::TypeKind;
fn make_alloc(ptr: usize, size: usize) -> crate::snapshot::types::ActiveAllocation {
crate::snapshot::types::ActiveAllocation {
ptr: Some(ptr),
kind: crate::core::types::TrackKind::HeapOwner { ptr, size },
size,
allocated_at: 0,
var_name: None,
type_name: None,
thread_id: 0,
call_stack_hash: None,
module_path: None,
stack_ptr: None,
}
}
fn make_record(id: usize, ptr: usize, size: usize) -> InferenceRecord {
InferenceRecord {
id,
ptr,
size,
memory: None,
type_kind: TypeKind::FatPtr,
confidence: 100,
call_stack_hash: None,
alloc_time: 0,
stack_ptr: None,
}
}
#[test]
fn test_slice_size_boundary_256() {
let slice_ptr: usize = 0x10050;
let allocs = vec![
make_alloc(0x5000, 256), make_alloc(0x10000, 0x2000),
];
let range_map = RangeMap::new(&allocs);
let records = vec![make_record(0, slice_ptr, 256)];
let edges = detect_slice(&records, &allocs, &range_map);
assert_eq!(edges.len(), 1);
let allocs2 = vec![make_alloc(0x5000, 257), make_alloc(0x10000, 0x2000)];
let records2 = vec![make_record(0, slice_ptr, 257)];
let edges2 = detect_slice(&records2, &allocs2, &range_map);
assert!(edges2.is_empty());
}
#[test]
fn test_slice_detection_basic() {
let slice_ptr: usize = 0x10050; let allocs = vec![
make_alloc(0x5000, 16), make_alloc(0x10000, 0x2000), ];
let range_map = RangeMap::new(&allocs);
let records = vec![make_record(0, slice_ptr, 16)];
let edges = detect_slice(&records, &allocs, &range_map);
assert_eq!(edges.len(), 1);
assert_eq!(edges[0].from, 0); assert_eq!(edges[0].to, 1); assert_eq!(edges[0].relation, Relation::Slice);
}
#[test]
fn test_slice_too_large() {
let allocs = vec![make_alloc(0x1000, 1024), make_alloc(0x5000, 4096)];
let range_map = RangeMap::new(&allocs);
let records = vec![make_record(0, 0x1000, 1024)];
let edges = detect_slice(&records, &allocs, &range_map);
assert!(edges.is_empty());
}
#[test]
fn test_slice_at_target_start_is_not_slice() {
let allocs = vec![make_alloc(0x5000, 16), make_alloc(0x10000, 0x2000)];
let range_map = RangeMap::new(&allocs);
let records = vec![make_record(0, 0x10000, 16)];
let edges = detect_slice(&records, &allocs, &range_map);
assert!(edges.is_empty()); }
#[test]
fn test_slice_overflowing_target() {
let slice_ptr: usize = 0x100F0;
let allocs = vec![
make_alloc(0x5000, 256), make_alloc(0x10000, 0x100), ];
let range_map = RangeMap::new(&allocs);
let records = vec![make_record(0, slice_ptr, 256)];
let edges = detect_slice(&records, &allocs, &range_map);
assert!(edges.is_empty()); }
#[test]
fn test_slice_no_containing_allocation() {
let allocs = vec![make_alloc(0x9000, 100)];
let range_map = RangeMap::new(&allocs);
let records = vec![make_record(0, 0x1000, 16)];
let edges = detect_slice(&records, &allocs, &range_map);
assert!(edges.is_empty());
}
#[test]
fn test_slice_self_reference_skipped() {
let allocs = vec![make_alloc(0x5000, 100)];
let range_map = RangeMap::new(&allocs);
let records = vec![make_record(0, 0x5010, 16)];
let edges = detect_slice(&records, &allocs, &range_map);
assert!(edges.is_empty()); }
#[test]
fn test_slice_multiple_candidates() {
let allocs = vec![
make_alloc(0x10000, 0x2000), make_alloc(0x20000, 0x2000), make_alloc(0x5000, 16), make_alloc(0x6000, 16), make_alloc(0x7000, 16), ];
let range_map = RangeMap::new(&allocs);
let records = vec![
make_record(2, 0x10050, 16), make_record(3, 0x20080, 16), make_record(4, 0x9000, 16), ];
let edges = detect_slice(&records, &allocs, &range_map);
assert_eq!(edges.len(), 2);
let mut targets: Vec<(usize, usize)> = edges.iter().map(|e| (e.from, e.to)).collect();
targets.sort();
assert_eq!(targets, vec![(2, 0), (3, 1)]);
}
}