bytediff
This commit is contained in:
		
							parent
							
								
									81518f48c9
								
							
						
					
					
						commit
						8e5aee0bbf
					
				
					 3 changed files with 144 additions and 0 deletions
				
			
		
							
								
								
									
										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…
	
	Add table
		Add a link
		
	
		Reference in a new issue