1use std::collections::HashMap;
2
3use triton_vm::prelude::*;
4
5use crate::list::get::Get;
6use crate::prelude::*;
7use crate::traits::basic_snippet::Reviewer;
8use crate::traits::basic_snippet::SignOffFingerprint;
9
10#[derive(Debug, Clone, Eq, PartialEq, Hash)]
32pub struct Pop {
33 element_type: DataType,
34}
35
36impl Pop {
37 pub const EMPTY_LIST_ERROR_ID: i128 = 400;
38
39 pub const MEM_PAGE_ACCESS_VIOLATION_ERROR_ID: i128 = 401;
42
43 pub fn new(element_type: DataType) -> Self {
48 Get::assert_element_type_is_supported(&element_type);
49
50 Self { element_type }
51 }
52}
53
54impl BasicSnippet for Pop {
55 fn inputs(&self) -> Vec<(DataType, String)> {
56 let list_type = DataType::List(Box::new(self.element_type.clone()));
57 vec![(list_type, "*list".to_string())]
58 }
59
60 fn outputs(&self) -> Vec<(DataType, String)> {
61 vec![(self.element_type.clone(), "element".to_string())]
62 }
63
64 fn entrypoint(&self) -> String {
65 let element_type = self.element_type.label_friendly_name();
66 format!("tasmlib_list_pop___{element_type}")
67 }
68
69 fn code(&self, _: &mut Library) -> Vec<LabelledInstruction> {
70 let mul_with_element_size = match self.element_type.stack_size() {
71 1 => triton_asm!(), n => triton_asm!(push {n} mul),
73 };
74
75 triton_asm!(
76 {self.entrypoint()}:
79 read_mem 1
81 addi 1 dup 1
83 push 0
84 eq push 0
86 eq assert error_id {Self::EMPTY_LIST_ERROR_ID}
88 dup 1
92 addi -1 pick 1
94 write_mem 1 pick 1
99 {&mul_with_element_size}
100 dup 0
104 split
105 pop 1
106 push 0
107 eq
108 assert error_id {Self::MEM_PAGE_ACCESS_VIOLATION_ERROR_ID}
109 add addi -1 {&self.element_type.read_value_from_memory_pop_pointer()}
115 return
118 )
119 }
120
121 fn sign_offs(&self) -> HashMap<Reviewer, SignOffFingerprint> {
122 let mut sign_offs = HashMap::new();
123 match self.element_type.stack_size() {
124 1 => _ = sign_offs.insert(Reviewer("ferdinand"), 0x52fe322ae5f58aaf.into()),
125 2 => _ = sign_offs.insert(Reviewer("ferdinand"), 0x7ef69decd22cdbb0.into()),
126 3 => _ = sign_offs.insert(Reviewer("ferdinand"), 0xa46002a8b06f3169.into()),
127 5 => _ = sign_offs.insert(Reviewer("ferdinand"), 0x3e328d0b595bb6af.into()),
128 _ => (),
129 }
130
131 sign_offs
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138 use crate::rust_shadowing_helper_functions::list::insert_random_list;
139 use crate::rust_shadowing_helper_functions::list::list_pop;
140 use crate::test_prelude::*;
141
142 impl Pop {
143 fn set_up_initial_state(
144 &self,
145 list_length: usize,
146 list_pointer: BFieldElement,
147 ) -> FunctionInitialState {
148 let mut memory = HashMap::default();
149 insert_random_list(&self.element_type, list_pointer, list_length, &mut memory);
150
151 let mut stack = self.init_stack_for_isolated_run();
152 stack.push(list_pointer);
153
154 FunctionInitialState { stack, memory }
155 }
156 }
157
158 impl Function for Pop {
159 fn rust_shadow(
160 &self,
161 stack: &mut Vec<BFieldElement>,
162 memory: &mut HashMap<BFieldElement, BFieldElement>,
163 ) {
164 let list_address = stack.pop().unwrap();
165 let element = list_pop(list_address, memory, self.element_type.stack_size());
166 stack.extend(element.into_iter().rev());
167 }
168
169 fn pseudorandom_initial_state(
170 &self,
171 seed: [u8; 32],
172 _: Option<BenchmarkCase>,
173 ) -> FunctionInitialState {
174 let mut rng = StdRng::from_seed(seed);
175 let list_length = rng.random_range(1..1 << 12);
176 let list_pointer = rng.random();
177
178 self.set_up_initial_state(list_length, list_pointer)
179 }
180 }
181
182 #[test]
183 fn rust_shadow() {
184 for ty in [
185 DataType::Bool,
186 DataType::Bfe,
187 DataType::U32,
188 DataType::U64,
189 DataType::Xfe,
190 DataType::Digest,
191 ] {
192 ShadowedFunction::new(Pop::new(ty)).test();
193 }
194 }
195
196 #[proptest]
197 fn empty_list_crashes_vm(#[strategy(arb())] list_pointer: BFieldElement) {
198 let pop = Pop::new(DataType::Digest);
199 let initial_state = pop.set_up_initial_state(0, list_pointer);
200 test_assertion_failure(
201 &ShadowedFunction::new(pop),
202 initial_state.into(),
203 &[Pop::EMPTY_LIST_ERROR_ID],
204 );
205 }
206
207 #[proptest(cases = 100)]
209 fn too_large_lists_crash_vm(
210 #[strategy(1_u64 << 22..1 << 32)] list_length: u64,
211 #[strategy(arb())] list_pointer: BFieldElement,
212 ) {
213 let mut memory = HashMap::default();
215 memory.insert(list_pointer, bfe!(list_length));
216
217 let tuple_ty = DataType::Tuple(vec![DataType::Bfe; 1 << 10]);
219 let set = Pop::new(tuple_ty);
220
221 let mut stack = set.init_stack_for_isolated_run();
222 stack.push(list_pointer);
223 let initial_state = AccessorInitialState { stack, memory };
224
225 test_assertion_failure(
226 &ShadowedFunction::new(set),
227 initial_state.into(),
228 &[Pop::MEM_PAGE_ACCESS_VIOLATION_ERROR_ID],
229 );
230 }
231}
232
233#[cfg(test)]
234mod benches {
235 use super::*;
236 use crate::test_prelude::*;
237
238 #[test]
239 fn benchmark() {
240 ShadowedFunction::new(Pop::new(DataType::Digest)).bench();
241 }
242}