Skip to main content

miden_crypto/utils/
mod.rs

1//! Utilities used in this crate which can also be generally useful downstream.
2
3use alloc::{boxed::Box, string::String, vec::Vec};
4use core::{
5    fmt::{self, Write},
6    mem::{ManuallyDrop, MaybeUninit},
7};
8
9// Re-export serialization traits from miden-serde-utils
10#[cfg(feature = "std")]
11pub use miden_serde_utils::ReadAdapter;
12pub use miden_serde_utils::{
13    BudgetedReader, ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
14    SliceReader,
15};
16use p3_maybe_rayon::prelude::*;
17
18use crate::{Felt, Word, field::QuotientMap};
19
20// CONSTANTS
21// ================================================================================================
22
23/// The number of byte chunks that can be safely embedded in a field element
24const BINARY_CHUNK_SIZE: usize = 7;
25
26// RE-EXPORTS
27// ================================================================================================
28
29pub use k256::elliptic_curve::zeroize;
30
31// UTILITY FUNCTIONS
32// ================================================================================================
33
34/// Converts a [Word] into hex.
35pub fn word_to_hex(w: &Word) -> Result<String, fmt::Error> {
36    let mut s = String::new();
37
38    for byte in w.iter().flat_map(|&e| e.to_bytes()) {
39        write!(s, "{byte:02x}")?;
40    }
41
42    Ok(s)
43}
44
45pub use miden_field::utils::{HexParseError, bytes_to_hex_string, hex_to_bytes};
46
47// CONVERSIONS BETWEEN BYTES AND ELEMENTS
48// ================================================================================================
49
50/// Converts a sequence of bytes into vector field elements with padding. This guarantees that no
51/// two sequences or bytes map to the same sequence of field elements.
52///
53/// Packs bytes into chunks of `BINARY_CHUNK_SIZE` and adds padding to the final chunk using a `1`
54/// bit followed by zeros. This ensures the original bytes can be recovered during decoding without
55/// any ambiguity.
56///
57/// Note that by the endianness of the conversion as well as the fact that we are packing at most
58/// `56 = 7 * 8` bits in each field element, the padding above with `1` should never overflow the
59/// field size.
60///
61/// # Arguments
62/// * `bytes` - Byte slice to encode
63///
64/// # Returns
65/// Vector of `Felt` elements with the last element containing padding
66pub fn bytes_to_elements_with_padding(bytes: &[u8]) -> Vec<Felt> {
67    if bytes.is_empty() {
68        return vec![];
69    }
70
71    // determine the number of field elements needed to encode `bytes` when each field element
72    // represents at most 7 bytes.
73    let num_field_elem = bytes.len().div_ceil(BINARY_CHUNK_SIZE);
74
75    // initialize a buffer to receive the little-endian elements.
76    let mut buf = [0_u8; 8];
77
78    // iterate the chunks of bytes, creating a field element from each chunk
79    let last_chunk_idx = num_field_elem - 1;
80
81    bytes
82        .chunks(BINARY_CHUNK_SIZE)
83        .enumerate()
84        .map(|(current_chunk_idx, chunk)| {
85            // copy the chunk into the buffer
86            if current_chunk_idx != last_chunk_idx {
87                buf[..BINARY_CHUNK_SIZE].copy_from_slice(chunk);
88            } else {
89                // on the last iteration, we pad `buf` with a 1 followed by as many 0's as are
90                // needed to fill it
91                buf.fill(0);
92                buf[..chunk.len()].copy_from_slice(chunk);
93                buf[chunk.len()] = 1;
94            }
95
96            Felt::new(u64::from_le_bytes(buf))
97        })
98        .collect()
99}
100
101/// Converts a sequence of padded field elements back to the original bytes.
102///
103/// Reconstructs the original byte sequence by removing the padding added by
104/// `bytes_to_elements_with_padding`.
105/// The padding consists of a `1` bit followed by zeros in the final field element.
106/// Any bytes after the last `1` marker in the final field element are ignored and are not
107/// validated to be zero.
108///
109/// Note that by the endianness of the conversion as well as the fact that we are packing at most
110/// `56 = 7 * 8` bits in each field element, the padding above with `1` should never overflow the
111/// field size.
112///
113/// # Arguments
114/// * `felts` - Slice of field elements with padding in the last element
115///
116/// # Returns
117/// * `Some(Vec<u8>)` - The original byte sequence with padding removed
118/// * `None` - If no padding marker (`1` bit) is found
119pub fn padded_elements_to_bytes(felts: &[Felt]) -> Option<Vec<u8>> {
120    let number_felts = felts.len();
121    if number_felts == 0 {
122        return Some(vec![]);
123    }
124
125    let mut result = Vec::with_capacity(number_felts * BINARY_CHUNK_SIZE);
126    for felt in felts.iter().take(number_felts - 1) {
127        let felt_bytes = felt.as_canonical_u64().to_le_bytes();
128        result.extend_from_slice(&felt_bytes[..BINARY_CHUNK_SIZE]);
129    }
130
131    // handle the last field element
132    let felt_bytes = felts[number_felts - 1].as_canonical_u64().to_le_bytes();
133    let pos = felt_bytes.iter().rposition(|entry| *entry == 1_u8)?;
134
135    result.extend_from_slice(&felt_bytes[..pos]);
136    Some(result)
137}
138
139/// Converts field elements to raw byte representation.
140///
141/// Each `Felt` is converted to its full `NUM_BYTES` representation, in little-endian form
142/// and canonical form, without any padding removal or validation. This is the inverse
143/// of `bytes_to_elements_exact`.
144///
145/// # Arguments
146/// * `felts` - Slice of field elements to convert
147///
148/// # Returns
149/// Vector containing the raw bytes from all field elements
150pub fn elements_to_bytes(felts: &[Felt]) -> Vec<u8> {
151    let number_felts = felts.len();
152    let mut result = Vec::with_capacity(number_felts * Felt::NUM_BYTES);
153    for felt in felts.iter().take(number_felts) {
154        let felt_bytes = felt.as_canonical_u64().to_le_bytes();
155        result.extend_from_slice(&felt_bytes);
156    }
157
158    result
159}
160
161/// Converts bytes to field elements with validation.
162///
163/// This function validates that:
164/// - The input bytes length is divisible by `Felt::NUM_BYTES`
165/// - All `Felt::NUM_BYTES`-byte sequences represent valid field elements
166///
167/// # Arguments
168/// * `bytes` - Byte slice that must be a multiple of `Felt::NUM_BYTES` in length
169///
170/// # Returns
171/// `Option<Vec<Felt>>` - Vector of `Felt` elements if all validations pass, or None otherwise
172pub fn bytes_to_elements_exact(bytes: &[u8]) -> Option<Vec<Felt>> {
173    // Check that the length is divisible by NUM_BYTES
174    if !bytes.len().is_multiple_of(Felt::NUM_BYTES) {
175        return None;
176    }
177
178    let mut result = Vec::with_capacity(bytes.len() / Felt::NUM_BYTES);
179
180    for chunk in bytes.chunks_exact(Felt::NUM_BYTES) {
181        let chunk_array: [u8; Felt::NUM_BYTES] =
182            chunk.try_into().expect("should succeed given the length check above");
183
184        let value = u64::from_le_bytes(chunk_array);
185
186        // Validate that the value represents a valid field element
187        let felt = Felt::from_canonical_checked(value)?;
188        result.push(felt);
189    }
190
191    Some(result)
192}
193
194/// Converts bytes to field elements using u32 packing in little-endian format.
195///
196/// Each field element contains a u32 value representing up to 4 bytes. If the byte length
197/// is not a multiple of 4, the final field element is zero-padded.
198///
199/// # Arguments
200/// - `bytes`: The byte slice to convert
201///
202/// # Returns
203/// A vector of field elements, each containing 4 bytes packed in little-endian order.
204///
205/// # Examples
206/// ```rust
207/// # use miden_crypto::{Felt, utils::bytes_to_packed_u32_elements};
208///
209/// let bytes = vec![0x01, 0x02, 0x03, 0x04, 0x05];
210/// let felts = bytes_to_packed_u32_elements(&bytes);
211/// assert_eq!(felts, vec![Felt::new(0x04030201), Felt::new(0x00000005)]);
212/// ```
213pub fn bytes_to_packed_u32_elements(bytes: &[u8]) -> Vec<Felt> {
214    const BYTES_PER_U32: usize = core::mem::size_of::<u32>();
215
216    bytes
217        .chunks(BYTES_PER_U32)
218        .map(|chunk| {
219            // Pack up to 4 bytes into a u32 in little-endian format
220            let mut packed = [0u8; BYTES_PER_U32];
221            packed[..chunk.len()].copy_from_slice(chunk);
222            Felt::from_u32(u32::from_le_bytes(packed))
223        })
224        .collect()
225}
226
227// VECTOR FUNCTIONS (ported from Winterfell's winter-utils)
228// ================================================================================================
229
230/// Returns a vector of the specified length with un-initialized memory.
231///
232/// This is usually faster than requesting a vector with initialized memory and is useful when we
233/// overwrite all contents of the vector immediately after memory allocation.
234pub fn uninit_vector<T>(length: usize) -> Vec<MaybeUninit<T>> {
235    Vec::from(Box::new_uninit_slice(length))
236}
237
238/// Converts a fully-initialized `Vec<MaybeUninit<T>>` into `Vec<T>`.
239///
240/// # Safety
241/// All elements must be initialized before calling this function.
242pub unsafe fn assume_init_vec<T>(v: Vec<MaybeUninit<T>>) -> Vec<T> {
243    let mut v = ManuallyDrop::new(v);
244    let ptr = v.as_mut_ptr();
245    let len = v.len();
246    let cap = v.capacity();
247    // SAFETY: caller guarantees all elements are initialized.
248    unsafe { Vec::from_raw_parts(ptr.cast::<T>(), len, cap) }
249}
250
251// GROUPING / UN-GROUPING FUNCTIONS (ported from Winterfell's winter-utils)
252// ================================================================================================
253
254/// Transmutes a slice of `n` elements into a slice of `n` / `N` elements, each of which is
255/// an array of `N` elements.
256///
257/// This function just re-interprets the underlying memory and is thus zero-copy.
258/// # Panics
259/// Panics if `n` is not divisible by `N`.
260pub fn group_slice_elements<T, const N: usize>(source: &[T]) -> &[[T; N]] {
261    let (chunks, remainder) = source.as_chunks::<N>();
262    assert!(remainder.is_empty(), "source length must be divisible by {N}");
263    chunks
264}
265
266/// Transmutes a slice of `n` arrays each of length `N`, into a slice of `N` * `n` elements.
267///
268/// This function just re-interprets the underlying memory and is thus zero-copy.
269pub fn flatten_slice_elements<T, const N: usize>(source: &[[T; N]]) -> &[T] {
270    // SAFETY: [T; N] has the same alignment and memory layout as an array of T.
271    // p3-util's as_base_slice handles the conversion safely.
272    unsafe { p3_util::as_base_slice(source) }
273}
274
275/// Transmutes a vector of `n` arrays each of length `N`, into a vector of `N` * `n` elements.
276///
277/// This function just re-interprets the underlying memory and is thus zero-copy.
278pub fn flatten_vector_elements<T, const N: usize>(source: Vec<[T; N]>) -> Vec<T> {
279    // SAFETY: [T; N] has the same alignment and memory layout as an array of T.
280    // p3-util's flatten_to_base handles the conversion without reallocations.
281    unsafe { p3_util::flatten_to_base(source) }
282}
283
284// TRANSPOSING (ported from Winterfell's winter-utils)
285// ================================================================================================
286
287/// Transposes a slice of `n` elements into a matrix with `N` columns and `n`/`N` rows.
288///
289/// When `concurrent` feature is enabled, the slice will be transposed using multiple threads.
290/// Uses uninit_vector for ~31% speedup at 1024x1024 (benches/transpose.rs).
291///
292/// # Panics
293/// Panics if `n` is not divisible by `N`.
294pub fn transpose_slice<T: Copy + Send + Sync, const N: usize>(source: &[T]) -> Vec<[T; N]> {
295    let row_count = source.len() / N;
296    assert_eq!(
297        row_count * N,
298        source.len(),
299        "source length must be divisible by {}, but was {}",
300        N,
301        source.len()
302    );
303
304    let mut result = uninit_vector::<[T; N]>(row_count);
305    result.par_iter_mut().enumerate().for_each(|(i, slot)| {
306        let row = core::array::from_fn(|j| source[i + j * row_count]);
307        slot.write(row);
308    });
309    // SAFETY: all rows are written above.
310    unsafe { assume_init_vec(result) }
311}