cairo_vm/vm/vm_memory/
memory_segments.rs

1use crate::stdlib::collections::HashSet;
2use core::cmp::max;
3use core::fmt;
4
5use crate::vm::runners::cairo_pie::CairoPieMemory;
6use crate::Felt252;
7use num_traits::Zero;
8
9use crate::stdlib::prelude::*;
10use crate::stdlib::{any::Any, collections::HashMap};
11use crate::vm::runners::cairo_runner::CairoArg;
12
13use crate::{
14    types::relocatable::{MaybeRelocatable, Relocatable},
15    vm::{
16        errors::memory_errors::MemoryError, errors::vm_errors::VirtualMachineError,
17        vm_memory::memory::Memory,
18    },
19};
20
21use super::memory::MemoryCell;
22
23pub struct MemorySegmentManager {
24    pub segment_sizes: HashMap<usize, usize>,
25    pub segment_used_sizes: Option<Vec<usize>>,
26    pub memory: Memory,
27    // A map from segment index to a list of pairs (offset, page_id) that constitute the
28    // public memory. Note that the offset is absolute (not based on the page_id).
29    pub public_memory_offsets: HashMap<usize, Vec<(usize, usize)>>,
30    // Segment index of the zero segment index, a memory segment filled with zeroes, used exclusively by builtin runners
31    // This segment will never have index 0 so we use 0 to represent uninitialized value
32    zero_segment_index: usize,
33    // Segment size of the zero segment index
34    zero_segment_size: usize,
35}
36
37impl MemorySegmentManager {
38    /// Number of segments in the real memory
39    pub fn num_segments(&self) -> usize {
40        self.memory.data.len()
41    }
42
43    /// Number of segments in the temporary memory
44    pub fn num_temp_segments(&self) -> usize {
45        self.memory.temp_data.len()
46    }
47
48    ///Adds a new segment and returns its starting location as a Relocatable value. Its segment index will always be positive.
49    pub fn add(&mut self) -> Relocatable {
50        self.memory.data.push(Vec::new());
51        Relocatable {
52            segment_index: (self.memory.data.len() - 1) as isize,
53            offset: 0,
54        }
55    }
56
57    /// Adds a new temporary segment and returns its starting location as a Relocatable value. Its segment index will always be negative.
58    pub fn add_temporary_segment(&mut self) -> Relocatable {
59        self.memory.temp_data.push(Vec::new());
60        Relocatable {
61            // We dont substract 1 as we need to take into account the index shift (temporary memory begins from -1 instead of 0)
62            segment_index: -((self.memory.temp_data.len()) as isize),
63            offset: 0,
64        }
65    }
66
67    ///Writes data into the memory from address ptr and returns the first address after the data.
68    pub fn load_data(
69        &mut self,
70        ptr: Relocatable,
71        data: &[MaybeRelocatable],
72    ) -> Result<Relocatable, MemoryError> {
73        // Starting from the end ensures any necessary resize
74        // is performed once with enough room for everything
75        for (num, value) in data.iter().enumerate().rev() {
76            self.memory.insert((ptr + num)?, value)?;
77        }
78        (ptr + data.len()).map_err(MemoryError::Math)
79    }
80
81    pub fn new() -> MemorySegmentManager {
82        MemorySegmentManager {
83            segment_sizes: HashMap::new(),
84            segment_used_sizes: None,
85            public_memory_offsets: HashMap::new(),
86            memory: Memory::new(),
87            zero_segment_index: 0,
88            zero_segment_size: 0,
89        }
90    }
91
92    /// Calculates the size of each memory segment.
93    pub fn compute_effective_sizes(&mut self) -> &Vec<usize> {
94        self.segment_used_sizes
95            .get_or_insert_with(|| self.memory.data.iter().map(Vec::len).collect())
96    }
97
98    ///Returns the number of used segments if they have been computed.
99    ///Returns None otherwise.
100    pub fn get_segment_used_size(&self, index: usize) -> Option<usize> {
101        self.segment_used_sizes.as_ref()?.get(index).copied()
102    }
103
104    pub fn get_segment_size(&self, index: usize) -> Option<usize> {
105        self.segment_sizes
106            .get(&index)
107            .cloned()
108            .or_else(|| self.get_segment_used_size(index))
109    }
110
111    ///Returns a vector containing the first relocated address of each memory segment
112    pub fn relocate_segments(&self) -> Result<Vec<usize>, MemoryError> {
113        let first_addr = 1;
114        let mut relocation_table = vec![first_addr];
115        match &self.segment_used_sizes {
116            Some(segment_used_sizes) => {
117                for (i, _size) in segment_used_sizes.iter().enumerate() {
118                    let segment_size = self
119                        .get_segment_size(i)
120                        .ok_or(MemoryError::MissingSegmentUsedSizes)?;
121
122                    relocation_table.push(relocation_table[i] + segment_size);
123                }
124            }
125            None => return Err(MemoryError::MissingSegmentUsedSizes),
126        }
127        //The last value corresponds to the total amount of elements across all segments, which isnt needed for relocation.
128        relocation_table.pop();
129        Ok(relocation_table)
130    }
131
132    pub fn gen_arg(&mut self, arg: &dyn Any) -> Result<MaybeRelocatable, MemoryError> {
133        if let Some(value) = arg.downcast_ref::<MaybeRelocatable>() {
134            Ok(value.clone())
135        } else if let Some(value) = arg.downcast_ref::<Vec<MaybeRelocatable>>() {
136            let base = self.add();
137            self.write_arg(base, value)?;
138            Ok(base.into())
139        } else if let Some(value) = arg.downcast_ref::<Vec<Relocatable>>() {
140            let base = self.add();
141            self.write_arg(base, value)?;
142            Ok(base.into())
143        } else {
144            Err(MemoryError::GenArgInvalidType)
145        }
146    }
147
148    pub fn gen_cairo_arg(
149        &mut self,
150        arg: &CairoArg,
151    ) -> Result<MaybeRelocatable, VirtualMachineError> {
152        match arg {
153            CairoArg::Single(value) => Ok(value.clone()),
154            CairoArg::Array(values) => {
155                let base = self.add();
156                self.load_data(base, values)?;
157                Ok(base.into())
158            }
159            CairoArg::Composed(cairo_args) => {
160                let args = cairo_args
161                    .iter()
162                    .map(|cairo_arg| self.gen_cairo_arg(cairo_arg))
163                    .collect::<Result<Vec<MaybeRelocatable>, VirtualMachineError>>()?;
164                let base = self.add();
165                self.load_data(base, &args)?;
166                Ok(base.into())
167            }
168        }
169    }
170
171    pub fn write_arg(
172        &mut self,
173        ptr: Relocatable,
174        arg: &dyn Any,
175    ) -> Result<MaybeRelocatable, MemoryError> {
176        if let Some(vector) = arg.downcast_ref::<Vec<MaybeRelocatable>>() {
177            self.load_data(ptr, vector).map(Into::into)
178        } else if let Some(vector) = arg.downcast_ref::<Vec<Relocatable>>() {
179            let data: &Vec<MaybeRelocatable> = &vector.iter().map(|value| value.into()).collect();
180            self.load_data(ptr, data).map(Into::into)
181        } else {
182            Err(MemoryError::WriteArg)
183        }
184    }
185
186    pub fn is_valid_memory_value(&self, value: &MaybeRelocatable) -> Result<bool, MemoryError> {
187        match &self.segment_used_sizes {
188            Some(segment_used_sizes) => match value {
189                MaybeRelocatable::Int(_) => Ok(true),
190                MaybeRelocatable::RelocatableValue(relocatable) => {
191                    let segment_index: usize =
192                        relocatable.segment_index.try_into().map_err(|_| {
193                            MemoryError::AddressInTemporarySegment(relocatable.segment_index)
194                        })?;
195
196                    Ok(segment_index < segment_used_sizes.len())
197                }
198            },
199            None => Err(MemoryError::MissingSegmentUsedSizes),
200        }
201    }
202
203    pub fn is_accessed(&self, addr: &Relocatable) -> Result<bool, MemoryError> {
204        self.memory.is_accessed(addr)
205    }
206
207    /// Counts the memory holes (aka unaccessed memory cells) in memory
208    /// # Parameters
209    /// - `builtin_segment_indexes`: Set representing the segments indexes of the builtins initialized in the VM, except for the output builtin.
210    pub fn get_memory_holes(
211        &self,
212        builtin_segment_indexes: HashSet<usize>,
213    ) -> Result<usize, MemoryError> {
214        let data = &self.memory.data;
215        let mut memory_holes = 0;
216        for i in 0..data.len() {
217            // Instead of marking all of the builtin segment's address as accessed, we just skip them when counting memory holes
218            // Output builtin is extempt from this behaviour
219            if builtin_segment_indexes.contains(&i) {
220                continue;
221            }
222            let accessed_amount =
223                // Instead of marking the values in the zero segment until zero_segment_size as accessed we use zero_segment_size as accessed_amount
224                if self.has_zero_segment() && i == self.zero_segment_index {
225                    self.zero_segment_size
226                } else {
227                    match self.memory.get_amount_of_accessed_addresses_for_segment(i) {
228                        Some(accessed_amount) if accessed_amount > 0 => accessed_amount,
229                        _ => continue,
230                    }
231                };
232            let segment_size = self
233                .get_segment_size(i)
234                .ok_or(MemoryError::MissingSegmentUsedSizes)?;
235            if accessed_amount > segment_size {
236                return Err(MemoryError::SegmentHasMoreAccessedAddressesThanSize(
237                    Box::new((i, accessed_amount, segment_size)),
238                ));
239            }
240            memory_holes += segment_size - accessed_amount;
241        }
242        Ok(memory_holes)
243    }
244
245    /// Returns a list of addresses of memory cells that constitute the public memory.
246    /// segment_offsets is the result of self.relocate_segments()
247    pub fn get_public_memory_addresses(
248        &self,
249        segment_offsets: &[usize],
250    ) -> Result<Vec<(usize, usize)>, MemoryError> {
251        let mut addresses = Vec::with_capacity(self.num_segments());
252        let empty_vec = vec![];
253        for segment_index in 0..self.num_segments() {
254            let offsets = &self
255                .public_memory_offsets
256                .get(&segment_index)
257                .unwrap_or(&empty_vec);
258            let segment_start = segment_offsets
259                .get(segment_index)
260                .ok_or(MemoryError::MalformedPublicMemory)?;
261            for (offset, page_id) in offsets.iter() {
262                addresses.push((segment_start + offset, *page_id));
263            }
264        }
265        Ok(addresses)
266    }
267
268    // Writes the following information for the given segment:
269    // * size - The size of the segment (to be used in relocate_segments).
270    // * public_memory - A list of offsets for memory cells that will be considered as public
271    // memory.
272    pub fn finalize(
273        &mut self,
274        size: Option<usize>,
275        segment_index: usize,
276        public_memory: Option<&Vec<(usize, usize)>>,
277    ) {
278        if let Some(size) = size {
279            self.segment_sizes.insert(segment_index, size);
280        }
281        self.public_memory_offsets
282            .insert(segment_index, public_memory.cloned().unwrap_or_default());
283    }
284
285    pub fn has_zero_segment(&self) -> bool {
286        !self.zero_segment_index.is_zero()
287    }
288
289    // Creates the zero segment if it wasn't previously created
290    // Fills the segment with the value 0 until size is reached
291    // Returns the index of the zero segment
292    pub(crate) fn add_zero_segment(&mut self, size: usize) -> usize {
293        if !self.has_zero_segment() {
294            self.zero_segment_index = self.add().segment_index as usize;
295        }
296
297        // Fil zero segment with zero values until size is reached
298        for _ in 0..(size.saturating_sub(self.zero_segment_size)) {
299            // As zero_segment_index is only accessible to the segment manager
300            // we can asume that it is always valid and index direcly into it
301            self.memory.data[self.zero_segment_index].push(MemoryCell::new(Felt252::ZERO.into()))
302        }
303        self.zero_segment_size = max(self.zero_segment_size, size);
304        self.zero_segment_index
305    }
306
307    // Finalizes the zero segment and clears it's tracking data from the manager
308    pub(crate) fn finalize_zero_segment(&mut self) {
309        if self.has_zero_segment() {
310            self.finalize(Some(self.zero_segment_size), self.zero_segment_index, None);
311            self.zero_segment_index = 0;
312            self.zero_segment_size = 0;
313        }
314    }
315
316    pub(crate) fn load_pie_memory(
317        &mut self,
318        pie_memory: &CairoPieMemory,
319        n_extra_segments: usize,
320    ) -> Result<(), MemoryError> {
321        // Create extra segments
322        for _ in 0..n_extra_segments {
323            self.add();
324        }
325        // Load previous execution memory
326        for ((si, so), val) in pie_memory.0.iter() {
327            self.memory.insert((*si as isize, *so).into(), val)?;
328        }
329        Ok(())
330    }
331}
332
333impl Default for MemorySegmentManager {
334    fn default() -> Self {
335        Self::new()
336    }
337}
338
339impl fmt::Display for MemorySegmentManager {
340    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
341        writeln!(f, "Memory:\n{}", self.memory)?;
342        if let Some(used_sizes) = &self.segment_used_sizes {
343            writeln!(f, "Segment Info:")?;
344            for (index, used_size) in used_sizes.iter().enumerate() {
345                writeln!(
346                    f,
347                    "Segment Number: {}, Used Size: {}, Size {}",
348                    index,
349                    used_size,
350                    self.segment_sizes
351                        .get(&index)
352                        .map(|n| n.to_string())
353                        .unwrap_or(String::from("None"))
354                )?;
355            }
356        }
357        Ok(())
358    }
359}
360
361#[cfg(test)]
362mod tests {
363    use super::*;
364    use crate::Felt252;
365    use crate::{relocatable, utils::test_utils::*, vm::vm_memory::memory::MemoryCell};
366    use assert_matches::assert_matches;
367
368    #[cfg(target_arch = "wasm32")]
369    use wasm_bindgen_test::*;
370
371    #[test]
372    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
373    fn add_segment_no_size() {
374        let mut segments = MemorySegmentManager::new();
375        let base = segments.add();
376        assert_eq!(base, relocatable!(0, 0));
377        assert_eq!(segments.num_segments(), 1);
378    }
379
380    #[test]
381    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
382    fn add_segment_no_size_test_two_segments() {
383        let mut segments = MemorySegmentManager::new();
384        let mut _base = segments.add();
385        _base = segments.add();
386        assert_eq!(
387            _base,
388            Relocatable {
389                segment_index: 1,
390                offset: 0
391            }
392        );
393        assert_eq!(segments.num_segments(), 2);
394    }
395
396    #[test]
397    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
398    fn add_one_temporary_segment() {
399        let mut segments = MemorySegmentManager::new();
400        let base = segments.add_temporary_segment();
401        assert_eq!(base, relocatable!(-1, 0));
402        assert_eq!(segments.num_temp_segments(), 1);
403    }
404
405    #[test]
406    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
407    fn add_two_temporary_segments() {
408        let mut segments = MemorySegmentManager::new();
409        segments.add_temporary_segment();
410        let base = segments.add_temporary_segment();
411        assert_eq!(
412            base,
413            Relocatable {
414                segment_index: -2,
415                offset: 0
416            }
417        );
418        assert_eq!(segments.num_temp_segments(), 2);
419    }
420
421    #[test]
422    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
423    fn load_data_empty() {
424        let data = Vec::new();
425        let ptr = Relocatable::from((0, 3));
426        let mut segments = MemorySegmentManager::new();
427        let current_ptr = segments.load_data(ptr, &data).unwrap();
428        assert_eq!(current_ptr, Relocatable::from((0, 3)));
429    }
430
431    #[test]
432    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
433    fn load_data_one_element() {
434        let data = vec![MaybeRelocatable::from(Felt252::from(4))];
435        let ptr = Relocatable::from((0, 0));
436        let mut segments = MemorySegmentManager::new();
437        segments.add();
438        let current_ptr = segments.load_data(ptr, &data).unwrap();
439        assert_eq!(current_ptr, Relocatable::from((0, 1)));
440        assert_eq!(
441            segments.memory.get(&ptr).unwrap().as_ref(),
442            &MaybeRelocatable::from(Felt252::from(4))
443        );
444    }
445
446    #[test]
447    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
448    fn load_data_three_elements() {
449        let data = vec![
450            MaybeRelocatable::from(Felt252::from(4)),
451            MaybeRelocatable::from(Felt252::from(5)),
452            MaybeRelocatable::from(Felt252::from(6)),
453        ];
454        let ptr = Relocatable::from((0, 0));
455        let mut segments = MemorySegmentManager::new();
456        segments.add();
457        let current_ptr = segments.load_data(ptr, &data).unwrap();
458        assert_eq!(current_ptr, Relocatable::from((0, 3)));
459
460        assert_eq!(
461            segments.memory.get(&ptr).unwrap().as_ref(),
462            &MaybeRelocatable::from(Felt252::from(4))
463        );
464        assert_eq!(
465            segments
466                .memory
467                .get(&MaybeRelocatable::from((0, 1)))
468                .unwrap()
469                .as_ref(),
470            &MaybeRelocatable::from(Felt252::from(5))
471        );
472        assert_eq!(
473            segments
474                .memory
475                .get(&MaybeRelocatable::from((0, 2)))
476                .unwrap()
477                .as_ref(),
478            &MaybeRelocatable::from(Felt252::from(6))
479        );
480    }
481    #[test]
482    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
483    fn compute_effective_sizes_for_one_segment_memory() {
484        let mut segments = segments![((0, 0), 1), ((0, 1), 1), ((0, 2), 1)];
485        segments.compute_effective_sizes();
486        assert_eq!(Some(vec![3]), segments.segment_used_sizes);
487    }
488
489    #[test]
490    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
491    fn compute_effective_sizes_for_one_segment_memory_with_gap() {
492        let mut segments = MemorySegmentManager::new();
493        segments.add();
494        segments
495            .memory
496            .insert(
497                Relocatable::from((0, 6)),
498                &MaybeRelocatable::from(Felt252::from(1)),
499            )
500            .unwrap();
501        segments.compute_effective_sizes();
502        assert_eq!(Some(vec![7]), segments.segment_used_sizes);
503    }
504
505    #[test]
506    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
507    fn compute_effective_sizes_for_one_segment_memory_with_gaps() {
508        let mut segments = segments![((0, 3), 1), ((0, 4), 1), ((0, 7), 1), ((0, 9), 1)];
509        segments.compute_effective_sizes();
510        assert_eq!(Some(vec![10]), segments.segment_used_sizes);
511    }
512
513    #[test]
514    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
515    fn compute_effective_sizes_for_three_segment_memory() {
516        let mut segments = segments![
517            ((0, 0), 1),
518            ((0, 1), 1),
519            ((0, 2), 1),
520            ((1, 0), 1),
521            ((1, 1), 1),
522            ((1, 2), 1),
523            ((2, 0), 1),
524            ((2, 1), 1),
525            ((2, 2), 1)
526        ];
527        segments.compute_effective_sizes();
528        assert_eq!(Some(vec![3, 3, 3]), segments.segment_used_sizes);
529    }
530
531    #[test]
532    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
533    fn compute_effective_sizes_for_three_segment_memory_with_gaps() {
534        let mut segments = segments![
535            ((0, 2), 1),
536            ((0, 5), 1),
537            ((0, 7), 1),
538            ((1, 1), 1),
539            ((2, 2), 1),
540            ((2, 4), 1),
541            ((2, 7), 1)
542        ];
543        segments.compute_effective_sizes();
544        assert_eq!(Some(vec![8, 2, 8]), segments.segment_used_sizes);
545    }
546
547    #[test]
548    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
549    fn get_segment_used_size_after_computing_used() {
550        let mut segments = segments![
551            ((0, 2), 1),
552            ((0, 5), 1),
553            ((0, 7), 1),
554            ((1, 1), 1),
555            ((2, 2), 1),
556            ((2, 4), 1),
557            ((2, 7), 1)
558        ];
559        segments.compute_effective_sizes();
560        assert_eq!(Some(8), segments.get_segment_used_size(2));
561    }
562
563    #[test]
564    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
565    fn get_segment_used_size_before_computing_used() {
566        let segments = MemorySegmentManager::new();
567        assert_eq!(None, segments.get_segment_used_size(2));
568    }
569
570    #[test]
571    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
572    fn relocate_segments_one_segment() {
573        let mut segments = MemorySegmentManager::new();
574        segments.segment_used_sizes = Some(vec![3]);
575        assert_eq!(
576            segments
577                .relocate_segments()
578                .expect("Couldn't relocate after compute effective sizes"),
579            vec![1]
580        )
581    }
582
583    #[test]
584    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
585    fn relocate_segments_five_segment() {
586        let mut segments = MemorySegmentManager::new();
587        segments.segment_used_sizes = Some(vec![3, 3, 56, 78, 8]);
588        assert_eq!(
589            segments
590                .relocate_segments()
591                .expect("Couldn't relocate after compute effective sizes"),
592            vec![1, 4, 7, 63, 141]
593        )
594    }
595
596    #[test]
597    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
598    fn write_arg_relocatable() {
599        let data = vec![
600            Relocatable::from((0, 1)),
601            Relocatable::from((0, 2)),
602            Relocatable::from((0, 3)),
603        ];
604        let ptr = Relocatable::from((1, 0));
605        let mut segments = MemorySegmentManager::new();
606        for _ in 0..2 {
607            segments.add();
608        }
609
610        let exec = segments.write_arg(ptr, &data);
611
612        assert_eq!(exec, Ok(MaybeRelocatable::from((1, 3))));
613        assert_eq!(
614            segments.memory.data[1],
615            vec![
616                MemoryCell::new(MaybeRelocatable::from((0, 1))),
617                MemoryCell::new(MaybeRelocatable::from((0, 2))),
618                MemoryCell::new(MaybeRelocatable::from((0, 3))),
619            ]
620        );
621    }
622
623    #[test]
624    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
625    fn write_arg_invalid_type() {
626        let data = vec!["Invalid Type 1", "Invalid Type 2"];
627        let ptr = Relocatable::from((1, 0));
628        let mut segments = MemorySegmentManager::new();
629        for _ in 0..2 {
630            segments.add();
631        }
632
633        let exec = segments.write_arg(ptr, &data);
634
635        assert_eq!(exec, Err(MemoryError::WriteArg));
636        assert!(segments.memory.data[1].is_empty())
637    }
638
639    #[test]
640    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
641    fn segment_default() {
642        let segment_mng_new = MemorySegmentManager::new();
643        let segment_mng_def: MemorySegmentManager = Default::default();
644        assert_eq!(
645            segment_mng_new.num_segments(),
646            segment_mng_def.num_segments()
647        );
648        assert_eq!(
649            segment_mng_new.segment_used_sizes,
650            segment_mng_def.segment_used_sizes
651        );
652    }
653
654    #[test]
655    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
656    fn is_valid_memory_value_missing_effective_sizes() {
657        let segment_manager = MemorySegmentManager::new();
658
659        assert_eq!(
660            segment_manager.is_valid_memory_value(&mayberelocatable!(0)),
661            Err(MemoryError::MissingSegmentUsedSizes),
662        );
663    }
664
665    #[test]
666    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
667    fn is_valid_memory_value_temporary_segment() {
668        let mut segment_manager = MemorySegmentManager::new();
669
670        segment_manager.segment_used_sizes = Some(vec![10]);
671        assert_eq!(
672            segment_manager.is_valid_memory_value(&mayberelocatable!(-1, 0)),
673            Err(MemoryError::AddressInTemporarySegment(-1)),
674        );
675    }
676
677    #[test]
678    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
679    fn is_valid_memory_value_invalid_segment() {
680        let mut segment_manager = MemorySegmentManager::new();
681
682        segment_manager.segment_used_sizes = Some(vec![10]);
683        assert_eq!(
684            segment_manager.is_valid_memory_value(&mayberelocatable!(1, 0)),
685            Ok(false),
686        );
687    }
688
689    #[test]
690    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
691    fn is_valid_memory_value() {
692        let mut segment_manager = MemorySegmentManager::new();
693
694        segment_manager.segment_used_sizes = Some(vec![10]);
695        assert_eq!(
696            segment_manager.is_valid_memory_value(&mayberelocatable!(0, 5)),
697            Ok(true),
698        );
699    }
700
701    #[test]
702    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
703    fn is_valid_memory_value_felt() {
704        let mut segment_manager = MemorySegmentManager::new();
705
706        segment_manager.segment_used_sizes = Some(vec![10]);
707        assert_eq!(
708            segment_manager.is_valid_memory_value(&mayberelocatable!(1)),
709            Ok(true),
710        );
711    }
712
713    #[test]
714    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
715    fn get_memory_holes_missing_segment_used_sizes() {
716        let mut memory_segment_manager = MemorySegmentManager::new();
717        memory_segment_manager.memory = memory![((0, 0), 0)];
718        memory_segment_manager
719            .memory
720            .mark_as_accessed((0, 0).into());
721        assert_eq!(
722            memory_segment_manager.get_memory_holes(HashSet::new()),
723            Err(MemoryError::MissingSegmentUsedSizes),
724        );
725    }
726
727    #[test]
728    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
729    fn get_memory_holes_out_of_address_offset_bigger_than_size() {
730        let mut memory_segment_manager = MemorySegmentManager::new();
731        memory_segment_manager.segment_used_sizes = Some(vec![2]);
732        memory_segment_manager.memory = memory![((0, 0), 1), ((0, 1), 1), ((0, 2), 2)];
733        for i in 0..3 {
734            memory_segment_manager
735                .memory
736                .mark_as_accessed((0, i).into());
737        }
738        assert_eq!(
739            memory_segment_manager.get_memory_holes(HashSet::new()),
740            Err(MemoryError::SegmentHasMoreAccessedAddressesThanSize(
741                Box::new((0, 3, 2))
742            )),
743        );
744    }
745
746    #[test]
747    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
748    fn get_memory_holes_empty() {
749        let mut memory_segment_manager = MemorySegmentManager::new();
750        memory_segment_manager.segment_used_sizes = Some(Vec::new());
751        assert_eq!(
752            memory_segment_manager.get_memory_holes(HashSet::new()),
753            Ok(0),
754        );
755    }
756
757    #[test]
758    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
759    fn get_memory_holes_empty2() {
760        let mut memory_segment_manager = MemorySegmentManager::new();
761        memory_segment_manager.segment_used_sizes = Some(vec![4]);
762        assert_eq!(
763            memory_segment_manager.get_memory_holes(HashSet::new()),
764            Ok(0),
765        );
766    }
767
768    #[test]
769    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
770    fn get_memory_holes() {
771        let mut memory_segment_manager = MemorySegmentManager::new();
772        memory_segment_manager.segment_used_sizes = Some(vec![10]);
773        memory_segment_manager.memory = memory![
774            ((0, 0), 0),
775            ((0, 1), 0),
776            ((0, 2), 0),
777            ((0, 3), 0),
778            ((0, 6), 0),
779            ((0, 7), 0),
780            ((0, 8), 0),
781            ((0, 9), 0)
782        ];
783        for i in [0, 1, 2, 3, 6, 7, 8, 9] {
784            memory_segment_manager
785                .memory
786                .mark_as_accessed((0, i).into());
787        }
788        assert_eq!(
789            memory_segment_manager.get_memory_holes(HashSet::new()),
790            Ok(2),
791        );
792    }
793
794    #[test]
795    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
796    fn get_memory_holes2() {
797        let mut memory_segment_manager = MemorySegmentManager::new();
798
799        memory_segment_manager.segment_sizes = HashMap::from([(0, 15)]);
800        memory_segment_manager.memory = memory![
801            ((0, 0), 0),
802            ((0, 1), 0),
803            ((0, 2), 0),
804            ((0, 3), 0),
805            ((0, 6), 0),
806            ((0, 7), 0),
807            ((0, 8), 0),
808            ((0, 9), 0)
809        ];
810        memory_segment_manager.segment_used_sizes = Some(vec![10]);
811        for i in [0, 1, 2, 3, 6, 7, 8, 9] {
812            memory_segment_manager
813                .memory
814                .mark_as_accessed((0, i).into());
815        }
816        assert_eq!(
817            memory_segment_manager.get_memory_holes(HashSet::new()),
818            Ok(7),
819        );
820    }
821
822    #[test]
823    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
824    fn get_memory_size_missing_segment() {
825        let memory_segment_manager = MemorySegmentManager::new();
826
827        assert_eq!(memory_segment_manager.get_segment_size(0), None);
828    }
829
830    #[test]
831    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
832    fn get_memory_size_used() {
833        let mut memory_segment_manager = MemorySegmentManager::new();
834        memory_segment_manager.segment_used_sizes = Some(vec![5]);
835
836        assert_eq!(memory_segment_manager.get_segment_size(0), Some(5));
837    }
838
839    #[test]
840    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
841    fn get_memory_size() {
842        let mut memory_segment_manager = MemorySegmentManager::new();
843        memory_segment_manager.segment_sizes = HashMap::from([(0, 5)]);
844
845        assert_eq!(memory_segment_manager.get_segment_size(0), Some(5));
846    }
847
848    #[test]
849    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
850    fn get_memory_size2() {
851        let mut memory_segment_manager = MemorySegmentManager::new();
852        memory_segment_manager.segment_sizes = HashMap::from([(0, 5)]);
853        memory_segment_manager.segment_used_sizes = Some(vec![3]);
854
855        assert_eq!(memory_segment_manager.get_segment_size(0), Some(5));
856    }
857
858    /// Test that the call to .gen_arg() with a relocatable just passes the
859    /// value through.
860    #[test]
861    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
862    fn gen_arg_relocatable() {
863        let mut memory_segment_manager = MemorySegmentManager::new();
864
865        assert_matches!(
866            memory_segment_manager.gen_arg(&mayberelocatable!(0, 0)),
867            Ok(x) if x == mayberelocatable!(0, 0)
868        );
869    }
870
871    /// Test that the call to .gen_arg() with a bigint and no prime number just
872    /// passes the value through.
873    #[test]
874    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
875    fn gen_arg_bigint() {
876        let mut memory_segment_manager = MemorySegmentManager::new();
877
878        assert_matches!(
879            memory_segment_manager.gen_arg(&mayberelocatable!(1234)),
880            Ok(x) if x == mayberelocatable!(1234)
881        );
882    }
883
884    /// Test that the call to .gen_arg() with a Vec<MaybeRelocatable> writes its
885    /// contents into a new segment and returns a pointer to it.
886    #[test]
887    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
888    fn gen_arg_vec() {
889        let mut memory_segment_manager = MemorySegmentManager::new();
890
891        assert_matches!(
892            memory_segment_manager.gen_arg(
893                &vec![
894                    mayberelocatable!(0),
895                    mayberelocatable!(1),
896                    mayberelocatable!(2),
897                    mayberelocatable!(3),
898                    mayberelocatable!(0, 0),
899                    mayberelocatable!(0, 1),
900                    mayberelocatable!(0, 2),
901                    mayberelocatable!(0, 3),
902                ],
903            ),
904            Ok(x) if x == mayberelocatable!(0, 0)
905        );
906    }
907
908    /// Test that the call to .gen_arg() with a Vec<MaybeRelocatable::Relocatable>
909    /// writes its contents into a new segment and returns a pointer to it.
910    #[test]
911    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
912    fn gen_arg_vec_mayberelocatables() {
913        let mut memory_segment_manager = MemorySegmentManager::new();
914
915        assert_matches!(
916            memory_segment_manager.gen_arg(
917                &vec![
918                    MaybeRelocatable::from((0, 0)),
919                    MaybeRelocatable::from((0, 1)),
920                    MaybeRelocatable::from((0, 2)),
921                    MaybeRelocatable::from((0, 3)),
922                ],
923            ),
924            Ok(x) if x == mayberelocatable!(0, 0)
925        );
926        assert_eq!(
927            memory_segment_manager.memory.data[0],
928            vec![
929                MemoryCell::new(MaybeRelocatable::from((0, 0))),
930                MemoryCell::new(MaybeRelocatable::from((0, 1))),
931                MemoryCell::new(MaybeRelocatable::from((0, 2))),
932                MemoryCell::new(MaybeRelocatable::from((0, 3))),
933            ],
934        );
935    }
936
937    /// Test that call to .gen_arg() with a Vec<Relocatable> writes its
938    /// contents into a new segment and returns a pointer to it.
939    #[test]
940    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
941    fn gen_arg_vec_relocatables() {
942        let mut memory_segment_manager = MemorySegmentManager::new();
943
944        assert_matches!(
945            memory_segment_manager.gen_arg(
946                &vec![
947                    Relocatable::from((0, 0)),
948                    Relocatable::from((0, 1)),
949                    Relocatable::from((0, 2)),
950                    Relocatable::from((0, 3)),
951                ],
952            ),
953            Ok(x) if x == mayberelocatable!(0, 0)
954        );
955        assert_eq!(
956            memory_segment_manager.memory.data[0],
957            vec![
958                MemoryCell::new(MaybeRelocatable::from((0, 0))),
959                MemoryCell::new(MaybeRelocatable::from((0, 1))),
960                MemoryCell::new(MaybeRelocatable::from((0, 2))),
961                MemoryCell::new(MaybeRelocatable::from((0, 3))),
962            ],
963        );
964    }
965
966    /// Test that the call to .gen_arg() with any other argument returns a not
967    /// implemented error.
968    #[test]
969    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
970    fn gen_arg_invalid_type() {
971        let mut memory_segment_manager = MemorySegmentManager::new();
972
973        assert_matches!(
974            memory_segment_manager.gen_arg(&""),
975            Err(MemoryError::GenArgInvalidType)
976        );
977    }
978
979    #[test]
980    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
981    fn finalize_no_size_nor_memory() {
982        let mut segments = MemorySegmentManager::new();
983        segments.finalize(None, 0, None);
984        assert!(segments.memory.data.is_empty());
985        assert!(segments.memory.temp_data.is_empty());
986        assert_eq!(segments.public_memory_offsets, HashMap::from([(0, vec![])]));
987        assert_eq!(segments.num_segments(), 0);
988        assert_eq!(segments.num_temp_segments(), 0);
989    }
990
991    #[test]
992    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
993    fn finalize_no_memory() {
994        let mut segments = MemorySegmentManager::new();
995        segments.finalize(Some(42), 0, None);
996        assert_eq!(segments.public_memory_offsets, HashMap::from([(0, vec![])]));
997        assert_eq!(segments.segment_sizes, HashMap::from([(0, 42)]));
998    }
999
1000    #[test]
1001    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1002    fn finalize_no_size() {
1003        let mut segments = MemorySegmentManager::new();
1004        segments.finalize(None, 0, Some(&vec![(1_usize, 2_usize)]));
1005        assert_eq!(
1006            segments.public_memory_offsets,
1007            HashMap::from([(0_usize, vec![(1_usize, 2_usize)])])
1008        );
1009        assert!(segments.segment_sizes.is_empty());
1010    }
1011
1012    #[test]
1013    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1014    fn finalize_all_args() {
1015        let mut segments = MemorySegmentManager::new();
1016        segments.finalize(Some(42), 0, Some(&vec![(1_usize, 2_usize)]));
1017        assert_eq!(
1018            segments.public_memory_offsets,
1019            HashMap::from([(0_usize, vec![(1_usize, 2_usize)])])
1020        );
1021        assert_eq!(segments.segment_sizes, HashMap::from([(0, 42)]));
1022    }
1023
1024    #[test]
1025    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1026    fn gen_cairo_arg_single() {
1027        let mut memory_segment_manager = MemorySegmentManager::new();
1028
1029        assert_matches!(
1030            memory_segment_manager.gen_cairo_arg(&mayberelocatable!(1234).into()),
1031            Ok(x) if x == mayberelocatable!(1234)
1032        );
1033    }
1034
1035    #[test]
1036    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1037    fn gen_cairo_arg_array() {
1038        let mut memory_segment_manager = MemorySegmentManager::new();
1039
1040        assert_matches!(
1041            memory_segment_manager.gen_cairo_arg(
1042                &vec![
1043                    mayberelocatable!(0),
1044                    mayberelocatable!(1),
1045                    mayberelocatable!(2),
1046                    mayberelocatable!(3),
1047                    mayberelocatable!(0, 0),
1048                    mayberelocatable!(0, 1),
1049                    mayberelocatable!(0, 2),
1050                    mayberelocatable!(0, 3),
1051                ]
1052                .into(),
1053            ),
1054            Ok(x) if x == mayberelocatable!(0, 0)
1055        );
1056    }
1057
1058    #[test]
1059    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1060    fn gen_cairo_arg_composed() {
1061        let mut memory_segment_manager = MemorySegmentManager::new();
1062        let cairo_args = CairoArg::Composed(vec![
1063            CairoArg::Array(vec![
1064                mayberelocatable!(0),
1065                mayberelocatable!(1),
1066                mayberelocatable!(2),
1067            ]),
1068            CairoArg::Single(mayberelocatable!(1234)),
1069            CairoArg::Single(mayberelocatable!(5678)),
1070            CairoArg::Array(vec![
1071                mayberelocatable!(3),
1072                mayberelocatable!(4),
1073                mayberelocatable!(5),
1074            ]),
1075        ]);
1076
1077        assert_matches!(
1078            memory_segment_manager.gen_cairo_arg(&cairo_args),
1079            Ok(x) if x == mayberelocatable!(2, 0)
1080        );
1081    }
1082
1083    #[test]
1084    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1085    fn test_add_zero_segment() {
1086        // Create MemorySegmentManager with program and execution segments
1087        let mut memory_segment_manager = MemorySegmentManager::new();
1088        memory_segment_manager.add();
1089        memory_segment_manager.add();
1090
1091        // Add zero segment
1092        memory_segment_manager.add_zero_segment(3);
1093        assert_eq!(memory_segment_manager.zero_segment_index, 2);
1094        assert_eq!(memory_segment_manager.zero_segment_size, 3);
1095        assert_eq!(
1096            &memory_segment_manager.memory.data[2],
1097            &Vec::from([
1098                MemoryCell::new(MaybeRelocatable::from(0)),
1099                MemoryCell::new(MaybeRelocatable::from(0)),
1100                MemoryCell::new(MaybeRelocatable::from(0))
1101            ])
1102        );
1103
1104        // Resize zero segment
1105        memory_segment_manager.add_zero_segment(5);
1106        assert_eq!(memory_segment_manager.zero_segment_index, 2);
1107        assert_eq!(memory_segment_manager.zero_segment_size, 5);
1108
1109        assert_eq!(
1110            &memory_segment_manager.memory.data[2],
1111            &Vec::from([
1112                MemoryCell::new(MaybeRelocatable::from(0)),
1113                MemoryCell::new(MaybeRelocatable::from(0)),
1114                MemoryCell::new(MaybeRelocatable::from(0)),
1115                MemoryCell::new(MaybeRelocatable::from(0)),
1116                MemoryCell::new(MaybeRelocatable::from(0))
1117            ])
1118        );
1119    }
1120}