commit 24dedd4b57818f9f450e1856012f927c58e88b54 Author: Michael Zhang Date: Mon May 9 11:55:41 2022 -0500 hexdump? idk what this was diff --git a/hexdump/.gitignore b/hexdump/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/hexdump/.gitignore @@ -0,0 +1 @@ +/target diff --git a/hexdump/Cargo.lock b/hexdump/Cargo.lock new file mode 100644 index 0000000..a6d12e5 --- /dev/null +++ b/hexdump/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "hexdump" +version = "0.1.0" diff --git a/hexdump/Cargo.toml b/hexdump/Cargo.toml new file mode 100644 index 0000000..adf060e --- /dev/null +++ b/hexdump/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "hexdump" +version = "0.1.0" +authors = ["Michael Zhang "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/hexdump/src/lib.rs b/hexdump/src/lib.rs new file mode 100644 index 0000000..8325bd9 --- /dev/null +++ b/hexdump/src/lib.rs @@ -0,0 +1,221 @@ +use std::io::{Error, Read, Write}; + +const HEXCHARS: &[u8] = b"0123456789abcdef"; + +pub enum Size { + Word8(u8), + Word16(u16), + Word32(u32), + Word64(u64), +} + +pub fn hexdump( + mut w: W, + starting_addr: Size, + mut input: R, +) -> Result<(), Error> { + let (starting_value, mut offset) = match starting_addr { + Size::Word8(n) => (n as u64, (n % 16) as u8), + Size::Word16(n) => (n as u64, (n % 16) as u8), + Size::Word32(n) => (n as u64, (n % 16) as u8), + Size::Word64(n) => (n as u64, (n % 16) as u8), + }; + + // first row + match starting_addr { + Size::Word8(_) => write!(w, "{:>4}", "Addr")?, + Size::Word16(_) => write!(w, "{:>6}", "Addr")?, + Size::Word32(_) => write!(w, "{:>10}", "Addr")?, + Size::Word64(_) => write!(w, "{:>18}", "Addr")?, + }; + for (i, c) in HEXCHARS.iter().enumerate() { + if i == 8 { + write!(w, " ")?; + } + write!(w, " {}", *c as char)?; + } + write!(w, " ")?; + for (i, c) in HEXCHARS.iter().enumerate() { + if i == 8 { + write!(w, " ")?; + } + write!(w, "{}", *c as char)?; + } + write!(w, "\n")?; + + // write every row + let mut row_offset = starting_value; + let mut buf = vec![0; 16 - offset as usize]; + loop { + let is_first_line = if offset > 0 { + row_offset -= offset as u64; + true + } else { + false + }; + + if !is_first_line && buf.len() < 16 { + buf.resize(16, 0); + } + + let bytes = input.read(&mut buf)?; + if bytes == 0 { + break; + } + + match starting_addr { + Size::Word8(_) => write!(w, "0x{:02x}", row_offset)?, + Size::Word16(_) => write!(w, "0x{:04x}", row_offset)?, + Size::Word32(_) => write!(w, "0x{:08x}", row_offset)?, + Size::Word64(_) => write!(w, "0x{:16x}", row_offset)?, + }; + + if is_first_line { + write!(w, "{}", " ".repeat(offset as usize))?; + if offset > 8 { + write!(w, " ")?; + } + } + for (i, c) in buf.iter().enumerate() { + if (i + offset as usize) == 8 { + write!(w, " ")?; + } + if i < bytes { + write!(w, " {:02x}", c)?; + } else { + write!(w, " ")?; + } + } + write!(w, " ")?; + if is_first_line { + write!(w, "{}", " ".repeat(offset as usize))?; + if offset > 8 { + write!(w, " ")?; + } + } + for (i, c) in buf.iter().enumerate() { + if (i + offset as usize) == 8 { + write!(w, " ")?; + } + if i < bytes { + let ascii = match c { + 0 => '0', + b' ' | b'\n' | b'\r' | b'\t' => '_', + c if 0x20 < *c && *c < 0x7f => *c as char, + _ => '.', + }; + write!(w, "{}", ascii)?; + } else { + write!(w, " ")?; + } + } + + row_offset += 16; + offset = 0; + write!(w, "\n")?; + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::{hexdump, Size}; + use std::io::Cursor; + + macro_rules! generate_test { + ($test_name:ident, $start:expr, $input:expr, $expected:expr) => { + #[test] + fn $test_name() { + const EXPECTED: &[u8] = $expected; + let mut buf = Cursor::new(Vec::new()); + let input = Cursor::new($input); + hexdump(&mut buf, $start, input).unwrap(); + println!("{}", std::str::from_utf8(buf.get_ref()).unwrap()); + println!("{}", std::str::from_utf8(EXPECTED).unwrap()); + println!( + "{:?}", + std::str::from_utf8(EXPECTED) + .unwrap() + .trim_start_matches(std::str::from_utf8(buf.get_ref()).unwrap()) + ); + assert_eq!(buf.get_ref().as_slice(), &EXPECTED[..]); + } + }; + } + + generate_test!( + test1, + Size::Word8(0), + (0..=0x7f).collect::>(), + br##"Addr 0 1 2 3 4 5 6 7 8 9 a b c d e f 01234567 89abcdef +0x00 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 0....... .__.._.. +0x10 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f ........ ........ +0x20 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f _!"#$%&' ()*+,-./ +0x30 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 01234567 89:;<=>? +0x40 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFG HIJKLMNO +0x50 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f PQRSTUVW XYZ[\]^_ +0x60 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f `abcdefg hijklmno +0x70 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f pqrstuvw xyz{|}~. +"## + ); + + generate_test!( + test2, + Size::Word32(0x012c), + b"Hello, world!\n", + br##" Addr 0 1 2 3 4 5 6 7 8 9 a b c d e f 01234567 89abcdef +0x00000120 48 65 6c 6c Hell +0x00000130 6f 2c 20 77 6f 72 6c 64 21 0a o,_world !_ +"## + ); + + generate_test!( + test3, + Size::Word32(0x032c), + b"Hello, world!\nGoodbye, world!\n", + br##" Addr 0 1 2 3 4 5 6 7 8 9 a b c d e f 01234567 89abcdef +0x00000320 48 65 6c 6c Hell +0x00000330 6f 2c 20 77 6f 72 6c 64 21 0a 47 6f 6f 64 62 79 o,_world !_Goodby +0x00000340 65 2c 20 77 6f 72 6c 64 21 0a e,_world !_ +"## + ); + + generate_test!( + test4, + Size::Word32(0x0526), + b"Hello, world!\nGoodbye, world!\n", + br##" Addr 0 1 2 3 4 5 6 7 8 9 a b c d e f 01234567 89abcdef +0x00000520 48 65 6c 6c 6f 2c 20 77 6f 72 He llo,_wor +0x00000530 6c 64 21 0a 47 6f 6f 64 62 79 65 2c 20 77 6f 72 ld!_Good bye,_wor +0x00000540 6c 64 21 0a ld!_ +"## + ); + + generate_test!( + test5, + Size::Word16(0x0201), + b"Hello, world!\n", + br##" Addr 0 1 2 3 4 5 6 7 8 9 a b c d e f 01234567 89abcdef +0x0200 48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 21 0a Hello,_ world!_ +"## + ); + + generate_test!( + test6, + Size::Word8(0xf4), + b"foo", + br##"Addr 0 1 2 3 4 5 6 7 8 9 a b c d e f 01234567 89abcdef +0xf0 66 6f 6f foo +"## + ); + + generate_test!( + test7, + Size::Word32(0xff00ff0a), + b"foo", + br##" Addr 0 1 2 3 4 5 6 7 8 9 a b c d e f 01234567 89abcdef +0xff00ff00 66 6f 6f foo +"## + ); +}