1use alloc::vec::Vec;
2use core::cmp::Ordering;
3
4use miden_processor::fast::ExecutionOutput;
5use miden_processor::{AdviceMutation, ContextId, ProcessState};
6use miden_protocol::{Felt, LexicographicWord, Word, ZERO};
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<'_>) -> Vec<AdviceMutation> {
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 vec![AdviceMutation::extend_stack([Felt::from(set_op as u8), Felt::from(entry_ptr)])]
55 }
56
57 pub fn handle_get_event(process: &ProcessState<'_>) -> Vec<AdviceMutation> {
62 let map_ptr = process.get_stack_item(1);
63 let map_key = process.get_stack_word_be(2);
64
65 let mem_viewer = MemoryViewer::ProcessState(process);
66 let link_map = LinkMap::new(map_ptr, &mem_viewer);
67 let (get_op, entry_ptr) = link_map.compute_get_operation(LexicographicWord::from(map_key));
68
69 vec![AdviceMutation::extend_stack([Felt::from(get_op as u8), Felt::from(entry_ptr)])]
70 }
71
72 pub fn is_empty(&self) -> bool {
74 self.head().is_none()
75 }
76
77 pub fn iter(&self) -> impl Iterator<Item = Entry> {
79 LinkMapIter {
80 current_entry_ptr: self.head().unwrap_or(0),
81 map: *self,
82 }
83 }
84
85 fn head(&self) -> Option<u32> {
90 self.mem.get_kernel_mem_element(self.map_ptr).and_then(|head_ptr| {
94 if head_ptr == ZERO {
95 None
96 } else {
97 Some(u32::try_from(head_ptr).expect("head ptr should be a valid ptr"))
98 }
99 })
100 }
101
102 fn entry(&self, entry_ptr: u32) -> Entry {
104 let key = self.key(entry_ptr);
105 let (value0, value1) = self.value(entry_ptr);
106 let metadata = self.metadata(entry_ptr);
107
108 Entry {
109 ptr: entry_ptr,
110 metadata,
111 key,
112 value0,
113 value1,
114 }
115 }
116
117 fn key(&self, entry_ptr: u32) -> LexicographicWord {
119 LexicographicWord::from(
120 self.mem
121 .get_kernel_mem_word(entry_ptr + 4)
122 .expect("entry pointer should be valid"),
123 )
124 }
125
126 fn value(&self, entry_ptr: u32) -> (Word, Word) {
128 let value0 = self
129 .mem
130 .get_kernel_mem_word(entry_ptr + 8)
131 .expect("entry pointer should be valid");
132 let value1 = self
133 .mem
134 .get_kernel_mem_word(entry_ptr + 12)
135 .expect("entry pointer should be valid");
136 (value0, value1)
137 }
138
139 fn metadata(&self, entry_ptr: u32) -> EntryMetadata {
141 let entry_metadata =
142 self.mem.get_kernel_mem_word(entry_ptr).expect("entry pointer should be valid");
143
144 let map_ptr = entry_metadata[0];
145 let map_ptr = map_ptr.try_into().expect("entry_ptr should point to a u32 map_ptr");
146
147 let prev_entry_ptr = entry_metadata[1];
148 let prev_entry_ptr = prev_entry_ptr
149 .try_into()
150 .expect("entry_ptr should point to a u32 prev_entry_ptr");
151
152 let next_entry_ptr = entry_metadata[2];
153 let next_entry_ptr = next_entry_ptr
154 .try_into()
155 .expect("entry_ptr should point to a u32 next_entry_ptr");
156
157 EntryMetadata { map_ptr, prev_entry_ptr, next_entry_ptr }
158 }
159
160 fn compute_set_operation(&self, key: LexicographicWord) -> (SetOperation, u32) {
172 let Some(current_head) = self.head() else {
173 return (SetOperation::InsertAtHead, 0);
174 };
175
176 let mut last_entry_ptr: u32 = current_head;
177
178 for entry in self.iter() {
179 match key.cmp(&entry.key) {
180 Ordering::Equal => {
181 return (SetOperation::Update, entry.ptr);
182 },
183 Ordering::Less => {
184 if entry.ptr == current_head {
185 return (SetOperation::InsertAtHead, entry.ptr);
186 }
187
188 break;
189 },
190 Ordering::Greater => {
191 last_entry_ptr = entry.ptr;
192 },
193 }
194 }
195
196 (SetOperation::InsertAfterEntry, last_entry_ptr)
197 }
198
199 fn compute_get_operation(&self, key: LexicographicWord) -> (GetOperation, u32) {
210 let (set_op, entry_ptr) = self.compute_set_operation(key);
211 let get_op = match set_op {
212 SetOperation::Update => GetOperation::Found,
213 SetOperation::InsertAtHead => GetOperation::AbsentAtHead,
214 SetOperation::InsertAfterEntry => GetOperation::AbsentAfterEntry,
215 };
216 (get_op, entry_ptr)
217 }
218}
219
220struct LinkMapIter<'process> {
225 current_entry_ptr: u32,
226 map: LinkMap<'process>,
227}
228
229impl<'process> Iterator for LinkMapIter<'process> {
230 type Item = Entry;
231
232 fn next(&mut self) -> Option<Self::Item> {
233 if self.current_entry_ptr == 0 {
234 return None;
235 }
236
237 let current_entry = self.map.entry(self.current_entry_ptr);
238
239 self.current_entry_ptr = current_entry.metadata.next_entry_ptr;
240
241 Some(current_entry)
242 }
243}
244
245#[derive(Debug, Clone, Copy)]
252pub struct Entry {
253 pub ptr: u32,
254 pub metadata: EntryMetadata,
255 pub key: LexicographicWord,
256 pub value0: Word,
257 pub value1: Word,
258}
259
260#[derive(Debug, Clone, Copy)]
264pub struct EntryMetadata {
265 pub map_ptr: u32,
266 pub prev_entry_ptr: u32,
267 pub next_entry_ptr: u32,
268}
269
270#[derive(Debug, Clone, Copy)]
275#[repr(u8)]
276enum GetOperation {
277 Found = 0,
278 AbsentAtHead = 1,
279 AbsentAfterEntry = 2,
280}
281
282#[derive(Debug, Clone, Copy)]
284#[repr(u8)]
285enum SetOperation {
286 Update = 0,
287 InsertAtHead = 1,
288 InsertAfterEntry = 2,
289}
290
291pub enum MemoryViewer<'mem> {
303 ProcessState(&'mem ProcessState<'mem>),
304 ExecutionOutputs(&'mem ExecutionOutput),
305}
306
307impl<'mem> MemoryViewer<'mem> {
308 fn get_kernel_mem_element(&self, addr: u32) -> Option<Felt> {
310 match self {
311 MemoryViewer::ProcessState(process_state) => {
312 process_state.get_mem_value(ContextId::root(), addr)
313 },
314 MemoryViewer::ExecutionOutputs(_execution_output) => {
315 let idx = addr % miden_protocol::WORD_SIZE as u32;
320 let word_addr = addr - idx;
321
322 Some(self.get_kernel_mem_word(word_addr)?[idx as usize])
323 },
324 }
325 }
326
327 fn get_kernel_mem_word(&self, addr: u32) -> Option<Word> {
329 match self {
330 MemoryViewer::ProcessState(process_state) => process_state
331 .get_mem_word(ContextId::root(), addr)
332 .expect("address should be word-aligned"),
333 MemoryViewer::ExecutionOutputs(execution_output) => {
334 let tx_kernel_context = ContextId::root();
335 let clk = 0u32;
336 let err_ctx = ();
337
338 Some(
341 execution_output
342 .memory
343 .read_word(tx_kernel_context, Felt::from(addr), clk.into(), &err_ctx)
344 .expect("expected address to be word-aligned"),
345 )
346 },
347 }
348 }
349}