Skip to main content

miden_core/utils/
mod.rs

1use alloc::vec::Vec;
2use core::ops::{Bound, Range};
3
4use crate::{
5    Felt, Word,
6    crypto::hash::Blake3_256,
7    field::{PrimeCharacteristicRing, PrimeField64},
8};
9
10// RE-EXPORTS
11// ================================================================================================
12
13mod col_matrix;
14pub use col_matrix::ColMatrix;
15#[cfg(feature = "std")]
16pub use miden_crypto::utils::ReadAdapter;
17pub use miden_crypto::{
18    stark::matrix::{Matrix, RowMajorMatrix},
19    utils::{flatten_slice_elements, flatten_vector_elements, group_slice_elements, uninit_vector},
20};
21pub use miden_formatting::hex::{DisplayHex, ToHex, to_hex};
22pub use miden_utils_indexing::{
23    CsrMatrix, CsrValidationError, DenseIdMap, Idx, IndexVec, IndexedVecError, LookupByIdx,
24    newtype_id,
25};
26
27// TO ELEMENTS
28// ================================================================================================
29
30pub trait ToElements {
31    fn to_elements(&self) -> Vec<Felt>;
32}
33
34impl<const N: usize> ToElements for [u64; N] {
35    fn to_elements(&self) -> Vec<Felt> {
36        self.iter().map(|&v| Felt::from_u64(v)).collect()
37    }
38}
39
40impl ToElements for Vec<u64> {
41    fn to_elements(&self) -> Vec<Felt> {
42        self.iter().map(|&v| Felt::from_u64(v)).collect()
43    }
44}
45
46// TO WORD
47// ================================================================================================
48
49/// Hashes the provided string using the BLAKE3 hash function and converts the resulting digest into
50/// a [`Word`].
51pub fn hash_string_to_word<'a>(value: impl Into<&'a str>) -> Word {
52    let digest_bytes: [u8; 32] = Blake3_256::hash(value.into().as_bytes()).into();
53    [
54        Felt::new(u64::from_le_bytes(digest_bytes[0..8].try_into().unwrap())),
55        Felt::new(u64::from_le_bytes(digest_bytes[8..16].try_into().unwrap())),
56        Felt::new(u64::from_le_bytes(digest_bytes[16..24].try_into().unwrap())),
57        Felt::new(u64::from_le_bytes(digest_bytes[24..32].try_into().unwrap())),
58    ]
59    .into()
60}
61
62// INTO BYTES
63// ================================================================================================
64
65pub trait IntoBytes<const N: usize> {
66    fn into_bytes(self) -> [u8; N];
67}
68
69impl IntoBytes<32> for [Felt; 4] {
70    fn into_bytes(self) -> [u8; 32] {
71        let mut result = [0; 32];
72
73        result[..8].copy_from_slice(&self[0].as_canonical_u64().to_le_bytes());
74        result[8..16].copy_from_slice(&self[1].as_canonical_u64().to_le_bytes());
75        result[16..24].copy_from_slice(&self[2].as_canonical_u64().to_le_bytes());
76        result[24..].copy_from_slice(&self[3].as_canonical_u64().to_le_bytes());
77
78        result
79    }
80}
81
82// RANGE
83// ================================================================================================
84
85/// Returns a [Range] initialized with the specified `start` and with `end` set to `start` + `len`.
86pub const fn range(start: usize, len: usize) -> Range<usize> {
87    Range { start, end: start + len }
88}
89
90/// Converts and parses a [Bound] into an included u64 value.
91pub fn bound_into_included_u64<I>(bound: Bound<&I>, is_start: bool) -> u64
92where
93    I: Clone + Into<u64>,
94{
95    match bound {
96        Bound::Excluded(i) => i.clone().into().saturating_sub(1),
97        Bound::Included(i) => i.clone().into(),
98        Bound::Unbounded => {
99            if is_start {
100                0
101            } else {
102                u64::MAX
103            }
104        },
105    }
106}
107
108// BYTE CONVERSIONS
109// ================================================================================================
110
111/// Number of bytes packed into each u32 field element.
112///
113/// Used for converting between byte arrays and u32-packed field elements in memory.
114const BYTES_PER_U32: usize = core::mem::size_of::<u32>();
115
116/// Converts bytes to field elements using u32 packing in little-endian format.
117///
118/// Each field element contains a u32 value representing up to 4 bytes. If the byte length
119/// is not a multiple of 4, the final field element is zero-padded.
120///
121/// This is commonly used by precompile handlers (Keccak256, ECDSA) to convert byte data
122/// into field element commitments.
123///
124/// # Arguments
125/// - `bytes`: The byte slice to convert
126///
127/// # Returns
128/// A vector of field elements, each containing 4 bytes packed in little-endian order.
129///
130/// # Examples
131/// ```
132/// # use miden_core::{Felt, utils::bytes_to_packed_u32_elements, field::PrimeCharacteristicRing};
133/// let bytes = vec![0x01, 0x02, 0x03, 0x04, 0x05];
134/// let felts = bytes_to_packed_u32_elements(&bytes);
135/// assert_eq!(felts, vec![Felt::from_u32(0x04030201_u32), Felt::from_u32(0x00000005_u32)]);
136/// ```
137pub fn bytes_to_packed_u32_elements(bytes: &[u8]) -> Vec<Felt> {
138    bytes
139        .chunks(BYTES_PER_U32)
140        .map(|chunk| {
141            // Pack up to 4 bytes into a u32 in little-endian format
142            let mut packed = [0u8; BYTES_PER_U32];
143            packed[..chunk.len()].copy_from_slice(chunk);
144            Felt::from_u32(u32::from_le_bytes(packed))
145        })
146        .collect()
147}
148
149/// Converts u32-packed field elements back to bytes in little-endian format.
150///
151/// This is the inverse of [`bytes_to_packed_u32_elements`]. Each field element is expected
152/// to contain a u32 value, which is unpacked into 4 bytes.
153///
154/// # Arguments
155/// - `elements`: The field elements to convert
156///
157/// # Returns
158/// A vector of bytes representing the unpacked data.
159///
160/// # Examples
161/// ```
162/// # use miden_core::{Felt, utils::{bytes_to_packed_u32_elements, packed_u32_elements_to_bytes}};
163/// let original = vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
164/// let elements = bytes_to_packed_u32_elements(&original);
165/// let bytes = packed_u32_elements_to_bytes(&elements);
166/// assert_eq!(bytes, original);
167/// ```
168pub fn packed_u32_elements_to_bytes(elements: &[Felt]) -> Vec<u8> {
169    elements
170        .iter()
171        .flat_map(|felt| {
172            let value = felt.as_canonical_u64() as u32;
173            value.to_le_bytes()
174        })
175        .collect()
176}
177
178// TESTS
179// ================================================================================================
180
181#[cfg(test)]
182mod tests {
183    use proptest::prelude::*;
184
185    use super::*;
186    use crate::field::PrimeCharacteristicRing;
187
188    proptest! {
189        #[test]
190        fn proptest_packed_u32_elements_roundtrip(values in prop::collection::vec(any::<u32>(), 0..100)) {
191            // Convert u32 values to Felts
192            let felts: Vec<Felt> = values.iter().map(|&v| Felt::from_u32(v)).collect();
193
194            // Roundtrip: Felts -> bytes -> Felts
195            let bytes = packed_u32_elements_to_bytes(&felts);
196            let roundtrip_felts = bytes_to_packed_u32_elements(&bytes);
197
198            // Should be equal
199            prop_assert_eq!(felts, roundtrip_felts);
200        }
201    }
202
203    #[test]
204    #[should_panic]
205    fn debug_assert_is_checked() {
206        // enforce the release checks to always have `RUSTFLAGS="-C debug-assertions".
207        //
208        // some upstream tests are performed with `debug_assert`, and we want to assert its
209        // correctness downstream.
210        //
211        // for reference, check
212        // https://github.com/0xMiden/miden-vm/issues/433
213        debug_assert!(false);
214    }
215}