Skip to main content

miden_field_repr/
lib.rs

1//! Serialization/deserialization for felt representation.
2//!
3//! This crate provides traits and utilities for converting Rust types to and from
4//! a sequence of [`Felt`] elements.
5
6#![no_std]
7#![deny(warnings)]
8
9extern crate alloc;
10
11use alloc::vec::Vec;
12
13pub use miden_field::Felt;
14/// Re-export `DeriveFromFeltRepr` as `FromFeltRepr` for `#[derive(FromFeltRepr)]` ergonomics.
15pub use miden_field_repr_derive::DeriveFromFeltRepr as FromFeltRepr;
16/// Re-export `DeriveToFeltRepr` as `ToFeltRepr` for `#[derive(ToFeltRepr)]` ergonomics.
17pub use miden_field_repr_derive::DeriveToFeltRepr as ToFeltRepr;
18
19/// Error returned when decoding a type from its felt representation.
20#[derive(Debug, Clone, PartialEq, Eq)]
21#[non_exhaustive]
22pub enum FeltReprError {
23    /// Attempted to read beyond the end of the felt slice.
24    UnexpectedEof {
25        /// Current read position.
26        pos: usize,
27        /// Total number of felts available.
28        len: usize,
29    },
30    /// A decoded value did not fit into the target Rust type.
31    ValueOutOfRange {
32        /// Position of the decoded value.
33        pos: usize,
34        /// Total number of felts available.
35        len: usize,
36        /// Name of the target Rust type.
37        ty: &'static str,
38        /// The decoded value.
39        value: u64,
40        /// The maximum supported value for `ty`.
41        max: u64,
42    },
43    /// An `Option<T>` tag was neither `0` nor `1`.
44    InvalidOptionTag {
45        /// Position of the decoded tag.
46        pos: usize,
47        /// Total number of felts available.
48        len: usize,
49        /// The decoded tag.
50        tag: u64,
51    },
52    /// A boolean value was neither `0` nor `1`.
53    InvalidBool {
54        /// Position of the decoded value.
55        pos: usize,
56        /// Total number of felts available.
57        len: usize,
58        /// The decoded value.
59        value: u64,
60    },
61    /// An enum tag was not a valid variant ordinal.
62    UnknownEnumTag {
63        /// Position of the decoded tag.
64        pos: usize,
65        /// Total number of felts available.
66        len: usize,
67        /// Name of the decoded enum type.
68        ty: &'static str,
69        /// The decoded tag.
70        tag: u32,
71    },
72    /// Extra data remained after decoding a value.
73    TrailingData {
74        /// Current read position.
75        pos: usize,
76        /// Total number of felts available.
77        len: usize,
78    },
79    /// A custom decoding error provided by a downstream implementation.
80    Custom(&'static str),
81}
82
83impl core::fmt::Display for FeltReprError {
84    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
85        match self {
86            Self::UnexpectedEof { pos, len } => {
87                write!(f, "unexpected end of input at felt {pos} of {len}")
88            }
89            Self::ValueOutOfRange {
90                pos,
91                len,
92                ty,
93                value,
94                max,
95            } => {
96                write!(f, "value {value} out of range for {ty} at felt {pos} of {len} (max {max})")
97            }
98            Self::InvalidOptionTag { pos, len, tag } => {
99                write!(f, "invalid Option tag at felt {pos} of {len}: {tag}")
100            }
101            Self::InvalidBool { pos, len, value } => {
102                write!(f, "invalid bool value at felt {pos} of {len}: {value}")
103            }
104            Self::UnknownEnumTag { pos, len, ty, tag } => {
105                write!(f, "unknown enum tag for {ty} at felt {pos} of {len}: {tag}")
106            }
107            Self::TrailingData { pos, len } => {
108                write!(f, "trailing data starting at felt {pos} of {len}")
109            }
110            Self::Custom(msg) => f.write_str(msg),
111        }
112    }
113}
114
115/// Convenience alias for results returned by felt-repr decoding APIs.
116pub type FeltReprResult<T> = core::result::Result<T, FeltReprError>;
117
118/// A reader that wraps a slice of `Felt` elements and tracks the current position.
119pub struct FeltReader<'a> {
120    data: &'a [Felt],
121    pos: usize,
122}
123
124impl<'a> FeltReader<'a> {
125    /// Creates a new `FeltReader` from a slice of `Felt` elements.
126    #[inline(always)]
127    pub fn new(data: &'a [Felt]) -> Self {
128        Self { data, pos: 0 }
129    }
130
131    /// Returns the current read position.
132    #[inline(always)]
133    pub fn pos(&self) -> usize {
134        self.pos
135    }
136
137    /// Returns the total number of felts in the underlying slice.
138    #[inline(always)]
139    pub fn len(&self) -> usize {
140        self.data.len()
141    }
142
143    /// Returns `true` if the underlying slice is empty.
144    #[inline(always)]
145    pub fn is_empty(&self) -> bool {
146        self.data.is_empty()
147    }
148
149    /// Returns the number of unread felts remaining.
150    #[inline(always)]
151    pub fn remaining(&self) -> usize {
152        self.data.len().saturating_sub(self.pos)
153    }
154
155    /// Ensures there are no unread felts remaining.
156    #[inline(always)]
157    pub fn ensure_eof(&self) -> FeltReprResult<()> {
158        if self.remaining() != 0 {
159            return Err(FeltReprError::TrailingData {
160                pos: self.pos,
161                len: self.data.len(),
162            });
163        }
164        Ok(())
165    }
166
167    /// Reads the next `Felt` element, advancing the position.
168    #[inline(always)]
169    pub fn read(&mut self) -> FeltReprResult<Felt> {
170        if self.pos >= self.data.len() {
171            return Err(FeltReprError::UnexpectedEof {
172                pos: self.pos,
173                len: self.data.len(),
174            });
175        }
176
177        let felt = self.data[self.pos];
178        self.pos += 1;
179        Ok(felt)
180    }
181
182    /// Reads the next element and decodes it as a `u32`.
183    #[inline(always)]
184    pub fn read_u32(&mut self) -> FeltReprResult<u32> {
185        let pos = self.pos;
186        let len = self.data.len();
187        let value = self.read()?.as_canonical_u64();
188        if value > u32::MAX as u64 {
189            return Err(FeltReprError::ValueOutOfRange {
190                pos,
191                len,
192                ty: "u32",
193                value,
194                max: u32::MAX as u64,
195            });
196        }
197        Ok(value as u32)
198    }
199
200    /// Reads the next element and decodes it as a `u8`.
201    #[inline(always)]
202    pub fn read_u8(&mut self) -> FeltReprResult<u8> {
203        let pos = self.pos;
204        let len = self.data.len();
205        let value = self.read()?.as_canonical_u64();
206        if value > u8::MAX as u64 {
207            return Err(FeltReprError::ValueOutOfRange {
208                pos,
209                len,
210                ty: "u8",
211                value,
212                max: u8::MAX as u64,
213            });
214        }
215        Ok(value as u8)
216    }
217
218    /// Reads the next element and decodes it as a boolean.
219    ///
220    /// Only `0` and `1` are accepted.
221    #[inline(always)]
222    pub fn read_bool(&mut self) -> FeltReprResult<bool> {
223        let pos = self.pos;
224        let len = self.data.len();
225        match self.read()?.as_canonical_u64() {
226            0 => Ok(false),
227            1 => Ok(true),
228            value => Err(FeltReprError::InvalidBool { pos, len, value }),
229        }
230    }
231
232    /// Reads the next element and decodes it as a length prefix.
233    ///
234    /// The length is encoded as a `u32` in a single `Felt`.
235    #[inline(always)]
236    pub fn read_len_u32(&mut self) -> FeltReprResult<usize> {
237        Ok(self.read_u32()? as usize)
238    }
239}
240
241/// A writer that wraps a `Vec<Felt>` and appends elements to it.
242pub struct FeltWriter<'a> {
243    data: &'a mut Vec<Felt>,
244}
245
246impl<'a> FeltWriter<'a> {
247    /// Creates a new `FeltWriter` from a mutable reference to a `Vec<Felt>`.
248    #[inline(always)]
249    pub fn new(data: &'a mut Vec<Felt>) -> Self {
250        Self { data }
251    }
252
253    /// Writes a `Felt` element to the output.
254    #[inline(always)]
255    pub fn write(&mut self, felt: Felt) {
256        self.data.push(felt);
257    }
258}
259
260/// Trait for deserialization from felt memory representation.
261pub trait FromFeltRepr: Sized {
262    /// Deserializes from a `FeltReader`, consuming the required elements.
263    fn from_felt_repr(reader: &mut FeltReader<'_>) -> FeltReprResult<Self>;
264}
265
266impl FromFeltRepr for Felt {
267    #[inline(always)]
268    fn from_felt_repr(reader: &mut FeltReader<'_>) -> FeltReprResult<Self> {
269        reader.read()
270    }
271}
272
273impl FromFeltRepr for u64 {
274    #[inline(always)]
275    fn from_felt_repr(reader: &mut FeltReader<'_>) -> FeltReprResult<Self> {
276        // Encode u64 as 2 u32 limbs
277        let lo = reader.read_u32()? as u64;
278        let hi = reader.read_u32()? as u64;
279        Ok((hi << 32) | lo)
280    }
281}
282
283impl FromFeltRepr for u32 {
284    #[inline(always)]
285    fn from_felt_repr(reader: &mut FeltReader<'_>) -> FeltReprResult<Self> {
286        reader.read_u32()
287    }
288}
289
290impl FromFeltRepr for u8 {
291    #[inline(always)]
292    fn from_felt_repr(reader: &mut FeltReader<'_>) -> FeltReprResult<Self> {
293        reader.read_u8()
294    }
295}
296
297impl FromFeltRepr for bool {
298    #[inline(always)]
299    fn from_felt_repr(reader: &mut FeltReader<'_>) -> FeltReprResult<Self> {
300        reader.read_bool()
301    }
302}
303
304/// Encodes an `Option<T>` as a 1-felt tag followed by the payload (if present).
305///
306/// Format:
307/// - `None` => `[0]`
308/// - `Some(x)` => `[1, x...]`
309impl<T> FromFeltRepr for Option<T>
310where
311    T: FromFeltRepr,
312{
313    #[inline(always)]
314    fn from_felt_repr(reader: &mut FeltReader<'_>) -> FeltReprResult<Self> {
315        let pos = reader.pos();
316        let len = reader.len();
317        match reader.read()?.as_canonical_u64() {
318            0 => Ok(None),
319            1 => Ok(Some(T::from_felt_repr(reader)?)),
320            tag => Err(FeltReprError::InvalidOptionTag { pos, len, tag }),
321        }
322    }
323}
324
325/// Encodes a `Vec<T>` as a length prefix followed by elements.
326///
327/// Format: `[len, elem0..., elemN-1...]` where `len` is a `u32` encoded in a single `Felt`.
328impl<T> FromFeltRepr for Vec<T>
329where
330    T: FromFeltRepr,
331{
332    #[inline(always)]
333    fn from_felt_repr(reader: &mut FeltReader<'_>) -> FeltReprResult<Self> {
334        let len = reader.read_len_u32()?;
335
336        let mut result = Vec::with_capacity(len);
337
338        let mut i = 0usize;
339        while i < len {
340            result.push(T::from_felt_repr(reader)?);
341            i += 1;
342        }
343        Ok(result)
344    }
345}
346
347/// Trait for serializing a type into its felt memory representation.
348pub trait ToFeltRepr {
349    /// Writes this value's felt representation to the writer.
350    fn write_felt_repr(&self, writer: &mut FeltWriter<'_>);
351
352    /// Convenience method that allocates and returns a `Vec<Felt>`.
353    fn to_felt_repr(&self) -> Vec<Felt> {
354        // Allocate ahead to avoid reallocations
355        let mut data = Vec::with_capacity(256);
356        self.write_felt_repr(&mut FeltWriter::new(&mut data));
357        data
358    }
359}
360
361impl ToFeltRepr for Felt {
362    #[inline(always)]
363    fn write_felt_repr(&self, writer: &mut FeltWriter<'_>) {
364        writer.write(*self);
365    }
366}
367
368impl ToFeltRepr for u64 {
369    #[inline(always)]
370    fn write_felt_repr(&self, writer: &mut FeltWriter<'_>) {
371        let lo = (*self & 0xffff_ffff) as u32;
372        let hi = (*self >> 32) as u32;
373        writer.write(Felt::new(lo as u64).unwrap());
374        writer.write(Felt::new(hi as u64).unwrap());
375    }
376}
377
378impl ToFeltRepr for u32 {
379    #[inline(always)]
380    fn write_felt_repr(&self, writer: &mut FeltWriter<'_>) {
381        writer.write(Felt::new(*self as u64).unwrap());
382    }
383}
384
385impl ToFeltRepr for u8 {
386    #[inline(always)]
387    fn write_felt_repr(&self, writer: &mut FeltWriter<'_>) {
388        writer.write(Felt::new(*self as u64).unwrap());
389    }
390}
391
392impl ToFeltRepr for bool {
393    #[inline(always)]
394    fn write_felt_repr(&self, writer: &mut FeltWriter<'_>) {
395        writer.write(Felt::new(*self as u64).unwrap());
396    }
397}
398
399/// Encodes an `Option<T>` as a 1-felt tag followed by the payload (if present).
400///
401/// Format:
402/// - `None` => `[0]`
403/// - `Some(x)` => `[1, x...]`
404impl<T> ToFeltRepr for Option<T>
405where
406    T: ToFeltRepr,
407{
408    #[inline(always)]
409    fn write_felt_repr(&self, writer: &mut FeltWriter<'_>) {
410        match self {
411            None => writer.write(Felt::new(0).unwrap()),
412            Some(value) => {
413                writer.write(Felt::new(1).unwrap());
414                value.write_felt_repr(writer);
415            }
416        }
417    }
418}
419
420/// Encodes a `Vec<T>` as a length prefix followed by elements.
421///
422/// Format: `[len, elem0..., elemN-1...]` where `len` is a `u32` encoded in a single `Felt`.
423impl<T> ToFeltRepr for Vec<T>
424where
425    T: ToFeltRepr,
426{
427    #[inline(always)]
428    fn write_felt_repr(&self, writer: &mut FeltWriter<'_>) {
429        let len = self.len();
430        assert!(len <= u32::MAX as usize, "Vec: length out of range");
431        writer.write(Felt::new(len as u64).unwrap());
432
433        let mut i = 0usize;
434        while i < len {
435            self[i].write_felt_repr(writer);
436            i += 1;
437        }
438    }
439}