use alloc::vec::Vec;
use core::cmp::Ordering;
use miden_processor::advice::AdviceMutation;
use miden_processor::{ContextId, ExecutionOutput, ProcessorState};
use miden_protocol::{Felt, Word, ZERO};
#[derive(Clone, Copy)]
pub struct LinkMap<'process> {
map_ptr: u32,
mem: &'process MemoryViewer<'process>,
}
impl<'process> LinkMap<'process> {
pub fn new(map_ptr: Felt, mem: &'process MemoryViewer<'process>) -> Self {
let map_ptr: u32 =
u32::try_from(map_ptr.as_canonical_u64()).expect("map_ptr must be a valid u32");
Self { map_ptr, mem }
}
pub fn handle_set_event(process: &ProcessorState<'_>) -> Vec<AdviceMutation> {
let map_ptr = process.get_stack_item(1);
let map_key = process.get_stack_word(2);
let mem_viewer = MemoryViewer::ProcessState(process);
let link_map = LinkMap::new(map_ptr, &mem_viewer);
let (set_op, entry_ptr) = link_map.compute_set_operation(map_key);
vec![AdviceMutation::extend_stack([Felt::from(entry_ptr), Felt::from(set_op as u8)])]
}
pub fn handle_get_event(process: &ProcessorState<'_>) -> Vec<AdviceMutation> {
let map_ptr = process.get_stack_item(1);
let map_key = process.get_stack_word(2);
let mem_viewer = MemoryViewer::ProcessState(process);
let link_map = LinkMap::new(map_ptr, &mem_viewer);
let (get_op, entry_ptr) = link_map.compute_get_operation(map_key);
vec![AdviceMutation::extend_stack([Felt::from(entry_ptr), Felt::from(get_op as u8)])]
}
pub fn is_empty(&self) -> bool {
self.head().is_none()
}
pub fn iter(&self) -> impl Iterator<Item = Entry> {
LinkMapIter {
current_entry_ptr: self.head().unwrap_or(0),
map: *self,
}
}
fn head(&self) -> Option<u32> {
self.mem.get_kernel_mem_element(self.map_ptr).and_then(|head_ptr| {
if head_ptr == ZERO {
None
} else {
Some(
u32::try_from(head_ptr.as_canonical_u64())
.expect("head ptr should be a valid ptr"),
)
}
})
}
fn entry(&self, entry_ptr: u32) -> Entry {
let key = self.key(entry_ptr);
let (value0, value1) = self.value(entry_ptr);
let metadata = self.metadata(entry_ptr);
Entry {
ptr: entry_ptr,
metadata,
key,
value0,
value1,
}
}
fn key(&self, entry_ptr: u32) -> Word {
self.mem
.get_kernel_mem_word(entry_ptr + 4)
.expect("entry pointer should be valid")
}
fn value(&self, entry_ptr: u32) -> (Word, Word) {
let value0 = self
.mem
.get_kernel_mem_word(entry_ptr + 8)
.expect("entry pointer should be valid");
let value1 = self
.mem
.get_kernel_mem_word(entry_ptr + 12)
.expect("entry pointer should be valid");
(value0, value1)
}
fn metadata(&self, entry_ptr: u32) -> EntryMetadata {
let entry_metadata =
self.mem.get_kernel_mem_word(entry_ptr).expect("entry pointer should be valid");
let map_ptr = entry_metadata[0];
let map_ptr = u32::try_from(map_ptr.as_canonical_u64())
.expect("entry_ptr should point to a u32 map_ptr");
let prev_entry_ptr = entry_metadata[1];
let prev_entry_ptr = u32::try_from(prev_entry_ptr.as_canonical_u64())
.expect("entry_ptr should point to a u32 prev_entry_ptr");
let next_entry_ptr = entry_metadata[2];
let next_entry_ptr = u32::try_from(next_entry_ptr.as_canonical_u64())
.expect("entry_ptr should point to a u32 next_entry_ptr");
EntryMetadata { map_ptr, prev_entry_ptr, next_entry_ptr }
}
fn compute_set_operation(&self, key: Word) -> (SetOperation, u32) {
let Some(current_head) = self.head() else {
return (SetOperation::InsertAtHead, 0);
};
let mut last_entry_ptr: u32 = current_head;
for entry in self.iter() {
match key.cmp(&entry.key) {
Ordering::Equal => {
return (SetOperation::Update, entry.ptr);
},
Ordering::Less => {
if entry.ptr == current_head {
return (SetOperation::InsertAtHead, entry.ptr);
}
break;
},
Ordering::Greater => {
last_entry_ptr = entry.ptr;
},
}
}
(SetOperation::InsertAfterEntry, last_entry_ptr)
}
fn compute_get_operation(&self, key: Word) -> (GetOperation, u32) {
let (set_op, entry_ptr) = self.compute_set_operation(key);
let get_op = match set_op {
SetOperation::Update => GetOperation::Found,
SetOperation::InsertAtHead => GetOperation::AbsentAtHead,
SetOperation::InsertAfterEntry => GetOperation::AbsentAfterEntry,
};
(get_op, entry_ptr)
}
}
struct LinkMapIter<'process> {
current_entry_ptr: u32,
map: LinkMap<'process>,
}
impl<'process> Iterator for LinkMapIter<'process> {
type Item = Entry;
fn next(&mut self) -> Option<Self::Item> {
if self.current_entry_ptr == 0 {
return None;
}
let current_entry = self.map.entry(self.current_entry_ptr);
self.current_entry_ptr = current_entry.metadata.next_entry_ptr;
Some(current_entry)
}
}
#[derive(Debug, Clone, Copy)]
pub struct Entry {
pub ptr: u32,
pub metadata: EntryMetadata,
pub key: Word,
pub value0: Word,
pub value1: Word,
}
#[derive(Debug, Clone, Copy)]
pub struct EntryMetadata {
pub map_ptr: u32,
pub prev_entry_ptr: u32,
pub next_entry_ptr: u32,
}
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
enum GetOperation {
Found = 0,
AbsentAtHead = 1,
AbsentAfterEntry = 2,
}
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
enum SetOperation {
Update = 0,
InsertAtHead = 1,
InsertAfterEntry = 2,
}
pub enum MemoryViewer<'mem> {
ProcessState(&'mem ProcessorState<'mem>),
ExecutionOutputs(&'mem ExecutionOutput),
}
impl<'mem> MemoryViewer<'mem> {
fn get_kernel_mem_element(&self, addr: u32) -> Option<Felt> {
match self {
MemoryViewer::ProcessState(process_state) => {
process_state.get_mem_value(ContextId::root(), addr)
},
MemoryViewer::ExecutionOutputs(_execution_output) => {
let idx = addr % miden_protocol::WORD_SIZE as u32;
let word_addr = addr - idx;
Some(self.get_kernel_mem_word(word_addr)?[idx as usize])
},
}
}
fn get_kernel_mem_word(&self, addr: u32) -> Option<Word> {
match self {
MemoryViewer::ProcessState(process_state) => process_state
.get_mem_word(ContextId::root(), addr)
.expect("address should be word-aligned"),
MemoryViewer::ExecutionOutputs(execution_output) => {
let tx_kernel_context = ContextId::root();
let clk = 0u32;
Some(
execution_output
.memory
.read_word(tx_kernel_context, Felt::from(addr), clk.into())
.expect("expected address to be word-aligned"),
)
},
}
}
}