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 inputs(&self) -> Vec<(DataType, String)> {
32        vec![(DataType::VoidPointer, "*list".to_string())]
33    }
34
35    fn outputs(&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"), 0xe8173cec5dd4649.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        ) {
75            let list_ptr = stack.pop().unwrap();
76            let list_length = memory[&list_ptr];
77            stack.push(list_length);
78        }
79
80        fn pseudorandom_initial_state(
81            &self,
82            seed: [u8; 32],
83            _: Option<BenchmarkCase>,
84        ) -> AccessorInitialState {
85            let mut rng = StdRng::from_seed(seed);
86            let list_ptr = rng.random();
87            let list_len = rng.random_range(0..=1 << 10);
88
89            let mut memory = HashMap::default();
90            insert_random_list(&DataType::Digest, list_ptr, list_len, &mut memory);
91            let stack = [self.init_stack_for_isolated_run(), vec![list_ptr]].concat();
92
93            AccessorInitialState { stack, memory }
94        }
95    }
96
97    #[test]
98    fn rust_shadow() {
99        ShadowedAccessor::new(Length).test();
100    }
101}
102
103#[cfg(test)]
104mod benches {
105    use super::*;
106    use crate::test_prelude::*;
107
108    #[test]
109    fn length_long_benchmark() {
110        ShadowedAccessor::new(Length).bench();
111    }
112}