Skip to main content

tasm_lib/list/
length.rs

1use std::collections::HashMap;
2
3use triton_vm::prelude::*;
4
5use crate::prelude::*;
6use crate::traits::basic_snippet::Reviewer;
7use crate::traits::basic_snippet::SignOffFingerprint;
8
9/// ### Behavior
10///
11/// ```text
12/// BEFORE: _ *list
13/// AFTER:  _ list_length
14/// ```
15///
16/// ### Preconditions
17///
18/// - the input argument is a pointer to a [`BFieldCodec`] encoded list
19///
20/// ### Postconditions
21///
22/// None.
23//
24// Todo: the return type used to be a u32. However, neither the tasm nor the
25//   rust shadowing ever performed any range checks. Should the return type be
26//   changed back to u32? If so, should range checks be introduced?
27#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
28pub struct Length;
29
30impl BasicSnippet for Length {
31    fn parameters(&self) -> Vec<(DataType, String)> {
32        vec![(DataType::VoidPointer, "*list".to_string())]
33    }
34
35    fn return_values(&self) -> Vec<(DataType, String)> {
36        vec![(DataType::Bfe, "list_length".to_string())]
37    }
38
39    fn entrypoint(&self) -> String {
40        "tasmlib_list_length".to_string()
41    }
42
43    fn code(&self, _: &mut Library) -> Vec<LabelledInstruction> {
44        triton_asm! {
45            // BEFORE: _ *list
46            // AFTER:  _ list_length
47            {self.entrypoint()}:
48                read_mem 1
49                pop 1
50                return
51        }
52    }
53
54    fn sign_offs(&self) -> HashMap<Reviewer, SignOffFingerprint> {
55        let mut sign_offs = HashMap::new();
56        sign_offs.insert(Reviewer("ferdinand"), 0x5d27afc762ace284.into());
57        sign_offs
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use rand::prelude::*;
64
65    use super::*;
66    use crate::rust_shadowing_helper_functions::list::insert_random_list;
67    use crate::test_prelude::*;
68
69    impl Accessor for Length {
70        fn rust_shadow(
71            &self,
72            stack: &mut Vec<BFieldElement>,
73            memory: &HashMap<BFieldElement, BFieldElement>,
74        ) -> Result<(), RustShadowError> {
75            let list_ptr = stack.pop().ok_or(RustShadowError::StackUnderflow)?;
76            let list_length = *memory.get(&list_ptr).ok_or(RustShadowError::Other)?;
77            stack.push(list_length);
78
79            Ok(())
80        }
81
82        fn pseudorandom_initial_state(
83            &self,
84            seed: [u8; 32],
85            _: Option<BenchmarkCase>,
86        ) -> AccessorInitialState {
87            let mut rng = StdRng::from_seed(seed);
88            let list_ptr = rng.random();
89            let list_len = rng.random_range(0..=1 << 10);
90
91            let mut memory = HashMap::default();
92            insert_random_list(&DataType::Digest, list_ptr, list_len, &mut memory);
93            let stack = [self.init_stack_for_isolated_run(), vec![list_ptr]].concat();
94
95            AccessorInitialState { stack, memory }
96        }
97    }
98
99    #[macro_rules_attr::apply(test)]
100    fn rust_shadow() {
101        ShadowedAccessor::new(Length).test();
102    }
103}
104
105#[cfg(test)]
106mod benches {
107    use super::*;
108    use crate::test_prelude::*;
109
110    #[macro_rules_attr::apply(test)]
111    fn length_long_benchmark() {
112        ShadowedAccessor::new(Length).bench();
113    }
114}