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        use miden_crypto::field::PrimeField64;
105
106        self.elements.iter().map(|e| (*e).as_canonical_u64()).collect()
107    }
108}
109
110impl Deref for StackOutputs {
111    type Target = [Felt; MIN_STACK_DEPTH];
112
113    fn deref(&self) -> &Self::Target {
114        &self.elements
115    }
116}
117
118impl From<[Felt; MIN_STACK_DEPTH]> for StackOutputs {
119    fn from(value: [Felt; MIN_STACK_DEPTH]) -> Self {
120        Self { elements: value }
121    }
122}
123
124#[cfg(any(test, feature = "testing"))]
125impl AsMut<[Felt]> for StackOutputs {
126    /// Returns mutable access to the stack outputs, to be used for testing.
127    fn as_mut(&mut self) -> &mut [Felt] {
128        &mut self.elements
129    }
130}
131
132// SERIALIZATION
133// ================================================================================================
134
135impl Serializable for StackOutputs {
136    fn write_into<W: ByteWriter>(&self, target: &mut W) {
137        let num_stack_values = get_num_stack_values(self);
138        target.write_u8(num_stack_values);
139        target.write_many(&self.elements[..num_stack_values as usize]);
140    }
141}
142
143impl Deserializable for StackOutputs {
144    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
145        let num_elements = source.read_u8()?;
146
147        let elements: Vec<Felt> =
148            source.read_many_iter::<Felt>(num_elements.into())?.collect::<Result<_, _>>()?;
149
150        StackOutputs::new(&elements).map_err(|err| {
151            DeserializationError::InvalidValue(format!("failed to create stack outputs: {err}",))
152        })
153    }
154}
155
156// OUTPUT ERROR
157// ================================================================================================
158
159#[derive(Clone, Debug, thiserror::Error)]
160pub enum OutputError {
161    #[error("value {0} exceeds field modulus")]
162    InvalidStackElement(u64),
163    #[error("number of output values on the stack cannot exceed {0}, but was {1}")]
164    OutputStackTooBig(usize, usize),
165}