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