1use std::{
5 error,
6 fmt::{self, Write},
7};
8
9pub struct DisplayHex<'a>(&'a [u8]);
10
11pub fn display(data: &[u8]) -> DisplayHex<'_> {
12 DisplayHex(data)
13}
14
15impl fmt::Display for DisplayHex<'_> {
16 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17 for byte in self.0 {
18 write!(f, "{:02x}", byte)?;
19 }
20 Ok(())
21 }
22}
23
24pub fn encode(data: &[u8]) -> String {
25 let mut result = String::with_capacity(data.len() * 2);
26 for byte in data {
27 write!(result, "{:02x}", byte).unwrap();
28 }
29 result
30}
31
32pub fn decode(hex: &str) -> Result<Vec<u8>, DecodeError> {
33 let hex = hex.trim();
34
35 if hex.is_empty() {
36 return Ok(Vec::new());
37 }
38
39 if !hex.len().is_multiple_of(2) {
40 return Err(DecodeError::OddLength);
41 }
42
43 let mut result = Vec::with_capacity(hex.len() / 2);
44
45 for chunk in hex.as_bytes().chunks(2) {
46 let high = decode_hex_digit(chunk[0])?;
47 let low = decode_hex_digit(chunk[1])?;
48 result.push((high << 4) | low);
49 }
50
51 Ok(result)
52}
53
54fn decode_hex_digit(byte: u8) -> Result<u8, DecodeError> {
55 match byte {
56 b'0'..=b'9' => Ok(byte - b'0'),
57 b'a'..=b'f' => Ok(byte - b'a' + 10),
58 b'A'..=b'F' => Ok(byte - b'A' + 10),
59 _ => Err(DecodeError::InvalidCharacter(byte as char)),
60 }
61}
62
63#[derive(Debug)]
64pub enum DecodeError {
65 InvalidCharacter(char),
66 OddLength,
67}
68
69impl fmt::Display for DecodeError {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 match self {
72 DecodeError::InvalidCharacter(ch) => {
73 write!(f, "Invalid hex character: '{}'", ch)
74 }
75 DecodeError::OddLength => {
76 write!(f, "Hex string has odd length")
77 }
78 }
79 }
80}
81
82impl error::Error for DecodeError {}
83
84#[cfg(test)]
85pub mod tests {
86 use super::*;
87
88 #[test]
89 fn test_encode() {
90 assert_eq!(encode(b"Hello"), "48656c6c6f");
91 assert_eq!(encode(b""), "");
92 assert_eq!(encode(&[0x00, 0xFF, 0x42]), "00ff42");
93 }
94
95 #[test]
96 fn test_decode() {
97 assert_eq!(decode("48656c6c6f").unwrap(), b"Hello");
98 assert_eq!(decode("48656C6C6F").unwrap(), b"Hello");
99 assert_eq!(decode("").unwrap(), b"");
100 assert_eq!(decode("00ff42").unwrap(), vec![0x00, 0xFF, 0x42]);
101 }
102
103 #[test]
104 fn test_decode_errors() {
105 assert!(decode("xyz").is_err());
106 assert!(decode("48656c6c6").is_err()); }
108
109 #[test]
110 fn test_roundtrip() {
111 let data = b"Hello, World! \x00\x01\x02\xFF";
112 let encoded = encode(data);
113 let decoded = decode(&encoded).unwrap();
114 assert_eq!(decoded, data);
115 }
116}