bytediff
This commit is contained in:
parent
81518f48c9
commit
8e5aee0bbf
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -59,6 +59,12 @@ dependencies = [
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.80"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.4.2"
|
version = "4.4.2"
|
||||||
|
@ -109,6 +115,7 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||||
name = "evenmoreutils"
|
name = "evenmoreutils"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"regex",
|
"regex",
|
||||||
|
|
|
@ -6,6 +6,7 @@ authors = ["xenofem <xenofem@xeno.science>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow = "1.0"
|
||||||
clap = { version = "4.3", features = ["derive"] }
|
clap = { version = "4.3", features = ["derive"] }
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
regex = "1.7"
|
regex = "1.7"
|
||||||
|
|
136
src/bin/bytediff.rs
Normal file
136
src/bin/bytediff.rs
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{prelude::*, BufReader};
|
||||||
|
|
||||||
|
use anyhow::{anyhow, Context, Result};
|
||||||
|
|
||||||
|
struct ZipLonger<A: Iterator, B: Iterator> {
|
||||||
|
a: A,
|
||||||
|
b: B,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A: Iterator, B: Iterator> Iterator for ZipLonger<A, B> {
|
||||||
|
type Item = (Option<A::Item>, Option<B::Item>);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
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<A: Iterator, B: Iterator> ZipLonger<A, B> {
|
||||||
|
fn new(a: A, b: B) -> Self {
|
||||||
|
Self { a, b }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Chunks<I: Iterator> {
|
||||||
|
n: usize,
|
||||||
|
inner: I,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, F, I: Iterator<Item = Result<T, F>>> Iterator for Chunks<I> {
|
||||||
|
type Item = Result<Vec<T>, F>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
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<I: Iterator> Chunks<I> {
|
||||||
|
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<u8> = rva
|
||||||
|
.transpose()
|
||||||
|
.with_context(|| format!("Error reading from input file {}", a))?
|
||||||
|
.unwrap_or_default();
|
||||||
|
let vb: Vec<u8> = 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(())
|
||||||
|
}
|
Loading…
Reference in a new issue