summaryrefslogtreecommitdiff
path: root/utils/hex_display
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-02-24 12:29:31 +0800
committer魏曹先生 <1992414357@qq.com>2026-02-24 12:29:31 +0800
commitfbf4054ea8e11bae65196b7069c56375ca4e79b9 (patch)
tree4d54f17f214ea196ac53e5b16a222958d19d6b6a /utils/hex_display
parenteec323baf28f1a588f835aa773b77e019f91446d (diff)
Add hex_display utility crate
Diffstat (limited to 'utils/hex_display')
-rw-r--r--utils/hex_display/Cargo.toml6
-rw-r--r--utils/hex_display/src/lib.rs96
-rw-r--r--utils/hex_display/src/macros.rs9
-rw-r--r--utils/hex_display/src/test.rs21
4 files changed, 132 insertions, 0 deletions
diff --git a/utils/hex_display/Cargo.toml b/utils/hex_display/Cargo.toml
new file mode 100644
index 0000000..0e096d7
--- /dev/null
+++ b/utils/hex_display/Cargo.toml
@@ -0,0 +1,6 @@
+[package]
+name = "hex_display"
+edition = "2024"
+version.workspace = true
+
+[dependencies]
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<u8>) -> 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);
+ }
+}