1use std::{io, mem};
4
5#[rustfmt::skip]
7static DECODE_TABLE: [u8; 256] = {
8 const __: u8 = u8::MAX;
9 [
10 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, __, __, __, __, __, __, __, 10, 11, 12, 13, 14, 15, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 10, 11, 12, 13, 14, 15, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, ]
28};
29
30#[inline]
31pub(crate) fn decode(bytes: &[u8]) -> io::Result<Vec<u8>> {
32 if bytes.len() % 2 != 0 {
33 bail!("invalid length {}", bytes.len());
34 }
35 let mut out = vec![0; bytes.len() / 2];
36 let hex2byte = hex2byte;
40 decode_to_slice(bytes, &mut out, hex2byte)?;
41 Ok(out)
42}
43
44#[inline]
45fn decode_to_slice(
46 bytes: &[u8],
47 out: &mut [u8],
48 hex2byte: fn(&[u8], &mut u8) -> io::Result<()>,
49) -> io::Result<()> {
50 const CHUNK_SIZE: usize = mem::size_of::<usize>();
51 let mut bytes = bytes.chunks_exact(CHUNK_SIZE);
54 let mut out = out.chunks_exact_mut(CHUNK_SIZE / 2);
55 for (bytes, out) in bytes.by_ref().zip(out.by_ref()) {
56 let mut num = [0; CHUNK_SIZE / 2];
57 for (bytes, num) in bytes.chunks_exact(2).zip(&mut num) {
58 hex2byte(bytes, num)?;
59 }
60 out.copy_from_slice(&num);
61 }
62 let bytes = bytes.remainder();
64 let out = out.into_remainder();
65 for (bytes, out) in bytes.chunks_exact(2).zip(out) {
66 hex2byte(bytes, out)?;
67 }
68 Ok(())
69}
70
71#[inline]
72fn hex2byte(bytes: &[u8], out: &mut u8) -> io::Result<()> {
73 let upper = DECODE_TABLE[bytes[0] as usize];
74 let lower = DECODE_TABLE[bytes[1] as usize];
75 if upper == u8::MAX {
76 bail!("invalid hex character {}", bytes[0] as char);
77 }
78 if lower == u8::MAX {
79 bail!("invalid hex character {}", bytes[1] as char);
80 }
81 *out = (upper << 4) | lower;
82 Ok(())
83}
84
85#[cfg(test)]
86static ENCODE_LOWER_TABLE: &[u8; 16] = b"0123456789abcdef";
87#[cfg(test)]
88static ENCODE_UPPER_TABLE: &[u8; 16] = b"0123456789ABCDEF";
89#[cfg(test)]
90#[inline]
91const fn byte2hex(byte: u8, table: &[u8; 16]) -> [u8; 2] {
92 let upper = table[((byte & 0xF0) >> 4) as usize];
93 let lower = table[(byte & 0x0F) as usize];
94 [upper, lower]
95}
96
97#[cfg(test)]
98#[inline]
99fn hex2byte16(bytes: &[u8], out: &mut u8) -> io::Result<()> {
100 static DECODE_TABLE: [u16; 65536] = {
101 let mut table = [u16::MAX; 65536];
102 let mut i = 0;
103 loop {
104 let lower = u16::from_ne_bytes(byte2hex(i, ENCODE_LOWER_TABLE));
105 let upper = u16::from_ne_bytes(byte2hex(i, ENCODE_UPPER_TABLE));
106 table[lower as usize] = i as u16;
107 table[upper as usize] = i as u16;
108 if i == u8::MAX {
109 break;
110 }
111 i += 1;
112 }
113 table
114 };
115 let n = u16::from_ne_bytes(bytes.try_into().unwrap());
116 let num = DECODE_TABLE[n as usize];
117 if num == u16::MAX {
118 bail!(
119 "invalid hex character {}{}",
120 bytes[0] as char,
121 bytes[1] as char
122 );
123 }
124 #[allow(clippy::cast_possible_truncation)]
125 {
126 *out = num as u8;
127 }
128 Ok(())
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 fn encode_naive(bytes: &[u8], table: &[u8; 16]) -> Vec<u8> {
136 let mut out = vec![0; bytes.len() * 2];
137 for (&byte, out) in bytes.iter().zip(out.chunks_exact_mut(2)) {
138 out.copy_from_slice(&byte2hex(byte, table));
139 }
140 out
141 }
142 fn decode_naive(
143 bytes: &[u8],
144 hex2byte: fn(&[u8], &mut u8) -> io::Result<()>,
145 ) -> io::Result<Vec<u8>> {
146 if bytes.len() % 2 != 0 {
147 bail!("invalid length {}", bytes.len());
148 }
149 let mut out = vec![0; bytes.len() / 2];
150 for (bytes, out) in bytes.chunks_exact(2).zip(&mut out) {
151 hex2byte(bytes, out)?;
152 }
153 Ok(out)
154 }
155 #[inline]
156 fn decode16(bytes: &[u8]) -> io::Result<Vec<u8>> {
157 if bytes.len() % 2 != 0 {
158 bail!("invalid length {}", bytes.len());
159 }
160 let mut out = vec![0; bytes.len() / 2];
161 decode_to_slice(bytes, &mut out, hex2byte16)?;
162 Ok(out)
163 }
164
165 #[test]
166 fn decode_max() {
167 let x = &[!0];
168 let hex_lower = encode_naive(x, ENCODE_LOWER_TABLE);
169 assert_eq!(decode(&hex_lower).unwrap(), x);
170 assert_eq!(decode16(&hex_lower).unwrap(), x);
171 assert_eq!(decode_naive(&hex_lower, hex2byte).unwrap(), x);
172 assert_eq!(decode_naive(&hex_lower, hex2byte16).unwrap(), x);
173 }
174 ::quickcheck::quickcheck! {
175 fn decode_valid(x: String) -> bool {
176 if x.is_empty() {
177 return true;
178 }
179 let x = x.as_bytes();
180 let hex_lower = encode_naive(x, ENCODE_LOWER_TABLE);
181 assert_eq!(decode(&hex_lower).unwrap(), x);
182 assert_eq!(decode16(&hex_lower).unwrap(), x);
183 assert_eq!(decode_naive(&hex_lower, hex2byte).unwrap(), x);
184 assert_eq!(decode_naive(&hex_lower, hex2byte16).unwrap(), x);
185 let hex_upper = encode_naive(x, ENCODE_UPPER_TABLE);
186 assert_eq!(decode(&hex_upper).unwrap(), x);
187 assert_eq!(decode16(&hex_lower).unwrap(), x);
188 assert_eq!(decode_naive(&hex_upper, hex2byte).unwrap(), x);
189 assert_eq!(decode_naive(&hex_upper, hex2byte16).unwrap(), x);
190 true
191 }
192 fn decode_invalid(x: String) -> bool {
193 if x.is_empty() {
194 return true;
195 }
196 let mut x = x.as_bytes();
197 if x.len() < 2 {
198 return true;
199 }
200 if x.len() % 2 != 0 {
201 x = &x[..x.len() - 2];
202 }
203 let res = decode(x).ok();
204 assert_eq!(res, decode16(x).ok());
205 assert_eq!(res, decode_naive(x, hex2byte).ok());
206 assert_eq!(res, decode_naive(x, hex2byte16).ok());
207 true
208 }
209 }
210}