rustolio_utils/bytes/
hex.rs1use bytes::Bytes;
12
13pub fn encode(bytes: impl AsRef<[u8]>) -> String {
14 const HEX_CHARS: &[u8; 16] = b"0123456789abcdef";
15
16 let bytes = bytes.as_ref();
17 let mut hex_string = String::with_capacity(bytes.len() * 2);
18
19 for &byte in bytes {
20 hex_string.push(HEX_CHARS[(byte >> 4) as usize] as char);
21 hex_string.push(HEX_CHARS[(byte & 0xF) as usize] as char);
22 }
23 hex_string
24}
25
26pub fn decode(hex: &str) -> Result<Bytes, &'static str> {
27 fn hex_char_to_value(c: u8) -> Result<u8, &'static str> {
28 match c {
29 b'0'..=b'9' => Ok(c - b'0'),
30 b'a'..=b'f' => Ok(c - b'a' + 10),
31 b'A'..=b'F' => Ok(c - b'A' + 10),
32 _ => Err("Invalid hex character"),
33 }
34 }
35
36 let hex = hex.as_bytes();
37
38 if !hex.len().is_multiple_of(2) {
40 return Err("Hex string must have even length");
41 }
42
43 let mut bytes = Vec::with_capacity(hex.len() * 2);
44
45 for i in (0..hex.len()).step_by(2) {
46 let high = hex_char_to_value(hex[i])?;
47 let low = hex_char_to_value(hex[i + 1])?;
48 bytes.push((high << 4) | low);
49 }
50
51 Ok(Bytes::from_owner(bytes))
52}
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57
58 #[test]
59 fn test_to_hex_empty() {
60 let empty = vec![];
61 assert_eq!(encode(empty), "");
62
63 let empty_array: [u8; 0] = [];
64 assert_eq!(encode(empty_array), "");
65 }
66
67 #[test]
68 fn test_to_hex_single_byte() {
69 let single = vec![0x00];
70 assert_eq!(encode(single), "00");
71
72 let single2 = vec![0xFF];
73 assert_eq!(encode(single2), "ff");
74
75 let single3 = vec![0x0A];
76 assert_eq!(encode(single3), "0a");
77
78 let single4 = vec![0xA0];
79 assert_eq!(encode(single4), "a0");
80 }
81
82 #[test]
83 fn test_to_hex_multiple_bytes() {
84 let bytes = vec![0x12, 0x34, 0x56, 0x78];
85 assert_eq!(encode(bytes), "12345678");
86
87 let bytes2 = vec![0xDE, 0xAD, 0xBE, 0xEF];
88 assert_eq!(encode(bytes2), "deadbeef");
89
90 let bytes3 = vec![0x00, 0xFF, 0x80, 0x7F];
91 assert_eq!(encode(bytes3), "00ff807f");
92 }
93
94 #[test]
95 fn test_to_hex_with_arrays() {
96 let array: [u8; 4] = [0x12, 0x34, 0x56, 0x78];
97 assert_eq!(encode(array), "12345678");
98
99 let slice: &[u8] = &[0xDE, 0xAD, 0xBE, 0xEF];
100 assert_eq!(encode(slice), "deadbeef");
101 }
102
103 #[test]
104 fn test_from_hex_empty() {
105 let empty = decode("").unwrap();
106 assert_eq!(empty, vec![]);
107 }
108
109 #[test]
110 fn test_from_hex_single_byte() {
111 let single = decode("00").unwrap();
112 assert_eq!(single, vec![0x00]);
113
114 let single2 = decode("ff").unwrap();
115 assert_eq!(single2, vec![0xFF]);
116
117 let single3 = decode("0a").unwrap();
118 assert_eq!(single3, vec![0x0A]);
119
120 let single4 = decode("A0").unwrap();
121 assert_eq!(single4, vec![0xA0]);
122 }
123
124 #[test]
125 fn test_from_hex_multiple_bytes() {
126 let bytes = decode("12345678").unwrap();
127 assert_eq!(bytes, vec![0x12, 0x34, 0x56, 0x78]);
128
129 let bytes2 = decode("deadbeef").unwrap();
130 assert_eq!(bytes2, vec![0xDE, 0xAD, 0xBE, 0xEF]);
131
132 let bytes3 = decode("00FF807F").unwrap();
133 assert_eq!(bytes3, vec![0x00, 0xFF, 0x80, 0x7F]);
134
135 let bytes4 = decode("DeAdBeEf").unwrap();
137 assert_eq!(bytes4, vec![0xDE, 0xAD, 0xBE, 0xEF]);
138 }
139
140 #[test]
141 fn test_from_hex_odd_length() {
142 let result = decode("123");
143 assert!(result.is_err());
144 assert_eq!(result.err().unwrap(), "Hex string must have even length");
145
146 let result2 = decode("1");
147 assert!(result2.is_err());
148 assert_eq!(result2.err().unwrap(), "Hex string must have even length");
149
150 let result3 = decode("12345");
151 assert!(result3.is_err());
152 assert_eq!(result3.err().unwrap(), "Hex string must have even length");
153 }
154
155 #[test]
156 fn test_from_hex_invalid_characters() {
157 let result = decode("1g");
159 assert!(result.is_err());
160 assert_eq!(result.err().unwrap(), "Invalid hex character");
161
162 let result2 = decode("az");
164 assert!(result2.is_err());
165 assert_eq!(result2.err().unwrap(), "Invalid hex character");
166
167 let result3 = decode("AG");
169 assert!(result3.is_err());
170 assert_eq!(result3.err().unwrap(), "Invalid hex character");
171
172 let result4 = decode("!@");
174 assert!(result4.is_err());
175 assert_eq!(result4.err().unwrap(), "Invalid hex character");
176
177 let result5 = decode("12 34");
179 assert!(result5.is_err());
180 assert_eq!(result5.err().unwrap(), "Invalid hex character");
181 }
182
183 #[test]
184 fn test_round_trip() {
185 let original = Bytes::from_static(&[
187 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD,
188 0xEE, 0xFF,
189 ]);
190 let hex = encode(original.clone());
191 let decoded = decode(&hex).unwrap();
192 assert_eq!(original, decoded);
193
194 let uppercase_hex = hex.to_uppercase();
196 let decoded_upper = decode(&uppercase_hex).unwrap();
197 assert_eq!(original, decoded_upper);
198 }
199
200 #[test]
218 fn test_edge_cases() {
219 let zeros = vec![0u8; 100];
221 let hex = encode(zeros.clone());
222 assert_eq!(hex.len(), 200);
223 assert!(hex.chars().all(|c| c == '0'));
224 let decoded = decode(&hex).unwrap();
225 assert_eq!(zeros, decoded);
226
227 let all_fs = vec![0xFFu8; 50];
229 let hex = encode(all_fs.clone());
230 assert_eq!(hex.len(), 100);
231 assert!(hex.chars().all(|c| c == 'f'));
232 let decoded = decode(&hex).unwrap();
233 assert_eq!(all_fs, decoded);
234
235 let pattern: Vec<u8> = (0..=255).collect();
237 let hex = encode(pattern.clone());
238 let decoded = decode(&hex).unwrap();
239 assert_eq!(pattern, decoded);
240 }
241}