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
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//! This crate provides the `hex!` macro for converting hexadecimal string literals
//! to a byte array at compile time.
//!
//! # Examples
//! ```
//! use hexlit::hex;
//!
//! fn main() {
//! const DATA: [u8; 4] = hex!("01020304");
//! assert_eq!(DATA, [1, 2, 3, 4]);
//! assert_eq!(hex!("a1b2c3d4"), [0xA1, 0xB2, 0xC3, 0xD4]);
//! assert_eq!(hex!("E5 E6 90 92"), [0xE5, 0xE6, 0x90, 0x92]);
//! assert_eq!(hex!("0a0B0C0d"), [10, 11, 12, 13]);
//! }
//! ```
#![no_std]

#[macro_export]
macro_rules! hex {
    ($arg:expr) => {{
        const SPACE: u8 = 32;
        const NUM_SPACES: usize = {
            let data = $arg.as_bytes();
            let mut space_count: usize = 0;
            let mut char_index: usize = 0;
            while char_index < data.len() {
                if data[char_index] == SPACE {
                    space_count += 1;
                }
                char_index += 1;
            }
            space_count
        };

        ["Odd number of hex digits provided."][(($arg.len() - NUM_SPACES) % 2) as usize];
        const ARRAY_LENGTH: usize = ($arg.len() - NUM_SPACES) / 2;
        const RESULT: [u8; ARRAY_LENGTH] = {
            // Hack needed for const-eval to work.
            const fn always_true() -> bool {
                true
            }

            /// Converts a individual byte into its correct integer counter-part.
            const fn to_ordinal(input: u8) -> u8 {
                const ZERO: u8 = 48;
                const NINE: u8 = 57;
                const UPPER_A: u8 = 65;
                const UPPER_F: u8 = 70;
                const LOWER_A: u8 = 97;
                const LOWER_F: u8 = 102;
                match input {
                    ZERO..=NINE => input - '0' as u8,
                    UPPER_A..=UPPER_F => input - 'A' as u8 + 10,
                    LOWER_A..=LOWER_F => input - 'a' as u8 + 10,
                    _ => {
                        ["Invalid hex digit."][(always_true() as usize)];
                        0 // Unreachable
                    }
                }
            }

            /// Converts a hex-string to its byte array representation.
            const fn convert(s: &str) -> [u8; ARRAY_LENGTH] {
                let s = s.as_bytes();
                let mut data = [0u8; ARRAY_LENGTH];
                let mut data_index: usize = 0;
                let mut char_index: usize = 0;
                let string_length = s.len();
                while data_index < string_length && char_index + 1 < string_length {
                    if s[char_index] != SPACE {
                        data[data_index] =
                            to_ordinal(s[char_index]) * 16 + to_ordinal(s[char_index + 1]);
                        char_index += 2;
                        data_index += 1;
                    } else {
                        char_index += 1;
                    }
                }
                data
            }
            convert($arg)
        };
        RESULT
    }};
}

#[cfg(test)]
mod tests {
    use super::hex;

    #[test]
    fn test_leading_zeros() {
        assert_eq!(hex!("01020304"), [1, 2, 3, 4]);
    }

    #[test]
    fn test_alphanumeric_lower() {
        assert_eq!(hex!("a1b2c3d4"), [0xA1, 0xB2, 0xC3, 0xD4]);
    }

    #[test]
    fn test_alphanumeric_upper() {
        assert_eq!(hex!("E5E69092"), [0xE5, 0xE6, 0x90, 0x92]);
    }

    #[test]
    fn test_alphanumeric_mixed() {
        assert_eq!(hex!("0a0B0C0d"), [10, 11, 12, 13]);
    }

    #[test]
    fn test_leading_zeros_space() {
        assert_eq!(hex!("01 02 03 04"), [1, 2, 3, 4]);
    }

    #[test]
    fn test_alphanumeric_lower_space() {
        assert_eq!(hex!("a1 b2 c3 d4"), [0xA1, 0xB2, 0xC3, 0xD4]);
    }

    #[test]
    fn test_alphanumeric_upper_space() {
        assert_eq!(hex!("E5 E6 90 92"), [0xE5, 0xE6, 0x90, 0x92]);
    }

    #[test]
    fn test_alphanumeric_mixed_space() {
        assert_eq!(hex!("0a 0B 0C 0d"), [10, 11, 12, 13]);
    }
}