Import hex-literal 0.4.1
[platform/upstream/rust-hex-literal.git] / src / 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
8 const 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
25 const 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)]
41 pub 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)]
59 pub 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]
81 macro_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 }