cairo_vm/vm/vm_memory/
memory.rs

1use crate::stdlib::{borrow::Cow, collections::HashMap, fmt, prelude::*};
2
3use crate::types::errors::math_errors::MathError;
4use crate::vm::runners::cairo_pie::CairoPieMemory;
5use crate::Felt252;
6use crate::{
7    types::relocatable::{MaybeRelocatable, Relocatable},
8    utils::from_relocatable_to_indexes,
9    vm::errors::memory_errors::MemoryError,
10};
11use bitvec::prelude as bv;
12use core::cmp::Ordering;
13use num_traits::ToPrimitive;
14
15pub struct ValidationRule(
16    #[allow(clippy::type_complexity)]
17    pub  Box<dyn Fn(&Memory, Relocatable) -> Result<Vec<Relocatable>, MemoryError>>,
18);
19
20/// [`MemoryCell`] represents an optimized storage layout for the VM memory.
21/// It's specified to have both size an alignment of 32 bytes to optimize cache access.
22/// Typical cache sizes are 64 bytes, a few cases might be 128 bytes, meaning 32 bytes aligned to
23/// 32 bytes boundaries will never get split into two separate lines, avoiding double stalls and
24/// reducing false sharing and evictions.
25/// The trade off is extra computation for conversion to our "in-flight" `MaybeRelocatable` and
26/// `Felt252` as well as some extra copies. Empirically, this seems to be offset by the improved
27/// locality of the bigger structure for Lambdaworks. There is a big hit from the conversions when
28/// using the `BigUint` implementation, since those force allocations on the heap, but since that's
29/// dropped in later versions anyway it's not a priority. For Lambdaworks the new copies are mostly
30/// to the stack, which is typically already in the cache.
31/// The layout uses the 4 MSB in the first `u64` as flags:
32/// - BIT63: NONE flag, 1 when the cell is actually empty.
33/// - BIT62: ACCESS flag, 1 when the cell has been accessed in a way observable to Cairo.
34/// - BIT61: RELOCATABLE flag, 1 when the contained value is a `Relocatable`, 0 when it is a
35///   `Felt252`.
36///
37/// `Felt252` values are stored in big-endian order to keep the flag bits free.
38/// `Relocatable` values are stored as native endian, with the 3rd word storing the segment index
39/// and the 4th word storing the offset.
40#[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd, Debug)]
41#[repr(align(32))]
42pub(crate) struct MemoryCell([u64; 4]);
43
44impl MemoryCell {
45    pub const NONE_MASK: u64 = 1 << 63;
46    pub const ACCESS_MASK: u64 = 1 << 62;
47    pub const RELOCATABLE_MASK: u64 = 1 << 61;
48    pub const NONE: Self = Self([Self::NONE_MASK, 0, 0, 0]);
49
50    pub fn new(value: MaybeRelocatable) -> Self {
51        value.into()
52    }
53
54    pub fn is_none(&self) -> bool {
55        self.0[0] & Self::NONE_MASK == Self::NONE_MASK
56    }
57
58    pub fn is_some(&self) -> bool {
59        !self.is_none()
60    }
61
62    pub fn mark_accessed(&mut self) {
63        self.0[0] |= Self::ACCESS_MASK;
64    }
65
66    pub fn is_accessed(&self) -> bool {
67        self.0[0] & Self::ACCESS_MASK == Self::ACCESS_MASK
68    }
69
70    pub fn get_value(&self) -> Option<MaybeRelocatable> {
71        self.is_some().then(|| (*self).into())
72    }
73}
74
75impl From<MaybeRelocatable> for MemoryCell {
76    fn from(value: MaybeRelocatable) -> Self {
77        match value {
78            MaybeRelocatable::Int(x) => Self(x.to_raw()),
79            MaybeRelocatable::RelocatableValue(x) => Self([
80                Self::RELOCATABLE_MASK,
81                0,
82                // NOTE: hack around signedness
83                usize::from_ne_bytes(x.segment_index.to_ne_bytes()) as u64,
84                x.offset as u64,
85            ]),
86        }
87    }
88}
89
90impl From<MemoryCell> for MaybeRelocatable {
91    fn from(cell: MemoryCell) -> Self {
92        debug_assert!(cell.is_some());
93        let flags = cell.0[0];
94        match flags & MemoryCell::RELOCATABLE_MASK {
95            MemoryCell::RELOCATABLE_MASK => Self::from((
96                // NOTE: hack around signedness
97                isize::from_ne_bytes((cell.0[2] as usize).to_ne_bytes()),
98                cell.0[3] as usize,
99            )),
100            _ => {
101                let mut value = cell.0;
102                // Remove all flag bits
103                value[0] &= 0x0fffffffffffffff;
104                Self::Int(Felt252::from_raw(value))
105            }
106        }
107    }
108}
109
110pub struct AddressSet(Vec<bv::BitVec>);
111
112impl AddressSet {
113    pub(crate) fn new() -> Self {
114        Self(Vec::new())
115    }
116
117    pub(crate) fn contains(&self, addr: &Relocatable) -> bool {
118        let segment = addr.segment_index;
119        if segment.is_negative() {
120            return false;
121        }
122
123        self.0
124            .get(segment as usize)
125            .and_then(|segment| segment.get(addr.offset))
126            .map(|bit| *bit)
127            .unwrap_or(false)
128    }
129
130    pub(crate) fn extend(&mut self, addresses: &[Relocatable]) {
131        for addr in addresses {
132            let segment = addr.segment_index;
133            if segment.is_negative() {
134                continue;
135            }
136            let segment = segment as usize;
137            if segment >= self.0.len() {
138                self.0.resize(segment + 1, bv::BitVec::new());
139            }
140
141            let offset = addr.offset;
142            if offset >= self.0[segment].len() {
143                self.0[segment].resize(offset + 1, false);
144            }
145            self.0[segment].replace(offset, true);
146        }
147    }
148}
149
150#[cfg(test)]
151impl AddressSet {
152    pub(crate) fn len(&self) -> usize {
153        self.0
154            .iter()
155            .map(|segment| segment.iter().map(|bit| *bit as usize).sum::<usize>())
156            .sum()
157    }
158}
159
160pub struct Memory {
161    pub(crate) data: Vec<Vec<MemoryCell>>,
162    /// Temporary segments are used when it's necessary to write data, but we
163    /// don't know yet where it will be located. These segments will eventually
164    /// be relocated to the main memory according to the `relocation_rules`. For
165    /// example, dictionaries are required to be contiguous, so each is stored in a
166    /// temporary segment and eventually relocated to a single segment.
167    pub(crate) temp_data: Vec<Vec<MemoryCell>>,
168    // relocation_rules's keys map to temp_data's indices and therefore begin at
169    // zero; that is, segment_index = -1 maps to key 0, -2 to key 1...
170    #[cfg(not(feature = "extensive_hints"))]
171    pub(crate) relocation_rules: HashMap<usize, Relocatable>,
172    #[cfg(feature = "extensive_hints")]
173    pub(crate) relocation_rules: HashMap<usize, MaybeRelocatable>,
174    pub validated_addresses: AddressSet,
175    validation_rules: Vec<Option<ValidationRule>>,
176}
177
178impl Memory {
179    pub fn new() -> Memory {
180        Memory {
181            data: Vec::new(),
182            temp_data: Vec::new(),
183            relocation_rules: HashMap::new(),
184            validated_addresses: AddressSet::new(),
185            validation_rules: Vec::with_capacity(7),
186        }
187    }
188
189    fn get_segment(&mut self, key: Relocatable) -> Result<&mut Vec<MemoryCell>, MemoryError> {
190        let (value_index, _) = from_relocatable_to_indexes(key);
191        let data = if key.segment_index.is_negative() {
192            &mut self.temp_data
193        } else {
194            &mut self.data
195        };
196        let data_len = data.len();
197        data.get_mut(value_index)
198            .ok_or_else(|| MemoryError::UnallocatedSegment(Box::new((value_index, data_len))))
199    }
200
201    /// Inserts a value into a memory address
202    /// Will return an Error if the segment index given by the address corresponds to a non-allocated segment,
203    /// or if the inserted value is inconsistent with the current value at the memory cell
204    /// If the address isnt contiguous with previously inserted data, memory gaps will be represented by None values
205    pub fn insert<V>(&mut self, key: Relocatable, val: V) -> Result<(), MemoryError>
206    where
207        MaybeRelocatable: From<V>,
208    {
209        let val = MaybeRelocatable::from(val);
210        let segment = self.get_segment(key)?;
211        let (_, value_offset) = from_relocatable_to_indexes(key);
212
213        //Check if the element is inserted next to the last one on the segment
214        //Forgoing this check would allow data to be inserted in a different index
215        let (len, capacity) = (segment.len(), segment.capacity());
216        if len <= value_offset {
217            let new_len = value_offset
218                .checked_add(1)
219                .ok_or(MemoryError::VecCapacityExceeded)?;
220            segment
221                .try_reserve(new_len.saturating_sub(capacity))
222                .map_err(|_| MemoryError::VecCapacityExceeded)?;
223            segment.resize(new_len, MemoryCell::NONE);
224        }
225        // At this point there's *something* in there
226
227        match segment[value_offset].get_value() {
228            None => segment[value_offset] = MemoryCell::new(val),
229            Some(current_cell) => {
230                if current_cell != val {
231                    //Existing memory cannot be changed
232                    return Err(MemoryError::InconsistentMemory(Box::new((
233                        key,
234                        current_cell,
235                        val,
236                    ))));
237                }
238            }
239        };
240        self.validate_memory_cell(key)
241    }
242
243    pub(crate) fn delete_unaccessed(&mut self, addr: Relocatable) -> Result<(), MemoryError> {
244        let (_, offset) = from_relocatable_to_indexes(addr);
245        let segment = self.get_segment(addr)?;
246
247        // Make sure the offset exists.
248        if offset >= segment.len() {
249            return Err(MemoryError::UnsetUnallocatedCell(addr));
250        }
251
252        // Ensure the cell has not been accessed.
253        if segment[offset].is_accessed() {
254            return Err(MemoryError::UnsetAccessedCell(addr));
255        }
256
257        // Unset the cell.
258        segment[offset] = MemoryCell::NONE;
259        Ok(())
260    }
261
262    /// Retrieve a value from memory (either normal or temporary) and apply relocation rules
263    pub(crate) fn get<'a, 'b: 'a, K: 'a>(&'b self, key: &'a K) -> Option<Cow<'b, MaybeRelocatable>>
264    where
265        Relocatable: TryFrom<&'a K>,
266    {
267        let relocatable: Relocatable = key.try_into().ok()?;
268
269        let data = if relocatable.segment_index.is_negative() {
270            &self.temp_data
271        } else {
272            &self.data
273        };
274        let (i, j) = from_relocatable_to_indexes(relocatable);
275        let value = data.get(i)?.get(j)?.get_value()?;
276        Some(Cow::Owned(self.relocate_value(&value).ok()?.into_owned()))
277    }
278
279    // Version of Memory.relocate_value() that doesn't require a self reference
280    #[cfg(not(feature = "extensive_hints"))]
281    fn relocate_address(
282        addr: Relocatable,
283        relocation_rules: &HashMap<usize, Relocatable>,
284    ) -> Result<MaybeRelocatable, MemoryError> {
285        if addr.segment_index < 0 {
286            // Adjust the segment index to begin at zero, as per the struct field's
287            // comment.
288            if let Some(x) = relocation_rules.get(&(-(addr.segment_index + 1) as usize)) {
289                return Ok((*x + addr.offset)?.into());
290            }
291        }
292        Ok(addr.into())
293    }
294
295    #[cfg(feature = "extensive_hints")]
296    fn relocate_address(
297        addr: Relocatable,
298        relocation_rules: &HashMap<usize, MaybeRelocatable>,
299    ) -> Result<MaybeRelocatable, MemoryError> {
300        if addr.segment_index < 0 {
301            // Adjust the segment index to begin at zero, as per the struct field's
302            // comment.
303            if let Some(x) = relocation_rules.get(&(-(addr.segment_index + 1) as usize)) {
304                return Ok(match x {
305                    MaybeRelocatable::RelocatableValue(r) => (*r + addr.offset)?.into(),
306                    MaybeRelocatable::Int(i) => i.into(),
307                });
308            }
309        }
310        Ok(addr.into())
311    }
312
313    #[cfg(not(feature = "extensive_hints"))]
314    fn flatten_relocation_rules(&mut self) -> Result<(), MemoryError> {
315        let keys: Vec<usize> = self.relocation_rules.keys().copied().collect();
316        let max_hops = self.relocation_rules.len().saturating_add(1);
317        for key in keys {
318            let mut dst = *self
319                .relocation_rules
320                .get(&key)
321                .expect("key taken from keys vec must exist");
322
323            let mut hops = 0;
324            while dst.segment_index < 0 {
325                let next_key = (-(dst.segment_index + 1)) as usize;
326                let next = *self
327                    .relocation_rules
328                    .get(&next_key)
329                    .ok_or(MemoryError::UnmappedTemporarySegment(dst.segment_index))?;
330                dst = (next + dst.offset).map_err(MemoryError::Math)?;
331                hops += 1;
332                if hops > max_hops {
333                    return Err(MemoryError::Relocation); // cycle guard
334                }
335            }
336            self.relocation_rules.insert(key, dst);
337        }
338        Ok(())
339    }
340
341    #[cfg(feature = "extensive_hints")]
342    fn flatten_relocation_rules(&mut self) -> Result<(), MemoryError> {
343        let keys: Vec<usize> = self.relocation_rules.keys().copied().collect();
344        let max_hops = self.relocation_rules.len().saturating_add(1);
345        for key in keys {
346            let mut dst = self
347                .relocation_rules
348                .get(&key)
349                .expect("key taken from keys vec must exist")
350                .clone();
351
352            let mut hops = 0;
353            loop {
354                match dst {
355                    MaybeRelocatable::RelocatableValue(relocatable)
356                        if relocatable.segment_index < 0 =>
357                    {
358                        let next_key = (-(relocatable.segment_index + 1)) as usize;
359                        let next = self
360                            .relocation_rules
361                            .get(&next_key)
362                            .ok_or(MemoryError::UnmappedTemporarySegment(
363                                relocatable.segment_index,
364                            ))?
365                            .clone();
366
367                        match next {
368                            MaybeRelocatable::RelocatableValue(next_relocatable) => {
369                                dst = MaybeRelocatable::RelocatableValue(
370                                    (next_relocatable + relocatable.offset)
371                                        .map_err(MemoryError::Math)?,
372                                );
373                            }
374                            MaybeRelocatable::Int(i) => {
375                                if relocatable.offset != 0 {
376                                    return Err(MemoryError::NonZeroOffset(relocatable.offset));
377                                }
378                                dst = MaybeRelocatable::Int(i);
379                            }
380                        }
381                    }
382                    _ => break,
383                }
384                hops += 1;
385                if hops > max_hops {
386                    return Err(MemoryError::Relocation);
387                }
388            }
389            self.relocation_rules.insert(key, dst);
390        }
391        Ok(())
392    }
393
394    /// Relocates the memory according to the relocation rules and clears `self.relocaction_rules`.
395    pub fn relocate_memory(&mut self) -> Result<(), MemoryError> {
396        if self.relocation_rules.is_empty() || self.temp_data.is_empty() {
397            return Ok(());
398        }
399
400        // flatten chains (temp->temp->...->real).
401        self.flatten_relocation_rules()?;
402
403        // Relocate temporary addresses in memory
404        for segment in self.data.iter_mut().chain(self.temp_data.iter_mut()) {
405            for cell in segment.iter_mut() {
406                let value = cell.get_value();
407                match value {
408                    Some(MaybeRelocatable::RelocatableValue(addr)) if addr.segment_index < 0 => {
409                        let mut new_cell = MemoryCell::new(Memory::relocate_address(
410                            addr,
411                            &self.relocation_rules,
412                        )?);
413                        if cell.is_accessed() {
414                            new_cell.mark_accessed();
415                        }
416                        *cell = new_cell;
417                    }
418                    _ => {}
419                }
420            }
421        }
422        // Move relocated temporary memory into the real memory
423        for index in (0..self.temp_data.len()).rev() {
424            if let Some(base_addr) = self.relocation_rules.get(&index) {
425                let data_segment = self.temp_data.remove(index);
426
427                #[cfg(feature = "extensive_hints")]
428                let base_addr = match base_addr {
429                    MaybeRelocatable::RelocatableValue(addr) => addr,
430                    MaybeRelocatable::Int(_) => {
431                        continue;
432                    }
433                };
434
435                // Insert the to-be relocated segment into the real memory
436                let mut addr = *base_addr;
437                if let Some(s) = self.data.get_mut(addr.segment_index as usize) {
438                    s.reserve_exact(data_segment.len())
439                }
440                for cell in data_segment {
441                    if let Some(v) = cell.get_value() {
442                        // Rely on Memory::insert to catch memory inconsistencies
443                        self.insert(addr, v)?;
444                        // If the cell is accessed, mark the relocated one as accessed too
445                        if cell.is_accessed() {
446                            self.mark_as_accessed(addr)
447                        }
448                    }
449                    addr = (addr + 1)?;
450                }
451            }
452        }
453        self.relocation_rules.clear();
454        Ok(())
455    }
456
457    /// Add a new relocation rule.
458    ///
459    /// When using feature "extensive_hints" the destination is allowed to be an Integer (via
460    /// MaybeRelocatable). Relocating memory to anything other than a `Relocatable` is generally
461    /// not useful, but it does make the implementation consistent with the pythonic version.
462    ///
463    /// Will return an error if any of the following conditions are not met:
464    ///   - Source address's segment must be negative (temporary).
465    ///   - Source address's offset must be zero.
466    ///   - There shouldn't already be relocation at the source segment.
467    #[cfg(not(feature = "extensive_hints"))]
468    pub(crate) fn add_relocation_rule(
469        &mut self,
470        src_ptr: Relocatable,
471        dst_ptr: Relocatable,
472    ) -> Result<(), MemoryError> {
473        if src_ptr.segment_index >= 0 {
474            return Err(MemoryError::AddressNotInTemporarySegment(
475                src_ptr.segment_index,
476            ));
477        }
478        if src_ptr.offset != 0 {
479            return Err(MemoryError::NonZeroOffset(src_ptr.offset));
480        }
481
482        // Adjust the segment index to begin at zero, as per the struct field's
483        // comment.
484        let segment_index = -(src_ptr.segment_index + 1) as usize;
485        if self.relocation_rules.contains_key(&segment_index) {
486            return Err(MemoryError::DuplicatedRelocation(src_ptr.segment_index));
487        }
488
489        self.relocation_rules.insert(segment_index, dst_ptr);
490        Ok(())
491    }
492    #[cfg(feature = "extensive_hints")]
493    pub(crate) fn add_relocation_rule(
494        &mut self,
495        src_ptr: Relocatable,
496        dst: MaybeRelocatable,
497    ) -> Result<(), MemoryError> {
498        if src_ptr.segment_index >= 0 {
499            return Err(MemoryError::AddressNotInTemporarySegment(
500                src_ptr.segment_index,
501            ));
502        }
503        if src_ptr.offset != 0 {
504            return Err(MemoryError::NonZeroOffset(src_ptr.offset));
505        }
506
507        // Adjust the segment index to begin at zero, as per the struct field's
508        // comment.
509        let segment_index = -(src_ptr.segment_index + 1) as usize;
510        if self.relocation_rules.contains_key(&segment_index) {
511            return Err(MemoryError::DuplicatedRelocation(src_ptr.segment_index));
512        }
513
514        self.relocation_rules.insert(segment_index, dst);
515        Ok(())
516    }
517
518    /// Gets the value from memory address as a Felt252 value.
519    /// Returns an Error if the value at the memory address is missing or not a Felt252.
520    pub fn get_integer(&'_ self, key: Relocatable) -> Result<Cow<'_, Felt252>, MemoryError> {
521        match self
522            .get(&key)
523            .ok_or_else(|| MemoryError::UnknownMemoryCell(Box::new(key)))?
524        {
525            Cow::Borrowed(MaybeRelocatable::Int(int)) => Ok(Cow::Borrowed(int)),
526            Cow::Owned(MaybeRelocatable::Int(int)) => Ok(Cow::Owned(int)),
527            _ => Err(MemoryError::ExpectedInteger(Box::new(key))),
528        }
529    }
530
531    /// Gets a u32 value from memory address.
532    /// Returns an Error if the value at the memory address is missing or not a u32.
533    pub fn get_u32(&self, key: Relocatable) -> Result<u32, MemoryError> {
534        let felt = self.get_integer(key)?.into_owned();
535        felt.to_u32()
536            .ok_or_else(|| MemoryError::Math(MathError::Felt252ToU32Conversion(Box::new(felt))))
537    }
538
539    /// Gets the value from memory address as a usize.
540    /// Returns an Error if the value at the memory address is missing not a Felt252, or can't be converted to usize.
541    pub fn get_usize(&self, key: Relocatable) -> Result<usize, MemoryError> {
542        let felt = self.get_integer(key)?.into_owned();
543        felt.to_usize()
544            .ok_or_else(|| MemoryError::Math(MathError::Felt252ToUsizeConversion(Box::new(felt))))
545    }
546
547    /// Gets the value from memory address as a Relocatable value.
548    /// Returns an Error if the value at the memory address is missing or not a Relocatable.
549    pub fn get_relocatable(&self, key: Relocatable) -> Result<Relocatable, MemoryError> {
550        match self
551            .get(&key)
552            .ok_or_else(|| MemoryError::UnknownMemoryCell(Box::new(key)))?
553        {
554            Cow::Borrowed(MaybeRelocatable::RelocatableValue(rel)) => Ok(*rel),
555            Cow::Owned(MaybeRelocatable::RelocatableValue(rel)) => Ok(rel),
556            _ => Err(MemoryError::ExpectedRelocatable(Box::new(key))),
557        }
558    }
559
560    /// Gets the value from memory address as a MaybeRelocatable value.
561    /// Returns an Error if the value at the memory address is missing or not a MaybeRelocatable.
562    pub fn get_maybe_relocatable(&self, key: Relocatable) -> Result<MaybeRelocatable, MemoryError> {
563        match self
564            .get(&key)
565            .ok_or_else(|| MemoryError::UnknownMemoryCell(Box::new(key)))?
566        {
567            // Note: the `Borrowed` variant will never occur.
568            Cow::Borrowed(maybe_rel) => Ok(maybe_rel.clone()),
569            Cow::Owned(maybe_rel) => Ok(maybe_rel),
570        }
571    }
572
573    /// Inserts a value into memory
574    /// Returns an error if the memory cell asignment is invalid
575    pub fn insert_value<T: Into<MaybeRelocatable>>(
576        &mut self,
577        key: Relocatable,
578        val: T,
579    ) -> Result<(), MemoryError> {
580        self.insert(key, &val.into())
581    }
582
583    pub fn add_validation_rule(&mut self, segment_index: usize, rule: ValidationRule) {
584        if segment_index >= self.validation_rules.len() {
585            // Fill gaps
586            self.validation_rules
587                .resize_with(segment_index + 1, || None);
588        }
589        self.validation_rules.insert(segment_index, Some(rule));
590    }
591
592    fn validate_memory_cell(&mut self, addr: Relocatable) -> Result<(), MemoryError> {
593        if let Some(Some(rule)) = addr
594            .segment_index
595            .to_usize()
596            .and_then(|x| self.validation_rules.get(x))
597        {
598            if !self.validated_addresses.contains(&addr) {
599                self.validated_addresses
600                    .extend(rule.0(self, addr)?.as_slice());
601            }
602        }
603        Ok(())
604    }
605
606    ///Applies validation_rules to the current memory
607    pub fn validate_existing_memory(&mut self) -> Result<(), MemoryError> {
608        for (index, rule) in self.validation_rules.iter().enumerate() {
609            if index >= self.data.len() {
610                continue;
611            }
612            let Some(rule) = rule else {
613                continue;
614            };
615            for offset in 0..self.data[index].len() {
616                let addr = Relocatable::from((index as isize, offset));
617                if !self.validated_addresses.contains(&addr) {
618                    self.validated_addresses
619                        .extend(rule.0(self, addr)?.as_slice());
620                }
621            }
622        }
623        Ok(())
624    }
625
626    /// Compares two ranges of values in memory of length `len`
627    /// Returns the ordering and the first relative position at which they differ
628    /// Special cases:
629    /// - `lhs` exists in memory but `rhs` doesn't -> (Ordering::Greater, 0)
630    /// - `rhs` exists in memory but `lhs` doesn't -> (Ordering::Less, 0)
631    /// - None of `lhs` or `rhs` exist in memory -> (Ordering::Equal, 0)
632    ///
633    /// Everything else behaves much like `memcmp` in C.
634    /// This is meant as an optimization for hints to avoid allocations.
635    pub(crate) fn memcmp(
636        &self,
637        lhs: Relocatable,
638        rhs: Relocatable,
639        len: usize,
640    ) -> (Ordering, usize) {
641        let get_segment = |idx: isize| {
642            if idx.is_negative() {
643                self.temp_data.get(-(idx + 1) as usize)
644            } else {
645                self.data.get(idx as usize)
646            }
647        };
648        match (
649            get_segment(lhs.segment_index),
650            get_segment(rhs.segment_index),
651        ) {
652            (None, None) => {
653                return (Ordering::Equal, 0);
654            }
655            (Some(_), None) => {
656                return (Ordering::Greater, 0);
657            }
658            (None, Some(_)) => {
659                return (Ordering::Less, 0);
660            }
661            (Some(lhs_segment), Some(rhs_segment)) => {
662                let (lhs_start, rhs_start) = (lhs.offset, rhs.offset);
663                for i in 0..len {
664                    let (lhs, rhs) = (
665                        lhs_segment.get(lhs_start + i),
666                        rhs_segment.get(rhs_start + i),
667                    );
668                    let ord = lhs.cmp(&rhs);
669                    if ord == Ordering::Equal {
670                        continue;
671                    }
672                    return (ord, i);
673                }
674            }
675        };
676        (Ordering::Equal, len)
677    }
678
679    /// Compares two ranges of values in memory of length `len`
680    /// Returns the ordering and the first relative position at which they differ
681    /// Special cases:
682    /// - `lhs` exists in memory but `rhs` doesn't -> (Ordering::Greater, 0)
683    /// - `rhs` exists in memory but `lhs` doesn't -> (Ordering::Less, 0)
684    /// - None of `lhs` or `rhs` exist in memory -> (Ordering::Equal, 0)
685    ///   Everything else behaves much like `memcmp` in C.
686    ///   This is meant as an optimization for hints to avoid allocations.
687    pub(crate) fn mem_eq(&self, lhs: Relocatable, rhs: Relocatable, len: usize) -> bool {
688        if lhs == rhs {
689            return true;
690        }
691        let get_segment = |idx: isize| {
692            if idx.is_negative() {
693                self.temp_data.get(-(idx + 1) as usize)
694            } else {
695                self.data.get(idx as usize)
696            }
697        };
698        match (
699            get_segment(lhs.segment_index).and_then(|s| s.get(lhs.offset..)),
700            get_segment(rhs.segment_index).and_then(|s| s.get(rhs.offset..)),
701        ) {
702            (Some(lhs), Some(rhs)) => {
703                let (lhs_len, rhs_len) = (lhs.len().min(len), rhs.len().min(len));
704                if lhs_len != rhs_len {
705                    return false;
706                }
707                lhs[..lhs_len] == rhs[..rhs_len]
708            }
709            (None, None) => true,
710            _ => false,
711        }
712    }
713
714    /// Gets a range of memory values from addr to addr + size
715    /// The outputed range may contain gaps if the original memory has them
716    pub fn get_range(
717        &'_ self,
718        addr: Relocatable,
719        size: usize,
720    ) -> Vec<Option<Cow<'_, MaybeRelocatable>>> {
721        let mut values = Vec::new();
722
723        for i in 0..size {
724            values.push((addr + i).ok().and_then(|x| self.get(&x)));
725        }
726
727        values
728    }
729
730    /// Gets a range of memory values from addr to addr + size
731    /// Fails if there if any of the values inside the range is missing (memory gap)
732    pub fn get_continuous_range(
733        &self,
734        addr: Relocatable,
735        size: usize,
736    ) -> Result<Vec<MaybeRelocatable>, MemoryError> {
737        let mut values = Vec::with_capacity(size);
738
739        for i in 0..size {
740            values.push(match self.get(&(addr + i)?) {
741                Some(elem) => elem.into_owned(),
742                None => return Err(MemoryError::GetRangeMemoryGap(Box::new((addr, size)))),
743            });
744        }
745
746        Ok(values)
747    }
748
749    /// Gets a range of Felt252 memory values from addr to addr + size
750    /// Fails if there if any of the values inside the range is missing (memory gap),
751    /// or is not a Felt252
752    pub fn get_integer_range(
753        &'_ self,
754        addr: Relocatable,
755        size: usize,
756    ) -> Result<Vec<Cow<'_, Felt252>>, MemoryError> {
757        let mut values = Vec::new();
758
759        for i in 0..size {
760            values.push(self.get_integer((addr + i)?)?);
761        }
762
763        Ok(values)
764    }
765
766    /// Gets a range of u32 memory values from addr to addr + size
767    /// Fails if any of the values inside the range is missing (memory gap) or is not a u32
768    pub fn get_u32_range(&self, addr: Relocatable, size: usize) -> Result<Vec<u32>, MemoryError> {
769        let mut values = Vec::new();
770
771        for i in 0..size {
772            values.push(self.get_u32((addr + i)?)?);
773        }
774
775        Ok(values)
776    }
777
778    fn get_cell(&self, addr: Relocatable) -> Option<&MemoryCell> {
779        let (i, j) = from_relocatable_to_indexes(addr);
780        let data = if addr.segment_index < 0 {
781            &self.temp_data
782        } else {
783            &self.data
784        };
785        data.get(i)?.get(j)
786    }
787
788    #[cfg(test)]
789    pub(crate) fn get_cell_for_testing(&self, addr: Relocatable) -> Option<&MemoryCell> {
790        self.get_cell(addr)
791    }
792
793    pub fn is_accessed(&self, addr: &Relocatable) -> Result<bool, MemoryError> {
794        Ok(self
795            .get_cell(*addr)
796            .ok_or(MemoryError::UnknownMemoryCell(Box::new(*addr)))?
797            .is_accessed())
798    }
799
800    pub fn mark_as_accessed(&mut self, addr: Relocatable) {
801        let (i, j) = from_relocatable_to_indexes(addr);
802        let data = if addr.segment_index < 0 {
803            &mut self.temp_data
804        } else {
805            &mut self.data
806        };
807        let cell = data.get_mut(i).and_then(|x| x.get_mut(j));
808        if let Some(cell) = cell {
809            cell.mark_accessed()
810        }
811    }
812
813    pub fn get_amount_of_accessed_addresses_for_segment(
814        &self,
815        segment_index: usize,
816    ) -> Option<usize> {
817        let segment = self.data.get(segment_index)?;
818        Some(
819            segment
820                .iter()
821                .filter(|x| x.is_some() && x.is_accessed())
822                .count(),
823        )
824    }
825
826    // Inserts a value into memory & inmediately marks it as accessed if insertion was succesful
827    // Used by ModBuiltinRunner, as it accesses memory outside of it's segment when operating
828    pub(crate) fn insert_as_accessed<V>(
829        &mut self,
830        key: Relocatable,
831        val: V,
832    ) -> Result<(), MemoryError>
833    where
834        MaybeRelocatable: From<V>,
835    {
836        self.insert(key, val)?;
837        self.mark_as_accessed(key);
838        Ok(())
839    }
840}
841
842impl From<&Memory> for CairoPieMemory {
843    fn from(mem: &Memory) -> CairoPieMemory {
844        let mut pie_memory = Vec::default();
845        for (i, segment) in mem.data.iter().enumerate() {
846            for (j, cell) in segment.iter().enumerate() {
847                if let Some(value) = cell.get_value() {
848                    pie_memory.push(((i, j), value))
849                }
850            }
851        }
852        CairoPieMemory(pie_memory)
853    }
854}
855
856impl fmt::Display for Memory {
857    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
858        for (i, segment) in self.temp_data.iter().enumerate() {
859            for (j, cell) in segment.iter().enumerate() {
860                if let Some(elem) = cell.get_value() {
861                    let temp_segment = i + 1;
862                    writeln!(f, "(-{temp_segment},{j}) : {elem}")?;
863                }
864            }
865        }
866        for (i, segment) in self.data.iter().enumerate() {
867            for (j, cell) in segment.iter().enumerate() {
868                if let Some(elem) = cell.get_value() {
869                    writeln!(f, "({i},{j}) : {elem}")?;
870                }
871            }
872        }
873        Ok(())
874    }
875}
876
877/// Applies `relocation_rules` to a value
878pub(crate) trait RelocateValue<'a, Input: 'a, Output: 'a> {
879    fn relocate_value(&self, value: Input) -> Result<Output, MemoryError>;
880}
881
882#[cfg(not(feature = "extensive_hints"))]
883impl RelocateValue<'_, Relocatable, Relocatable> for Memory {
884    fn relocate_value(&self, addr: Relocatable) -> Result<Relocatable, MemoryError> {
885        if addr.segment_index < 0 {
886            // Adjust the segment index to begin at zero, as per the struct field's
887            // comment.
888            if let Some(x) = self
889                .relocation_rules
890                .get(&(-(addr.segment_index + 1) as usize))
891            {
892                return (*x + addr.offset).map_err(MemoryError::Math);
893            }
894        }
895        Ok(addr)
896    }
897}
898#[cfg(feature = "extensive_hints")]
899impl RelocateValue<'_, Relocatable, MaybeRelocatable> for Memory {
900    fn relocate_value(&self, addr: Relocatable) -> Result<MaybeRelocatable, MemoryError> {
901        if addr.segment_index < 0 {
902            // Adjust the segment index to begin at zero, as per the struct field's
903            // comment.
904            if let Some(x) = self
905                .relocation_rules
906                .get(&(-(addr.segment_index + 1) as usize))
907            {
908                return Ok(match x {
909                    MaybeRelocatable::RelocatableValue(r) => {
910                        (*r + addr.offset).map_err(MemoryError::Math)?.into()
911                    }
912                    MaybeRelocatable::Int(i) => i.into(),
913                });
914            }
915        }
916        Ok(addr.into())
917    }
918}
919
920impl<'a> RelocateValue<'a, &'a Felt252, &'a Felt252> for Memory {
921    fn relocate_value(&self, value: &'a Felt252) -> Result<&'a Felt252, MemoryError> {
922        Ok(value)
923    }
924}
925
926impl<'a> RelocateValue<'a, &'a MaybeRelocatable, Cow<'a, MaybeRelocatable>> for Memory {
927    fn relocate_value(
928        &self,
929        value: &'a MaybeRelocatable,
930    ) -> Result<Cow<'a, MaybeRelocatable>, MemoryError> {
931        Ok(match value {
932            MaybeRelocatable::Int(_) => Cow::Borrowed(value),
933            MaybeRelocatable::RelocatableValue(addr) => {
934                #[cfg(not(feature = "extensive_hints"))]
935                let v = self.relocate_value(*addr)?.into();
936                #[cfg(feature = "extensive_hints")]
937                let v = self.relocate_value(*addr)?;
938
939                Cow::Owned(v)
940            }
941        })
942    }
943}
944
945impl Default for Memory {
946    fn default() -> Self {
947        Self::new()
948    }
949}
950
951#[cfg(test)]
952mod memory_tests {
953
954    use super::*;
955    use crate::{
956        felt_hex, relocatable,
957        utils::test_utils::*,
958        vm::{
959            runners::builtin_runner::{
960                RangeCheckBuiltinRunner, SignatureBuiltinRunner, RC_N_PARTS_STANDARD,
961            },
962            vm_memory::memory_segments::MemorySegmentManager,
963        },
964    };
965    use assert_matches::assert_matches;
966
967    use crate::vm::errors::memory_errors::MemoryError;
968
969    use crate::utils::test_utils::memory_from_memory;
970    use crate::utils::test_utils::memory_inner;
971
972    #[cfg(target_arch = "wasm32")]
973    use wasm_bindgen_test::*;
974
975    #[test]
976    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
977    fn insert_and_get_succesful() {
978        let key = Relocatable::from((0, 0));
979        let val = MaybeRelocatable::from(Felt252::from(5_u64));
980        let mut memory = Memory::new();
981        memory.data.push(Vec::new());
982        memory.insert(key, &val).unwrap();
983        assert_eq!(
984            memory.get(&key).unwrap().as_ref(),
985            &MaybeRelocatable::from(Felt252::from(5_u64))
986        );
987    }
988
989    #[test]
990    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
991    fn get_valuef_from_temp_segment() {
992        let mut memory = Memory::new();
993        memory.temp_data = vec![vec![
994            MemoryCell::NONE,
995            MemoryCell::NONE,
996            MemoryCell::new(mayberelocatable!(8)),
997        ]];
998        assert_eq!(
999            memory.get(&mayberelocatable!(-1, 2)).unwrap().as_ref(),
1000            &mayberelocatable!(8),
1001        );
1002    }
1003
1004    #[test]
1005    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1006    fn insert_value_in_temp_segment() {
1007        let key = Relocatable::from((-1, 3));
1008        let val = MaybeRelocatable::from(Felt252::from(8_u64));
1009        let mut memory = Memory::new();
1010        memory.temp_data.push(Vec::new());
1011        memory.insert(key, &val).unwrap();
1012        assert_eq!(
1013            memory.temp_data[0][3],
1014            MemoryCell::new(MaybeRelocatable::from(Felt252::from(8_u64)))
1015        );
1016    }
1017
1018    #[test]
1019    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1020    fn insert_and_get_from_temp_segment_succesful() {
1021        let key = Relocatable::from((-1, 0));
1022        let val = MaybeRelocatable::from(Felt252::from(5_u64));
1023        let mut memory = Memory::new();
1024        memory.temp_data.push(Vec::new());
1025        memory.insert(key, &val).unwrap();
1026        assert_eq!(
1027            memory.get(&key).unwrap().as_ref(),
1028            &MaybeRelocatable::from(Felt252::from(5_u64)),
1029        );
1030    }
1031
1032    #[test]
1033    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1034    fn insert_and_get_from_temp_segment_failed() {
1035        let key = relocatable!(-1, 1);
1036        let mut memory = Memory::new();
1037        memory.temp_data = vec![vec![
1038            MemoryCell::NONE,
1039            MemoryCell::new(mayberelocatable!(8)),
1040        ]];
1041        assert_eq!(
1042            memory.insert(key, &mayberelocatable!(5)),
1043            Err(MemoryError::InconsistentMemory(Box::new((
1044                relocatable!(-1, 1),
1045                mayberelocatable!(8),
1046                mayberelocatable!(5)
1047            ))))
1048        );
1049    }
1050
1051    #[test]
1052    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1053    fn get_non_allocated_memory() {
1054        let key = Relocatable::from((0, 0));
1055        let memory = Memory::new();
1056        assert_eq!(memory.get(&key), None);
1057    }
1058
1059    #[test]
1060    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1061    fn get_non_existant_element() {
1062        let key = Relocatable::from((0, 0));
1063        let memory = Memory::new();
1064        assert_eq!(memory.get(&key), None);
1065    }
1066
1067    #[test]
1068    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1069    fn insert_non_allocated_memory() {
1070        let key = Relocatable::from((0, 0));
1071        let val = MaybeRelocatable::from(Felt252::from(5_u64));
1072        let mut memory = Memory::new();
1073        let error = memory.insert(key, &val);
1074        assert_eq!(
1075            error,
1076            Err(MemoryError::UnallocatedSegment(Box::new((0, 0))))
1077        );
1078    }
1079
1080    #[test]
1081    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1082    fn insert_inconsistent_memory() {
1083        let key = Relocatable::from((0, 0));
1084        let val_a = MaybeRelocatable::from(Felt252::from(5_u64));
1085        let val_b = MaybeRelocatable::from(Felt252::from(6_u64));
1086        let mut memory = Memory::new();
1087        memory.data.push(Vec::new());
1088        memory
1089            .insert(key, &val_a)
1090            .expect("Unexpected memory insert fail");
1091        let error = memory.insert(key, &val_b);
1092        assert_eq!(
1093            error,
1094            Err(MemoryError::InconsistentMemory(Box::new((
1095                key, val_a, val_b
1096            ))))
1097        );
1098    }
1099
1100    #[test]
1101    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1102    fn insert_non_contiguous_element() {
1103        let key_a = Relocatable::from((0, 0));
1104        let key_b = Relocatable::from((0, 2));
1105        let val = MaybeRelocatable::from(Felt252::from(5_u64));
1106        let mut memory = Memory::new();
1107        memory.data.push(Vec::new());
1108        memory.insert(key_a, &val).unwrap();
1109        memory.insert(key_b, &val).unwrap();
1110        assert_eq!(memory.get(&key_b).unwrap().as_ref(), &val);
1111    }
1112
1113    #[test]
1114    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1115    fn insert_non_contiguous_element_memory_gaps_none() {
1116        let key_a = Relocatable::from((0, 0));
1117        let key_b = Relocatable::from((0, 5));
1118        let val = MaybeRelocatable::from(Felt252::from(5_u64));
1119        let mut memory = Memory::new();
1120        memory.data.push(Vec::new());
1121        memory.insert(key_a, &val).unwrap();
1122        memory.insert(key_b, &val).unwrap();
1123        assert_eq!(memory.get(&key_b).unwrap().as_ref(), &val);
1124        assert_eq!(memory.get(&MaybeRelocatable::from((0, 1))), None);
1125        assert_eq!(memory.get(&MaybeRelocatable::from((0, 2))), None);
1126        assert_eq!(memory.get(&MaybeRelocatable::from((0, 3))), None);
1127        assert_eq!(memory.get(&MaybeRelocatable::from((0, 4))), None);
1128    }
1129
1130    #[test]
1131    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1132    fn validate_existing_memory_for_range_check_within_bounds() {
1133        let mut builtin = RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(8), true);
1134        let mut segments = MemorySegmentManager::new();
1135        builtin.initialize_segments(&mut segments);
1136        builtin.add_validation_rule(&mut segments.memory);
1137        for _ in 0..3 {
1138            segments.add();
1139        }
1140
1141        segments
1142            .memory
1143            .insert(
1144                Relocatable::from((0, 0)),
1145                &MaybeRelocatable::from(Felt252::from(45_u64)),
1146            )
1147            .unwrap();
1148        segments.memory.validate_existing_memory().unwrap();
1149        assert!(segments
1150            .memory
1151            .validated_addresses
1152            .contains(&Relocatable::from((0, 0))));
1153    }
1154
1155    #[test]
1156    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1157    fn validate_existing_memory_for_range_check_outside_bounds() {
1158        let mut builtin = RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(8), true);
1159        let mut segments = MemorySegmentManager::new();
1160        segments.add();
1161        builtin.initialize_segments(&mut segments);
1162        segments
1163            .memory
1164            .insert(
1165                Relocatable::from((1, 0)),
1166                &MaybeRelocatable::from(Felt252::from(-10)),
1167            )
1168            .unwrap();
1169        builtin.add_validation_rule(&mut segments.memory);
1170        let error = segments.memory.validate_existing_memory();
1171        assert_eq!(
1172            error,
1173            Err(MemoryError::RangeCheckNumOutOfBounds(Box::new((
1174                Felt252::from(-10),
1175                Felt252::TWO.pow(128_u128)
1176            ))))
1177        );
1178    }
1179
1180    #[test]
1181    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1182    fn validate_existing_memory_for_invalid_signature() {
1183        let mut builtin = SignatureBuiltinRunner::new(Some(512), true);
1184        let mut segments = MemorySegmentManager::new();
1185        builtin.initialize_segments(&mut segments);
1186        segments.memory = memory![
1187            (
1188                (0, 0),
1189                (
1190                    "874739451078007766457464989774322083649278607533249481151382481072868806602",
1191                    10
1192                )
1193            ),
1194            (
1195                (0, 1),
1196                (
1197                    "-1472574760335685482768423018116732869320670550222259018541069375211356613248",
1198                    10
1199                )
1200            )
1201        ];
1202        builtin.add_validation_rule(&mut segments.memory);
1203        let error = segments.memory.validate_existing_memory();
1204        assert_eq!(
1205            error,
1206            Err(MemoryError::SignatureNotFound(Box::new((0, 0).into())))
1207        );
1208    }
1209
1210    #[test]
1211    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1212    fn validate_existing_memory_for_valid_signature() {
1213        let mut builtin = SignatureBuiltinRunner::new(Some(512), true);
1214
1215        let signature_r =
1216            felt_hex!("0x411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20");
1217        let signature_s =
1218            felt_hex!("0x405c3191ab3883ef2b763af35bc5f5d15b3b4e99461d70e84c654a351a7c81b");
1219
1220        builtin
1221            .add_signature(Relocatable::from((1, 0)), &(signature_r, signature_s))
1222            .unwrap();
1223
1224        let mut segments = MemorySegmentManager::new();
1225
1226        segments.memory = memory![
1227            (
1228                (1, 0),
1229                (
1230                    "874739451078007766457464989774322083649278607533249481151382481072868806602",
1231                    10
1232                )
1233            ),
1234            ((1, 1), 2)
1235        ];
1236
1237        builtin.initialize_segments(&mut segments);
1238
1239        builtin.add_validation_rule(&mut segments.memory);
1240
1241        let result = segments.memory.validate_existing_memory();
1242
1243        assert_eq!(result, Ok(()))
1244    }
1245
1246    #[test]
1247    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1248    fn validate_existing_memory_for_range_check_relocatable_value() {
1249        let mut builtin = RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(8), true);
1250        let mut segments = MemorySegmentManager::new();
1251        builtin.initialize_segments(&mut segments);
1252        segments.memory = memory![((0, 0), (0, 4))];
1253        builtin.add_validation_rule(&mut segments.memory);
1254        let error = segments.memory.validate_existing_memory();
1255        assert_eq!(
1256            error,
1257            Err(MemoryError::RangeCheckFoundNonInt(Box::new(relocatable!(
1258                0, 0
1259            ))))
1260        );
1261    }
1262
1263    #[test]
1264    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1265    fn validate_existing_memory_for_range_check_out_of_bounds_diff_segment() {
1266        let mut builtin = RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(8), true);
1267        let mut segments = MemorySegmentManager::new();
1268        segments.memory = Memory::new();
1269        segments.add();
1270        builtin.initialize_segments(&mut segments);
1271        segments
1272            .memory
1273            .insert(
1274                Relocatable::from((0, 0)),
1275                &MaybeRelocatable::from(Felt252::from(-45_i128)),
1276            )
1277            .unwrap();
1278        builtin.add_validation_rule(&mut segments.memory);
1279        assert_eq!(segments.memory.validate_existing_memory(), Ok(()));
1280    }
1281
1282    #[test]
1283    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1284    fn get_integer_valid() {
1285        let memory = memory![((0, 0), 10)];
1286        assert_eq!(
1287            memory
1288                .get_integer(Relocatable::from((0, 0)))
1289                .unwrap()
1290                .as_ref(),
1291            &Felt252::from(10)
1292        );
1293    }
1294
1295    #[test]
1296    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1297    fn get_integer_invalid_expected_integer() {
1298        let mut segments = MemorySegmentManager::new();
1299        segments.add();
1300        segments
1301            .memory
1302            .insert(Relocatable::from((0, 0)), &MaybeRelocatable::from((0, 10)))
1303            .unwrap();
1304        assert_matches!(
1305            segments.memory.get_integer(Relocatable::from((0, 0))),
1306            Err(MemoryError::ExpectedInteger(
1307                bx
1308            )) if *bx == Relocatable::from((0, 0))
1309        );
1310    }
1311
1312    #[test]
1313    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1314    fn get_u32_too_big() {
1315        let mut segments = MemorySegmentManager::new();
1316        segments.add();
1317        segments
1318            .memory
1319            .insert(Relocatable::from((0, 0)), &Felt252::from(1_u64 << 32))
1320            .unwrap();
1321        assert_matches!(
1322            segments.memory.get_u32(Relocatable::from((0, 0))),
1323            Err(MemoryError::Math(MathError::Felt252ToU32Conversion(
1324                bx
1325            ))) if *bx == Felt252::from(1_u64 << 32)
1326        );
1327    }
1328
1329    #[test]
1330    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1331    fn get_maybe_relocatable_valid_relocatable() {
1332        let memory = memory![((0, 0), (1, 0))];
1333        assert_eq!(
1334            memory
1335                .get_maybe_relocatable(Relocatable::from((0, 0)))
1336                .unwrap(),
1337            Relocatable::from((1, 0)).into()
1338        );
1339    }
1340
1341    #[test]
1342    fn get_maybe_relocatable_valid_integer() {
1343        let memory = memory![((0, 0), 10)];
1344        assert_eq!(
1345            memory
1346                .get_maybe_relocatable(Relocatable::from((0, 0)))
1347                .unwrap(),
1348            10.into()
1349        );
1350    }
1351
1352    #[test]
1353    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1354    fn default_memory() {
1355        let mem: Memory = Default::default();
1356        assert_eq!(mem.data.len(), 0);
1357    }
1358
1359    #[test]
1360    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1361    fn insert_and_get_temporary_succesful() {
1362        let mut memory = Memory::new();
1363        memory.temp_data.push(Vec::new());
1364
1365        let key = Relocatable::from((-1, 0));
1366        let val = MaybeRelocatable::from(Felt252::from(5));
1367        memory.insert(key, &val).unwrap();
1368
1369        assert_eq!(memory.get(&key).unwrap().as_ref(), &val);
1370    }
1371
1372    #[test]
1373    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1374    fn add_relocation_rule() {
1375        let mut memory = Memory::new();
1376
1377        assert_eq!(
1378            memory.add_relocation_rule((-1, 0).into(), (1, 2).into()),
1379            Ok(()),
1380        );
1381        assert_eq!(
1382            memory.add_relocation_rule((-2, 0).into(), (-1, 1).into()),
1383            Ok(()),
1384        );
1385        assert_eq!(
1386            memory.add_relocation_rule((5, 0).into(), (0, 0).into()),
1387            Err(MemoryError::AddressNotInTemporarySegment(5)),
1388        );
1389        assert_eq!(
1390            memory.add_relocation_rule((-3, 6).into(), (0, 0).into()),
1391            Err(MemoryError::NonZeroOffset(6)),
1392        );
1393        assert_eq!(
1394            memory.add_relocation_rule((-1, 0).into(), (0, 0).into()),
1395            Err(MemoryError::DuplicatedRelocation(-1)),
1396        );
1397    }
1398
1399    #[test]
1400    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1401    fn relocate_value_bigint() {
1402        let mut memory = Memory::new();
1403        memory
1404            .add_relocation_rule((-1, 0).into(), (2, 0).into())
1405            .unwrap();
1406        memory
1407            .add_relocation_rule((-2, 0).into(), (2, 2).into())
1408            .unwrap();
1409
1410        // Test when value is Some(BigInt):
1411        assert_eq!(
1412            memory
1413                .relocate_value(&MaybeRelocatable::Int(Felt252::from(0)))
1414                .unwrap(),
1415            Cow::Owned(MaybeRelocatable::Int(Felt252::from(0))),
1416        );
1417    }
1418
1419    #[test]
1420    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1421    fn relocate_value_mayberelocatable() {
1422        let mut memory = Memory::new();
1423        memory
1424            .add_relocation_rule((-1, 0).into(), (2, 0).into())
1425            .unwrap();
1426        memory
1427            .add_relocation_rule((-2, 0).into(), (2, 2).into())
1428            .unwrap();
1429
1430        // Test when value is Some(MaybeRelocatable) with segment_index >= 0:
1431        assert_eq!(
1432            memory
1433                .relocate_value(&MaybeRelocatable::RelocatableValue((0, 0).into()))
1434                .unwrap(),
1435            Cow::Owned(MaybeRelocatable::RelocatableValue((0, 0).into())),
1436        );
1437        assert_eq!(
1438            memory
1439                .relocate_value(&MaybeRelocatable::RelocatableValue((5, 0).into()))
1440                .unwrap(),
1441            Cow::Owned(MaybeRelocatable::RelocatableValue((5, 0).into())),
1442        );
1443    }
1444
1445    #[test]
1446    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1447    fn relocate_value_mayberelocatable_temporary_segment_no_rules() {
1448        let mut memory = Memory::new();
1449        memory
1450            .add_relocation_rule((-1, 0).into(), (2, 0).into())
1451            .unwrap();
1452        memory
1453            .add_relocation_rule((-2, 0).into(), (2, 2).into())
1454            .unwrap();
1455
1456        // Test when value is Some(MaybeRelocatable) with segment_index < 0 and
1457        // there are no applicable relocation rules:
1458        assert_eq!(
1459            memory
1460                .relocate_value(&MaybeRelocatable::RelocatableValue((-5, 0).into()))
1461                .unwrap(),
1462            Cow::Owned(MaybeRelocatable::RelocatableValue((-5, 0).into())),
1463        );
1464    }
1465
1466    #[test]
1467    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1468    fn relocate_value_mayberelocatable_temporary_segment_rules() {
1469        let mut memory = Memory::new();
1470        memory
1471            .add_relocation_rule((-1, 0).into(), (2, 0).into())
1472            .unwrap();
1473        memory
1474            .add_relocation_rule((-2, 0).into(), (2, 2).into())
1475            .unwrap();
1476
1477        // Test when value is Some(MaybeRelocatable) with segment_index < 0 and
1478        // there are applicable relocation rules:
1479        assert_eq!(
1480            memory
1481                .relocate_value(&MaybeRelocatable::RelocatableValue((-1, 0).into()))
1482                .unwrap(),
1483            Cow::Owned(MaybeRelocatable::RelocatableValue((2, 0).into())),
1484        );
1485        assert_eq!(
1486            memory
1487                .relocate_value(&MaybeRelocatable::RelocatableValue((-2, 0).into()))
1488                .unwrap(),
1489            Cow::Owned(MaybeRelocatable::RelocatableValue((2, 2).into())),
1490        );
1491        assert_eq!(
1492            memory
1493                .relocate_value(&MaybeRelocatable::RelocatableValue((-1, 5).into()))
1494                .unwrap(),
1495            Cow::Owned(MaybeRelocatable::RelocatableValue((2, 5).into())),
1496        );
1497        assert_eq!(
1498            memory
1499                .relocate_value(&MaybeRelocatable::RelocatableValue((-2, 5).into()))
1500                .unwrap(),
1501            Cow::Owned(MaybeRelocatable::RelocatableValue((2, 7).into())),
1502        );
1503    }
1504
1505    #[test]
1506    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1507    fn get_range_for_continuous_memory() {
1508        let memory = memory![((1, 0), 2), ((1, 1), 3), ((1, 2), 4)];
1509
1510        let value1 = MaybeRelocatable::from(Felt252::from(2));
1511        let value2 = MaybeRelocatable::from(Felt252::from(3));
1512        let value3 = MaybeRelocatable::from(Felt252::from(4));
1513
1514        let expected_vec = vec![
1515            Some(Cow::Borrowed(&value1)),
1516            Some(Cow::Borrowed(&value2)),
1517            Some(Cow::Borrowed(&value3)),
1518        ];
1519        assert_eq!(memory.get_range(Relocatable::from((1, 0)), 3), expected_vec);
1520    }
1521
1522    #[test]
1523    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1524    fn get_range_for_non_continuous_memory() {
1525        let memory = memory![((1, 0), 2), ((1, 1), 3), ((1, 3), 4)];
1526
1527        let value1 = MaybeRelocatable::from(Felt252::from(2));
1528        let value2 = MaybeRelocatable::from(Felt252::from(3));
1529        let value3 = MaybeRelocatable::from(Felt252::from(4));
1530
1531        let expected_vec = vec![
1532            Some(Cow::Borrowed(&value1)),
1533            Some(Cow::Borrowed(&value2)),
1534            None,
1535            Some(Cow::Borrowed(&value3)),
1536        ];
1537        assert_eq!(memory.get_range(Relocatable::from((1, 0)), 4), expected_vec);
1538    }
1539
1540    #[test]
1541    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1542    fn get_continuous_range_for_continuous_memory() {
1543        let memory = memory![((1, 0), 2), ((1, 1), 3), ((1, 2), 4)];
1544
1545        let value1 = MaybeRelocatable::from(Felt252::from(2));
1546        let value2 = MaybeRelocatable::from(Felt252::from(3));
1547        let value3 = MaybeRelocatable::from(Felt252::from(4));
1548
1549        let expected_vec = vec![value1, value2, value3];
1550        assert_eq!(
1551            memory.get_continuous_range(Relocatable::from((1, 0)), 3),
1552            Ok(expected_vec)
1553        );
1554    }
1555
1556    #[test]
1557    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1558    fn get_continuous_range_for_non_continuous_memory() {
1559        let memory = memory![((1, 0), 2), ((1, 1), 3), ((1, 3), 4)];
1560
1561        assert_eq!(
1562            memory.get_continuous_range(Relocatable::from((1, 0)), 3),
1563            Err(MemoryError::GetRangeMemoryGap(Box::new(((1, 0).into(), 3))))
1564        );
1565    }
1566
1567    #[test]
1568    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1569    fn get_u32_range_ok() {
1570        let memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 2), 4294967295), ((0, 3), 3)];
1571        let expected_vector = vec![1, 4294967295];
1572        assert_eq!(memory.get_u32_range((0, 1).into(), 2), Ok(expected_vector));
1573    }
1574
1575    #[test]
1576    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1577    fn get_u32_range_relocatable() {
1578        let memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 2), (0, 0)), ((0, 3), 3)];
1579        assert_matches!(memory.get_u32_range((0, 1).into(), 2), Err(MemoryError::ExpectedInteger(bx)) if *bx == (0, 2).into());
1580    }
1581
1582    #[test]
1583    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1584    fn get_u32_range_over_32_bits() {
1585        let memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 2), 4294967296), ((0, 3), 3)];
1586        assert_matches!(memory.get_u32_range((0, 1).into(), 2), Err(MemoryError::Math(MathError::Felt252ToU32Conversion(bx))) if *bx == Felt252::from(4294967296_u64));
1587    }
1588
1589    #[test]
1590    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1591    fn get_u32_range_memory_gap() {
1592        let memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 3), 3)];
1593        assert_matches!(memory.get_u32_range((0, 1).into(), 3), Err(MemoryError::UnknownMemoryCell(bx)) if *bx == (0, 2).into());
1594    }
1595
1596    /// Test that relocate_memory() works when there are no relocation rules.
1597    #[test]
1598    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1599    fn relocate_memory_empty_relocation_rules() {
1600        let mut memory = memory![((0, 0), 1), ((0, 1), 2), ((0, 2), 3)];
1601
1602        assert_eq!(memory.relocate_memory(), Ok(()));
1603        check_memory!(memory, ((0, 0), 1), ((0, 1), 2), ((0, 2), 3));
1604    }
1605
1606    #[test]
1607    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1608    fn relocate_memory_new_segment_with_gap() {
1609        let mut memory = memory![
1610            ((0, 0), 1),
1611            ((0, 1), (-1, 0)),
1612            ((0, 2), 3),
1613            ((1, 0), (-1, 1)),
1614            ((1, 1), 5),
1615            ((1, 2), (-1, 2)),
1616            ((-1, 0), 7),
1617            ((-1, 1), 8),
1618            ((-1, 2), 9)
1619        ];
1620        memory
1621            .add_relocation_rule((-1, 0).into(), (2, 1).into())
1622            .unwrap();
1623        memory.data.push(vec![]);
1624
1625        assert_eq!(memory.relocate_memory(), Ok(()));
1626        check_memory!(
1627            memory,
1628            ((0, 0), 1),
1629            ((0, 1), (2, 1)),
1630            ((0, 2), 3),
1631            ((1, 0), (2, 2)),
1632            ((1, 1), 5),
1633            ((1, 2), (2, 3)),
1634            ((2, 1), 7),
1635            ((2, 2), 8),
1636            ((2, 3), 9)
1637        );
1638        assert!(memory.temp_data.is_empty());
1639    }
1640
1641    #[test]
1642    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1643    fn relocate_memory_new_segment() {
1644        let mut memory = memory![
1645            ((0, 0), 1),
1646            ((0, 1), (-1, 0)),
1647            ((0, 2), 3),
1648            ((1, 0), (-1, 1)),
1649            ((1, 1), 5),
1650            ((1, 2), (-1, 2)),
1651            ((-1, 0), 7),
1652            ((-1, 1), 8),
1653            ((-1, 2), 9)
1654        ];
1655        memory
1656            .add_relocation_rule((-1, 0).into(), (2, 0).into())
1657            .unwrap();
1658        memory.data.push(vec![]);
1659
1660        assert_eq!(memory.relocate_memory(), Ok(()));
1661
1662        check_memory!(
1663            memory,
1664            ((0, 0), 1),
1665            ((0, 1), (2, 0)),
1666            ((0, 2), 3),
1667            ((1, 0), (2, 1)),
1668            ((1, 1), 5),
1669            ((1, 2), (2, 2)),
1670            ((2, 0), 7),
1671            ((2, 1), 8),
1672            ((2, 2), 9)
1673        );
1674        assert!(memory.temp_data.is_empty());
1675    }
1676
1677    #[test]
1678    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1679    fn relocate_memory_new_segment_unallocated() {
1680        let mut memory = memory![
1681            ((0, 0), 1),
1682            ((0, 1), (-1, 0)),
1683            ((0, 2), 3),
1684            ((1, 0), (-1, 1)),
1685            ((1, 1), 5),
1686            ((1, 2), (-1, 2)),
1687            ((-1, 0), 7),
1688            ((-1, 1), 8),
1689            ((-1, 2), 9)
1690        ];
1691        memory
1692            .add_relocation_rule((-1, 0).into(), (2, 0).into())
1693            .unwrap();
1694
1695        assert_eq!(
1696            memory.relocate_memory(),
1697            Err(MemoryError::UnallocatedSegment(Box::new((2, 2))))
1698        );
1699    }
1700
1701    #[test]
1702    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1703    fn relocate_memory_into_existing_segment() {
1704        let mut memory = memory![
1705            ((0, 0), 1),
1706            ((0, 1), (-1, 0)),
1707            ((0, 2), 3),
1708            ((1, 0), (-1, 1)),
1709            ((1, 1), 5),
1710            ((1, 2), (-1, 2)),
1711            ((-1, 0), 7),
1712            ((-1, 1), 8),
1713            ((-1, 2), 9)
1714        ];
1715        memory
1716            .add_relocation_rule((-1, 0).into(), (1, 3).into())
1717            .unwrap();
1718
1719        assert_eq!(memory.relocate_memory(), Ok(()));
1720
1721        check_memory!(
1722            memory,
1723            ((0, 0), 1),
1724            ((0, 1), (1, 3)),
1725            ((0, 2), 3),
1726            ((1, 0), (1, 4)),
1727            ((1, 1), 5),
1728            ((1, 2), (1, 5)),
1729            ((1, 3), 7),
1730            ((1, 4), 8),
1731            ((1, 5), 9)
1732        );
1733        assert!(memory.temp_data.is_empty());
1734    }
1735
1736    #[test]
1737    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1738    fn relocate_memory_into_existing_segment_inconsistent_memory() {
1739        let mut memory = memory![
1740            ((0, 0), 1),
1741            ((0, 1), (-1, 0)),
1742            ((0, 2), 3),
1743            ((1, 0), (-1, 1)),
1744            ((1, 1), 5),
1745            ((1, 2), (-1, 2)),
1746            ((-1, 0), 7),
1747            ((-1, 1), 8),
1748            ((-1, 2), 9)
1749        ];
1750        memory
1751            .add_relocation_rule((-1, 0).into(), (1, 0).into())
1752            .unwrap();
1753
1754        assert_eq!(
1755            memory.relocate_memory(),
1756            Err(MemoryError::InconsistentMemory(Box::new((
1757                (1, 0).into(),
1758                (1, 1).into(),
1759                7.into(),
1760            ))))
1761        );
1762    }
1763
1764    #[test]
1765    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1766    fn relocate_memory_new_segment_2_temporary_segments_one_relocated() {
1767        let mut memory = memory![
1768            ((0, 0), 1),
1769            ((0, 1), (-1, 0)),
1770            ((0, 2), 3),
1771            ((1, 0), (-1, 1)),
1772            ((1, 1), 5),
1773            ((1, 2), (-1, 2)),
1774            ((-1, 0), 7),
1775            ((-1, 1), 8),
1776            ((-1, 2), 9),
1777            ((-2, 0), 10),
1778            ((-2, 1), 11)
1779        ];
1780        memory
1781            .add_relocation_rule((-1, 0).into(), (2, 0).into())
1782            .unwrap();
1783        memory.data.push(vec![]);
1784
1785        assert_eq!(memory.relocate_memory(), Ok(()));
1786        check_memory!(
1787            memory,
1788            ((0, 0), 1),
1789            ((0, 1), (2, 0)),
1790            ((0, 2), 3),
1791            ((1, 0), (2, 1)),
1792            ((1, 1), 5),
1793            ((1, 2), (2, 2)),
1794            ((2, 0), 7),
1795            ((2, 1), 8),
1796            ((2, 2), 9),
1797            ((-1, 0), 10),
1798            ((-1, 1), 11)
1799        );
1800    }
1801
1802    #[test]
1803    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1804    fn relocate_memory_new_segment_2_temporary_segments_relocated() {
1805        let mut memory = memory![
1806            ((0, 0), 1),
1807            ((0, 1), (-1, 0)),
1808            ((0, 2), 3),
1809            ((1, 0), (-1, 1)),
1810            ((1, 1), 5),
1811            ((1, 2), (-1, 2)),
1812            ((-1, 0), 7),
1813            ((-1, 1), 8),
1814            ((-1, 2), 9),
1815            ((-2, 0), 10),
1816            ((-2, 1), 11)
1817        ];
1818        memory.data.push(vec![]);
1819        memory
1820            .add_relocation_rule((-1, 0).into(), (2, 0).into())
1821            .unwrap();
1822        memory.data.push(vec![]);
1823        memory
1824            .add_relocation_rule((-2, 0).into(), (3, 0).into())
1825            .unwrap();
1826
1827        assert_eq!(memory.relocate_memory(), Ok(()));
1828
1829        check_memory!(
1830            memory,
1831            ((0, 0), 1),
1832            ((0, 1), (2, 0)),
1833            ((0, 2), 3),
1834            ((1, 0), (2, 1)),
1835            ((1, 1), 5),
1836            ((1, 2), (2, 2)),
1837            ((2, 0), 7),
1838            ((2, 1), 8),
1839            ((2, 2), 9),
1840            ((3, 0), 10),
1841            ((3, 1), 11)
1842        );
1843        assert!(memory.temp_data.is_empty());
1844    }
1845
1846    #[test]
1847    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1848    fn test_memory_display() {
1849        let memory = memory![
1850            ((0, 0), 1),
1851            ((0, 1), (-1, 0)),
1852            ((0, 2), 3),
1853            ((1, 0), (-1, 1)),
1854            ((1, 1), 5),
1855            ((1, 2), (-1, 2)),
1856            ((-1, 0), (-1, 0)),
1857            ((-1, 1), 8),
1858            ((-1, 2), 9)
1859        ];
1860
1861        assert_eq!(
1862            format!("{}", memory),
1863            "(-1,0) : -1:0\n(-1,1) : 8\n(-1,2) : 9\n(0,0) : 1\n(0,1) : -1:0\n(0,2) : 3\n(1,0) : -1:1\n(1,1) : 5\n(1,2) : -1:2\n");
1864    }
1865
1866    #[test]
1867    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1868    fn relocate_memory_into_existing_segment_temporary_values_in_temporary_memory() {
1869        let mut memory = memory![
1870            ((0, 0), 1),
1871            ((0, 1), (-1, 0)),
1872            ((0, 2), 3),
1873            ((1, 0), (-1, 1)),
1874            ((1, 1), 5),
1875            ((1, 2), (-1, 2)),
1876            ((-1, 0), (-1, 0)),
1877            ((-1, 1), 8),
1878            ((-1, 2), 9)
1879        ];
1880        memory
1881            .add_relocation_rule((-1, 0).into(), (1, 3).into())
1882            .unwrap();
1883
1884        assert_eq!(memory.relocate_memory(), Ok(()));
1885        check_memory!(
1886            memory,
1887            ((0, 0), 1),
1888            ((0, 1), (1, 3)),
1889            ((0, 2), 3),
1890            ((1, 0), (1, 4)),
1891            ((1, 1), 5),
1892            ((1, 2), (1, 5)),
1893            ((1, 3), (1, 3)),
1894            ((1, 4), 8),
1895            ((1, 5), 9)
1896        );
1897        assert!(memory.temp_data.is_empty());
1898    }
1899
1900    #[test]
1901    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1902    fn relocate_address_with_rules() {
1903        let mut memory = Memory::new();
1904        memory
1905            .add_relocation_rule((-1, 0).into(), (2, 0).into())
1906            .unwrap();
1907        memory
1908            .add_relocation_rule((-2, 0).into(), (2, 2).into())
1909            .unwrap();
1910
1911        assert_eq!(
1912            Memory::relocate_address((-1, 0).into(), &memory.relocation_rules).unwrap(),
1913            MaybeRelocatable::RelocatableValue((2, 0).into()),
1914        );
1915        assert_eq!(
1916            Memory::relocate_address((-2, 1).into(), &memory.relocation_rules).unwrap(),
1917            MaybeRelocatable::RelocatableValue((2, 3).into()),
1918        );
1919    }
1920
1921    #[test]
1922    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1923    fn relocate_address_no_rules() {
1924        let memory = Memory::new();
1925        assert_eq!(
1926            Memory::relocate_address((-1, 0).into(), &memory.relocation_rules).unwrap(),
1927            MaybeRelocatable::RelocatableValue((-1, 0).into()),
1928        );
1929        assert_eq!(
1930            Memory::relocate_address((-2, 1).into(), &memory.relocation_rules).unwrap(),
1931            MaybeRelocatable::RelocatableValue((-2, 1).into()),
1932        );
1933    }
1934
1935    #[test]
1936    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1937    fn relocate_address_real_addr() {
1938        let memory = Memory::new();
1939        assert_eq!(
1940            Memory::relocate_address((1, 0).into(), &memory.relocation_rules).unwrap(),
1941            MaybeRelocatable::RelocatableValue((1, 0).into()),
1942        );
1943        assert_eq!(
1944            Memory::relocate_address((1, 1).into(), &memory.relocation_rules).unwrap(),
1945            MaybeRelocatable::RelocatableValue((1, 1).into()),
1946        );
1947    }
1948
1949    #[test]
1950    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1951    #[cfg(feature = "extensive_hints")]
1952    fn relocate_address_to_integer() {
1953        let mut memory = Memory::new();
1954        memory
1955            .add_relocation_rule((-1, 0).into(), 0.into())
1956            .unwrap();
1957        memory
1958            .add_relocation_rule((-2, 0).into(), 42.into())
1959            .unwrap();
1960
1961        assert_eq!(
1962            Memory::relocate_address((-1, 0).into(), &memory.relocation_rules).unwrap(),
1963            MaybeRelocatable::Int(0.into()),
1964        );
1965        assert_eq!(
1966            Memory::relocate_address((-2, 0).into(), &memory.relocation_rules).unwrap(),
1967            MaybeRelocatable::Int(42.into()),
1968        );
1969    }
1970
1971    #[test]
1972    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1973    #[cfg(feature = "extensive_hints")]
1974    fn relocate_address_integer_no_duplicates() {
1975        let mut memory = Memory::new();
1976        memory
1977            .add_relocation_rule((-1, 0).into(), 1.into())
1978            .unwrap();
1979        assert_eq!(
1980            memory.add_relocation_rule((-1, 0).into(), 42.into()),
1981            Err(MemoryError::DuplicatedRelocation(-1))
1982        );
1983        assert_eq!(
1984            memory.add_relocation_rule((-1, 0).into(), (2, 0).into()),
1985            Err(MemoryError::DuplicatedRelocation(-1))
1986        );
1987
1988        assert_eq!(
1989            Memory::relocate_address((-1, 0).into(), &memory.relocation_rules).unwrap(),
1990            MaybeRelocatable::Int(1.into()),
1991        );
1992
1993        memory
1994            .add_relocation_rule((-2, 0).into(), (3, 0).into())
1995            .unwrap();
1996        assert_eq!(
1997            memory.add_relocation_rule((-2, 0).into(), 1.into()),
1998            Err(MemoryError::DuplicatedRelocation(-2))
1999        );
2000
2001        assert_eq!(
2002            Memory::relocate_address((-2, 0).into(), &memory.relocation_rules).unwrap(),
2003            MaybeRelocatable::RelocatableValue((3, 0).into()),
2004        );
2005    }
2006
2007    #[test]
2008    fn mark_address_as_accessed() {
2009        let mut memory = memory![((0, 0), 0)];
2010        assert!(!memory.data[0][0].is_accessed());
2011        memory.mark_as_accessed(relocatable!(0, 0));
2012        assert!(memory.data[0][0].is_accessed());
2013    }
2014
2015    #[test]
2016    fn get_amount_of_accessed_addresses_for_segment_valid() {
2017        let mut memory = memory![((0, 0), 0)];
2018        assert_eq!(
2019            memory.get_amount_of_accessed_addresses_for_segment(0),
2020            Some(0)
2021        );
2022        memory.mark_as_accessed(relocatable!(0, 0));
2023        assert_eq!(
2024            memory.get_amount_of_accessed_addresses_for_segment(0),
2025            Some(1)
2026        );
2027    }
2028
2029    #[test]
2030    fn get_amount_of_accessed_addresses_for_segment_invalid_segment() {
2031        let memory = memory![((0, 0), 0)];
2032        assert_eq!(memory.get_amount_of_accessed_addresses_for_segment(1), None);
2033    }
2034
2035    #[test]
2036    fn memory_cell_new_is_not_accessed() {
2037        let cell = MemoryCell::new(mayberelocatable!(1));
2038        assert!(!cell.is_accessed())
2039    }
2040
2041    #[test]
2042    fn memory_cell_mark_accessed() {
2043        let mut cell = MemoryCell::new(mayberelocatable!(1));
2044        cell.mark_accessed();
2045        assert!(cell.is_accessed())
2046    }
2047
2048    #[test]
2049    fn memory_cell_get_value() {
2050        let cell = MemoryCell::new(mayberelocatable!(1));
2051        assert_eq!(cell.get_value(), Some(mayberelocatable!(1)));
2052    }
2053
2054    use core::cmp::Ordering::*;
2055
2056    fn check_memcmp(
2057        lhs: (isize, usize),
2058        rhs: (isize, usize),
2059        len: usize,
2060        ord: Ordering,
2061        pos: usize,
2062    ) {
2063        let mem = memory![
2064            ((-2, 0), 1),
2065            ((-2, 1), (1, 1)),
2066            ((-2, 3), 0),
2067            ((-2, 4), 0),
2068            ((-1, 0), 1),
2069            ((-1, 1), (1, 1)),
2070            ((-1, 3), 0),
2071            ((-1, 4), 3),
2072            ((0, 0), 1),
2073            ((0, 1), (1, 1)),
2074            ((0, 3), 0),
2075            ((0, 4), 0),
2076            ((1, 0), 1),
2077            ((1, 1), (1, 1)),
2078            ((1, 3), 0),
2079            ((1, 4), 3)
2080        ];
2081        assert_eq!((ord, pos), mem.memcmp(lhs.into(), rhs.into(), len));
2082    }
2083
2084    #[test]
2085    fn insert_alloc_fails_gracefully() {
2086        let mut mem = memory![((0, 0), 1)];
2087        let err = mem.insert((0, usize::MAX >> 1).into(), Felt252::ONE);
2088        assert_eq!(err, Err(MemoryError::VecCapacityExceeded));
2089    }
2090
2091    #[test]
2092    fn insert_overflow_fails_gracefully() {
2093        let mut mem = memory![((0, 0), 1)];
2094        let err = mem.insert((0, usize::MAX).into(), Felt252::ONE);
2095        assert_eq!(err, Err(MemoryError::VecCapacityExceeded));
2096    }
2097
2098    #[test]
2099    fn memcmp() {
2100        check_memcmp((0, 0), (0, 0), 3, Equal, 3);
2101        check_memcmp((0, 0), (1, 0), 3, Equal, 3);
2102        check_memcmp((0, 0), (1, 0), 5, Less, 4);
2103        check_memcmp((1, 0), (0, 0), 5, Greater, 4);
2104        check_memcmp((2, 2), (2, 5), 8, Equal, 0);
2105        check_memcmp((0, 0), (2, 5), 8, Greater, 0);
2106        check_memcmp((2, 5), (0, 0), 8, Less, 0);
2107        check_memcmp((-2, 0), (-2, 0), 3, Equal, 3);
2108        check_memcmp((-2, 0), (-1, 0), 3, Equal, 3);
2109        check_memcmp((-2, 0), (-1, 0), 5, Less, 4);
2110        check_memcmp((-1, 0), (-2, 0), 5, Greater, 4);
2111        check_memcmp((-3, 2), (-3, 5), 8, Equal, 0);
2112        check_memcmp((-2, 0), (-3, 5), 8, Greater, 0);
2113        check_memcmp((-3, 5), (-2, 0), 8, Less, 0);
2114    }
2115
2116    #[test]
2117    fn cairo_pie_memory_from_memory() {
2118        let memory = memory![((8, 9), 3), ((1, 2), 5), ((7, 6), (1, 2))];
2119
2120        assert_eq!(
2121            CairoPieMemory::from(&memory),
2122            CairoPieMemory(vec![
2123                ((1, 2), MaybeRelocatable::from(5)),
2124                ((7, 6), MaybeRelocatable::from((1, 2))),
2125                ((8, 9), MaybeRelocatable::from(3))
2126            ])
2127        )
2128    }
2129
2130    #[test]
2131    #[cfg(not(feature = "extensive_hints"))]
2132    fn flatten_relocation_rules_chain_happy() {
2133        let mut mem = Memory::new();
2134        // temp segments just to keep indices sensible (not strictly required here)
2135        mem.temp_data = vec![vec![], vec![]];
2136
2137        //  key 0 -> (-2, 3) ; key 1 -> (10, 4)
2138        //  after flatten: key 0 -> (10, 7)
2139        mem.relocation_rules.insert(0, Relocatable::from((-2, 3)));
2140        mem.relocation_rules.insert(1, Relocatable::from((10, 4)));
2141        // sanity: an unrelated rule stays as-is
2142        mem.relocation_rules.insert(2, Relocatable::from((7, 1)));
2143
2144        assert_eq!(mem.flatten_relocation_rules(), Ok(()));
2145        assert_eq!(
2146            *mem.relocation_rules.get(&0).unwrap(),
2147            Relocatable::from((10, 7))
2148        );
2149        assert_eq!(
2150            *mem.relocation_rules.get(&1).unwrap(),
2151            Relocatable::from((10, 4))
2152        );
2153        assert_eq!(
2154            *mem.relocation_rules.get(&2).unwrap(),
2155            Relocatable::from((7, 1))
2156        );
2157    }
2158
2159    #[test]
2160    #[cfg(not(feature = "extensive_hints"))]
2161    fn flatten_relocation_rules_cycle_err() {
2162        let mut mem = Memory::new();
2163        mem.temp_data = vec![vec![]];
2164
2165        // Self-loop: key 0 -> (-1, 0)  (i.e., points back to itself)
2166        mem.relocation_rules.insert(0, Relocatable::from((-1, 0)));
2167
2168        assert_eq!(mem.flatten_relocation_rules(), Err(MemoryError::Relocation));
2169    }
2170
2171    #[test]
2172    #[cfg(feature = "extensive_hints")]
2173    fn flatten_relocation_rules_chain_happy_extensive_reloc_and_int() {
2174        let mut mem = Memory::new();
2175        mem.temp_data = vec![vec![], vec![], vec![], vec![], vec![]];
2176
2177        // ---- Chain A (ends at relocatable) ----
2178        // key 0 -> (-2, 3)  (=> key 1)
2179        // key 1 -> (10, 4)  (real)
2180        // Expect: key 0 -> (10, 7)
2181        mem.relocation_rules.insert(
2182            0,
2183            MaybeRelocatable::RelocatableValue(Relocatable::from((-2, 3))),
2184        );
2185        mem.relocation_rules.insert(
2186            1,
2187            MaybeRelocatable::RelocatableValue(Relocatable::from((10, 4))),
2188        );
2189
2190        // ---- Chain B (ends at int) ----
2191        // key 2 -> (-4, 0)  (=> key 3)
2192        // key 3 -> (-5, 0)  (=> key 4)
2193        // key 4 -> 99       (final Int)
2194        // Expect: key 2 -> 99  (offset is zero along the way)
2195        mem.relocation_rules.insert(
2196            2,
2197            MaybeRelocatable::RelocatableValue(Relocatable::from((-4, 0))),
2198        );
2199        mem.relocation_rules.insert(
2200            3,
2201            MaybeRelocatable::RelocatableValue(Relocatable::from((-5, 0))),
2202        );
2203        mem.relocation_rules
2204            .insert(4, MaybeRelocatable::Int(Felt252::from(99)));
2205
2206        assert_eq!(mem.flatten_relocation_rules(), Ok(()));
2207
2208        assert_eq!(
2209            mem.relocation_rules.get(&0),
2210            Some(&MaybeRelocatable::RelocatableValue(Relocatable::from((
2211                10, 7
2212            ))))
2213        );
2214        assert_eq!(
2215            mem.relocation_rules.get(&1),
2216            Some(&MaybeRelocatable::RelocatableValue(Relocatable::from((
2217                10, 4
2218            ))))
2219        );
2220        assert_eq!(
2221            mem.relocation_rules.get(&2),
2222            Some(&MaybeRelocatable::Int(Felt252::from(99)))
2223        );
2224    }
2225
2226    #[test]
2227    #[cfg(feature = "extensive_hints")]
2228    fn flatten_relocation_rules_int_with_non_zero_offset_err() {
2229        let mut mem = Memory::new();
2230        // temp_data length only matters for error messages; keep it >= biggest temp key + 1
2231        mem.temp_data = vec![vec![], vec![], vec![]];
2232
2233        // key 0 -> (-2, 5)  (points to key 1, offset 5)
2234        // key 1 -> (-3, 0)  (points to key 2, offset 2)
2235        // key 2 -> 7        (final Int)
2236        mem.relocation_rules.insert(
2237            0,
2238            MaybeRelocatable::RelocatableValue(Relocatable::from((-2, 5))),
2239        );
2240        mem.relocation_rules.insert(
2241            1,
2242            MaybeRelocatable::RelocatableValue(Relocatable::from((-3, 0))),
2243        );
2244        mem.relocation_rules
2245            .insert(2, MaybeRelocatable::Int(Felt252::from(7)));
2246
2247        assert_eq!(
2248            mem.flatten_relocation_rules(),
2249            Err(MemoryError::NonZeroOffset(5))
2250        );
2251    }
2252
2253    #[test]
2254    #[cfg(feature = "extensive_hints")]
2255    fn flatten_relocation_rules_cycle_err_extensive() {
2256        let mut mem = Memory::new();
2257        mem.temp_data = vec![vec![], vec![]];
2258
2259        // 2-cycle:
2260        //   key 0 -> (-1, 0)  (points to key 0)
2261        //   key 1 -> (-2, 0)  (points to key 1)
2262        mem.relocation_rules.insert(
2263            0,
2264            MaybeRelocatable::RelocatableValue(Relocatable::from((-1, 0))),
2265        );
2266        mem.relocation_rules.insert(
2267            1,
2268            MaybeRelocatable::RelocatableValue(Relocatable::from((-2, 0))),
2269        );
2270
2271        assert_eq!(mem.flatten_relocation_rules(), Err(MemoryError::Relocation));
2272    }
2273
2274    #[test]
2275    #[cfg(not(feature = "extensive_hints"))]
2276    fn flatten_relocation_rules_missing_next_err() {
2277        let mut mem = Memory::new();
2278        mem.temp_data = vec![vec![], vec![], vec![]];
2279
2280        // key 0 -> (-4, 1)  => next_key = -( -4 + 1 ) = 3
2281        // No rule for key 3, so we expect UnmappedTemporarySegment(-4).
2282        mem.relocation_rules.insert(0, Relocatable::from((-4, 1)));
2283
2284        assert_eq!(
2285            mem.flatten_relocation_rules(),
2286            Err(MemoryError::UnmappedTemporarySegment(-4))
2287        );
2288    }
2289
2290    #[test]
2291    #[cfg(feature = "extensive_hints")]
2292    fn flatten_relocation_rules_missing_next_err_extensive() {
2293        let mut mem = Memory::new();
2294        mem.temp_data = vec![vec![], vec![], vec![]];
2295
2296        // key 0 -> (-4, 0)  => next_key = 3
2297        // No rule for key 3, so we expect UnmappedTemporarySegment(-4).
2298        mem.relocation_rules.insert(
2299            0,
2300            MaybeRelocatable::RelocatableValue(Relocatable::from((-4, 0))),
2301        );
2302
2303        assert_eq!(
2304            mem.flatten_relocation_rules(),
2305            Err(MemoryError::UnmappedTemporarySegment(-4))
2306        );
2307    }
2308
2309    #[test]
2310    #[cfg(not(feature = "extensive_hints"))]
2311    fn relocate_memory_temp_chain_multi_hop() {
2312        let mut memory = Memory::new();
2313
2314        // temp segments:
2315        //  -1: [100, 200]
2316        //  -2: [7, 8, 9]
2317        memory.temp_data = vec![
2318            vec![
2319                MemoryCell::new(mayberelocatable!(100)),
2320                MemoryCell::new(mayberelocatable!(200)),
2321            ],
2322            vec![
2323                MemoryCell::new(mayberelocatable!(7)),
2324                MemoryCell::new(mayberelocatable!(8)),
2325                MemoryCell::new(mayberelocatable!(9)),
2326            ],
2327        ];
2328
2329        // Rules:
2330        //   key 0 (-1) -> (-2, 3)
2331        //   key 1 (-2) -> (5, 10)
2332        // Chain: -1 -> -2-> (5,10)  => -1 relocates to (5,13)
2333        memory
2334            .relocation_rules
2335            .insert(0, Relocatable::from((-2, 3)));
2336        memory
2337            .relocation_rules
2338            .insert(1, Relocatable::from((5, 10)));
2339
2340        // Real memory references that point into temp segments
2341        // Expect after relocation:
2342        //   (0,0): (-1,0) -> (5,13)
2343        //   (0,1): (-1,1) -> (5,14)
2344        //   (0,2): (-2,0) -> (5,10)
2345        memory.data.push(vec![
2346            MemoryCell::new(mayberelocatable!(-1, 0)),
2347            MemoryCell::new(mayberelocatable!(-1, 1)),
2348            MemoryCell::new(mayberelocatable!(-2, 0)),
2349        ]);
2350
2351        // Allocate destination segment 5
2352        while memory.data.len() <= 5 {
2353            memory.data.push(Vec::new());
2354        }
2355
2356        assert_eq!(memory.relocate_memory(), Ok(()));
2357
2358        // References updated
2359        check_memory!(
2360            memory,
2361            ((0, 0), (5, 13)),
2362            ((0, 1), (5, 14)),
2363            ((0, 2), (5, 10)),
2364            ((5, 10), 7),
2365            ((5, 11), 8),
2366            ((5, 12), 9),
2367            ((5, 13), 100),
2368            ((5, 14), 200)
2369        );
2370        assert!(memory.temp_data.is_empty());
2371    }
2372
2373    #[test]
2374    #[cfg(feature = "extensive_hints")]
2375    fn relocate_memory_temp_chain_to_int_multi_hop() {
2376        let mut memory = Memory::new();
2377
2378        // temp segments:
2379        //  -1: [123]
2380        //  -2: [456]
2381        memory.temp_data = vec![
2382            vec![MemoryCell::new(mayberelocatable!(123))],
2383            vec![MemoryCell::new(mayberelocatable!(456))],
2384        ];
2385
2386        // Rules:
2387        //   key 0 (-1) -> (-2, 0)
2388        //   key 1 (-2) -> 99 (Int)
2389        // Chain: -1 -> -2 -> 99  (offsets are zero, so collapse to Int)
2390        memory.relocation_rules.insert(
2391            0,
2392            MaybeRelocatable::RelocatableValue(Relocatable::from((-2, 0))),
2393        );
2394        memory
2395            .relocation_rules
2396            .insert(1, MaybeRelocatable::Int(Felt252::from(99)));
2397
2398        // Real memory has a reference into temp
2399        // Expect after relocation: (0,0) becomes Int(99)
2400        memory
2401            .data
2402            .push(vec![MemoryCell::new(mayberelocatable!(-1, 0))]);
2403
2404        assert_eq!(memory.relocate_memory(), Ok(()));
2405
2406        // Reference collapsed to Int
2407        assert_eq!(
2408            memory.data[0][0].get_value(),
2409            Some(MaybeRelocatable::Int(Felt252::from(99)))
2410        );
2411
2412        // The temp segments whose rules end at Int are not moved
2413        assert!(memory.temp_data.is_empty());
2414    }
2415}