use object::{Bytes, LittleEndian, U32Bytes};
use serde_derive::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct InstructionAddressMap {
pub srcloc: FilePos,
pub code_offset: u32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct FilePos(u32);
impl FilePos {
pub fn new(pos: u32) -> FilePos {
assert!(pos != u32::MAX);
FilePos(pos)
}
pub fn none() -> FilePos {
FilePos(u32::MAX)
}
#[inline]
pub fn is_none(&self) -> bool {
*self == FilePos::none()
}
pub fn file_offset(self) -> Option<u32> {
if self.0 == u32::MAX {
None
} else {
Some(self.0)
}
}
}
impl Default for FilePos {
fn default() -> FilePos {
FilePos::none()
}
}
fn parse_address_map(
section: &[u8],
) -> Option<(&[U32Bytes<LittleEndian>], &[U32Bytes<LittleEndian>])> {
let mut section = Bytes(section);
let count = section.read::<U32Bytes<LittleEndian>>().ok()?;
let count = usize::try_from(count.get(LittleEndian)).ok()?;
let (offsets, section) =
object::slice_from_bytes::<U32Bytes<LittleEndian>>(section.0, count).ok()?;
let (positions, section) =
object::slice_from_bytes::<U32Bytes<LittleEndian>>(section, count).ok()?;
debug_assert!(section.is_empty());
Some((offsets, positions))
}
pub fn lookup_file_pos(section: &[u8], offset: usize) -> Option<FilePos> {
let (offsets, positions) = parse_address_map(section)?;
let offset = u32::try_from(offset).ok()?;
let index = match offsets.binary_search_by_key(&offset, |v| v.get(LittleEndian)) {
Ok(i) => i,
Err(0) => return None,
Err(n) => n - 1,
};
let pos = positions.get(index)?;
Some(FilePos(pos.get(LittleEndian)))
}
pub fn iterate_address_map<'a>(
section: &'a [u8],
) -> Option<impl Iterator<Item = (u32, FilePos)> + 'a> {
let (offsets, positions) = parse_address_map(section)?;
Some(
offsets
.iter()
.map(|o| o.get(LittleEndian))
.zip(positions.iter().map(|pos| FilePos(pos.get(LittleEndian)))),
)
}