1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
//! This module represents the Cairo memory, containing the
//! compiled Cairo program that occupies the first few entries
use std::{
fmt::{Display, Formatter, Result},
ops::{Index, IndexMut},
};
use crate::{helper::*, word::CairoWord};
use ark_ff::Field;
use core::iter::repeat;
/// This data structure stores the memory of the program
pub struct CairoMemory<F> {
/// length of the public memory
codelen: usize,
/// full memory vector, None if non initialized
data: Vec<Option<CairoWord<F>>>,
}
impl<F: Field> Index<F> for CairoMemory<F> {
type Output = Option<CairoWord<F>>;
fn index(&self, idx: F) -> &Self::Output {
// Safely convert idx from F to usize (since this is a memory address
// idx should not be too big, this should be safe)
let addr: u64 = idx.to_u64();
&self.data[addr as usize]
}
}
impl<F: Field> IndexMut<F> for CairoMemory<F> {
fn index_mut(&mut self, idx: F) -> &mut Self::Output {
let addr: u64 = idx.to_u64();
self.resize(addr); // Resize if necessary
&mut self.data[addr as usize]
}
}
impl<F: Field> Display for CairoMemory<F> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
for i in 1..self.len() {
// Visualize content of memory excluding the 0th dummy entry
if let Some(elem) = self[F::from(i)] {
writeln!(f, "{0:>6}: 0x{1:}", i, elem.word().to_hex_be())
.map_err(|_| core::fmt::Error)?;
} else {
writeln!(f, "{i:>6}: None").map_err(|_| core::fmt::Error)?;
}
}
Ok(())
}
}
impl<F: Field> CairoMemory<F> {
/// Create a new memory structure from a vector of field elements
pub fn new(input: Vec<F>) -> CairoMemory<F> {
// Initialized with the public memory (compiled instructions only)
// starts intentionally with a zero word for ease of testing
let mut aux = vec![F::zero()];
aux.extend(input);
CairoMemory {
codelen: aux.len() - 1,
data: aux.into_iter().map(|i| Some(CairoWord::new(i))).collect(),
}
}
/// Get size of the public memory
pub fn get_codelen(&self) -> usize {
self.codelen
}
/// Get size of the full memory including dummy 0th entry
pub fn len(&self) -> u64 {
self.data.len() as u64
}
/// Returns whether the memory is empty (either length 0, or with the dummy first entry)
pub fn is_empty(&self) -> bool {
self.data.len() < 2
}
/// Resizes memory with enough additional None slots if necessary before writing or reading
fn resize(&mut self, addr: u64) {
// if you want to access an index of the memory but its size is less or equal than this
// you will need to extend the vector with enough spaces (taking into account that
// vectors start by index 0, the 0 address is dummy, and size starts in 1)
if let Some(additional) = addr.checked_sub(self.len() - 1) {
self.data.extend(repeat(None).take(additional as usize));
}
}
/// Write u64 element in memory address
pub fn write(&mut self, addr: F, elem: F) {
self[addr] = Some(CairoWord::new(elem));
}
/// Read element in memory address
pub fn read(&mut self, addr: F) -> Option<F> {
self.resize(addr.to_u64()); // Resize if necessary
self[addr].map(|x| x.word())
}
}