stackforge_core/utils/
hex.rs1use std::fmt::Write;
4
5pub fn hexdump(data: &[u8]) -> String {
14 let mut output = String::new();
15 let mut offset = 0;
16
17 for chunk in data.chunks(16) {
18 write!(output, "{:08x} ", offset).unwrap();
20
21 for (i, byte) in chunk.iter().enumerate() {
23 if i == 8 {
24 output.push(' ');
25 }
26 write!(output, "{:02x} ", byte).unwrap();
27 }
28
29 if chunk.len() < 16 {
31 for i in chunk.len()..16 {
32 if i == 8 {
33 output.push(' ');
34 }
35 output.push_str(" ");
36 }
37 }
38
39 output.push(' ');
41 output.push('|');
42 for byte in chunk {
43 if byte.is_ascii_graphic() || *byte == b' ' {
44 output.push(*byte as char);
45 } else {
46 output.push('.');
47 }
48 }
49 output.push('|');
50 output.push('\n');
51
52 offset += 16;
53 }
54
55 output
56}
57
58pub fn hexstr(data: &[u8]) -> String {
60 let mut output = String::with_capacity(data.len() * 2);
61 for byte in data {
62 write!(output, "{:02x}", byte).unwrap();
63 }
64 output
65}
66
67pub fn hexstr_sep(data: &[u8], sep: &str) -> String {
69 data.iter()
70 .map(|b| format!("{:02x}", b))
71 .collect::<Vec<_>>()
72 .join(sep)
73}
74
75pub fn parse_hex(s: &str) -> Result<Vec<u8>, String> {
77 let s = s.trim().replace(" ", "").replace(":", "").replace("-", "");
78
79 if s.len() % 2 != 0 {
80 return Err("hex string must have even length".to_string());
81 }
82
83 (0..s.len())
84 .step_by(2)
85 .map(|i| {
86 u8::from_str_radix(&s[i..i + 2], 16)
87 .map_err(|e| format!("invalid hex at position {}: {}", i, e))
88 })
89 .collect()
90}
91
92pub fn pretty_bytes(data: &[u8], indent: usize) -> String {
94 let indent_str = " ".repeat(indent);
95 let mut output = String::new();
96
97 for (i, chunk) in data.chunks(16).enumerate() {
98 if i > 0 {
99 output.push('\n');
100 }
101 output.push_str(&indent_str);
102
103 for byte in chunk {
104 write!(output, "{:02x} ", byte).unwrap();
105 }
106 }
107
108 output
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn test_hexdump() {
117 let data = b"Hello, World!";
118 let dump = hexdump(data);
119 assert!(dump.contains("48 65 6c 6c")); assert!(dump.contains("|Hello, World!|"));
121 }
122
123 #[test]
124 fn test_hexstr() {
125 let data = [0xde, 0xad, 0xbe, 0xef];
126 assert_eq!(hexstr(&data), "deadbeef");
127 assert_eq!(hexstr_sep(&data, ":"), "de:ad:be:ef");
128 }
129
130 #[test]
131 fn test_parse_hex() {
132 assert_eq!(parse_hex("deadbeef").unwrap(), vec![0xde, 0xad, 0xbe, 0xef]);
133 assert_eq!(
134 parse_hex("de:ad:be:ef").unwrap(),
135 vec![0xde, 0xad, 0xbe, 0xef]
136 );
137 assert_eq!(
138 parse_hex("de ad be ef").unwrap(),
139 vec![0xde, 0xad, 0xbe, 0xef]
140 );
141 assert!(parse_hex("dea").is_err()); }
143}