cairo_vm/vm/runners/builtin_runner/
segment_arena.rs

1use crate::vm::errors::memory_errors::MemoryError;
2use crate::{
3    types::relocatable::{MaybeRelocatable, Relocatable},
4    vm::vm_memory::memory_segments::MemorySegmentManager,
5};
6
7#[cfg(not(feature = "std"))]
8use alloc::vec::Vec;
9use num_integer::div_ceil;
10
11pub(crate) const ARENA_BUILTIN_SIZE: u32 = 3;
12// The size of the builtin segment at the time of its creation.
13const INITIAL_SEGMENT_SIZE: usize = ARENA_BUILTIN_SIZE as usize;
14
15#[derive(Debug, Clone)]
16pub struct SegmentArenaBuiltinRunner {
17    base: Relocatable,
18    pub(crate) included: bool,
19    pub(crate) stop_ptr: Option<usize>,
20}
21
22impl SegmentArenaBuiltinRunner {
23    pub(crate) fn new(included: bool) -> Self {
24        SegmentArenaBuiltinRunner {
25            base: Relocatable::from((0, 0)),
26            included,
27            stop_ptr: None,
28        }
29    }
30
31    pub fn initialize_segments(&mut self, segments: &mut MemorySegmentManager) {
32        let info = &[
33            MaybeRelocatable::from(segments.add()),
34            MaybeRelocatable::from(0),
35            MaybeRelocatable::from(0),
36        ];
37        let segment_start = gen_arg(segments, info);
38        // 0 + 3 can't fail
39        self.base = (segment_start + INITIAL_SEGMENT_SIZE).unwrap();
40    }
41
42    pub fn get_used_cells(&self, segments: &MemorySegmentManager) -> Result<usize, MemoryError> {
43        let used = segments
44            .get_segment_used_size(self.base.segment_index as usize)
45            .ok_or(MemoryError::MissingSegmentUsedSizes)?;
46        if used < INITIAL_SEGMENT_SIZE {
47            return Err(MemoryError::InvalidUsedSizeSegmentArena);
48        }
49        Ok(used - INITIAL_SEGMENT_SIZE)
50    }
51
52    pub fn initial_stack(&self) -> Vec<MaybeRelocatable> {
53        if self.included {
54            vec![MaybeRelocatable::from(self.base)]
55        } else {
56            vec![]
57        }
58    }
59
60    pub fn get_used_instances(
61        &self,
62        segments: &MemorySegmentManager,
63    ) -> Result<usize, MemoryError> {
64        Ok(div_ceil(
65            self.get_used_cells(segments)?,
66            ARENA_BUILTIN_SIZE as usize,
67        ))
68    }
69
70    pub fn base(&self) -> usize {
71        self.base.segment_index as usize
72    }
73}
74
75// Specific non-failling version of gen_arg used specifically for SegmentArenaBuiltinRunner
76fn gen_arg(segments: &mut MemorySegmentManager, data: &[MaybeRelocatable; 3]) -> Relocatable {
77    let base = segments.add();
78    for (num, value) in data.iter().enumerate() {
79        // 0 + 3 can't fail, inserting into newly created segment can't fail
80        segments
81            .memory
82            .insert((base + num).unwrap(), value)
83            .unwrap();
84    }
85    base
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91    use crate::types::builtin_name::BuiltinName;
92    use crate::vm::errors::runner_errors::RunnerError;
93    use crate::{relocatable, utils::test_utils::*, vm::runners::builtin_runner::BuiltinRunner};
94    #[cfg(not(feature = "std"))]
95    use alloc::boxed::Box;
96    #[cfg(target_arch = "wasm32")]
97    use wasm_bindgen_test::*;
98
99    #[test]
100    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
101    fn gen_arg_test() {
102        let mut segments = MemorySegmentManager::new();
103        let data = &[
104            MaybeRelocatable::from(segments.add()),
105            MaybeRelocatable::from(0),
106            MaybeRelocatable::from(0),
107        ];
108        let base = gen_arg(&mut segments, data);
109        assert_eq!(base, (1, 0).into());
110        check_memory!(segments.memory, ((1, 0), (0, 0)), ((1, 1), 0), ((1, 2), 0));
111    }
112
113    #[test]
114    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
115    fn get_used_instances() {
116        let builtin = SegmentArenaBuiltinRunner::new(true);
117
118        let mut vm = vm!();
119        vm.segments.segment_used_sizes = Some(vec![3]);
120
121        assert_eq!(builtin.get_used_instances(&vm.segments), Ok(0));
122    }
123
124    #[test]
125    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
126    fn get_used_instances_enum() {
127        let builtin: BuiltinRunner = SegmentArenaBuiltinRunner::new(true).into();
128
129        let mut vm = vm!();
130        vm.segments.segment_used_sizes = Some(vec![3]);
131
132        assert_eq!(builtin.get_used_instances(&vm.segments), Ok(0));
133    }
134
135    #[test]
136    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
137    fn final_stack_error_stop_pointer() {
138        let mut builtin: BuiltinRunner = SegmentArenaBuiltinRunner::new(true).into();
139
140        let mut vm = vm!();
141
142        vm.segments = segments![
143            ((0, 0), (0, 0)),
144            ((0, 1), (0, 1)),
145            ((2, 0), (0, 0)),
146            ((2, 1), (0, 0))
147        ];
148
149        vm.segments.segment_used_sizes = Some(vec![6]);
150
151        let pointer = Relocatable::from((2, 2));
152        // USED_CELLS = SEGMENT_USED_SIZE - INITIAL_SIZE = 6 - 3 = 3
153        // NUM_INSTANCES = DIV_CEIL(USED_CELLS, CELLS_PER_INSTANCE) = DIV_CEIL(3, 3) = 1
154
155        // STOP_PTR == BASE + NUM_INSTANCES *  CELLS_PER_INSTANCE
156        // (0, 0) == (0, 3) + 1 * 3
157        // (0, 0) == (0, 6)
158        assert_eq!(
159            builtin.final_stack(&vm.segments, pointer),
160            Err(RunnerError::InvalidStopPointer(Box::new((
161                BuiltinName::segment_arena,
162                relocatable!(0, 6),
163                relocatable!(0, 0)
164            ))))
165        );
166    }
167
168    #[test]
169    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
170    fn final_stack_valid() {
171        let mut builtin: BuiltinRunner = SegmentArenaBuiltinRunner::new(false).into();
172
173        let mut vm = vm!();
174
175        vm.segments = segments![
176            ((0, 0), (0, 0)),
177            ((0, 1), (0, 1)),
178            ((2, 0), (0, 0)),
179            ((2, 1), (1, 0))
180        ];
181
182        vm.segments.segment_used_sizes = Some(vec![0]);
183
184        let pointer = Relocatable::from((2, 2));
185
186        assert_eq!(
187            builtin.final_stack(&vm.segments, pointer).unwrap(),
188            Relocatable::from((2, 2))
189        );
190    }
191
192    #[test]
193    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
194    fn final_stack_valid_from_enum() {
195        let mut builtin: BuiltinRunner = SegmentArenaBuiltinRunner::new(false).into();
196
197        let mut vm = vm!();
198
199        vm.segments = segments![
200            ((0, 0), (0, 0)),
201            ((0, 1), (0, 1)),
202            ((2, 0), (0, 0)),
203            ((2, 1), (1, 0))
204        ];
205
206        vm.segments.segment_used_sizes = Some(vec![0]);
207
208        let pointer = Relocatable::from((2, 2));
209
210        assert_eq!(
211            builtin.final_stack(&vm.segments, pointer).unwrap(),
212            Relocatable::from((2, 2))
213        );
214    }
215
216    #[test]
217    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
218    fn final_stack_error_non_relocatable() {
219        let mut builtin: BuiltinRunner = SegmentArenaBuiltinRunner::new(true).into();
220
221        let mut vm = vm!();
222
223        vm.segments = segments![
224            ((0, 0), (0, 0)),
225            ((0, 1), (0, 1)),
226            ((2, 0), (0, 0)),
227            ((2, 1), 2)
228        ];
229
230        vm.segments.segment_used_sizes = Some(vec![0]);
231
232        let pointer = Relocatable::from((2, 2));
233
234        assert_eq!(
235            builtin.final_stack(&vm.segments, pointer),
236            Err(RunnerError::NoStopPointer(Box::new(
237                BuiltinName::segment_arena
238            )))
239        );
240    }
241
242    #[test]
243    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
244    fn get_used_cells_and_allocated_size_test() {
245        let builtin: BuiltinRunner = SegmentArenaBuiltinRunner::new(true).into();
246
247        let mut vm = vm!();
248
249        vm.segments.segment_used_sizes = Some(vec![3]);
250
251        assert_eq!(
252            builtin.get_used_cells_and_allocated_size(&vm),
253            Ok((0_usize, 0))
254        );
255    }
256
257    #[test]
258    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
259    fn initialize_segments_for_output() {
260        let mut builtin = SegmentArenaBuiltinRunner::new(true);
261        let mut segments = MemorySegmentManager::new();
262        builtin.initialize_segments(&mut segments);
263        assert_eq!(builtin.base, (1, 3).into());
264    }
265
266    #[test]
267    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
268    fn get_initial_stack_for_output_with_base() {
269        let mut builtin = SegmentArenaBuiltinRunner::new(true);
270        builtin.base = relocatable!(1, 0);
271        let initial_stack = builtin.initial_stack();
272        assert_eq!(
273            initial_stack[0].clone(),
274            MaybeRelocatable::RelocatableValue((builtin.base() as isize, 0).into())
275        );
276        assert_eq!(initial_stack.len(), 1);
277    }
278
279    #[test]
280    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
281    fn get_used_cells_missing_segment_used_sizes() {
282        let builtin = BuiltinRunner::SegmentArena(SegmentArenaBuiltinRunner::new(true));
283        let vm = vm!();
284
285        assert_eq!(
286            builtin.get_used_cells(&vm.segments),
287            Err(MemoryError::MissingSegmentUsedSizes)
288        );
289    }
290
291    #[test]
292    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
293    fn get_used_cells_empty() {
294        let builtin = BuiltinRunner::SegmentArena(SegmentArenaBuiltinRunner::new(true));
295        let mut vm = vm!();
296
297        vm.segments.segment_used_sizes = Some(vec![0]);
298        assert_eq!(
299            builtin.get_used_cells(&vm.segments),
300            Err(MemoryError::InvalidUsedSizeSegmentArena)
301        );
302    }
303
304    #[test]
305    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
306    fn get_used_cells() {
307        let builtin = BuiltinRunner::SegmentArena(SegmentArenaBuiltinRunner::new(true));
308        let mut vm = vm!();
309
310        vm.segments.segment_used_sizes = Some(vec![4]);
311        assert_eq!(builtin.get_used_cells(&vm.segments), Ok(1));
312    }
313
314    #[test]
315    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
316    fn test_get_used_instances_missing_segments() {
317        let builtin = BuiltinRunner::SegmentArena(SegmentArenaBuiltinRunner::new(true));
318        let memory_segment_manager = MemorySegmentManager::new();
319
320        assert_eq!(
321            builtin.get_used_instances(&memory_segment_manager),
322            Err(MemoryError::MissingSegmentUsedSizes)
323        );
324    }
325
326    #[test]
327    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
328    fn test_get_used_instances_valid() {
329        let builtin = BuiltinRunner::SegmentArena(SegmentArenaBuiltinRunner::new(true));
330        let mut memory_segment_manager = MemorySegmentManager::new();
331        memory_segment_manager.segment_used_sizes = Some(vec![6]);
332        // (SIZE(6) - INITIAL_SIZE(3)) / CELLS_PER_INSTANCE(3)
333        assert_eq!(builtin.get_used_instances(&memory_segment_manager), Ok(1));
334    }
335
336    #[test]
337    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
338    fn test_deduce_memory_cell_output_builtin() {
339        let builtin = BuiltinRunner::SegmentArena(SegmentArenaBuiltinRunner::new(true));
340        let mut vm = vm!();
341
342        vm.segments = segments![
343            ((0, 0), (0, 0)),
344            ((0, 1), (0, 1)),
345            ((2, 0), (0, 0)),
346            ((2, 1), 2)
347        ];
348
349        let pointer = Relocatable::from((2, 2));
350
351        assert_eq!(
352            builtin.deduce_memory_cell(pointer, &vm.segments.memory),
353            Ok(None)
354        );
355    }
356
357    #[test]
358    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
359    fn initial_stackincluded_test() {
360        let ec_op_builtin: BuiltinRunner = SegmentArenaBuiltinRunner::new(true).into();
361        assert_eq!(ec_op_builtin.initial_stack(), vec![mayberelocatable!(0, 0)])
362    }
363
364    #[test]
365    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
366    fn initial_stack_notincluded_test() {
367        let ec_op_builtin = SegmentArenaBuiltinRunner::new(false);
368        assert_eq!(ec_op_builtin.initial_stack(), Vec::new())
369    }
370
371    #[test]
372    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
373    fn test_add_validation_rule_enum() {
374        let builtin: BuiltinRunner = SegmentArenaBuiltinRunner::new(true).into();
375        let mut vm = vm!();
376
377        vm.segments = segments![
378            ((0, 0), (0, 0)),
379            ((0, 1), (0, 1)),
380            ((2, 0), (0, 0)),
381            ((2, 1), 2)
382        ];
383
384        vm.segments.segment_used_sizes = Some(vec![0]);
385        builtin.add_validation_rule(&mut vm.segments.memory);
386    }
387
388    #[test]
389    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
390    fn cells_per_instance_enum() {
391        let builtin: BuiltinRunner = SegmentArenaBuiltinRunner::new(true).into();
392        assert_eq!(builtin.cells_per_instance(), ARENA_BUILTIN_SIZE)
393    }
394
395    #[test]
396    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
397    fn n_input_cells_enum() {
398        let builtin: BuiltinRunner = SegmentArenaBuiltinRunner::new(true).into();
399        assert_eq!(builtin.n_input_cells(), ARENA_BUILTIN_SIZE)
400    }
401
402    #[test]
403    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
404    fn instances_per_component_enum() {
405        let builtin: BuiltinRunner = SegmentArenaBuiltinRunner::new(true).into();
406        assert_eq!(builtin.instances_per_component(), 1)
407    }
408
409    #[test]
410    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
411    fn get_air_private_input() {
412        let builtin: BuiltinRunner = SegmentArenaBuiltinRunner::new(true).into();
413
414        let segments = segments![((0, 0), 0), ((0, 1), 1), ((0, 2), 2), ((0, 3), 3)];
415        assert!(builtin.air_private_input(&segments).is_empty());
416    }
417}