Skip to main content

miden_core/program/stack/
outputs.rs

1use alloc::vec::Vec;
2use core::ops::Deref;
3
4use super::{MIN_STACK_DEPTH, get_num_stack_values};
5use crate::{
6    Felt, WORD_SIZE, Word, ZERO,
7    serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
8};
9
10// STACK OUTPUTS
11// ================================================================================================
12
13/// Defines the final state of the VM's operand stack at the end of program execution.
14///
15/// The first element is at position 0 (top of stack).
16#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
17pub struct StackOutputs {
18    elements: [Felt; MIN_STACK_DEPTH],
19}
20
21impl StackOutputs {
22    // CONSTRUCTORS
23    // --------------------------------------------------------------------------------------------
24
25    /// Constructs a new [StackOutputs] struct from the provided stack elements.
26    ///
27    /// # Errors
28    ///  Returns an error if the number of stack elements is greater than `MIN_STACK_DEPTH` (16).
29    pub fn new(values: &[Felt]) -> Result<Self, OutputError> {
30        if values.len() > MIN_STACK_DEPTH {
31            return Err(OutputError::OutputStackTooBig(MIN_STACK_DEPTH, values.len()));
32        }
33
34        let mut elements = [ZERO; MIN_STACK_DEPTH];
35        elements[..values.len()].copy_from_slice(values);
36
37        Ok(Self { elements })
38    }
39
40    // PUBLIC ACCESSORS
41    // --------------------------------------------------------------------------------------------
42
43    /// Returns the element located at the specified position on the stack or `None` if out of
44    /// bounds.
45    pub fn get_element(&self, idx: usize) -> Option<Felt> {
46        self.elements.get(idx).cloned()
47    }
48
49    /// Returns the word located starting at the specified Felt position on the stack in
50    /// little-endian order, or `None` if out of bounds.
51    ///
52    /// For example, passing in `0` returns the word at the top of the stack, and passing in `4`
53    /// returns the word starting at element index `4`.
54    ///
55    /// Stack element N will be at position 0 of the word, N+1 at position 1, N+2 at position 2,
56    /// and N+3 at position 3. `Word[0]` corresponds to the top of the stack.
57    pub fn get_word(&self, idx: usize) -> Option<Word> {
58        if idx > MIN_STACK_DEPTH - WORD_SIZE {
59            return None;
60        }
61
62        Some(Word::from([
63            self.elements[idx],
64            self.elements[idx + 1],
65            self.elements[idx + 2],
66            self.elements[idx + 3],
67        ]))
68    }
69
70    /// Returns the number of requested elements up to the maximum available in the stack outputs.
71    pub fn get_num_elements(&self, num_outputs: usize) -> &[Felt] {
72        let len = self.elements.len().min(num_outputs);
73        &self.elements[..len]
74    }
75
76    // TESTING UTILITIES
77    // --------------------------------------------------------------------------------------------
78
79    /// Attempts to create [StackOutputs] struct from the provided stack elements represented as
80    /// vector of `u64` values.
81    ///
82    /// # Errors
83    /// Returns an error if:
84    /// - Any of the provided stack elements are invalid field elements.
85    #[cfg(any(test, feature = "testing"))]
86    pub fn try_from_ints<I>(iter: I) -> Result<Self, OutputError>
87    where
88        I: IntoIterator<Item = u64>,
89    {
90        use miden_crypto::field::QuotientMap;
91
92        // Validate stack elements
93        let values = iter
94            .into_iter()
95            .map(|v| Felt::from_canonical_checked(v).ok_or(OutputError::InvalidStackElement(v)))
96            .collect::<Result<Vec<Felt>, _>>()?;
97
98        Self::new(&values)
99    }
100
101    /// Converts the [`StackOutputs`] into the vector of `u64` values.
102    #[cfg(any(test, feature = "testing"))]
103    pub fn as_int_vec(&self) -> Vec<u64> {
104        self.elements.iter().map(|e| (*e).as_canonical_u64()).collect()
105    }
106}
107
108impl Deref for StackOutputs {
109    type Target = [Felt; MIN_STACK_DEPTH];
110
111    fn deref(&self) -> &Self::Target {
112        &self.elements
113    }
114}
115
116impl From<[Felt; MIN_STACK_DEPTH]> for StackOutputs {
117    fn from(value: [Felt; MIN_STACK_DEPTH]) -> Self {
118        Self { elements: value }
119    }
120}
121
122#[cfg(any(test, feature = "testing"))]
123impl AsMut<[Felt]> for StackOutputs {
124    /// Returns mutable access to the stack outputs, to be used for testing.
125    fn as_mut(&mut self) -> &mut [Felt] {
126        &mut self.elements
127    }
128}
129
130// SERIALIZATION
131// ================================================================================================
132
133impl Serializable for StackOutputs {
134    fn write_into<W: ByteWriter>(&self, target: &mut W) {
135        let num_stack_values = get_num_stack_values(self);
136        target.write_u8(num_stack_values);
137        target.write_many(&self.elements[..num_stack_values as usize]);
138    }
139}
140
141impl Deserializable for StackOutputs {
142    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
143        let num_elements = source.read_u8()?;
144
145        let elements: Vec<Felt> =
146            source.read_many_iter::<Felt>(num_elements.into())?.collect::<Result<_, _>>()?;
147
148        StackOutputs::new(&elements).map_err(|err| {
149            DeserializationError::InvalidValue(format!("failed to create stack outputs: {err}",))
150        })
151    }
152}
153
154// OUTPUT ERROR
155// ================================================================================================
156
157#[derive(Clone, Debug, thiserror::Error)]
158pub enum OutputError {
159    #[error("value {0} exceeds field modulus")]
160    InvalidStackElement(u64),
161    #[error("number of output values on the stack cannot exceed {0}, but was {1}")]
162    OutputStackTooBig(usize, usize),
163}