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