use std::fs::File; use std::io::{prelude::*, BufReader}; use anyhow::{anyhow, Context, Result}; struct ZipLonger { a: A, b: B, } impl Iterator for ZipLonger { type Item = (Option, Option); fn next(&mut self) -> Option { let a_next = self.a.next(); let b_next = self.b.next(); if a_next.is_none() && b_next.is_none() { return None; } Some((a_next, b_next)) } } impl ZipLonger { fn new(a: A, b: B) -> Self { Self { a, b } } } struct Chunks { n: usize, inner: I, } impl>> Iterator for Chunks { type Item = Result, F>; fn next(&mut self) -> Option { let mut values = Vec::new(); for _ in 0..(self.n) { let next = self.inner.next(); match next { Some(Ok(v)) => { values.push(v); } Some(Err(e)) => { return Some(Err(e)); } None => { break; } } } if values.is_empty() { return None; } Some(Ok(values)) } } impl Chunks { fn new(inner: I, n: usize) -> Self { Self { inner, n } } } fn color_byte_display(this: &[u8], that: &[u8], color: u8) -> String { let mut result = String::new(); for (i, b) in this.iter().enumerate() { if i % 2 == 0 && i > 0 { result.push(' '); } if that.get(i) == Some(b) { result.push_str(&format!("{:02x}", b)); } else { result.push_str(&format!("\x1b[1;{}m{:02x}\x1b[0m", color, b)); } } result } fn main() -> Result<()> { let mut args = std::env::args(); let name = args .next() .ok_or_else(|| anyhow!("Missing executable name in args"))?; let (a, b) = match (args.next(), args.next()) { (Some(a), Some(b)) => (a, b), _ => return Err(anyhow!("Usage: {} file file", name)), }; let a_bytes = BufReader::new( File::open(a.clone()).with_context(|| format!("Failed to open input file {}", a))?, ) .bytes(); let b_bytes = BufReader::new( File::open(b.clone()).with_context(|| format!("Failed to open input file {}", b))?, ) .bytes(); let mut in_differing_region = false; for (i, (rva, rvb)) in ZipLonger::new(Chunks::new(a_bytes, 16), Chunks::new(b_bytes, 16)).enumerate() { let va: Vec = rva .transpose() .with_context(|| format!("Error reading from input file {}", a))? .unwrap_or_default(); let vb: Vec = rvb .transpose() .with_context(|| format!("Error reading from input file {}", b))? .unwrap_or_default(); if va == vb { if in_differing_region { in_differing_region = false; println!("---"); } continue; } in_differing_region = true; println!( "{0:08x}: {1}\n{0:08x}: {2}", i * 16, color_byte_display(&va, &vb, 31), color_byte_display(&vb, &va, 32), ); } Ok(()) }