hex_literal/
lib.rs

1#![doc = include_str!("../README.md")]
2#![no_std]
3#![doc(
4    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
5    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
6)]
7
8const fn next_hex_char(string: &[u8], mut pos: usize) -> Option<(u8, usize)> {
9    while pos < string.len() {
10        let raw_val = string[pos];
11        pos += 1;
12        let val = match raw_val {
13            b'0'..=b'9' => raw_val - 48,
14            b'A'..=b'F' => raw_val - 55,
15            b'a'..=b'f' => raw_val - 87,
16            b' ' | b'\r' | b'\n' | b'\t' => continue,
17            0..=127 => panic!("Encountered invalid ASCII character"),
18            _ => panic!("Encountered non-ASCII character"),
19        };
20        return Some((val, pos));
21    }
22    None
23}
24
25const fn next_byte(string: &[u8], pos: usize) -> Option<(u8, usize)> {
26    let (half1, pos) = match next_hex_char(string, pos) {
27        Some(v) => v,
28        None => return None,
29    };
30    let (half2, pos) = match next_hex_char(string, pos) {
31        Some(v) => v,
32        None => panic!("Odd number of hex characters"),
33    };
34    Some(((half1 << 4) + half2, pos))
35}
36
37/// Compute length of a byte array which will be decoded from the strings.
38///
39/// This function is an implementation detail and SHOULD NOT be called directly!
40#[doc(hidden)]
41pub const fn len(strings: &[&[u8]]) -> usize {
42    let mut i = 0;
43    let mut len = 0;
44    while i < strings.len() {
45        let mut pos = 0;
46        while let Some((_, new_pos)) = next_byte(strings[i], pos) {
47            len += 1;
48            pos = new_pos;
49        }
50        i += 1;
51    }
52    len
53}
54
55/// Decode hex strings into a byte array of pre-computed length.
56///
57/// This function is an implementation detail and SHOULD NOT be called directly!
58#[doc(hidden)]
59pub const fn decode<const LEN: usize>(strings: &[&[u8]]) -> [u8; LEN] {
60    let mut i = 0;
61    let mut buf = [0u8; LEN];
62    let mut buf_pos = 0;
63    while i < strings.len() {
64        let mut pos = 0;
65        while let Some((byte, new_pos)) = next_byte(strings[i], pos) {
66            buf[buf_pos] = byte;
67            buf_pos += 1;
68            pos = new_pos;
69        }
70        i += 1;
71    }
72    if LEN != buf_pos {
73        panic!("Length mismatch. Please report this bug.");
74    }
75    buf
76}
77
78/// Macro for converting sequence of string literals containing hex-encoded data
79/// into an array of bytes.
80#[macro_export]
81macro_rules! hex {
82    ($($s:literal)*) => {{
83        const STRINGS: &[&'static [u8]] = &[$($s.as_bytes(),)*];
84        const LEN: usize = $crate::len(STRINGS);
85        const RES: [u8; LEN] = $crate::decode(STRINGS);
86        RES
87    }};
88}