winter_utils/lib.rs
1// Copyright (c) Facebook, Inc. and its affiliates.
2//
3// This source code is licensed under the MIT license found in the
4// LICENSE file in the root directory of this source tree.
5
6//! This crate contains utility traits, functions, and macros used by other crates of Winterfell
7//! STARK prover and verifier.
8#![no_std]
9
10#[macro_use]
11extern crate alloc;
12
13#[cfg(feature = "std")]
14extern crate std;
15
16pub mod iterators;
17
18use alloc::vec::Vec;
19use core::{mem, slice};
20
21mod serde;
22#[cfg(feature = "std")]
23pub use serde::ReadAdapter;
24pub use serde::{ByteReader, ByteWriter, Deserializable, Serializable, SliceReader};
25
26mod errors;
27pub use errors::DeserializationError;
28
29#[cfg(test)]
30mod tests;
31
32// FEATURE-BASED RE-EXPORTS
33// ================================================================================================
34
35#[cfg(feature = "concurrent")]
36use iterators::*;
37#[cfg(feature = "concurrent")]
38pub use rayon;
39
40// AS BYTES
41// ================================================================================================
42
43/// Defines a zero-copy representation of `Self` as a sequence of bytes.
44pub trait AsBytes {
45 /// Returns a byte representation of `self`.
46 ///
47 /// This method is intended to re-interpret the underlying memory as a sequence of bytes, and
48 /// thus, should be zero-copy.
49 fn as_bytes(&self) -> &[u8];
50}
51
52impl<const N: usize, const M: usize> AsBytes for [[u8; N]; M] {
53 /// Flattens a two-dimensional array of bytes into a slice of bytes.
54 fn as_bytes(&self) -> &[u8] {
55 let p = self.as_ptr();
56 let len = N * M;
57 unsafe { slice::from_raw_parts(p as *const u8, len) }
58 }
59}
60
61impl<const N: usize> AsBytes for [[u8; N]] {
62 /// Flattens a slice of byte arrays into a slice of bytes.
63 fn as_bytes(&self) -> &[u8] {
64 let p = self.as_ptr();
65 let len = self.len() * N;
66 unsafe { slice::from_raw_parts(p as *const u8, len) }
67 }
68}
69
70// VECTOR FUNCTIONS
71// ================================================================================================
72
73/// Returns a vector of the specified length with un-initialized memory.
74///
75/// This is usually faster than requesting a vector with initialized memory and is useful when we
76/// overwrite all contents of the vector immediately after memory allocation.
77///
78/// # Safety
79/// Using values from the returned vector before initializing them will lead to undefined behavior.
80#[allow(clippy::uninit_vec)]
81pub unsafe fn uninit_vector<T>(length: usize) -> Vec<T> {
82 let mut vector = Vec::with_capacity(length);
83 vector.set_len(length);
84 vector
85}
86
87// GROUPING / UN-GROUPING FUNCTIONS
88// ================================================================================================
89
90/// Transmutes a slice of `n` elements into a slice of `n` / `N` elements, each of which is
91/// an array of `N` elements.
92///
93/// This function just re-interprets the underlying memory and is thus zero-copy.
94/// # Panics
95/// Panics if `n` is not divisible by `N`.
96///
97/// # Example
98/// ```
99/// # use winter_utils::group_slice_elements;
100/// let a = [0_u32, 1, 2, 3, 4, 5, 6, 7];
101/// let b: &[[u32; 2]] = group_slice_elements(&a);
102///
103/// assert_eq!(&[[0, 1], [2, 3], [4, 5], [6, 7]], b);
104/// ```
105pub fn group_slice_elements<T, const N: usize>(source: &[T]) -> &[[T; N]] {
106 assert_eq!(source.len() % N, 0, "source length must be divisible by {N}");
107 let p = source.as_ptr();
108 let len = source.len() / N;
109 unsafe { slice::from_raw_parts(p as *const [T; N], len) }
110}
111
112/// Transmutes a slice of `n` arrays each of length `N`, into a slice of `N` * `n` elements.
113///
114/// This function just re-interprets the underlying memory and is thus zero-copy.
115/// # Example
116/// ```
117/// # use winter_utils::flatten_slice_elements;
118/// let a = vec![[1, 2, 3, 4], [5, 6, 7, 8]];
119///
120/// let b = flatten_slice_elements(&a);
121/// assert_eq!(&[1, 2, 3, 4, 5, 6, 7, 8], b);
122/// ```
123pub fn flatten_slice_elements<T, const N: usize>(source: &[[T; N]]) -> &[T] {
124 let p = source.as_ptr();
125 let len = source.len() * N;
126 unsafe { slice::from_raw_parts(p as *const T, len) }
127}
128
129/// Transmutes a vector of `n` arrays each of length `N`, into a vector of `N` * `n` elements.
130///
131/// This function just re-interprets the underlying memory and is thus zero-copy.
132/// # Example
133/// ```
134/// # use winter_utils::flatten_vector_elements;
135/// let a = vec![[1, 2, 3, 4], [5, 6, 7, 8]];
136///
137/// let b = flatten_vector_elements(a);
138/// assert_eq!(vec![1, 2, 3, 4, 5, 6, 7, 8], b);
139/// ```
140pub fn flatten_vector_elements<T, const N: usize>(source: Vec<[T; N]>) -> Vec<T> {
141 let v = mem::ManuallyDrop::new(source);
142 let p = v.as_ptr();
143 let len = v.len() * N;
144 let cap = v.capacity() * N;
145 unsafe { Vec::from_raw_parts(p as *mut T, len, cap) }
146}
147
148// TRANSPOSING
149// ================================================================================================
150
151/// Transposes a slice of `n` elements into a matrix with `N` columns and `n`/`N` rows.
152///
153/// When `concurrent` feature is enabled, the slice will be transposed using multiple threads.
154///
155/// # Panics
156/// Panics if `n` is not divisible by `N`.
157///
158/// # Example
159/// ```
160/// # use winter_utils::transpose_slice;
161/// let a = [0_u32, 1, 2, 3, 4, 5, 6, 7];
162/// let b: Vec<[u32; 2]> = transpose_slice(&a);
163///
164/// assert_eq!(vec![[0, 4], [1, 5], [2, 6], [3, 7]], b);
165/// ```
166pub fn transpose_slice<T: Copy + Send + Sync, const N: usize>(source: &[T]) -> Vec<[T; N]> {
167 let row_count = source.len() / N;
168 assert_eq!(
169 row_count * N,
170 source.len(),
171 "source length must be divisible by {}, but was {}",
172 N,
173 source.len()
174 );
175
176 let mut result: Vec<[T; N]> = unsafe { uninit_vector(row_count) };
177 iter_mut!(result, 1024).enumerate().for_each(|(i, element)| {
178 for j in 0..N {
179 element[j] = source[i + j * row_count]
180 }
181 });
182 result
183}
184
185// RANDOMNESS
186// ================================================================================================
187
188/// Defines how `Self` can be read from a sequence of random bytes.
189pub trait Randomizable: Sized {
190 /// Size of `Self` in bytes.
191 ///
192 /// This is used to determine how many bytes should be passed to the
193 /// [from_random_bytes()](Self::from_random_bytes) function.
194 const VALUE_SIZE: usize;
195
196 /// Returns `Self` if the set of bytes forms a valid value, otherwise returns None.
197 fn from_random_bytes(source: &[u8]) -> Option<Self>;
198}
199
200impl Randomizable for u128 {
201 const VALUE_SIZE: usize = 16;
202
203 fn from_random_bytes(source: &[u8]) -> Option<Self> {
204 if let Ok(bytes) = source[..Self::VALUE_SIZE].try_into() {
205 Some(u128::from_le_bytes(bytes))
206 } else {
207 None
208 }
209 }
210}
211
212impl Randomizable for u64 {
213 const VALUE_SIZE: usize = 8;
214
215 fn from_random_bytes(source: &[u8]) -> Option<Self> {
216 if let Ok(bytes) = source[..Self::VALUE_SIZE].try_into() {
217 Some(u64::from_le_bytes(bytes))
218 } else {
219 None
220 }
221 }
222}
223
224impl Randomizable for u32 {
225 const VALUE_SIZE: usize = 4;
226
227 fn from_random_bytes(source: &[u8]) -> Option<Self> {
228 if let Ok(bytes) = source[..Self::VALUE_SIZE].try_into() {
229 Some(u32::from_le_bytes(bytes))
230 } else {
231 None
232 }
233 }
234}
235
236impl Randomizable for u16 {
237 const VALUE_SIZE: usize = 2;
238
239 fn from_random_bytes(source: &[u8]) -> Option<Self> {
240 if let Ok(bytes) = source[..Self::VALUE_SIZE].try_into() {
241 Some(u16::from_le_bytes(bytes))
242 } else {
243 None
244 }
245 }
246}
247
248impl Randomizable for u8 {
249 const VALUE_SIZE: usize = 1;
250
251 fn from_random_bytes(source: &[u8]) -> Option<Self> {
252 Some(source[0])
253 }
254}