use alloc::{collections::BTreeMap, vec::Vec};
use miden_air::trace::RowIndex;
use miden_core::{EMPTY_WORD, Felt, WORD_SIZE, Word, ZERO};
use crate::{ContextId, ExecutionOptions, MemoryAddress, MemoryError, processor::MemoryInterface};
#[derive(Debug)]
pub struct Memory {
memory: BTreeMap<(ContextId, u32), Word>,
max_entries: usize,
}
impl Default for Memory {
fn default() -> Self {
Self::new(ExecutionOptions::DEFAULT_MAX_MEMORY_ELEMENTS)
}
}
impl Memory {
pub fn new(max_elements: usize) -> Self {
Self {
memory: BTreeMap::new(),
max_entries: max_elements.div_ceil(WORD_SIZE),
}
}
pub(crate) fn set_max_elements(&mut self, max_elements: usize) {
self.max_entries = max_elements.div_ceil(WORD_SIZE);
}
#[inline(always)]
pub fn read_element(&self, ctx: ContextId, addr: Felt) -> Result<Felt, MemoryError> {
let element = self.read_element_impl(ctx, clean_addr(addr)?).unwrap_or(ZERO);
Ok(element)
}
#[inline(always)]
pub fn read_word(
&self,
ctx: ContextId,
addr: Felt,
_clk: RowIndex,
) -> Result<Word, MemoryError> {
let addr = clean_addr(addr)?;
let word = self.read_word_impl(ctx, addr)?.unwrap_or(EMPTY_WORD);
Ok(word)
}
#[inline(always)]
pub fn write_element(
&mut self,
ctx: ContextId,
addr: Felt,
element: Felt,
) -> Result<(), MemoryError> {
let (word_addr, idx) = split_addr(clean_addr(addr)?);
if !self.memory.contains_key(&(ctx, word_addr)) && self.memory.len() >= self.max_entries {
return Err(MemoryError::MemoryElementLimitExceeded {
ctx,
addr: word_addr,
max: self.max_element_limit(),
});
}
self.memory
.entry((ctx, word_addr))
.and_modify(|word| {
let mut result: [Felt; WORD_SIZE];
result = (*word).into();
result[idx as usize] = element;
*word = result.into();
})
.or_insert_with(|| {
let mut word = [ZERO; WORD_SIZE];
word[idx as usize] = element;
word.into()
});
Ok(())
}
#[inline(always)]
pub fn write_word(
&mut self,
ctx: ContextId,
addr: Felt,
_clk: RowIndex,
word: Word,
) -> Result<(), MemoryError> {
let addr = enforce_word_aligned_addr(ctx, clean_addr(addr)?)?;
if !self.memory.contains_key(&(ctx, addr)) && self.memory.len() >= self.max_entries {
return Err(MemoryError::MemoryElementLimitExceeded {
ctx,
addr,
max: self.max_element_limit(),
});
}
self.memory.insert((ctx, addr), word);
Ok(())
}
pub fn get_memory_state(&self, ctx: ContextId) -> Vec<(MemoryAddress, Felt)> {
self.memory
.iter()
.filter(|((c, _), _)| *c == ctx)
.flat_map(|(&(_c, addr), word)| {
let addr: MemoryAddress = addr.into();
[
(addr, word[0]),
(addr + 1_u32, word[1]),
(addr + 2_u32, word[2]),
(addr + 3_u32, word[3]),
]
})
.collect()
}
#[inline]
fn max_element_limit(&self) -> usize {
self.max_entries.saturating_mul(WORD_SIZE)
}
pub(crate) fn read_element_impl(&self, ctx: ContextId, addr: u32) -> Option<Felt> {
let (word_addr, idx) = split_addr(addr);
self.memory.get(&(ctx, word_addr)).copied().map(|word| word[idx as usize])
}
#[inline(always)]
pub(crate) fn read_word_impl(
&self,
ctx: ContextId,
addr: u32,
) -> Result<Option<Word>, MemoryError> {
let addr = enforce_word_aligned_addr(ctx, addr)?;
let word = self.memory.get(&(ctx, addr)).copied();
Ok(word)
}
#[cfg(test)]
pub fn num_accessed_words(&self) -> usize {
self.memory.len()
}
}
#[inline(always)]
fn clean_addr(addr: Felt) -> Result<u32, MemoryError> {
let addr = addr.as_canonical_u64();
addr.try_into().map_err(|_| MemoryError::AddressOutOfBounds { addr })
}
fn split_addr(addr: u32) -> (u32, u32) {
let idx = addr % WORD_SIZE as u32;
(addr - idx, idx)
}
#[inline(always)]
fn enforce_word_aligned_addr(ctx: ContextId, addr: u32) -> Result<u32, MemoryError> {
if !addr.is_multiple_of(WORD_SIZE as u32) {
return Err(MemoryError::UnalignedWordAccess { addr, ctx });
}
Ok(addr)
}
impl MemoryInterface for Memory {
fn read_element(&mut self, ctx: ContextId, addr: Felt) -> Result<Felt, MemoryError> {
Memory::read_element(self, ctx, addr)
}
fn read_word(
&mut self,
ctx: ContextId,
addr: Felt,
clk: RowIndex,
) -> Result<Word, MemoryError> {
Memory::read_word(self, ctx, addr, clk)
}
fn write_element(
&mut self,
ctx: ContextId,
addr: Felt,
element: Felt,
) -> Result<(), MemoryError> {
Memory::write_element(self, ctx, addr, element)
}
fn write_word(
&mut self,
ctx: ContextId,
addr: Felt,
clk: RowIndex,
word: Word,
) -> Result<(), MemoryError> {
Memory::write_word(self, ctx, addr, clk, word)
}
}