miden_processor/host/
debug.rs1use alloc::{
2 string::{String, ToString},
3 vec::Vec,
4};
5use core::fmt;
6
7use miden_core::Felt;
8
9#[derive(Default)]
14pub struct StdoutWriter;
15
16impl fmt::Write for StdoutWriter {
17 fn write_str(&mut self, _s: &str) -> fmt::Result {
18 #[cfg(feature = "std")]
19 std::print!("{_s}");
20 Ok(())
21 }
22}
23
24pub fn write_stack<W: fmt::Write>(
36 writer: &mut W,
37 stack: &[Felt],
38 n: Option<usize>,
39 label: &str,
40 clk: impl fmt::Display,
41) -> fmt::Result {
42 if stack.is_empty() {
43 writeln!(writer, "{label} empty before step {clk}.")?;
44 return Ok(());
45 }
46
47 let num_items = n.unwrap_or(stack.len());
49
50 if num_items == 0 {
52 writeln!(writer, "{label} state in interval [0, 0) before step {clk}:")?;
53 return Ok(());
54 }
55
56 let is_partial = num_items < stack.len();
57 if is_partial {
58 writeln!(writer, "{label} state in interval [0, {}] before step {clk}:", num_items - 1)?
59 } else {
60 writeln!(writer, "{label} state before step {clk}:")?
61 }
62
63 let mut stack_items = Vec::new();
65 for (i, element) in stack.iter().enumerate().take(num_items) {
66 stack_items.push((i.to_string(), Some(element.to_string())));
67 }
68 for i in stack.len()..num_items {
70 stack_items.push((i.to_string(), None));
71 }
72
73 let remaining = if num_items < stack.len() {
75 Some(stack.len() - num_items)
76 } else {
77 None
78 };
79
80 write_interval(writer, stack_items, remaining)
81}
82
83pub fn write_interval<W: fmt::Write>(
90 writer: &mut W,
91 items: Vec<(String, Option<String>)>,
92 remaining: Option<usize>,
93) -> fmt::Result {
94 let max_addr_width = items.iter().map(|(addr, _)| addr.len()).max().unwrap_or(0);
96
97 let mut formatted_items: Vec<String> = items
99 .into_iter()
100 .map(|(addr, value_opt)| {
101 let value_string = format_value(value_opt);
102 format!("{addr:>max_addr_width$}: {value_string}")
103 })
104 .collect();
105
106 if let Some(count) = remaining {
108 formatted_items.push(format!("({count} more items)"));
109 }
110
111 if let Some((last, front)) = formatted_items.split_last() {
114 for item in front {
116 writeln!(writer, "├── {item}")?;
117 }
118 writeln!(writer, "└── {last}")?;
120 }
121
122 Ok(())
123}
124
125pub fn format_value<T: ToString>(value: Option<T>) -> String {
130 value.map(|v| v.to_string()).unwrap_or_else(|| "EMPTY".to_string())
131}
132
133#[cfg(test)]
137mod tests {
138 use alloc::{string::String, vec};
139
140 use miden_core::Felt;
141
142 use super::{format_value, write_interval, write_stack};
143
144 #[test]
145 fn write_stack_full_uses_tree_style() {
146 let mut out = String::new();
147 let stack = [Felt::new_unchecked(3), Felt::new_unchecked(2), Felt::new_unchecked(1)];
148 write_stack(&mut out, &stack, None, "Stack", 0u32).unwrap();
149 assert_eq!(out, "Stack state before step 0:\n├── 0: 3\n├── 1: 2\n└── 2: 1\n");
150 }
151
152 #[test]
153 fn write_stack_partial_shows_remaining() {
154 let mut out = String::new();
155 let stack = [Felt::new_unchecked(9), Felt::new_unchecked(8), Felt::new_unchecked(7)];
156 write_stack(&mut out, &stack, Some(2), "Stack", 4u32).unwrap();
157 assert_eq!(
158 out,
159 "Stack state in interval [0, 1] before step 4:\n├── 0: 9\n├── 1: 8\n└── (1 more items)\n"
160 );
161 }
162
163 #[test]
164 fn write_stack_zero_count_does_not_underflow() {
165 let mut out = String::new();
166 let stack = [Felt::new_unchecked(9), Felt::new_unchecked(8), Felt::new_unchecked(7)];
167 write_stack(&mut out, &stack, Some(0), "Stack", 4u32).unwrap();
168 assert_eq!(out, "Stack state in interval [0, 0) before step 4:\n");
169 }
170
171 #[test]
172 fn write_interval_marks_empty_slots() {
173 let mut out = String::new();
174 let items = vec![("0".into(), Some("9".into())), ("1".into(), None)];
175 write_interval(&mut out, items, None).unwrap();
176 assert_eq!(out, "├── 0: 9\n└── 1: EMPTY\n");
177 }
178
179 #[test]
180 fn format_value_uses_empty_for_none() {
181 assert_eq!(format_value::<&str>(None), "EMPTY");
182 assert_eq!(format_value(Some(5)), "5");
183 }
184}