From 8e5aee0bbf01db91795305d7e4d34f66a261e6f7 Mon Sep 17 00:00:00 2001 From: xenofem Date: Wed, 6 Mar 2024 21:13:17 -0500 Subject: [PATCH] bytediff --- Cargo.lock | 7 +++ Cargo.toml | 1 + src/bin/bytediff.rs | 136 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 src/bin/bytediff.rs diff --git a/Cargo.lock b/Cargo.lock index e692060..54b001e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -59,6 +59,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "anyhow" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" + [[package]] name = "clap" version = "4.4.2" @@ -109,6 +115,7 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" name = "evenmoreutils" version = "0.2.0" dependencies = [ + "anyhow", "clap", "lazy_static", "regex", diff --git a/Cargo.toml b/Cargo.toml index 72bdbd1..b938abb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ authors = ["xenofem "] license = "MIT" [dependencies] +anyhow = "1.0" clap = { version = "4.3", features = ["derive"] } lazy_static = "1.4" regex = "1.7" diff --git a/src/bin/bytediff.rs b/src/bin/bytediff.rs new file mode 100644 index 0000000..e63c600 --- /dev/null +++ b/src/bin/bytediff.rs @@ -0,0 +1,136 @@ +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(()) +}