hexdump? idk what this was

This commit is contained in:
Michael Zhang 2022-05-09 11:55:41 -05:00
commit 24dedd4b57
Signed by: michael
GPG Key ID: BDA47A31A3C8EE6B
4 changed files with 236 additions and 0 deletions

1
hexdump/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

5
hexdump/Cargo.lock generated Normal file
View File

@ -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"

9
hexdump/Cargo.toml Normal file
View File

@ -0,0 +1,9 @@
[package]
name = "hexdump"
version = "0.1.0"
authors = ["Michael Zhang <iptq@protonmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

221
hexdump/src/lib.rs Normal file
View File

@ -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<R: Read, W: Write>(
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::<Vec<u8>>(),
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
"##
);
}