1#![allow(clippy::cast_possible_truncation)]
14
15use ud_core::VAddr;
16
17#[derive(Debug, thiserror::Error, Clone, Copy, PartialEq, Eq)]
19pub enum Error {
20 #[error("address {addr:#06x} is outside the image (load=[{load:#06x}, {end:#06x}))")]
21 OutOfRange { addr: u64, load: u64, end: u64 },
22 #[error("multi-byte read at {addr:#06x} ({len} bytes) crosses the end of the image")]
23 Truncated { addr: u64, len: usize },
24}
25
26pub type Result<T, E = Error> = std::result::Result<T, E>;
27
28#[derive(Debug, Clone)]
30pub struct RawImage {
31 pub bytes: Vec<u8>,
32 pub load_addr: u64,
33}
34
35impl RawImage {
36 #[must_use]
38 pub fn new(bytes: Vec<u8>, load_addr: u64) -> Self {
39 Self { bytes, load_addr }
40 }
41
42 #[must_use]
44 pub fn start(&self) -> u64 {
45 self.load_addr
46 }
47
48 #[must_use]
50 pub fn end(&self) -> u64 {
51 self.load_addr + self.bytes.len() as u64
52 }
53
54 #[must_use]
56 pub fn contains(&self, addr: u64) -> bool {
57 addr >= self.start() && addr < self.end()
58 }
59
60 #[must_use]
63 pub fn offset_of(&self, addr: u64) -> Option<usize> {
64 if self.contains(addr) {
65 Some((addr - self.load_addr) as usize)
66 } else {
67 None
68 }
69 }
70
71 pub fn read_u8(&self, addr: u64) -> Result<u8> {
73 let off = self.offset_of(addr).ok_or(Error::OutOfRange {
74 addr,
75 load: self.start(),
76 end: self.end(),
77 })?;
78 Ok(self.bytes[off])
79 }
80
81 pub fn read_u16_le(&self, addr: u64) -> Result<u16> {
84 let off = self.offset_of(addr).ok_or(Error::OutOfRange {
85 addr,
86 load: self.start(),
87 end: self.end(),
88 })?;
89 if off + 2 > self.bytes.len() {
90 return Err(Error::Truncated { addr, len: 2 });
91 }
92 Ok(u16::from_le_bytes([self.bytes[off], self.bytes[off + 1]]))
93 }
94
95 #[must_use]
98 pub fn slice_at(&self, addr: u64) -> Option<&[u8]> {
99 let off = self.offset_of(addr)?;
100 Some(&self.bytes[off..])
101 }
102
103 #[must_use]
105 pub fn bounds(&self) -> (VAddr, VAddr) {
106 (VAddr(self.start()), VAddr(self.end()))
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn read_and_slice() {
116 let img = RawImage::new(vec![0xAA, 0xBB, 0xCC, 0xDD], 0xFF00);
117 assert_eq!(img.read_u8(0xFF00).unwrap(), 0xAA);
118 assert_eq!(img.read_u16_le(0xFF00).unwrap(), 0xBBAA);
119 assert_eq!(img.slice_at(0xFF02), Some(&[0xCC, 0xDD][..]));
120 assert!(matches!(img.read_u8(0xFEFF), Err(Error::OutOfRange { .. })));
121 }
122
123 #[test]
126 fn reset_vector_layout() {
127 let mut bytes = vec![0; 256];
129 bytes[0xFC] = 0x00; bytes[0xFD] = 0xFF; let img = RawImage::new(bytes, 0xFF00);
132 assert_eq!(img.read_u16_le(0xFFFC).unwrap(), 0xFF00);
133 }
134}