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