miden_crypto/word/
macros.rs

1use super::{Felt, StarkField};
2
3// MACROS
4// ================================================================================================
5
6/// Construct a new [Word](super::Word) from a hex value.
7///
8/// Expects a '0x' prefixed hex string followed by up to 64 hex digits.
9#[macro_export]
10macro_rules! word {
11    ($hex:expr) => {{
12        let felts: [$crate::Felt; 4] = match $crate::word::parse_hex_string_as_word($hex) {
13            Ok(v) => v,
14            Err(e) => panic!("{}", e),
15        };
16
17        $crate::Word::new(felts)
18    }};
19}
20
21/// Parses a hex string into a `[Felt; 4]` array.
22pub const fn parse_hex_string_as_word(hex: &str) -> Result<[Felt; 4], &'static str> {
23    const fn parse_hex_digit(digit: u8) -> Result<u8, &'static str> {
24        match digit {
25            b'0'..=b'9' => Ok(digit - b'0'),
26            b'A'..=b'F' => Ok(digit - b'A' + 0x0a),
27            b'a'..=b'f' => Ok(digit - b'a' + 0x0a),
28            _ => Err("Invalid hex character"),
29        }
30    }
31    // Enforce and skip the '0x' prefix.
32    let hex_bytes = match hex.as_bytes() {
33        [b'0', b'x', rest @ ..] => rest,
34        _ => return Err("Hex string must have a \"0x\" prefix"),
35    };
36
37    if hex_bytes.len() > 64 {
38        return Err("Hex string has more than 64 characters");
39    }
40
41    let mut felts = [0u64; 4];
42    let mut i = 0;
43    while i < hex_bytes.len() {
44        let hex_digit = match parse_hex_digit(hex_bytes[i]) {
45            // SAFETY: u8 cast to u64 is safe. We cannot use u64::from in const context so we
46            // are forced to cast.
47            Ok(v) => v as u64,
48            Err(e) => return Err(e),
49        };
50
51        // This digit's nibble offset within the felt. We need to invert the nibbles per
52        // byte for endianness reasons i.e. ABCD -> BADC.
53        let inibble = if i.is_multiple_of(2) {
54            (i + 1) % 16
55        } else {
56            (i - 1) % 16
57        };
58
59        let value = hex_digit << (inibble * 4);
60        felts[i / 2 / 8] += value;
61
62        i += 1;
63    }
64
65    // Ensure each felt is within bounds as `Felt::new` silently wraps around.
66    // This matches the behavior of `Word::try_from(String)`.
67    let mut idx = 0;
68    while idx < felts.len() {
69        if felts[idx] >= Felt::MODULUS {
70            return Err("Felt overflow");
71        }
72        idx += 1;
73    }
74
75    Ok([
76        Felt::new(felts[0]),
77        Felt::new(felts[1]),
78        Felt::new(felts[2]),
79        Felt::new(felts[3]),
80    ])
81}