1use alloc::{
2 collections::BTreeMap,
3 rc::Rc,
4 string::{String, ToString},
5 sync::Arc,
6 vec::Vec,
7};
8use core::cell::RefCell;
9
10use miden_assembly_syntax::ast::{
11 DebugFrameBase, DebugLocationExpression, DebugLocationExpressionOp, DebugVarInfo,
12 DebugVarLocation, types::Type,
13};
14use miden_core::Felt;
15use miden_processor::{ContextId, FastProcessor, trace::RowIndex};
16
17use super::InvocationId;
18
19type DebugVarEvents = Rc<RefCell<BTreeMap<RowIndex, Vec<DebugVarInfo>>>>;
20type CapturedDebugValues = Rc<RefCell<BTreeMap<(RowIndex, Arc<str>), Vec<Felt>>>>;
21
22#[derive(Debug, Clone)]
24pub struct DebugVarSnapshot {
25 pub clk: RowIndex,
27 pub info: DebugVarInfo,
29}
30
31#[derive(Debug, Clone)]
33pub struct ResolvedDebugVariable {
34 pub raw: Option<Felt>,
35 pub value: String,
36 pub type_name: String,
37}
38
39#[derive(Default)]
40struct InvocationVariables {
41 variables: BTreeMap<String, DebugVarSnapshot>,
42 captured: BTreeMap<String, Vec<Felt>>,
43 locations: BTreeMap<String, DebugVarLocation>,
45 context: ContextId,
46 fmp: Option<u32>,
47}
48
49impl InvocationVariables {
50 fn refresh_storage(&mut self, processor: &FastProcessor) {
51 let read_memory = |address: u32| {
52 processor.memory().read_element(self.context, Felt::from_u32(address)).ok()
53 };
54 self.fmp = read_memory(miden_core::FMP_ADDR.as_canonical_u64() as u32)
55 .and_then(|value| u32::try_from(value.as_canonical_u64()).ok());
56 let read_local =
57 |offset: i16| read_memory(self.fmp?.checked_add_signed(i32::from(offset))?);
58 self.locations = self
59 .variables
60 .iter()
61 .map(|(name, variable)| {
62 (
63 name.clone(),
64 anchor_frame_bases(variable.info.value_location(), &read_memory, &read_local)
65 .unwrap_or(DebugVarLocation::Unavailable),
66 )
67 })
68 .collect();
69 }
70}
71
72pub struct DebugVarTracker {
74 invocations: BTreeMap<InvocationId, InvocationVariables>,
75 current_invocation: Option<InvocationId>,
76 invocation_mode: bool,
77 events: DebugVarEvents,
79 captured_values: CapturedDebugValues,
81 current_vars: BTreeMap<String, DebugVarSnapshot>,
83 current_captured_values: BTreeMap<String, Vec<Felt>>,
85 processed_up_to: RowIndex,
87}
88
89impl DebugVarTracker {
90 pub fn new(events: DebugVarEvents) -> Self {
92 Self {
93 invocations: BTreeMap::new(),
94 current_invocation: None,
95 invocation_mode: false,
96 events,
97 captured_values: Rc::new(Default::default()),
98 current_vars: BTreeMap::new(),
99 current_captured_values: BTreeMap::new(),
100 processed_up_to: RowIndex::from(0),
101 }
102 }
103
104 pub fn current_invocation(&self) -> Option<InvocationId> {
105 self.current_invocation
106 }
107
108 pub fn has_invocation(&self, invocation: InvocationId) -> bool {
109 self.invocations.contains_key(&invocation)
110 }
111
112 pub fn variables_for(
113 &self,
114 invocation: InvocationId,
115 ) -> impl Iterator<Item = &DebugVarSnapshot> {
116 self.invocations
117 .get(&invocation)
118 .into_iter()
119 .flat_map(|frame| frame.variables.values())
120 }
121
122 pub fn observe_execution(
125 &mut self,
126 live_ids: impl IntoIterator<Item = InvocationId>,
127 current: Option<InvocationId>,
128 processor: &FastProcessor,
129 clk: RowIndex,
130 mut infos: Vec<DebugVarInfo>,
131 ) {
132 let current = current.filter(|id| {
136 self.invocations
137 .get(id)
138 .is_none_or(|frame| frame.context == processor.state().ctx())
139 });
140 if self.current_invocation != current
144 && let Some(previous) = self.current_invocation
145 && let Some(frame) = self.invocations.get_mut(&previous)
146 && frame.context == processor.state().ctx()
147 {
148 frame.refresh_storage(processor);
149 }
150 self.invocation_mode = true;
151 self.current_invocation = current;
152 let live_ids = live_ids.into_iter().collect::<alloc::collections::BTreeSet<_>>();
153 self.invocations.retain(|id, _| live_ids.contains(id));
154 let Some(current) = current else { return };
155 let state = processor.state();
156 let context = state.ctx();
157 let stack = state.get_stack_state();
158 let frame = self.invocations.entry(current).or_default();
159 frame.context = context;
160 let kills = infos.iter().map(is_debug_var_kill).collect::<Vec<_>>();
161 let captured = snapshot_transient_debug_values(&mut infos, &stack);
162 for (info, is_kill) in infos.into_iter().zip(kills) {
163 let name = info.name().to_string();
164 if is_kill {
165 frame.variables.remove(&name);
166 frame.captured.remove(&name);
167 frame.locations.remove(&name);
168 } else {
169 match captured.get(info.name()) {
170 Some(values) => {
171 frame.captured.insert(name.clone(), values.clone());
172 }
173 None => {
174 frame.captured.remove(&name);
175 }
176 }
177 frame.variables.insert(name, DebugVarSnapshot { clk, info });
178 }
179 }
180 frame.refresh_storage(processor);
181 }
182
183 pub fn resolve(
185 &self,
186 invocation: InvocationId,
187 name: &str,
188 processor: &FastProcessor,
189 ) -> Option<ResolvedDebugVariable> {
190 let frame = self.invocations.get(&invocation)?;
191 let variable = frame.variables.get(name)?;
192 let read_memory = |address: u32| {
193 processor.memory().read_element(frame.context, Felt::from_u32(address)).ok()
194 };
195 let active =
196 self.current_invocation == Some(invocation) && processor.state().ctx() == frame.context;
197 let fmp = if active {
198 read_memory(miden_core::FMP_ADDR.as_canonical_u64() as u32)
199 .and_then(|value| u32::try_from(value.as_canonical_u64()).ok())
200 } else {
201 frame.fmp
202 };
203 let read_local = |offset: i16| read_memory(fmp?.checked_add_signed(i32::from(offset))?);
204 let location = if active {
205 anchor_frame_bases(variable.info.value_location(), &read_memory, &read_local)
206 .unwrap_or(DebugVarLocation::Unavailable)
207 } else {
208 frame.locations.get(name)?.clone()
209 };
210 let captured = frame.captured.get(name);
211 let raw = captured
212 .and_then(|values| values.first().copied())
213 .or_else(|| resolve_variable_value(&location, &[], read_memory, read_local));
214 let type_name =
215 variable.info.ty().map(ToString::to_string).unwrap_or_else(|| "Felt".into());
216 let typed = variable.info.ty().and_then(|ty| {
217 super::format_value(ty, |count| {
218 captured.filter(|values| values.len() == count).cloned().or_else(|| {
219 resolve_typed_variable_values(
220 &location,
221 ty,
222 count,
223 &[],
224 read_memory,
225 read_local,
226 )
227 })
228 })
229 });
230 let value = typed
231 .or_else(|| raw.map(|value| value.as_canonical_u64().to_string()))
232 .unwrap_or_else(|| "<unavailable>".into());
233 Some(ResolvedDebugVariable {
234 raw,
235 value,
236 type_name,
237 })
238 }
239
240 pub fn record_events(&self, clk: RowIndex, infos: Vec<DebugVarInfo>) {
242 if !infos.is_empty() {
243 self.events.borrow_mut().entry(clk).or_default().extend(infos);
244 }
245 }
246
247 pub fn record_events_with_stack(
249 &self,
250 clk: RowIndex,
251 mut infos: Vec<DebugVarInfo>,
252 stack: &[Felt],
253 ) {
254 let names = infos.iter().map(|info| info.name().clone()).collect::<Vec<_>>();
255 let captured_values = snapshot_transient_debug_values(&mut infos, stack);
256 {
257 let mut stored_values = self.captured_values.borrow_mut();
258 for name in names {
259 stored_values.remove(&(clk, name));
260 }
261 stored_values
262 .extend(captured_values.into_iter().map(|(name, values)| ((clk, name), values)));
263 }
264 self.record_events(clk, infos);
265 }
266
267 pub fn update_to_cycle(&mut self, clk: RowIndex) {
269 let events = self.events.borrow();
270 let captured_values = self.captured_values.borrow();
271
272 for (event_clk, var_infos) in events.range(self.processed_up_to..=clk) {
274 for info in var_infos {
275 if is_debug_var_kill(info) {
276 self.current_vars.remove(info.name().as_ref());
277 self.current_captured_values.remove(info.name().as_ref());
278 continue;
279 }
280 let snapshot = DebugVarSnapshot {
281 clk: *event_clk,
282 info: info.clone(),
283 };
284 let name = info.name().to_string();
285 match captured_values.get(&(*event_clk, info.name().clone())) {
286 Some(values) => {
287 self.current_captured_values.insert(name.clone(), values.clone());
288 }
289 None => {
290 self.current_captured_values.remove(&name);
291 }
292 }
293 self.current_vars.insert(name, snapshot);
294 }
295 }
296
297 self.processed_up_to = clk;
298 }
299
300 pub fn reset(&mut self) {
302 self.invocations.clear();
303 self.current_invocation = None;
304 self.current_vars.clear();
305 self.current_captured_values.clear();
306 self.processed_up_to = RowIndex::from(0);
307 }
308
309 fn active_variables(&self) -> Option<&BTreeMap<String, DebugVarSnapshot>> {
310 if self.invocation_mode {
311 Some(&self.invocations.get(&self.current_invocation?)?.variables)
312 } else {
313 Some(&self.current_vars)
314 }
315 }
316
317 pub fn current_variables(&self) -> impl Iterator<Item = &DebugVarSnapshot> {
319 self.active_variables().into_iter().flat_map(|variables| variables.values())
320 }
321
322 pub fn get_variable(&self, name: &str) -> Option<&DebugVarSnapshot> {
324 self.active_variables()?.get(name)
325 }
326
327 pub fn captured_values(&self, name: &str) -> Option<&[Felt]> {
329 if self.invocation_mode {
330 self.invocations
331 .get(&self.current_invocation?)?
332 .captured
333 .get(name)
334 .map(Vec::as_slice)
335 } else {
336 self.current_captured_values.get(name).map(Vec::as_slice)
337 }
338 }
339
340 pub fn variable_count(&self) -> usize {
342 self.active_variables().map_or(0, BTreeMap::len)
343 }
344
345 pub fn has_variables(&self) -> bool {
347 self.variable_count() != 0
348 }
349}
350
351fn anchor_frame_bases(
354 location: &DebugVarLocation,
355 read_memory: &impl Fn(u32) -> Option<Felt>,
356 read_local: &impl Fn(i16) -> Option<Felt>,
357) -> Option<DebugVarLocation> {
358 let operations = match location {
359 DebugVarLocation::ResolvedFrameBase { base, byte_offset } => vec![
360 DebugLocationExpressionOp::ConstU64(resolve_frame_base_address(
361 *base,
362 *byte_offset,
363 read_memory,
364 read_local,
365 )?),
366 DebugLocationExpressionOp::DerefBytes,
367 ],
368 DebugVarLocation::Expression(expression) => expression
369 .operations()
370 .iter()
371 .map(|operation| match operation {
372 DebugLocationExpressionOp::FrameBaseAddress { base, byte_offset } => {
373 Some(DebugLocationExpressionOp::ConstU64(resolve_frame_base_address(
374 *base,
375 *byte_offset,
376 read_memory,
377 read_local,
378 )?))
379 }
380 operation => Some(*operation),
381 })
382 .collect::<Option<Vec<_>>>()?,
383 location => return Some(location.clone()),
384 };
385 Some(DebugVarLocation::Expression(DebugLocationExpression::new(operations).ok()?))
386}
387
388pub fn snapshot_transient_debug_values(
395 infos: &mut [DebugVarInfo],
396 stack: &[Felt],
397) -> BTreeMap<Arc<str>, Vec<Felt>> {
398 let mut captured_values = BTreeMap::new();
399
400 for info in infos {
401 captured_values.remove(info.name().as_ref());
402 match info.value_location() {
403 DebugVarLocation::Stack(position) => {
404 let count = info.ty().and_then(super::abi_types::value_felt_count).unwrap_or(1);
405 let start = *position as usize;
406 let values = start
407 .checked_add(count)
408 .and_then(|end| stack.get(start..end))
409 .map(<[Felt]>::to_vec);
410 let location = values
411 .as_ref()
412 .and_then(|values| values.first().copied())
413 .or_else(|| (count == 0).then(|| Felt::from_u32(0)))
414 .map(DebugVarLocation::Const)
415 .unwrap_or(DebugVarLocation::Unavailable);
416 if let Some(values) = values {
417 captured_values.insert(info.name().clone(), values);
418 }
419 info.set_value_location(location);
420 }
421 DebugVarLocation::Expression(expression) => {
422 let operations = expression
423 .operations()
424 .iter()
425 .map(|operation| match operation {
426 DebugLocationExpressionOp::ReadStack(position) => stack
427 .get(*position as usize)
428 .map(|value| {
429 DebugLocationExpressionOp::ConstU64(value.as_canonical_u64())
430 })
431 .ok_or(()),
432 operation => Ok(*operation),
433 })
434 .collect::<Result<Vec<_>, _>>();
435 let location = operations
436 .and_then(|ops| DebugLocationExpression::new(ops).map_err(|_| ()))
437 .map(DebugVarLocation::Expression)
438 .unwrap_or(DebugVarLocation::Unavailable);
439 info.set_value_location(location);
440 }
441 _ => {}
442 }
443 }
444
445 captured_values
446}
447
448fn is_debug_var_kill(info: &DebugVarInfo) -> bool {
449 matches!(info.value_location(), DebugVarLocation::Unavailable)
450}
451
452pub fn resolve_variable_value(
454 location: &DebugVarLocation,
455 stack: &[Felt],
456 get_memory: impl Fn(u32) -> Option<Felt>,
457 get_local: impl Fn(i16) -> Option<Felt>,
458) -> Option<Felt> {
459 resolve_variable_values(location, 1, stack, get_memory, get_local)?.pop()
460}
461
462pub fn resolve_variable_values(
464 location: &DebugVarLocation,
465 count: usize,
466 stack: &[Felt],
467 get_memory: impl Fn(u32) -> Option<Felt>,
468 get_local: impl Fn(i16) -> Option<Felt>,
469) -> Option<Vec<Felt>> {
470 if count == 0 {
471 return Some(Vec::new());
472 }
473
474 match location {
475 DebugVarLocation::Stack(pos) => {
476 let start = *pos as usize;
477 let end = start.checked_add(count)?;
478 Some(stack.get(start..end)?.to_vec())
479 }
480 DebugVarLocation::Memory(addr) => resolve_consecutive_memory(*addr, count, &get_memory),
481 DebugVarLocation::Const(felt) => (count == 1).then_some(vec![*felt]),
482 DebugVarLocation::Local(offset) => {
483 let mut values = Vec::with_capacity(count);
484 for index in 0..count {
485 let index = i16::try_from(index).ok()?;
486 values.push(get_local(offset.checked_add(index)?)?);
487 }
488 Some(values)
489 }
490 DebugVarLocation::ResolvedFrameBase { base, byte_offset } => {
491 resolve_frame_base_values(*base, *byte_offset, count, &get_memory, &get_local)
492 }
493 DebugVarLocation::Expression(expression) => {
494 match resolve_expression(expression.operations(), stack, &get_memory, &get_local)? {
495 ResolvedExpression::Scalar(value) => {
496 (count == 1).then(|| integer_to_felt(value)).flatten().map(|value| vec![value])
497 }
498 ResolvedExpression::ByteAddress(address) => {
499 let element_address = u32::try_from(address / 4).ok()?;
500 resolve_consecutive_memory(element_address, count, &get_memory)
501 }
502 }
503 }
504 DebugVarLocation::Unavailable => None,
505 }
506}
507
508pub fn resolve_typed_variable_values(
514 location: &DebugVarLocation,
515 ty: &Type,
516 count: usize,
517 stack: &[Felt],
518 get_memory: impl Fn(u32) -> Option<Felt>,
519 get_local: impl Fn(i16) -> Option<Felt>,
520) -> Option<Vec<Felt>> {
521 match location {
522 DebugVarLocation::ResolvedFrameBase { base, byte_offset } => {
523 let byte_address =
524 resolve_frame_base_address(*base, *byte_offset, &get_memory, &get_local)?;
525 resolve_typed_memory_value(ty, byte_address, count, &get_memory)
526 }
527 DebugVarLocation::Expression(expression) => {
528 match resolve_expression(expression.operations(), stack, &get_memory, &get_local)? {
529 ResolvedExpression::ByteAddress(byte_address) => {
530 resolve_typed_memory_value(ty, byte_address, count, &get_memory)
531 }
532 ResolvedExpression::Scalar(value) => {
533 (count == 1).then(|| integer_to_felt(value)).flatten().map(|value| vec![value])
534 }
535 }
536 }
537 _ => resolve_variable_values(location, count, stack, get_memory, get_local),
538 }
539}
540
541fn resolve_consecutive_memory(
542 start_addr: u32,
543 count: usize,
544 get_memory: &impl Fn(u32) -> Option<Felt>,
545) -> Option<Vec<Felt>> {
546 let mut values = Vec::with_capacity(count);
547 for index in 0..count {
548 let addr = start_addr.checked_add(u32::try_from(index).ok()?)?;
549 values.push(get_memory(addr)?);
550 }
551 Some(values)
552}
553
554#[derive(Clone, Copy, Debug, Eq, PartialEq)]
555enum ResolvedExpression {
556 Scalar(i128),
557 ByteAddress(u64),
558}
559
560fn resolve_expression(
561 ops: &[DebugLocationExpressionOp],
562 stack: &[Felt],
563 get_memory: &impl Fn(u32) -> Option<Felt>,
564 get_local: &impl Fn(i16) -> Option<Felt>,
565) -> Option<ResolvedExpression> {
566 let mut values = Vec::<i128>::new();
567
568 for (index, op) in ops.iter().enumerate() {
569 match op {
570 DebugLocationExpressionOp::ReadStack(index) => {
571 values.push(i128::from(stack.get(*index as usize)?.as_canonical_u64()));
572 }
573 DebugLocationExpressionOp::ReadMemory(index) => {
574 values.push(i128::from(get_memory(*index)?.as_canonical_u64()));
575 }
576 DebugLocationExpressionOp::ReadLocal(index) => {
577 values.push(i128::from(get_local(*index)?.as_canonical_u64()));
578 }
579 DebugLocationExpressionOp::ConstU64(value) => {
580 values.push(i128::from(*value));
581 }
582 DebugLocationExpressionOp::ConstI64(value) => {
583 values.push(i128::from(*value));
584 }
585 DebugLocationExpressionOp::AddUnsigned(value) => {
586 let lhs = values.pop()?;
587 values.push(lhs.checked_add(i128::from(*value))?);
588 }
589 DebugLocationExpressionOp::Add => {
590 let rhs = values.pop()?;
591 let lhs = values.pop()?;
592 values.push(lhs.checked_add(rhs)?);
593 }
594 DebugLocationExpressionOp::Sub => {
595 let rhs = values.pop()?;
596 let lhs = values.pop()?;
597 values.push(lhs.checked_sub(rhs)?);
598 }
599 DebugLocationExpressionOp::DerefBytes => {
600 let byte_address = u64::try_from(values.pop()?).ok()?;
601 if index + 1 == ops.len() {
602 return Some(ResolvedExpression::ByteAddress(byte_address));
603 }
604 let element_address = u32::try_from(byte_address / 4).ok()?;
605 values.push(i128::from(get_memory(element_address)?.as_canonical_u64()));
606 }
607 DebugLocationExpressionOp::FrameBaseAddress { base, byte_offset } => {
608 values.push(i128::from(resolve_frame_base_address(
609 *base,
610 *byte_offset,
611 get_memory,
612 get_local,
613 )?));
614 }
615 }
616 }
617
618 values.pop().map(ResolvedExpression::Scalar)
619}
620
621fn resolve_frame_base_values(
622 base: DebugFrameBase,
623 byte_offset: i64,
624 count: usize,
625 get_memory: &impl Fn(u32) -> Option<Felt>,
626 get_local: &impl Fn(i16) -> Option<Felt>,
627) -> Option<Vec<Felt>> {
628 let byte_address = resolve_frame_base_address(base, byte_offset, get_memory, get_local)?;
629 resolve_byte_address_values(byte_address, count, get_memory)
630}
631
632fn resolve_frame_base_address(
633 base: DebugFrameBase,
634 byte_offset: i64,
635 get_memory: &impl Fn(u32) -> Option<Felt>,
636 get_local: &impl Fn(i16) -> Option<Felt>,
637) -> Option<u64> {
638 let base = match base {
639 DebugFrameBase::Local(offset) => get_local(offset)?,
640 DebugFrameBase::Memory(address) => get_memory(address)?,
641 };
642 base.as_canonical_u64().checked_add_signed(byte_offset)
643}
644
645fn resolve_byte_address_values(
646 byte_address: u64,
647 count: usize,
648 get_memory: &impl Fn(u32) -> Option<Felt>,
649) -> Option<Vec<Felt>> {
650 if !byte_address.is_multiple_of(4) {
651 return None;
652 }
653 let element_address = u32::try_from(byte_address / 4).ok()?;
654 resolve_consecutive_memory(element_address, count, get_memory)
655}
656
657fn integer_to_felt(value: i128) -> Option<Felt> {
658 Felt::new(u64::try_from(value).ok()?).ok()
659}
660
661fn read_memory_bytes(
662 byte_address: u64,
663 size: usize,
664 get_memory: &impl Fn(u32) -> Option<Felt>,
665) -> Option<Vec<u8>> {
666 if size == 0 {
667 return Some(Vec::new());
668 }
669
670 let element_address = u32::try_from(byte_address / 4).ok()?;
671 let byte_offset = usize::try_from(byte_address % 4).ok()?;
672 let end = byte_offset.checked_add(size)?;
673 let element_count = end.div_ceil(4);
674 let mut bytes = Vec::with_capacity(element_count.checked_mul(4)?);
675
676 for index in 0..element_count {
677 let address = element_address.checked_add(u32::try_from(index).ok()?)?;
678 let value = get_memory(address)?.as_canonical_u64() as u32;
679 bytes.extend_from_slice(&value.to_le_bytes());
680 }
681
682 Some(bytes.get(byte_offset..end)?.to_vec())
683}
684
685fn resolve_typed_memory_value(
686 ty: &Type,
687 byte_address: u64,
688 expected_count: usize,
689 get_memory: &impl Fn(u32) -> Option<Felt>,
690) -> Option<Vec<Felt>> {
691 let mut values = Vec::with_capacity(expected_count);
692 append_typed_memory_value(ty, byte_address, get_memory, &mut values)?;
693 (values.len() == expected_count).then_some(values)
694}
695
696fn append_typed_memory_value(
697 ty: &Type,
698 byte_address: u64,
699 get_memory: &impl Fn(u32) -> Option<Felt>,
700 values: &mut Vec<Felt>,
701) -> Option<()> {
702 match ty {
703 Type::Felt => {
704 if !byte_address.is_multiple_of(4) {
705 return None;
706 }
707 values.push(get_memory(u32::try_from(byte_address / 4).ok()?)?);
708 }
709 Type::I1 => {
710 let byte = *read_memory_bytes(byte_address, 1, get_memory)?.first()?;
711 values.push(Felt::from_u32(u32::from(byte & 1)));
712 }
713 Type::I8 | Type::I16 | Type::I32 | Type::I64 | Type::I128 => {
714 let value = read_unsigned_integer(ty, byte_address, get_memory)?;
715 let bit_width = u32::try_from(ty.size_in_bits()).ok()?;
716 let shift = 128_u32.checked_sub(bit_width)?;
717 let signed = ((value << shift) as i128) >> shift;
718 let slot_bits = u32::try_from(ty.size_in_felts().checked_mul(32)?).ok()?;
719 let canonical = (signed as u128) & low_bits_mask(slot_bits);
720 append_u128_limbs(canonical, ty.size_in_felts(), values);
721 }
722 Type::U8 | Type::U16 | Type::U32 | Type::U64 | Type::U128 | Type::F64 => {
723 let value = read_unsigned_integer(ty, byte_address, get_memory)?;
724 append_u128_limbs(value, ty.size_in_felts(), values);
725 }
726 Type::Struct(struct_ty) => {
727 for field in struct_ty.get().fields() {
728 let field_address = byte_address.checked_add(u64::from(field.offset))?;
729 append_typed_memory_value(&field.ty, field_address, get_memory, values)?;
730 }
731 }
732 Type::Array(array_ty) => {
733 let element_size = array_ty.ty.size_in_bytes();
734 let alignment = array_ty.ty.min_alignment();
735 let stride = align_up(element_size, alignment)?;
736 for index in 0..array_ty.len {
737 let offset = index.checked_mul(stride)?;
738 let element_address = byte_address.checked_add(u64::try_from(offset).ok()?)?;
739 append_typed_memory_value(&array_ty.ty, element_address, get_memory, values)?;
740 }
741 }
742 Type::U256
743 | Type::List(_)
744 | Type::Ptr(_)
745 | Type::Function(_)
746 | Type::Enum(_)
747 | Type::Unknown
748 | Type::Never
749 | Type::Variadic => return None,
750 }
751
752 Some(())
753}
754
755fn read_unsigned_integer(
756 ty: &Type,
757 byte_address: u64,
758 get_memory: &impl Fn(u32) -> Option<Felt>,
759) -> Option<u128> {
760 let bytes = read_memory_bytes(byte_address, ty.size_in_bytes(), get_memory)?;
761 let mut value = 0_u128;
762 for (index, byte) in bytes.into_iter().enumerate() {
763 value |= u128::from(byte) << index.checked_mul(8)?;
764 }
765 Some(value)
766}
767
768fn append_u128_limbs(value: u128, count: usize, values: &mut Vec<Felt>) {
769 values.extend((0..count).map(|index| Felt::from_u32((value >> (index * 32)) as u32)));
770}
771
772fn low_bits_mask(bits: u32) -> u128 {
773 if bits == 128 {
774 u128::MAX
775 } else {
776 (1_u128 << bits) - 1
777 }
778}
779
780fn align_up(size: usize, alignment: usize) -> Option<usize> {
781 if size == 0 {
782 return Some(0);
783 }
784 size.checked_add(alignment.checked_sub(1)?)?
785 .checked_div(alignment)?
786 .checked_mul(alignment)
787}
788
789#[cfg(test)]
790mod tests;