Skip to main content

miden_stdlib_sys/stdlib/
mem.rs

1#![allow(dead_code)]
2
3extern crate alloc;
4use alloc::vec::Vec;
5
6#[cfg(all(target_family = "wasm", miden))]
7use crate::felt;
8use crate::intrinsics::{Felt, Word};
9
10#[cfg(all(target_family = "wasm", miden))]
11unsafe extern "C" {
12
13    /// Moves an arbitrary number of words from the advice stack to memory.
14    ///
15    /// Input: [num_words, write_ptr, ...]
16    /// Output: [R0, R1, C, write_ptr', ...]
17    ///
18    /// Where R0, R1, C are the final hasher state, and `write_ptr'` points to the end of the
19    /// copied words.
20    ///
21    /// Cycles:
22    /// - Even num_words: 43 + 9 * num_words / 2
23    /// - Odd num_words: 60 + 9 * round_down(num_words / 2)
24    #[link_name = "miden::core::mem::pipe_words_to_memory"]
25    fn extern_pipe_words_to_memory(num_words: Felt, write_ptr: *mut Felt, out_ptr: *mut Felt);
26
27    /// Moves an even number of words from the advice stack to memory.
28    ///
29    /// Input: [R0, R1, C, write_ptr, end_ptr, ...]
30    /// Output: [R0', R1', C', write_ptr, ...]
31    ///
32    /// Where:
33    /// - The words R0, R1, and C are the hasher state (R0 on top)
34    /// - C is the capacity
35    /// - R0, R1 are the rate portion of the state
36    /// - The value num_words = end_ptr - write_ptr must be positive and even
37    ///
38    /// Cycles: 9 + 6 * (num_words / 2)
39    #[link_name = "miden::core::mem::pipe_double_words_to_memory"]
40    fn extern_pipe_double_words_to_memory(
41        r00: Felt,
42        r01: Felt,
43        r02: Felt,
44        r03: Felt,
45        r10: Felt,
46        r11: Felt,
47        r12: Felt,
48        r13: Felt,
49        c0: Felt,
50        c1: Felt,
51        c2: Felt,
52        c3: Felt,
53        write_ptr: *mut Felt,
54        end_ptr: *mut Felt,
55        out_ptr: *mut Felt,
56    );
57
58    /// Moves an arbitrary number of words from the advice stack to memory and asserts it matches the commitment.
59    ///
60    /// Input: [num_words, write_ptr, COM, ...]
61    /// Output: [write_ptr', ...]
62    ///
63    /// Cycles:
64    /// - Even num_words: 58 + 9 * (num_words / 2)
65    /// - Odd num_words: 75 + 9 * round_down(num_words / 2)
66    #[link_name = "miden::core::mem::pipe_preimage_to_memory"]
67    pub(crate) fn extern_pipe_preimage_to_memory(
68        num_words: Felt,
69        write_ptr: *mut Felt,
70        com0: Felt,
71        com1: Felt,
72        com2: Felt,
73        com3: Felt,
74    ) -> i32;
75}
76
77/// Reads an arbitrary number of words `num_words` from the advice stack and returns them along with
78/// the digest of all read words.
79///
80/// Cycles:
81/// - Even num_words: 43 + 9 * num_words / 2
82/// - Odd num_words: 60 + 9 * round_down(num_words / 2)
83#[cfg(all(target_family = "wasm", miden))]
84pub fn pipe_words_to_memory(num_words: Felt) -> (Word, Vec<Felt>) {
85    #[repr(C)]
86    struct Result {
87        r0: Word,
88        r1: Word,
89        c: Word,
90        write_ptr: *mut Felt,
91    }
92
93    unsafe {
94        let num_words_usize =
95            usize::try_from(num_words.as_canonical_u64()).expect("num_words must fit in usize");
96        let num_felts = num_words_usize.checked_mul(4).expect("num_words too large");
97
98        let mut ret_area = ::core::mem::MaybeUninit::<Result>::uninit();
99        let mut buf: Vec<Felt> = Vec::with_capacity(num_felts);
100
101        let rust_write_ptr = buf.as_mut_ptr().addr();
102        let rust_write_ptr_u32 = u32::try_from(rust_write_ptr).expect("write_ptr must fit in u32");
103        assert_eq!(rust_write_ptr_u32 % 4, 0, "write_ptr must be word-aligned");
104        let miden_write_ptr = rust_write_ptr_u32 / 4;
105
106        extern_pipe_words_to_memory(
107            num_words,
108            miden_write_ptr as usize as *mut Felt,
109            ret_area.as_mut_ptr() as *mut Felt,
110        );
111        buf.set_len(num_felts);
112        let Result { r0, .. } = ret_area.assume_init();
113        (r0, buf)
114    }
115}
116
117/// Reads an arbitrary number of words `num_words` from the advice stack and returns them along with
118/// sequantial RPO hash of all read words.
119#[cfg(not(all(target_family = "wasm", miden)))]
120pub fn pipe_words_to_memory(_num_words: Felt) -> (Word, Vec<Felt>) {
121    unimplemented!("miden::core::mem bindings are only available when targeting the Miden VM")
122}
123
124/// Returns an even number of words from the advice stack along with the RPO hash of all read words.
125///
126/// Cycles: 9 + 6 * (num_words / 2)
127#[cfg(all(target_family = "wasm", miden))]
128pub fn pipe_double_words_to_memory(num_words: Felt) -> (Word, Vec<Felt>) {
129    #[repr(C)]
130    struct Result {
131        r0: Word,
132        r1: Word,
133        c: Word,
134        write_ptr: *mut Felt,
135    }
136
137    let num_words_usize =
138        usize::try_from(num_words.as_canonical_u64()).expect("num_words must fit in usize");
139    let num_felts = num_words_usize.checked_mul(4).expect("num_words too large");
140
141    let mut buf: Vec<Felt> = Vec::with_capacity(num_felts);
142
143    let rust_write_ptr = buf.as_mut_ptr().addr();
144    let rust_write_ptr_u32 = u32::try_from(rust_write_ptr).expect("write_ptr must fit in u32");
145    assert_eq!(rust_write_ptr_u32 % 4, 0, "write_ptr must be word-aligned");
146    let miden_write_ptr = rust_write_ptr_u32 / 4;
147    let num_felts_u32 = u32::try_from(num_felts).expect("num_felts must fit in u32");
148    let miden_end_ptr = miden_write_ptr + num_felts_u32;
149
150    // Place for returned R0, R1, C, write_ptr
151    let mut ret_area = ::core::mem::MaybeUninit::<Result>::uninit();
152    let zero = felt!(0);
153    unsafe {
154        extern_pipe_double_words_to_memory(
155            zero,
156            zero,
157            zero,
158            zero, // R0
159            zero,
160            zero,
161            zero,
162            zero, // R1
163            zero,
164            zero,
165            zero,
166            zero, // C
167            miden_write_ptr as usize as *mut Felt,
168            miden_end_ptr as usize as *mut Felt,
169            ret_area.as_mut_ptr() as *mut Felt,
170        );
171        buf.set_len(num_felts);
172        let Result { r0, .. } = ret_area.assume_init();
173        (r0, buf)
174    }
175}
176
177/// Returns an even number of words from the advice stack along with the RPO hash of all read words.
178#[cfg(not(all(target_family = "wasm", miden)))]
179pub fn pipe_double_words_to_memory(_num_words: Felt) -> (Word, Vec<Felt>) {
180    unimplemented!("miden::core::mem bindings are only available when targeting the Miden VM")
181}
182
183/// Pops an arbitrary number of words from the advice stack and asserts it matches the commitment.
184/// Returns a Vec containing the loaded words.
185#[inline]
186#[cfg(all(target_family = "wasm", miden))]
187pub fn adv_load_preimage(num_words: Felt, commitment: Word) -> Vec<Felt> {
188    // Allocate a Vec with the specified capacity
189    let num_words_usize = num_words.as_canonical_u64() as usize;
190    let num_felts = num_words_usize * 4;
191    let mut result: Vec<Felt> = Vec::with_capacity(num_felts);
192
193    let result_miden_ptr = (result.as_mut_ptr() as usize) / 4;
194    unsafe {
195        // Call pipe_preimage_to_memory to load words from advice stack
196        extern_pipe_preimage_to_memory(
197            num_words,
198            result_miden_ptr as *mut Felt,
199            commitment[0],
200            commitment[1],
201            commitment[2],
202            commitment[3],
203        );
204
205        // Set the length of the Vec to match what was loaded
206        result.set_len(num_felts);
207    }
208
209    result
210}
211
212/// Pops an arbitrary number of words from the advice stack and asserts it matches the commitment.
213/// Returns a Vec containing the loaded words.
214#[cfg(not(all(target_family = "wasm", miden)))]
215#[inline]
216pub fn adv_load_preimage(_num_words: Felt, _commitment: Word) -> Vec<Felt> {
217    unimplemented!("miden::core::mem bindings are only available when targeting the Miden VM")
218}