use std::collections::{BTreeMap, BTreeSet};
#[derive(Default, Clone)]
pub struct CoverageMap {
executed: BTreeSet<u32>,
writes: BTreeMap<u32, u8>,
}
impl CoverageMap {
pub fn clear(&mut self) {
self.executed.clear();
self.writes.clear();
}
pub fn record_exec(&mut self, eip: u32, _insn_size: u8) {
self.executed.insert(eip);
}
pub fn record_write(&mut self, addr: u32, size: u32) {
let size_u8 = size.min(8).max(1) as u8;
for off in 0..size {
let target = addr.wrapping_add(off);
let slot = self.writes.entry(target).or_insert(size_u8);
if size_u8 > *slot {
*slot = size_u8;
}
}
}
pub fn executed_addresses(&self) -> impl Iterator<Item = u32> + '_ {
self.executed.iter().copied()
}
pub fn executed_count(&self) -> usize {
self.executed.len()
}
pub fn written_addresses(&self) -> impl Iterator<Item = u32> + '_ {
self.writes.keys().copied()
}
pub fn is_self_modifying(&self, addr: u32) -> bool {
self.writes.contains_key(&addr) && self.executed.contains(&addr)
}
pub fn self_modifying_addresses(&self) -> impl Iterator<Item = u32> + '_ {
self.executed
.iter()
.copied()
.filter(move |a| self.writes.contains_key(a))
}
pub fn executed_ranges(&self) -> Vec<std::ops::Range<u32>> {
let mut out = Vec::new();
let mut current: Option<std::ops::Range<u32>> = None;
for &addr in &self.executed {
match current.as_mut() {
Some(r) if r.end == addr => {
r.end = addr.wrapping_add(1);
}
_ => {
if let Some(r) = current.take() {
out.push(r);
}
current = Some(addr..addr.wrapping_add(1));
}
}
}
if let Some(r) = current {
out.push(r);
}
out
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn record_exec_dedupes() {
let mut c = CoverageMap::default();
c.record_exec(0x1000, 3);
c.record_exec(0x1000, 3);
c.record_exec(0x1003, 2);
assert_eq!(c.executed_count(), 2);
}
#[test]
fn record_write_expands_per_byte() {
let mut c = CoverageMap::default();
c.record_write(0x2000, 4);
assert_eq!(c.written_addresses().count(), 4);
}
#[test]
fn is_self_modifying_detects_write_then_exec() {
let mut c = CoverageMap::default();
c.record_write(0x3000, 4);
c.record_exec(0x3001, 1);
assert!(c.is_self_modifying(0x3001));
assert!(!c.is_self_modifying(0x4000));
}
#[test]
fn executed_ranges_merges_adjacent() {
let mut c = CoverageMap::default();
for a in &[0x1000u32, 0x1001, 0x1002, 0x1005] {
c.record_exec(*a, 1);
}
let ranges = c.executed_ranges();
assert_eq!(ranges.len(), 2);
assert_eq!(ranges[0], 0x1000..0x1003);
assert_eq!(ranges[1], 0x1005..0x1006);
}
}