Skip to main content

miden_field/word/
mod.rs

1//! A [Word] type used in the Miden protocol and associated utilities.
2
3use alloc::{string::String, vec::Vec};
4#[cfg(not(all(target_family = "wasm", miden)))]
5use core::fmt::Display;
6use core::{
7    cmp::Ordering,
8    hash::{Hash, Hasher},
9    ops::{Deref, DerefMut, Index, IndexMut, Range},
10    slice,
11};
12
13#[cfg(not(all(target_family = "wasm", miden)))]
14use miden_serde_utils::{
15    ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
16};
17use thiserror::Error;
18
19pub const WORD_SIZE_FELTS: usize = 4;
20pub const WORD_SIZE_BYTES: usize = 32;
21
22#[cfg(not(all(target_family = "wasm", miden)))]
23use p3_field::integers::QuotientMap;
24
25use super::Felt;
26use crate::utils::bytes_to_hex_string;
27
28mod lexicographic;
29pub use lexicographic::LexicographicWord;
30
31#[cfg(test)]
32mod tests;
33
34// WORD
35// ================================================================================================
36
37/// A unit of data consisting of 4 field elements.
38///
39/// For ordering a word with `Ord` the word's elements are treated as limbs of an integer
40/// in little-endian limb order and thus comparison starts from the most significant element.
41#[derive(Default, Copy, Clone, Eq, PartialEq)]
42#[cfg_attr(
43    not(all(target_family = "wasm", miden)),
44    derive(serde::Deserialize, serde::Serialize)
45)]
46#[cfg_attr(
47    not(all(target_family = "wasm", miden)),
48    serde(into = "String", try_from = "&str")
49)]
50#[repr(C)]
51#[cfg_attr(all(target_family = "wasm", miden), repr(align(16)))]
52pub struct Word {
53    /// The underlying elements of this word.
54    pub a: Felt,
55    pub b: Felt,
56    pub c: Felt,
57    pub d: Felt,
58    // The fields have to be public since the WIT->Rust bindings generation uses the fields
59    // directly.
60    // We cannot define this type as `Word([Felt;4])` since there is no struct tuple support
61    // and fixed array support is not complete in WIT. For the type remapping to work the
62    // bindings are expecting the remapped type to be the same shape as the one generated from
63    // WIT.
64    //
65    // see sdk/base-macros/wit/miden.wit in the compiler repo, so we have to define it like that
66    // here.
67}
68
69// Compile-time assertions to ensure `Word` has the same layout as `[Felt; 4]`. This is relied upon
70// in `as_elements_array`/`as_elements_array_mut`.
71const _: () = {
72    assert!(WORD_SIZE_FELTS == 4, "WORD_SIZE_FELTS is assumed to be 4");
73    assert!(WORD_SIZE_BYTES == 32, "WORD_SIZE_BYTES is assumed to be 32");
74    assert!(core::mem::size_of::<Word>() == WORD_SIZE_FELTS * core::mem::size_of::<Felt>());
75    assert!(core::mem::offset_of!(Word, a) == 0);
76    assert!(core::mem::offset_of!(Word, b) == core::mem::size_of::<Felt>());
77    assert!(core::mem::offset_of!(Word, c) == 2 * core::mem::size_of::<Felt>());
78    assert!(core::mem::offset_of!(Word, d) == 3 * core::mem::size_of::<Felt>());
79};
80
81impl core::fmt::Debug for Word {
82    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
83        f.debug_tuple("Word").field(&self.into_elements()).finish()
84    }
85}
86
87impl Word {
88    /// The serialized size of the word in bytes.
89    pub const SERIALIZED_SIZE: usize = WORD_SIZE_BYTES;
90
91    /// Creates a new [`Word`] from the given field elements.
92    pub const fn new(value: [Felt; WORD_SIZE_FELTS]) -> Self {
93        let [a, b, c, d] = value;
94        Self { a, b, c, d }
95    }
96
97    /// Returns the elements of this word as an array.
98    pub const fn into_elements(self) -> [Felt; WORD_SIZE_FELTS] {
99        [self.a, self.b, self.c, self.d]
100    }
101
102    /// Returns the elements of this word as an array reference.
103    ///
104    /// # Safety
105    /// This assumes the four fields of [`Word`] are laid out contiguously with no padding, in
106    /// the same order as `[Felt; 4]`.
107    fn as_elements_array(&self) -> &[Felt; WORD_SIZE_FELTS] {
108        unsafe { &*(&self.a as *const Felt as *const [Felt; WORD_SIZE_FELTS]) }
109    }
110
111    /// Returns the elements of this word as a mutable array reference.
112    ///
113    /// # Safety
114    /// This assumes the four fields of [`Word`] are laid out contiguously with no padding, in
115    /// the same order as `[Felt; 4]`.
116    fn as_elements_array_mut(&mut self) -> &mut [Felt; WORD_SIZE_FELTS] {
117        unsafe { &mut *(&mut self.a as *mut Felt as *mut [Felt; WORD_SIZE_FELTS]) }
118    }
119
120    /// Parses a hex string into a new [`Word`].
121    ///
122    /// The input must contain valid hex prefixed with `0x`. The input after the prefix
123    /// must contain between 0 and 64 characters (inclusive).
124    ///
125    /// The input is interpreted to have little-endian byte ordering. Nibbles are interpreted
126    /// to have big-endian ordering so that "0x10" represents Felt::new(16), not Felt::new(1).
127    ///
128    /// This function is usually used via the `word!` macro.
129    ///
130    /// ```
131    /// use miden_field::{Felt, Word, word};
132    /// let word = word!("0x1000000000000000200000000000000030000000000000004000000000000000");
133    /// assert_eq!(word, Word::new([Felt::new(16), Felt::new(32), Felt::new(48), Felt::new(64)]));
134    /// ```
135    #[cfg(not(all(target_family = "wasm", miden)))]
136    pub const fn parse(hex: &str) -> Result<Self, &'static str> {
137        const fn parse_hex_digit(digit: u8) -> Result<u8, &'static str> {
138            match digit {
139                b'0'..=b'9' => Ok(digit - b'0'),
140                b'A'..=b'F' => Ok(digit - b'A' + 0x0a),
141                b'a'..=b'f' => Ok(digit - b'a' + 0x0a),
142                _ => Err("Invalid hex character"),
143            }
144        }
145        // Enforce and skip the '0x' prefix.
146        let hex_bytes = match hex.as_bytes() {
147            [b'0', b'x', rest @ ..] => rest,
148            _ => return Err("Hex string must have a \"0x\" prefix"),
149        };
150
151        if hex_bytes.len() > 64 {
152            return Err("Hex string has more than 64 characters");
153        }
154
155        let mut felts = [0u64; 4];
156        let mut i = 0;
157        while i < hex_bytes.len() {
158            let hex_digit = match parse_hex_digit(hex_bytes[i]) {
159                // SAFETY: u8 cast to u64 is safe. We cannot use u64::from in const context so we
160                // are forced to cast.
161                Ok(v) => v as u64,
162                Err(e) => return Err(e),
163            };
164
165            // This digit's nibble offset within the felt. We need to invert the nibbles per
166            // byte to ensure little-endian ordering i.e. ABCD -> BADC.
167            let inibble = if i.is_multiple_of(2) {
168                (i + 1) % 16
169            } else {
170                (i - 1) % 16
171            };
172
173            let value = hex_digit << (inibble * 4);
174            felts[i / 2 / 8] += value;
175
176            i += 1;
177        }
178
179        // Ensure each felt is within bounds as `Felt::new` silently wraps around.
180        // This matches the behavior of `Word::try_from(String)`.
181        let mut idx = 0;
182        while idx < felts.len() {
183            if felts[idx] >= Felt::ORDER {
184                return Err("Felt overflow");
185            }
186            idx += 1;
187        }
188
189        Ok(Self::new([
190            Felt::new(felts[0]),
191            Felt::new(felts[1]),
192            Felt::new(felts[2]),
193            Felt::new(felts[3]),
194        ]))
195    }
196
197    /// Returns a new [Word] consisting of four ZERO elements.
198    pub const fn empty() -> Self {
199        Self::new([Felt::ZERO; WORD_SIZE_FELTS])
200    }
201
202    /// Returns true if the word consists of four ZERO elements.
203    pub fn is_empty(&self) -> bool {
204        let elements = self.as_elements_array();
205        elements[0] == Felt::ZERO
206            && elements[1] == Felt::ZERO
207            && elements[2] == Felt::ZERO
208            && elements[3] == Felt::ZERO
209    }
210
211    /// Returns the word as a slice of field elements.
212    pub fn as_elements(&self) -> &[Felt] {
213        self.as_elements_array()
214    }
215
216    /// Returns the word as a byte array.
217    pub fn as_bytes(&self) -> [u8; WORD_SIZE_BYTES] {
218        let mut result = [0; WORD_SIZE_BYTES];
219
220        let elements = self.as_elements_array();
221        result[..8].copy_from_slice(&elements[0].as_canonical_u64().to_le_bytes());
222        result[8..16].copy_from_slice(&elements[1].as_canonical_u64().to_le_bytes());
223        result[16..24].copy_from_slice(&elements[2].as_canonical_u64().to_le_bytes());
224        result[24..].copy_from_slice(&elements[3].as_canonical_u64().to_le_bytes());
225
226        result
227    }
228
229    /// Returns an iterator over the elements of multiple words.
230    pub fn words_as_elements_iter<'a, I>(words: I) -> impl Iterator<Item = &'a Felt>
231    where
232        I: Iterator<Item = &'a Self>,
233    {
234        words.flat_map(|d| d.as_elements().iter())
235    }
236
237    /// Returns all elements of multiple words as a slice.
238    pub fn words_as_elements(words: &[Self]) -> &[Felt] {
239        let len = words.len() * WORD_SIZE_FELTS;
240        unsafe { slice::from_raw_parts(words.as_ptr() as *const Felt, len) }
241    }
242
243    /// Returns hexadecimal representation of this word prefixed with `0x`.
244    pub fn to_hex(&self) -> String {
245        bytes_to_hex_string(self.as_bytes())
246    }
247
248    /// Returns internal elements of this word as a vector.
249    pub fn to_vec(&self) -> Vec<Felt> {
250        self.as_elements().to_vec()
251    }
252
253    /// Returns a copy of this word with its elements in reverse order.
254    pub fn reversed(&self) -> Self {
255        Word {
256            a: self.d,
257            b: self.c,
258            c: self.b,
259            d: self.a,
260        }
261    }
262}
263
264impl Hash for Word {
265    fn hash<H: Hasher>(&self, state: &mut H) {
266        state.write(&self.as_bytes());
267    }
268}
269
270impl Deref for Word {
271    type Target = [Felt; WORD_SIZE_FELTS];
272
273    fn deref(&self) -> &Self::Target {
274        self.as_elements_array()
275    }
276}
277
278impl DerefMut for Word {
279    fn deref_mut(&mut self) -> &mut Self::Target {
280        self.as_elements_array_mut()
281    }
282}
283
284impl Index<usize> for Word {
285    type Output = Felt;
286
287    fn index(&self, index: usize) -> &Self::Output {
288        &self.as_elements_array()[index]
289    }
290}
291
292impl IndexMut<usize> for Word {
293    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
294        &mut self.as_elements_array_mut()[index]
295    }
296}
297
298impl Index<Range<usize>> for Word {
299    type Output = [Felt];
300
301    fn index(&self, index: Range<usize>) -> &Self::Output {
302        &self.as_elements_array()[index]
303    }
304}
305
306impl IndexMut<Range<usize>> for Word {
307    fn index_mut(&mut self, index: Range<usize>) -> &mut Self::Output {
308        &mut self.as_elements_array_mut()[index]
309    }
310}
311
312impl Ord for Word {
313    fn cmp(&self, other: &Self) -> Ordering {
314        // Compare the canonical u64 representation of both words.
315        //
316        // It will iterate the elements in reverse and will return the first computation different
317        // than `Equal`. Otherwise, the ordering is equal.
318        //
319        // We use `as_canonical_u64()` to ensure we're comparing the actual field element values
320        // in their canonical form (that is, `x in [0,p)`). P3's Goldilocks field uses unreduced
321        // representation (not Montgomery form), meaning internal values may be in [0, 2^64) even
322        // though the field order is p = 2^64 - 2^32 + 1. This method canonicalizes to [0, p).
323        //
324        // We must iterate over and compare each element individually. A simple bytestring
325        // comparison would be inappropriate because the `Word`s are represented in
326        // "lexicographical" order.
327        for (felt0, felt1) in self
328            .iter()
329            .rev()
330            .map(Felt::as_canonical_u64)
331            .zip(other.iter().rev().map(Felt::as_canonical_u64))
332        {
333            let ordering = felt0.cmp(&felt1);
334            if let Ordering::Less | Ordering::Greater = ordering {
335                return ordering;
336            }
337        }
338
339        Ordering::Equal
340    }
341}
342
343impl PartialOrd for Word {
344    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
345        Some(self.cmp(other))
346    }
347}
348
349#[cfg(not(all(target_family = "wasm", miden)))]
350impl Display for Word {
351    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
352        write!(f, "{}", self.to_hex())
353    }
354}
355
356// CONVERSIONS: FROM WORD
357// ================================================================================================
358
359/// Errors that can occur when working with a [Word].
360#[derive(Debug, Error)]
361pub enum WordError {
362    /// Hex-encoded field elements parsed are invalid.
363    #[error("hex encoded values of a word are invalid")]
364    HexParse(#[from] crate::utils::HexParseError),
365    /// Field element conversion failed due to invalid value.
366    #[error("failed to convert to field element: {0}")]
367    InvalidFieldElement(String),
368    /// Failed to convert a slice to an array of expected length.
369    #[error("invalid input length: expected {1} {0}, but received {2}")]
370    InvalidInputLength(&'static str, usize, usize),
371    /// Failed to convert the word's field elements to the specified type.
372    #[error("failed to convert the word's field elements to type {0}")]
373    TypeConversion(&'static str),
374}
375
376impl TryFrom<&Word> for [bool; WORD_SIZE_FELTS] {
377    type Error = WordError;
378
379    fn try_from(value: &Word) -> Result<Self, Self::Error> {
380        (*value).try_into()
381    }
382}
383
384impl TryFrom<Word> for [bool; WORD_SIZE_FELTS] {
385    type Error = WordError;
386
387    fn try_from(value: Word) -> Result<Self, Self::Error> {
388        fn to_bool(v: u64) -> Option<bool> {
389            if v <= 1 { Some(v == 1) } else { None }
390        }
391
392        let [a, b, c, d] = value.into_elements();
393        Ok([
394            to_bool(a.as_canonical_u64()).ok_or(WordError::TypeConversion("bool"))?,
395            to_bool(b.as_canonical_u64()).ok_or(WordError::TypeConversion("bool"))?,
396            to_bool(c.as_canonical_u64()).ok_or(WordError::TypeConversion("bool"))?,
397            to_bool(d.as_canonical_u64()).ok_or(WordError::TypeConversion("bool"))?,
398        ])
399    }
400}
401
402impl TryFrom<&Word> for [u8; WORD_SIZE_FELTS] {
403    type Error = WordError;
404
405    fn try_from(value: &Word) -> Result<Self, Self::Error> {
406        (*value).try_into()
407    }
408}
409
410impl TryFrom<Word> for [u8; WORD_SIZE_FELTS] {
411    type Error = WordError;
412
413    fn try_from(value: Word) -> Result<Self, Self::Error> {
414        let [a, b, c, d] = value.into_elements();
415        Ok([
416            a.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u8"))?,
417            b.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u8"))?,
418            c.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u8"))?,
419            d.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u8"))?,
420        ])
421    }
422}
423
424impl TryFrom<&Word> for [u16; WORD_SIZE_FELTS] {
425    type Error = WordError;
426
427    fn try_from(value: &Word) -> Result<Self, Self::Error> {
428        (*value).try_into()
429    }
430}
431
432impl TryFrom<Word> for [u16; WORD_SIZE_FELTS] {
433    type Error = WordError;
434
435    fn try_from(value: Word) -> Result<Self, Self::Error> {
436        let [a, b, c, d] = value.into_elements();
437        Ok([
438            a.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u16"))?,
439            b.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u16"))?,
440            c.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u16"))?,
441            d.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u16"))?,
442        ])
443    }
444}
445
446impl TryFrom<&Word> for [u32; WORD_SIZE_FELTS] {
447    type Error = WordError;
448
449    fn try_from(value: &Word) -> Result<Self, Self::Error> {
450        (*value).try_into()
451    }
452}
453
454impl TryFrom<Word> for [u32; WORD_SIZE_FELTS] {
455    type Error = WordError;
456
457    fn try_from(value: Word) -> Result<Self, Self::Error> {
458        let [a, b, c, d] = value.into_elements();
459        Ok([
460            a.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u32"))?,
461            b.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u32"))?,
462            c.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u32"))?,
463            d.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u32"))?,
464        ])
465    }
466}
467
468impl From<&Word> for [u64; WORD_SIZE_FELTS] {
469    fn from(value: &Word) -> Self {
470        (*value).into()
471    }
472}
473
474impl From<Word> for [u64; WORD_SIZE_FELTS] {
475    fn from(value: Word) -> Self {
476        value.into_elements().map(|felt| felt.as_canonical_u64())
477    }
478}
479
480impl From<&Word> for [Felt; WORD_SIZE_FELTS] {
481    fn from(value: &Word) -> Self {
482        (*value).into()
483    }
484}
485
486impl From<Word> for [Felt; WORD_SIZE_FELTS] {
487    fn from(value: Word) -> Self {
488        value.into_elements()
489    }
490}
491
492impl From<&Word> for [u8; WORD_SIZE_BYTES] {
493    fn from(value: &Word) -> Self {
494        (*value).into()
495    }
496}
497
498impl From<Word> for [u8; WORD_SIZE_BYTES] {
499    fn from(value: Word) -> Self {
500        value.as_bytes()
501    }
502}
503
504#[cfg(not(all(target_family = "wasm", miden)))]
505impl From<&Word> for String {
506    /// The returned string starts with `0x`.
507    fn from(value: &Word) -> Self {
508        (*value).into()
509    }
510}
511
512#[cfg(not(all(target_family = "wasm", miden)))]
513impl From<Word> for String {
514    /// The returned string starts with `0x`.
515    fn from(value: Word) -> Self {
516        value.to_hex()
517    }
518}
519
520// CONVERSIONS: TO WORD
521// ================================================================================================
522
523impl From<&[bool; WORD_SIZE_FELTS]> for Word {
524    fn from(value: &[bool; WORD_SIZE_FELTS]) -> Self {
525        (*value).into()
526    }
527}
528
529impl From<[bool; WORD_SIZE_FELTS]> for Word {
530    fn from(value: [bool; WORD_SIZE_FELTS]) -> Self {
531        [value[0] as u32, value[1] as u32, value[2] as u32, value[3] as u32].into()
532    }
533}
534
535impl From<&[u8; WORD_SIZE_FELTS]> for Word {
536    fn from(value: &[u8; WORD_SIZE_FELTS]) -> Self {
537        (*value).into()
538    }
539}
540
541impl From<[u8; WORD_SIZE_FELTS]> for Word {
542    fn from(value: [u8; WORD_SIZE_FELTS]) -> Self {
543        Self::new([
544            Felt::from_u8(value[0]),
545            Felt::from_u8(value[1]),
546            Felt::from_u8(value[2]),
547            Felt::from_u8(value[3]),
548        ])
549    }
550}
551
552impl From<&[u16; WORD_SIZE_FELTS]> for Word {
553    fn from(value: &[u16; WORD_SIZE_FELTS]) -> Self {
554        (*value).into()
555    }
556}
557
558impl From<[u16; WORD_SIZE_FELTS]> for Word {
559    fn from(value: [u16; WORD_SIZE_FELTS]) -> Self {
560        Self::new([
561            Felt::from_u16(value[0]),
562            Felt::from_u16(value[1]),
563            Felt::from_u16(value[2]),
564            Felt::from_u16(value[3]),
565        ])
566    }
567}
568
569impl From<&[u32; WORD_SIZE_FELTS]> for Word {
570    fn from(value: &[u32; WORD_SIZE_FELTS]) -> Self {
571        (*value).into()
572    }
573}
574
575impl From<[u32; WORD_SIZE_FELTS]> for Word {
576    fn from(value: [u32; WORD_SIZE_FELTS]) -> Self {
577        Self::new([
578            Felt::from_u32(value[0]),
579            Felt::from_u32(value[1]),
580            Felt::from_u32(value[2]),
581            Felt::from_u32(value[3]),
582        ])
583    }
584}
585
586impl TryFrom<&[u64; WORD_SIZE_FELTS]> for Word {
587    type Error = WordError;
588
589    fn try_from(value: &[u64; WORD_SIZE_FELTS]) -> Result<Self, WordError> {
590        (*value).try_into()
591    }
592}
593
594impl TryFrom<[u64; WORD_SIZE_FELTS]> for Word {
595    type Error = WordError;
596
597    fn try_from(value: [u64; WORD_SIZE_FELTS]) -> Result<Self, WordError> {
598        let err = || WordError::InvalidFieldElement("value >= field modulus".into());
599        Ok(Self::new([
600            Felt::from_canonical_checked(value[0]).ok_or_else(err)?,
601            Felt::from_canonical_checked(value[1]).ok_or_else(err)?,
602            Felt::from_canonical_checked(value[2]).ok_or_else(err)?,
603            Felt::from_canonical_checked(value[3]).ok_or_else(err)?,
604        ]))
605    }
606}
607
608impl From<&[Felt; WORD_SIZE_FELTS]> for Word {
609    fn from(value: &[Felt; WORD_SIZE_FELTS]) -> Self {
610        Self::new(*value)
611    }
612}
613
614impl From<[Felt; WORD_SIZE_FELTS]> for Word {
615    fn from(value: [Felt; WORD_SIZE_FELTS]) -> Self {
616        Self::new(value)
617    }
618}
619
620impl TryFrom<&[u8; WORD_SIZE_BYTES]> for Word {
621    type Error = WordError;
622
623    fn try_from(value: &[u8; WORD_SIZE_BYTES]) -> Result<Self, Self::Error> {
624        (*value).try_into()
625    }
626}
627
628impl TryFrom<[u8; WORD_SIZE_BYTES]> for Word {
629    type Error = WordError;
630
631    fn try_from(value: [u8; WORD_SIZE_BYTES]) -> Result<Self, Self::Error> {
632        // Note: the input length is known, the conversion from slice to array must succeed so the
633        // `unwrap`s below are safe
634        let a = u64::from_le_bytes(value[0..8].try_into().unwrap());
635        let b = u64::from_le_bytes(value[8..16].try_into().unwrap());
636        let c = u64::from_le_bytes(value[16..24].try_into().unwrap());
637        let d = u64::from_le_bytes(value[24..32].try_into().unwrap());
638
639        let err = || WordError::InvalidFieldElement("value >= field modulus".into());
640        let a: Felt = Felt::from_canonical_checked(a).ok_or_else(err)?;
641        let b: Felt = Felt::from_canonical_checked(b).ok_or_else(err)?;
642        let c: Felt = Felt::from_canonical_checked(c).ok_or_else(err)?;
643        let d: Felt = Felt::from_canonical_checked(d).ok_or_else(err)?;
644
645        Ok(Self::new([a, b, c, d]))
646    }
647}
648
649impl TryFrom<&[u8]> for Word {
650    type Error = WordError;
651
652    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
653        let value: [u8; WORD_SIZE_BYTES] = value
654            .try_into()
655            .map_err(|_| WordError::InvalidInputLength("bytes", WORD_SIZE_BYTES, value.len()))?;
656        value.try_into()
657    }
658}
659
660impl TryFrom<&[Felt]> for Word {
661    type Error = WordError;
662
663    fn try_from(value: &[Felt]) -> Result<Self, Self::Error> {
664        let value: [Felt; WORD_SIZE_FELTS] = value
665            .try_into()
666            .map_err(|_| WordError::InvalidInputLength("elements", WORD_SIZE_FELTS, value.len()))?;
667        Ok(value.into())
668    }
669}
670
671#[cfg(not(all(target_family = "wasm", miden)))]
672impl TryFrom<&str> for Word {
673    type Error = WordError;
674
675    /// Expects the string to start with `0x`.
676    fn try_from(value: &str) -> Result<Self, Self::Error> {
677        crate::utils::hex_to_bytes::<WORD_SIZE_BYTES>(value)
678            .map_err(WordError::HexParse)
679            .and_then(Word::try_from)
680    }
681}
682
683#[cfg(not(all(target_family = "wasm", miden)))]
684impl TryFrom<String> for Word {
685    type Error = WordError;
686
687    /// Expects the string to start with `0x`.
688    fn try_from(value: String) -> Result<Self, Self::Error> {
689        value.as_str().try_into()
690    }
691}
692
693#[cfg(not(all(target_family = "wasm", miden)))]
694impl TryFrom<&String> for Word {
695    type Error = WordError;
696
697    /// Expects the string to start with `0x`.
698    fn try_from(value: &String) -> Result<Self, Self::Error> {
699        value.as_str().try_into()
700    }
701}
702
703// SERIALIZATION / DESERIALIZATION
704// ================================================================================================
705
706#[cfg(not(all(target_family = "wasm", miden)))]
707impl Serializable for Word {
708    fn write_into<W: ByteWriter>(&self, target: &mut W) {
709        target.write_bytes(&self.as_bytes());
710    }
711
712    fn get_size_hint(&self) -> usize {
713        Self::SERIALIZED_SIZE
714    }
715}
716
717#[cfg(not(all(target_family = "wasm", miden)))]
718impl Deserializable for Word {
719    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
720        let mut inner: [Felt; WORD_SIZE_FELTS] = [Felt::ZERO; WORD_SIZE_FELTS];
721        for inner in inner.iter_mut() {
722            let e = source.read_u64()?;
723            if e >= Felt::ORDER {
724                return Err(DeserializationError::InvalidValue(String::from(
725                    "value not in the appropriate range",
726                )));
727            }
728            *inner = Felt::new(e);
729        }
730
731        Ok(Self::new(inner))
732    }
733
734    fn min_serialized_size() -> usize {
735        Self::SERIALIZED_SIZE
736    }
737}
738
739// ITERATORS
740// ================================================================================================
741impl IntoIterator for Word {
742    type Item = Felt;
743    type IntoIter = <[Felt; 4] as IntoIterator>::IntoIter;
744
745    fn into_iter(self) -> Self::IntoIter {
746        self.into_elements().into_iter()
747    }
748}
749
750// MACROS
751// ================================================================================================
752
753/// Construct a new [Word](super::Word) from a hex value.
754///
755/// Expects a '0x' prefixed hex string followed by up to 64 hex digits.
756#[cfg(not(all(target_family = "wasm", miden)))]
757#[macro_export]
758macro_rules! word {
759    ($hex:expr) => {{
760        let word: Word = match $crate::word::Word::parse($hex) {
761            Ok(v) => v,
762            Err(e) => panic!("{}", e),
763        };
764
765        word
766    }};
767}
768
769// ARBITRARY (proptest)
770// ================================================================================================
771
772#[cfg(all(any(test, feature = "testing"), not(all(target_family = "wasm", miden))))]
773mod arbitrary {
774    use proptest::prelude::*;
775
776    use super::{Felt, Word};
777
778    impl Arbitrary for Word {
779        type Parameters = ();
780        type Strategy = BoxedStrategy<Self>;
781
782        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
783            prop::array::uniform4(any::<Felt>()).prop_map(Word::new).no_shrink().boxed()
784        }
785    }
786}