#![allow(clippy::cast_possible_truncation)]
use ud_core::VAddr;
#[derive(Debug, thiserror::Error, Clone, Copy, PartialEq, Eq)]
pub enum Error {
#[error("address {addr:#06x} is outside the image (load=[{load:#06x}, {end:#06x}))")]
OutOfRange { addr: u64, load: u64, end: u64 },
#[error("multi-byte read at {addr:#06x} ({len} bytes) crosses the end of the image")]
Truncated { addr: u64, len: usize },
}
pub type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Debug, Clone)]
pub struct RawImage {
pub bytes: Vec<u8>,
pub load_addr: u64,
}
impl RawImage {
#[must_use]
pub fn new(bytes: Vec<u8>, load_addr: u64) -> Self {
Self { bytes, load_addr }
}
#[must_use]
pub fn start(&self) -> u64 {
self.load_addr
}
#[must_use]
pub fn end(&self) -> u64 {
self.load_addr + self.bytes.len() as u64
}
#[must_use]
pub fn contains(&self, addr: u64) -> bool {
addr >= self.start() && addr < self.end()
}
#[must_use]
pub fn offset_of(&self, addr: u64) -> Option<usize> {
if self.contains(addr) {
Some((addr - self.load_addr) as usize)
} else {
None
}
}
pub fn read_u8(&self, addr: u64) -> Result<u8> {
let off = self.offset_of(addr).ok_or(Error::OutOfRange {
addr,
load: self.start(),
end: self.end(),
})?;
Ok(self.bytes[off])
}
pub fn read_u16_le(&self, addr: u64) -> Result<u16> {
let off = self.offset_of(addr).ok_or(Error::OutOfRange {
addr,
load: self.start(),
end: self.end(),
})?;
if off + 2 > self.bytes.len() {
return Err(Error::Truncated { addr, len: 2 });
}
Ok(u16::from_le_bytes([self.bytes[off], self.bytes[off + 1]]))
}
#[must_use]
pub fn slice_at(&self, addr: u64) -> Option<&[u8]> {
let off = self.offset_of(addr)?;
Some(&self.bytes[off..])
}
#[must_use]
pub fn bounds(&self) -> (VAddr, VAddr) {
(VAddr(self.start()), VAddr(self.end()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn read_and_slice() {
let img = RawImage::new(vec![0xAA, 0xBB, 0xCC, 0xDD], 0xFF00);
assert_eq!(img.read_u8(0xFF00).unwrap(), 0xAA);
assert_eq!(img.read_u16_le(0xFF00).unwrap(), 0xBBAA);
assert_eq!(img.slice_at(0xFF02), Some(&[0xCC, 0xDD][..]));
assert!(matches!(img.read_u8(0xFEFF), Err(Error::OutOfRange { .. })));
}
#[test]
fn reset_vector_layout() {
let mut bytes = vec![0; 256];
bytes[0xFC] = 0x00; bytes[0xFD] = 0xFF; let img = RawImage::new(bytes, 0xFF00);
assert_eq!(img.read_u16_le(0xFFFC).unwrap(), 0xFF00);
}
}