1use std::fmt;
3
4pub trait ToHex {
6 fn to_hex(&self) -> String;
9}
10
11const CHARS: &[u8] = b"0123456789abcdef";
12
13impl ToHex for [u8] {
17 fn to_hex(&self) -> String {
18 let mut v = Vec::with_capacity(self.len() * 2);
19 for &byte in self {
20 v.push(CHARS[(byte >> 4) as usize]);
21 v.push(CHARS[(byte & 0xf) as usize]);
22 }
23
24 unsafe { String::from_utf8_unchecked(v) }
25 }
26}
27
28pub trait FromHex: Sized {
29 type Error;
30
31 fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error>;
32}
33
34#[derive(Copy, Clone, Debug)]
36pub enum FromHexError {
37 InvalidHexCharacter {
39 c: char,
40 index: usize,
41 },
42 InvalidHexLength,
44 OddLength,
45}
46
47impl std::error::Error for FromHexError {
48 fn description(&self) -> &str {
49 match *self {
50 Self::InvalidHexCharacter { .. } => "invalid character",
51 Self::OddLength => "odd number of digits",
52 Self::InvalidHexLength => "invalid hex length",
53 }
54 }
55}
56
57impl fmt::Display for FromHexError {
58 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59 match *self {
60 Self::InvalidHexCharacter { c, index } => {
61 write!(f, "Invalid character '{}' at position {}", c, index)
62 }
63 Self::OddLength => write!(f, "Odd number of digits"),
64 Self::InvalidHexLength => write!(f, "Invalid hex length"),
65 }
66 }
67}
68
69fn val(c: u8, idx: usize) -> Result<u8, FromHexError> {
70 match c {
71 b'A'..=b'F' => Ok(c - b'A' + 10),
72 b'a'..=b'f' => Ok(c - b'a' + 10),
73 b'0'..=b'9' => Ok(c - b'0'),
74 _ => Err(FromHexError::InvalidHexCharacter {
75 c: c as char,
76 index: idx,
77 }),
78 }
79}
80
81impl FromHex for Vec<u8> {
82 type Error = FromHexError;
83
84 fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, FromHexError> {
85 let hex = hex.as_ref();
86 if hex.len() % 2 != 0 {
87 return Err(FromHexError::OddLength);
88 }
89
90 hex.chunks(2)
91 .enumerate()
92 .map(|(i, pair)| Ok(val(pair[0], 2 * i)? << 4 | val(pair[1], 2 * i + 1)?))
93 .collect()
94 }
95}