const ENTRY_SIZE: usize = 28;
#[derive(Debug, Clone, Copy)]
pub(crate) struct MapEntry {
pub map_offset: u64,
pub length: u64,
pub target_offset: u64,
pub target_id: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum TargetKind {
ImageStream,
Zero,
SymbolicFF,
Unknown,
}
pub(crate) struct LoadedMap {
pub entries: Vec<MapEntry>,
pub targets: Vec<TargetKind>,
pub gap_default: TargetKind,
}
pub(crate) fn parse_map_entries(data: &[u8]) -> Vec<MapEntry> {
let n = data.len() / ENTRY_SIZE;
let mut entries: Vec<MapEntry> = (0..n)
.map(|i| {
let off = i * ENTRY_SIZE;
MapEntry {
map_offset: u64::from_le_bytes(data[off..off + 8].try_into().expect("slice")),
length: u64::from_le_bytes(data[off + 8..off + 16].try_into().expect("slice")),
target_offset: u64::from_le_bytes(
data[off + 16..off + 24].try_into().expect("slice"),
),
target_id: u32::from_le_bytes(data[off + 24..off + 28].try_into().expect("slice")),
}
})
.filter(|e| e.length > 0)
.collect();
entries.sort_by_key(|e| e.map_offset);
entries
}
pub(crate) fn parse_idx(data: &str, image_stream_arn: &str) -> Vec<TargetKind> {
data.lines()
.filter(|l| !l.trim().is_empty())
.map(|line| {
let s = line.trim();
if s.ends_with("#Zero") || s == "aff4:Zero" {
TargetKind::Zero
} else if s.ends_with("#SymbolicStreamFF") {
TargetKind::SymbolicFF
} else if s == image_stream_arn {
TargetKind::ImageStream
} else if s.starts_with("aff4://") {
TargetKind::Unknown
} else {
TargetKind::Unknown
}
})
.collect()
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct ResolvedRegion {
pub kind: TargetKind,
pub target_offset: u64,
pub bytes_in_region: u64,
}
pub(crate) fn resolve(map: &LoadedMap, virtual_pos: u64, virtual_size: u64) -> ResolvedRegion {
let idx = map
.entries
.partition_point(|e| e.map_offset <= virtual_pos);
if idx > 0 {
let e = &map.entries[idx - 1];
if virtual_pos < e.map_offset + e.length {
let offset_in_entry = virtual_pos - e.map_offset;
let kind = map
.targets
.get(e.target_id as usize)
.copied()
.unwrap_or(TargetKind::Unknown);
return ResolvedRegion {
kind,
target_offset: e.target_offset + offset_in_entry,
bytes_in_region: e.length - offset_in_entry,
};
}
}
let gap_end = map
.entries
.get(idx)
.map(|e| e.map_offset)
.unwrap_or(virtual_size);
let bytes_in_gap = gap_end.saturating_sub(virtual_pos).max(1);
ResolvedRegion {
kind: map.gap_default,
target_offset: 0,
bytes_in_region: bytes_in_gap,
}
}