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