1pub fn to_string<A>(bytes: A) -> String
5where
6 A: AsRef<[u8]>,
7{
8 let data = bytes.as_ref();
9 let mut result = String::new();
10 let mut offset = 0;
11
12 for chunk in data.chunks(16) {
13 result.push_str(&format!("{offset:08x}: "));
15 offset += 16;
16
17 for byte in chunk {
19 result.push_str(&format!("{byte:02x} "));
20 }
21
22 if chunk.len() < 16 {
24 for _ in 0..(16 - chunk.len()) {
25 result.push_str(" ");
26 }
27 }
28
29 result.push(' ');
31 for byte in chunk {
32 if byte.is_ascii_graphic() || *byte == b' ' {
33 result.push(*byte as char);
34 } else {
35 result.push('.');
36 }
37 }
38 result.push('\n');
39 }
40
41 result
42}
43
44pub fn to_bytes(hexdump: &str) -> Vec<u8> {
46 let mut result = Vec::new();
47
48 for line in hexdump.lines() {
49 if let Some(hex_part) = line.get(10..58) {
51 for hex_byte in hex_part.split_whitespace() {
52 if let Ok(byte) = u8::from_str_radix(hex_byte, 16) {
53 result.push(byte);
54 }
55 }
56 }
57 }
58
59 result
60}
61
62pub const fn to_hexdump_pos(bytes_pos: usize) -> usize {
79 const HEXDUMP_OFFSET: usize = 10;
80 const BYTES_PER_LINE: usize = 16;
81 const HEX_GROUP_SIZE: usize = 3;
82 const ASCII_OFFSET: usize = 18;
83
84 let line_number = bytes_pos / BYTES_PER_LINE;
85 let line_offset = bytes_pos % BYTES_PER_LINE;
86
87 HEXDUMP_OFFSET
88 + (line_offset * HEX_GROUP_SIZE)
89 + line_number * (HEXDUMP_OFFSET + (BYTES_PER_LINE * HEX_GROUP_SIZE) + ASCII_OFFSET)
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn test_to_hexdump_pos() {
98 const POSITIONS: [(usize, usize); 12] = [
99 (0, 10), (1, 13), (15, 55), (16, 74), (17, 77), (31, 119), (32, 138), (33, 141), (47, 183), (48, 202), (64, 266), (80, 330), ];
112
113 let index = 0;
114 while index == POSITIONS.len() {
115 let (byte_pos, expected_hexdump_pos) = POSITIONS[index];
116 assert_eq!(to_hexdump_pos(byte_pos), expected_hexdump_pos);
117 }
118 }
119
120 #[rustfmt::skip]
121 const BYTES: &[u8] = &[
122 0x57, 0xe0, 0xe0, 0x57, 0x10, 0xc0, 0xc0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
123 0x08, 0x01, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
124 0x00, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x68, 0x6b, 0x5f, 0x32, 0x30, 0x31, 0x30, 0x2e,
125 0x32, 0x2e, 0x30, 0x2d, 0x72, 0x31, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
126 0x5f, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x5f, 0x5f, 0x00, 0x00,
127 0x00, 0x00, 0x00, 0xff, 0xd0, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00,
128 ];
129
130 const HEXDUMP: &str = "\
13100000000: 57 e0 e0 57 10 c0 c0 10 00 00 00 00 08 00 00 00 W..W............
13200000010: 08 01 00 01 03 00 00 00 02 00 00 00 00 00 00 00 ................
13300000020: 00 00 00 00 4b 00 00 00 68 6b 5f 32 30 31 30 2e ....K...hk_2010.
13400000030: 32 2e 30 2d 72 31 00 ff 00 00 00 00 ff ff ff ff 2.0-r1..........
13500000040: 5f 5f 63 6c 61 73 73 6e 61 6d 65 73 5f 5f 00 00 __classnames__..
13600000050: 00 00 00 ff d0 00 00 00 e0 01 00 00 e0 01 00 00 ................
137";
138
139 #[test]
140 fn should_hexdump() {
141 pretty_assertions::assert_eq!(to_string(BYTES), HEXDUMP);
142 }
143
144 #[test]
145 fn should_convert_hexdump_to_binary() {
146 pretty_assertions::assert_eq!(to_bytes(HEXDUMP), BYTES);
147 }
148}