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