From fbf4054ea8e11bae65196b7069c56375ca4e79b9 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Tue, 24 Feb 2026 12:29:31 +0800 Subject: Add hex_display utility crate --- utils/hex_display/src/lib.rs | 96 +++++++++++++++++++++++++++++++++++++++++ utils/hex_display/src/macros.rs | 9 ++++ utils/hex_display/src/test.rs | 21 +++++++++ 3 files changed, 126 insertions(+) create mode 100644 utils/hex_display/src/lib.rs create mode 100644 utils/hex_display/src/macros.rs create mode 100644 utils/hex_display/src/test.rs (limited to 'utils/hex_display/src') diff --git a/utils/hex_display/src/lib.rs b/utils/hex_display/src/lib.rs new file mode 100644 index 0000000..833ab22 --- /dev/null +++ b/utils/hex_display/src/lib.rs @@ -0,0 +1,96 @@ +pub mod macros; +pub mod test; + +/// Format a byte slice into a hex display string +pub fn hex_display_slice(slice: &[u8]) -> String { + let mut result = String::new(); + + for (chunk_index, chunk) in slice.chunks(16).enumerate() { + draw_range(&mut result, chunk_index * 16, chunk); + } + + // Remove trailing newline + if result.ends_with('\n') { + result.pop(); + } + + result +} + +/// Format a byte vector into a hex display string +pub fn hex_display_vec(vec: Vec) -> String { + hex_display_slice(&vec) +} + +/// Draw one line of hex display +fn draw_range(result: &mut String, offset: usize, chunk: &[u8]) { + write_offset(result, offset); + write_data(result, chunk); + write_ascii(result, chunk); + result.push('\n'); +} + +/// Write the offset part +fn write_offset(result: &mut String, offset: usize) { + result.push_str(&format!("{:08x} ", offset)); +} + +/// Write the hex data part +fn write_data(result: &mut String, chunk: &[u8]) { + for (i, &byte) in chunk.iter().enumerate() { + result.push_str(&format!("{:02x}", byte)); + + if i < 15 { + result.push(' '); + } + + if i == 7 { + result.push(' '); + } + } + + if chunk.len() < 16 { + pad_data(result, chunk.len()); + } +} + +/// Pad missing hex data +fn pad_data(result: &mut String, actual_len: usize) { + let missing = 16 - actual_len; + + for i in 0..missing { + result.push_str(" "); + + if actual_len + i == 7 { + result.push(' '); + } + } + + if result.ends_with(' ') { + result.pop(); + } +} + +/// Write the ASCII representation part +fn write_ascii(result: &mut String, chunk: &[u8]) { + result.push_str(" |"); + + for &byte in chunk { + if byte >= 32 && byte <= 126 { + result.push(byte as char); + } else { + result.push('.'); + } + } + + pad_ascii(result, chunk.len()); + + result.push('|'); +} + +/// Pad the ASCII part to a fixed width +fn pad_ascii(result: &mut String, actual_len: usize) { + for _ in actual_len..16 { + result.push(' '); + } +} diff --git a/utils/hex_display/src/macros.rs b/utils/hex_display/src/macros.rs new file mode 100644 index 0000000..77f9d43 --- /dev/null +++ b/utils/hex_display/src/macros.rs @@ -0,0 +1,9 @@ +#[macro_export] +macro_rules! hex_display { + ($slice:expr) => { + hex_display::hex_display_slice($slice) + }; + ($vec:expr) => { + hex_display::hex_display_vec($vec) + }; +} diff --git a/utils/hex_display/src/test.rs b/utils/hex_display/src/test.rs new file mode 100644 index 0000000..d68cebe --- /dev/null +++ b/utils/hex_display/src/test.rs @@ -0,0 +1,21 @@ +#[cfg(test)] +mod tests { + use crate::hex_display_slice; + + #[test] + fn test_hex_display_slice() { + const RAW_DATA: [u8; 64] = [ + 0x01, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0d, + 0x01, 0x05, 0x00, 0x00, 0x64, 0x69, 0x72, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x32, 0x2e, + 0x74, 0x78, 0x74, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x09, 0x00, 0x00, 0x01, 0x00, 0x66, + 0x69, 0x6c, 0x65, 0x31, 0x2e, 0x74, 0x78, 0x74, + ]; + let expected = "\ +00000000 01 02 00 00 00 02 00 1b 00 00 00 0f 00 00 00 02 |................| +00000010 00 00 00 02 00 01 00 00 00 01 00 0d 01 05 00 00 |................| +00000020 64 69 72 2f 66 69 6c 65 32 2e 74 78 74 6f 74 68 |dir/file2.txtoth| +00000030 65 72 09 00 00 01 00 66 69 6c 65 31 2e 74 78 74 |er.....file1.txt|"; + assert_eq!(hex_display_slice(&RAW_DATA), expected); + } +} -- cgit