#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LineRow {
pub addr: u32,
pub line: u32,
pub file: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SourceLoc {
pub line: u32,
pub file: u32,
}
pub fn op_offsets_to_source(
op_offsets: &[u32],
code_base: u32,
rows: &[LineRow],
) -> Vec<Option<SourceLoc>> {
let mut sorted: Vec<LineRow> = rows.to_vec();
sorted.sort_by_key(|r| r.addr);
op_offsets
.iter()
.map(|&off| {
let a = off.checked_sub(code_base)?;
sorted
.iter()
.rev()
.find(|r| r.addr <= a)
.map(|r| SourceLoc {
line: r.line,
file: r.file,
})
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn covering_row_lookup() {
let rows = [
LineRow {
addr: 0,
line: 10,
file: 1,
},
LineRow {
addr: 8,
line: 11,
file: 1,
},
LineRow {
addr: 20,
line: 12,
file: 1,
},
];
let got = op_offsets_to_source(&[100, 104, 108, 130], 100, &rows);
assert_eq!(got[0].map(|s| s.line), Some(10)); assert_eq!(got[1].map(|s| s.line), Some(10)); assert_eq!(got[2].map(|s| s.line), Some(11)); assert_eq!(got[3].map(|s| s.line), Some(12)); }
#[test]
fn op_before_first_row_is_none() {
let rows = [LineRow {
addr: 8,
line: 11,
file: 1,
}];
let got = op_offsets_to_source(&[100], 100, &rows);
assert_eq!(got[0], None);
}
#[test]
fn op_before_code_base_is_none() {
let rows = [LineRow {
addr: 0,
line: 1,
file: 1,
}];
let got = op_offsets_to_source(&[50], 100, &rows);
assert_eq!(got[0], None);
}
}