1pub struct Hex<T>(pub T);
2
3struct Iter<'a> {
4 s: &'a [u8],
5 i: usize,
6}
7
8impl<'a> Iter<'a> {
9 const fn new(s: &'a str) -> Self {
10 Self {
11 s: s.as_bytes(),
12 i: 0,
13 }
14 }
15
16 const fn next(mut self) -> (Self, Option<u8>) {
17 let mut i = self.i;
18 let s = self.s;
19
20 macro_rules! next {
21 () => {{
22 if i < s.len() {
23 let b = s[i];
24 i += 1;
25 Some(b)
26 } else {
27 None
28 }
29 }};
30 }
31
32 while let Some(b) = next!() {
33 let high = match b {
34 b'0'..=b'9' => b - b'0',
35 b'a'..=b'f' => b - b'a' + 10,
36 b'A'..=b'F' => b - b'A' + 10,
37 b' ' | b'\r' | b'\n' | b'\t' => continue,
38 _ => panic!("invalid character"),
39 };
40
41 let low = match next!() {
42 None => panic!("expected even number of hex characters"),
43 Some(b) => match b {
44 b'0'..=b'9' => b - b'0',
45 b'a'..=b'f' => b - b'a' + 10,
46 b'A'..=b'F' => b - b'A' + 10,
47 _ => panic!("expected hex character"),
48 },
49 };
50
51 self.i = i;
52 let val = (high << 4) | low;
53 return (self, Some(val));
54 }
55 (self, None)
56 }
57}
58
59impl Hex<&[&str]> {
60 pub const fn output_len(&self) -> usize {
61 let mut i = 0;
62 let mut ans = 0;
63
64 while i < self.0.len() {
65 let mut iter = Iter::new(self.0[i]);
66 while let (next, Some(_)) = iter.next() {
67 iter = next;
68 ans += 1;
69 }
70 i += 1;
71 }
72 ans
73 }
74
75 pub const fn const_eval<const N: usize>(&self) -> [u8; N] {
76 let mut buf = [0; N];
77 let mut pos = 0;
78
79 let mut i = 0;
80 while i < self.0.len() {
81 let mut iter = Iter::new(self.0[i]);
82 while let (next, Some(val)) = iter.next() {
83 iter = next;
84 buf[pos] = val;
85 pos += 1;
86 }
87 i += 1;
88 }
89 assert!(pos == N);
90 buf
91 }
92}
93
94impl Hex<&str> {
95 pub const fn output_len(&self) -> usize {
96 let ss: &[&str] = &[self.0];
97 Hex(ss).output_len()
98 }
99 pub const fn const_eval<const N: usize>(&self) -> [u8; N] {
100 let ss: &[&str] = &[self.0];
101 Hex(ss).const_eval()
102 }
103}
104
105impl<const L: usize> Hex<[&str; L]> {
106 pub const fn output_len(&self) -> usize {
107 let ss: &[&str] = &self.0;
108 Hex(ss).output_len()
109 }
110 pub const fn const_eval<const N: usize>(&self) -> [u8; N] {
111 let ss: &[&str] = &self.0;
112 Hex(ss).const_eval()
113 }
114}
115
116#[macro_export]
150macro_rules! hex {
151 ($s: expr) => {{
152 const OUTPUT_LEN: usize = $crate::__ctfe::Hex($s).output_len();
153 const OUTPUT_BUF: [u8; OUTPUT_LEN] = $crate::__ctfe::Hex($s).const_eval();
154 OUTPUT_BUF
155 }};
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 #[test]
163 fn test_hex() {
164 const DATA: [u8; 4] = hex!("01020304");
165 assert_eq!(DATA, [1, 2, 3, 4]);
166
167 const DATA2: [u8; 4] = hex!("a1 b2 c3 d4");
168 assert_eq!(DATA2, [0xA1, 0xB2, 0xC3, 0xD4]);
169
170 const DATA3: [u8; 4] = hex!("E5 E6 90 92");
171 assert_eq!(DATA3, [0xE5, 0xE6, 0x90, 0x92]);
172
173 const DATA4: [u8; 4] = hex!(["0a0B", "0C0d"]);
174 assert_eq!(DATA4, [10, 11, 12, 13]);
175
176 const S1: &str = "00010203 04050607 08090a0b 0c0d0e0f";
177 const B1: &[u8] = &hex!(S1);
178 const B2: &[u8] = &hex!([
179 "00010203 04050607", "08090a0b 0c0d0e0f", ]);
182
183 assert_eq!(B1, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
184 assert_eq!(B2, B1);
185
186 const EMPTY: [u8; 0] = hex!("");
187 assert_eq!(EMPTY, []);
188
189 const WITH_NEWLINE: [u8; 2] = hex!("0A\n0B");
190 assert_eq!(WITH_NEWLINE, [10, 11]);
191
192 const WITH_TAB: [u8; 2] = hex!("0C\t0D");
193 assert_eq!(WITH_TAB, [12, 13]);
194 }
195
196 #[test]
197 fn test_hex_runtime() {
198 let strs: &[&str] = &["01", "02", "03"];
200 let hex_slice = Hex(strs);
201 assert_eq!(hex_slice.output_len(), 3);
202 let buf: [u8; 3] = hex_slice.const_eval();
203 assert_eq!(buf, [1, 2, 3]);
204
205 let single = "FF00";
207 let hex_str = Hex(single);
208 assert_eq!(hex_str.output_len(), 2);
209 let buf2: [u8; 2] = hex_str.const_eval();
210 assert_eq!(buf2, [0xFF, 0x00]);
211
212 let arr = ["AB", "CD"];
214 let hex_arr = Hex(arr);
215 assert_eq!(hex_arr.output_len(), 2);
216 let buf3: [u8; 2] = hex_arr.const_eval();
217 assert_eq!(buf3, [0xAB, 0xCD]);
218
219 let with_space = "12 34\t56\n78";
221 let hex_ws = Hex(with_space);
222 assert_eq!(hex_ws.output_len(), 4);
223 let buf4: [u8; 4] = hex_ws.const_eval();
224 assert_eq!(buf4, [0x12, 0x34, 0x56, 0x78]);
225
226 let mixed = "aAbBcC";
228 let hex_mixed = Hex(mixed);
229 assert_eq!(hex_mixed.output_len(), 3);
230 let buf5: [u8; 3] = hex_mixed.const_eval();
231 assert_eq!(buf5, [0xAA, 0xBB, 0xCC]);
232 }
233}