1use std::path::PathBuf;
9
10pub type Result<T, E = Error> = std::result::Result<T, E>;
12
13#[derive(Debug, thiserror::Error)]
20pub enum Error {
21 #[error("I/O error on {path}: {source}")]
22 Io {
23 path: PathBuf,
24 #[source]
25 source: std::io::Error,
26 },
27
28 #[error(
29 "round-trip failed: output differs from input at byte {offset} \
30 (input=0x{input_byte:02x}, output=0x{output_byte:02x}); {extra} more byte(s) differ"
31 )]
32 RoundTripMismatch {
33 offset: usize,
34 input_byte: u8,
35 output_byte: u8,
36 extra: usize,
37 },
38
39 #[error("round-trip failed: output length {output_len} differs from input length {input_len}")]
40 RoundTripLengthMismatch { input_len: usize, output_len: usize },
41}
42
43#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
49pub struct VAddr(pub u64);
50
51impl std::fmt::Debug for VAddr {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 write!(f, "VAddr(0x{:x})", self.0)
54 }
55}
56
57impl std::fmt::Display for VAddr {
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 write!(f, "0x{:x}", self.0)
60 }
61}
62
63pub fn assert_bytes_equal(input: &[u8], output: &[u8]) -> Result<()> {
69 if input.len() != output.len() {
70 return Err(Error::RoundTripLengthMismatch {
71 input_len: input.len(),
72 output_len: output.len(),
73 });
74 }
75 let Some(offset) = input.iter().zip(output).position(|(a, b)| a != b) else {
76 return Ok(());
77 };
78 let extra = input[offset + 1..]
79 .iter()
80 .zip(&output[offset + 1..])
81 .filter(|(a, b)| a != b)
82 .count();
83 Err(Error::RoundTripMismatch {
84 offset,
85 input_byte: input[offset],
86 output_byte: output[offset],
87 extra,
88 })
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn equal_buffers_pass() {
97 assert!(assert_bytes_equal(b"abc", b"abc").is_ok());
98 }
99
100 #[test]
101 fn length_mismatch_reports_lengths() {
102 let err = assert_bytes_equal(b"abc", b"abcd").unwrap_err();
103 assert!(matches!(
104 err,
105 Error::RoundTripLengthMismatch {
106 input_len: 3,
107 output_len: 4
108 }
109 ));
110 }
111
112 #[test]
113 fn byte_mismatch_reports_offset_and_count() {
114 let err = assert_bytes_equal(b"abcde", b"abXdY").unwrap_err();
115 match err {
116 Error::RoundTripMismatch {
117 offset,
118 input_byte,
119 output_byte,
120 extra,
121 } => {
122 assert_eq!(offset, 2);
123 assert_eq!(input_byte, b'c');
124 assert_eq!(output_byte, b'X');
125 assert_eq!(extra, 1);
126 }
127 other => panic!("unexpected error: {other:?}"),
128 }
129 }
130}