1use alloc::vec::Vec;
2use core::cmp::Ordering;
3
4use miden_objects::{Felt, LexicographicWord, Word, ZERO};
5use miden_processor::fast::ExecutionOutput;
6use miden_processor::{AdviceMutation, ContextId, EventError, ProcessState};
7
8#[derive(Clone, Copy)]
22pub struct LinkMap<'process> {
23 map_ptr: u32,
24 mem: &'process MemoryViewer<'process>,
25}
26
27impl<'process> LinkMap<'process> {
28 pub fn new(map_ptr: Felt, mem: &'process MemoryViewer<'process>) -> Self {
33 let map_ptr: u32 = map_ptr.try_into().expect("map_ptr must be a valid u32");
34
35 Self { map_ptr, mem }
36 }
37
38 pub fn handle_set_event(process: &ProcessState<'_>) -> Result<Vec<AdviceMutation>, EventError> {
46 let map_ptr = process.get_stack_item(1);
47 let map_key = process.get_stack_word_be(2);
48
49 let mem_viewer = MemoryViewer::ProcessState(process);
50 let link_map = LinkMap::new(map_ptr, &mem_viewer);
51
52 let (set_op, entry_ptr) = link_map.compute_set_operation(LexicographicWord::from(map_key));
53
54 Ok(vec![AdviceMutation::extend_stack([
55 Felt::from(set_op as u8),
56 Felt::from(entry_ptr),
57 ])])
58 }
59
60 pub fn handle_get_event(process: &ProcessState<'_>) -> Result<Vec<AdviceMutation>, EventError> {
65 let map_ptr = process.get_stack_item(1);
66 let map_key = process.get_stack_word_be(2);
67
68 let mem_viewer = MemoryViewer::ProcessState(process);
69 let link_map = LinkMap::new(map_ptr, &mem_viewer);
70 let (get_op, entry_ptr) = link_map.compute_get_operation(LexicographicWord::from(map_key));
71
72 Ok(vec![AdviceMutation::extend_stack([
73 Felt::from(get_op as u8),
74 Felt::from(entry_ptr),
75 ])])
76 }
77
78 pub fn is_empty(&self) -> bool {
80 self.head().is_none()
81 }
82
83 pub fn iter(&self) -> impl Iterator<Item = Entry> {
85 LinkMapIter {
86 current_entry_ptr: self.head().unwrap_or(0),
87 map: *self,
88 }
89 }
90
91 fn head(&self) -> Option<u32> {
96 self.mem.get_kernel_mem_element(self.map_ptr).and_then(|head_ptr| {
100 if head_ptr == ZERO {
101 None
102 } else {
103 Some(u32::try_from(head_ptr).expect("head ptr should be a valid ptr"))
104 }
105 })
106 }
107
108 fn entry(&self, entry_ptr: u32) -> Entry {
110 let key = self.key(entry_ptr);
111 let (value0, value1) = self.value(entry_ptr);
112 let metadata = self.metadata(entry_ptr);
113
114 Entry {
115 ptr: entry_ptr,
116 metadata,
117 key,
118 value0,
119 value1,
120 }
121 }
122
123 fn key(&self, entry_ptr: u32) -> LexicographicWord {
125 LexicographicWord::from(
126 self.mem
127 .get_kernel_mem_word(entry_ptr + 4)
128 .expect("entry pointer should be valid"),
129 )
130 }
131
132 fn value(&self, entry_ptr: u32) -> (Word, Word) {
134 let value0 = self
135 .mem
136 .get_kernel_mem_word(entry_ptr + 8)
137 .expect("entry pointer should be valid");
138 let value1 = self
139 .mem
140 .get_kernel_mem_word(entry_ptr + 12)
141 .expect("entry pointer should be valid");
142 (value0, value1)
143 }
144
145 fn metadata(&self, entry_ptr: u32) -> EntryMetadata {
147 let entry_metadata =
148 self.mem.get_kernel_mem_word(entry_ptr).expect("entry pointer should be valid");
149
150 let map_ptr = entry_metadata[0];
151 let map_ptr = map_ptr.try_into().expect("entry_ptr should point to a u32 map_ptr");
152
153 let prev_entry_ptr = entry_metadata[1];
154 let prev_entry_ptr = prev_entry_ptr
155 .try_into()
156 .expect("entry_ptr should point to a u32 prev_entry_ptr");
157
158 let next_entry_ptr = entry_metadata[2];
159 let next_entry_ptr = next_entry_ptr
160 .try_into()
161 .expect("entry_ptr should point to a u32 next_entry_ptr");
162
163 EntryMetadata { map_ptr, prev_entry_ptr, next_entry_ptr }
164 }
165
166 fn compute_set_operation(&self, key: LexicographicWord) -> (SetOperation, u32) {
178 let Some(current_head) = self.head() else {
179 return (SetOperation::InsertAtHead, 0);
180 };
181
182 let mut last_entry_ptr: u32 = current_head;
183
184 for entry in self.iter() {
185 match key.cmp(&entry.key) {
186 Ordering::Equal => {
187 return (SetOperation::Update, entry.ptr);
188 },
189 Ordering::Less => {
190 if entry.ptr == current_head {
191 return (SetOperation::InsertAtHead, entry.ptr);
192 }
193
194 break;
195 },
196 Ordering::Greater => {
197 last_entry_ptr = entry.ptr;
198 },
199 }
200 }
201
202 (SetOperation::InsertAfterEntry, last_entry_ptr)
203 }
204
205 fn compute_get_operation(&self, key: LexicographicWord) -> (GetOperation, u32) {
216 let (set_op, entry_ptr) = self.compute_set_operation(key);
217 let get_op = match set_op {
218 SetOperation::Update => GetOperation::Found,
219 SetOperation::InsertAtHead => GetOperation::AbsentAtHead,
220 SetOperation::InsertAfterEntry => GetOperation::AbsentAfterEntry,
221 };
222 (get_op, entry_ptr)
223 }
224}
225
226struct LinkMapIter<'process> {
231 current_entry_ptr: u32,
232 map: LinkMap<'process>,
233}
234
235impl<'process> Iterator for LinkMapIter<'process> {
236 type Item = Entry;
237
238 fn next(&mut self) -> Option<Self::Item> {
239 if self.current_entry_ptr == 0 {
240 return None;
241 }
242
243 let current_entry = self.map.entry(self.current_entry_ptr);
244
245 self.current_entry_ptr = current_entry.metadata.next_entry_ptr;
246
247 Some(current_entry)
248 }
249}
250
251#[derive(Debug, Clone, Copy)]
258pub struct Entry {
259 pub ptr: u32,
260 pub metadata: EntryMetadata,
261 pub key: LexicographicWord,
262 pub value0: Word,
263 pub value1: Word,
264}
265
266#[derive(Debug, Clone, Copy)]
270pub struct EntryMetadata {
271 pub map_ptr: u32,
272 pub prev_entry_ptr: u32,
273 pub next_entry_ptr: u32,
274}
275
276#[derive(Debug, Clone, Copy)]
281#[repr(u8)]
282enum GetOperation {
283 Found = 0,
284 AbsentAtHead = 1,
285 AbsentAfterEntry = 2,
286}
287
288#[derive(Debug, Clone, Copy)]
290#[repr(u8)]
291enum SetOperation {
292 Update = 0,
293 InsertAtHead = 1,
294 InsertAfterEntry = 2,
295}
296
297pub enum MemoryViewer<'mem> {
309 ProcessState(&'mem ProcessState<'mem>),
310 ExecutionOutputs(&'mem ExecutionOutput),
311}
312
313impl<'mem> MemoryViewer<'mem> {
314 fn get_kernel_mem_element(&self, addr: u32) -> Option<Felt> {
316 match self {
317 MemoryViewer::ProcessState(process_state) => {
318 process_state.get_mem_value(ContextId::root(), addr)
319 },
320 MemoryViewer::ExecutionOutputs(_execution_output) => {
321 let idx = addr % miden_objects::WORD_SIZE as u32;
326 let word_addr = addr - idx;
327
328 Some(self.get_kernel_mem_word(word_addr)?[idx as usize])
329 },
330 }
331 }
332
333 fn get_kernel_mem_word(&self, addr: u32) -> Option<Word> {
335 match self {
336 MemoryViewer::ProcessState(process_state) => process_state
337 .get_mem_word(ContextId::root(), addr)
338 .expect("address should be word-aligned"),
339 MemoryViewer::ExecutionOutputs(execution_output) => {
340 let tx_kernel_context = ContextId::root();
341 let clk = 0u32;
342 let err_ctx = ();
343
344 Some(
347 execution_output
348 .memory
349 .read_word(tx_kernel_context, Felt::from(addr), clk.into(), &err_ctx)
350 .expect("expected address to be word-aligned"),
351 )
352 },
353 }
354 }
355}