1use alloc::{boxed::Box, vec::Vec};
2use core::fmt;
3
4use miden_crypto::{Felt, Word};
5use miden_formatting::{
6 hex::ToHex,
7 prettier::{Document, PrettyPrint, const_text, nl, text},
8};
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12use super::{MastForestContributor, MastNodeErrorContext, MastNodeExt};
13use crate::mast::{
14 DecoratedOpLink, DecoratorId, DecoratorStore, MastForest, MastForestError, MastNodeId,
15};
16
17#[derive(Clone, Debug, PartialEq, Eq)]
29#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
30#[cfg_attr(all(feature = "arbitrary", test), miden_test_serde_macros::serde_test)]
31pub struct ExternalNode {
32 digest: Word,
33 decorator_store: DecoratorStore,
34}
35
36impl MastNodeErrorContext for ExternalNode {
37 fn decorators<'a>(
38 &'a self,
39 forest: &'a MastForest,
40 ) -> impl Iterator<Item = DecoratedOpLink> + 'a {
41 let before_enter = self.decorator_store.before_enter(forest);
43 let after_exit = self.decorator_store.after_exit(forest);
44
45 before_enter
47 .iter()
48 .map(|&deco_id| (0, deco_id))
49 .chain(after_exit.iter().map(|&deco_id| (1, deco_id)))
50 }
51}
52
53impl ExternalNode {
57 pub(super) fn to_display<'a>(&'a self, mast_forest: &'a MastForest) -> impl fmt::Display + 'a {
58 ExternalNodePrettyPrint { node: self, mast_forest }
59 }
60
61 pub(super) fn to_pretty_print<'a>(
62 &'a self,
63 mast_forest: &'a MastForest,
64 ) -> impl PrettyPrint + 'a {
65 ExternalNodePrettyPrint { node: self, mast_forest }
66 }
67}
68
69struct ExternalNodePrettyPrint<'a> {
70 node: &'a ExternalNode,
71 mast_forest: &'a MastForest,
72}
73
74impl ExternalNodePrettyPrint<'_> {
75 fn concatenate_decorators(
78 &self,
79 decorator_ids: &[DecoratorId],
80 prepend: Document,
81 append: Document,
82 ) -> Document {
83 let decorators = decorator_ids
84 .iter()
85 .map(|&decorator_id| self.mast_forest[decorator_id].render())
86 .reduce(|acc, doc| acc + const_text(" ") + doc)
87 .unwrap_or_default();
88
89 if decorators.is_empty() {
90 decorators
91 } else {
92 prepend + decorators + append
93 }
94 }
95
96 fn single_line_pre_decorators(&self) -> Document {
97 self.concatenate_decorators(
98 self.node.before_enter(self.mast_forest),
99 Document::Empty,
100 const_text(" "),
101 )
102 }
103
104 fn single_line_post_decorators(&self) -> Document {
105 self.concatenate_decorators(
106 self.node.after_exit(self.mast_forest),
107 const_text(" "),
108 Document::Empty,
109 )
110 }
111
112 fn multi_line_pre_decorators(&self) -> Document {
113 self.concatenate_decorators(self.node.before_enter(self.mast_forest), Document::Empty, nl())
114 }
115
116 fn multi_line_post_decorators(&self) -> Document {
117 self.concatenate_decorators(self.node.after_exit(self.mast_forest), nl(), Document::Empty)
118 }
119}
120
121impl crate::prettier::PrettyPrint for ExternalNodePrettyPrint<'_> {
122 fn render(&self) -> crate::prettier::Document {
123 let external = const_text("external")
124 + const_text(".")
125 + text(self.node.digest.as_bytes().to_hex_with_prefix());
126
127 let single_line = self.single_line_pre_decorators()
128 + external.clone()
129 + self.single_line_post_decorators();
130 let multi_line =
131 self.multi_line_pre_decorators() + external + self.multi_line_post_decorators();
132
133 single_line | multi_line
134 }
135}
136
137impl fmt::Display for ExternalNodePrettyPrint<'_> {
138 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139 use crate::prettier::PrettyPrint;
140 self.pretty_print(f)
141 }
142}
143
144impl MastNodeExt for ExternalNode {
148 fn digest(&self) -> Word {
154 self.digest
155 }
156
157 fn before_enter<'a>(&'a self, forest: &'a MastForest) -> &'a [DecoratorId] {
159 self.decorator_store.before_enter(forest)
160 }
161
162 fn after_exit<'a>(&'a self, forest: &'a MastForest) -> &'a [DecoratorId] {
164 self.decorator_store.after_exit(forest)
165 }
166
167 fn to_display<'a>(&'a self, mast_forest: &'a MastForest) -> Box<dyn fmt::Display + 'a> {
168 Box::new(ExternalNode::to_display(self, mast_forest))
169 }
170
171 fn to_pretty_print<'a>(&'a self, mast_forest: &'a MastForest) -> Box<dyn PrettyPrint + 'a> {
172 Box::new(ExternalNode::to_pretty_print(self, mast_forest))
173 }
174
175 fn has_children(&self) -> bool {
176 false
177 }
178
179 fn append_children_to(&self, _target: &mut Vec<MastNodeId>) {
180 }
182
183 fn for_each_child<F>(&self, _f: F)
184 where
185 F: FnMut(MastNodeId),
186 {
187 }
189
190 fn domain(&self) -> Felt {
191 panic!("Can't fetch domain for an `External` node.")
192 }
193
194 type Builder = ExternalNodeBuilder;
195
196 fn to_builder(self, forest: &MastForest) -> Self::Builder {
197 match self.decorator_store {
199 DecoratorStore::Owned { before_enter, after_exit, .. } => {
200 let mut builder = ExternalNodeBuilder::new(self.digest);
201 builder = builder.with_before_enter(before_enter).with_after_exit(after_exit);
202 builder
203 },
204 DecoratorStore::Linked { id } => {
205 let before_enter = forest.before_enter_decorators(id).to_vec();
207 let after_exit = forest.after_exit_decorators(id).to_vec();
208 let mut builder = ExternalNodeBuilder::new(self.digest);
209 builder = builder.with_before_enter(before_enter).with_after_exit(after_exit);
210 builder
211 },
212 }
213 }
214
215 #[cfg(debug_assertions)]
216 fn verify_node_in_forest(&self, forest: &MastForest) {
217 if let Some(id) = self.decorator_store.linked_id() {
218 let self_ptr = self as *const Self;
220 let forest_node = &forest.nodes[id];
221 let forest_node_ptr = match forest_node {
222 crate::mast::MastNode::External(external) => {
223 external as *const ExternalNode as *const ()
224 },
225 _ => panic!("Node type mismatch at {:?}", id),
226 };
227 let self_as_void = self_ptr as *const ();
228 debug_assert_eq!(
229 self_as_void, forest_node_ptr,
230 "Node pointer mismatch: expected node at {:?} to be self",
231 id
232 );
233 }
234 }
235}
236
237#[cfg(all(feature = "arbitrary", test))]
241impl proptest::prelude::Arbitrary for ExternalNode {
242 type Parameters = ();
243
244 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
245 use proptest::prelude::*;
246
247 use crate::Felt;
248
249 any::<[u64; 4]>()
251 .prop_map(|[a, b, c, d]| {
252 let word = Word::from([Felt::new(a), Felt::new(b), Felt::new(c), Felt::new(d)]);
253 ExternalNodeBuilder::new(word).build()
254 })
255 .no_shrink() .boxed()
257 }
258
259 type Strategy = proptest::prelude::BoxedStrategy<Self>;
260}
261
262#[derive(Debug)]
265pub struct ExternalNodeBuilder {
266 digest: Word,
267 before_enter: Vec<DecoratorId>,
268 after_exit: Vec<DecoratorId>,
269}
270
271impl ExternalNodeBuilder {
272 pub fn new(digest: Word) -> Self {
274 Self {
275 digest,
276 before_enter: Vec::new(),
277 after_exit: Vec::new(),
278 }
279 }
280
281 pub fn build(self) -> ExternalNode {
283 ExternalNode {
284 digest: self.digest,
285 decorator_store: DecoratorStore::new_owned_with_decorators(
286 self.before_enter,
287 self.after_exit,
288 ),
289 }
290 }
291}
292
293impl MastForestContributor for ExternalNodeBuilder {
294 fn add_to_forest(self, forest: &mut MastForest) -> Result<MastNodeId, MastForestError> {
295 let future_node_id = MastNodeId::new_unchecked(forest.nodes.len() as u32);
297
298 forest.register_node_decorators(future_node_id, &self.before_enter, &self.after_exit);
300
301 let node_id = forest
304 .nodes
305 .push(
306 ExternalNode {
307 digest: self.digest,
308 decorator_store: DecoratorStore::Linked { id: future_node_id },
309 }
310 .into(),
311 )
312 .map_err(|_| MastForestError::TooManyNodes)?;
313
314 Ok(node_id)
315 }
316
317 fn fingerprint_for_node(
318 &self,
319 forest: &MastForest,
320 _hash_by_node_id: &impl crate::LookupByIdx<MastNodeId, crate::mast::MastNodeFingerprint>,
321 ) -> Result<crate::mast::MastNodeFingerprint, MastForestError> {
322 crate::mast::node_fingerprint::fingerprint_from_parts(
325 forest,
326 _hash_by_node_id,
327 &self.before_enter,
328 &self.after_exit,
329 &[], self.digest, )
332 }
333
334 fn remap_children(
335 self,
336 _remapping: &impl crate::LookupByIdx<crate::mast::MastNodeId, crate::mast::MastNodeId>,
337 ) -> Self {
338 self
340 }
341
342 fn with_before_enter(mut self, decorators: impl Into<Vec<crate::mast::DecoratorId>>) -> Self {
343 self.before_enter = decorators.into();
344 self
345 }
346
347 fn with_after_exit(mut self, decorators: impl Into<Vec<crate::mast::DecoratorId>>) -> Self {
348 self.after_exit = decorators.into();
349 self
350 }
351
352 fn append_before_enter(
353 &mut self,
354 decorators: impl IntoIterator<Item = crate::mast::DecoratorId>,
355 ) {
356 self.before_enter.extend(decorators);
357 }
358
359 fn append_after_exit(
360 &mut self,
361 decorators: impl IntoIterator<Item = crate::mast::DecoratorId>,
362 ) {
363 self.after_exit.extend(decorators);
364 }
365
366 fn with_digest(mut self, digest: crate::Word) -> Self {
367 self.digest = digest;
368 self
369 }
370}
371
372impl ExternalNodeBuilder {
373 pub(in crate::mast) fn add_to_forest_relaxed(
386 self,
387 forest: &mut MastForest,
388 ) -> Result<MastNodeId, MastForestError> {
389 let future_node_id = MastNodeId::new_unchecked(forest.nodes.len() as u32);
390
391 forest.register_node_decorators(future_node_id, &self.before_enter, &self.after_exit);
393
394 let node_id = forest
397 .nodes
398 .push(
399 ExternalNode {
400 digest: self.digest,
401 decorator_store: DecoratorStore::Linked { id: future_node_id },
402 }
403 .into(),
404 )
405 .map_err(|_| MastForestError::TooManyNodes)?;
406
407 Ok(node_id)
408 }
409}
410
411#[cfg(any(test, feature = "arbitrary"))]
412impl proptest::prelude::Arbitrary for ExternalNodeBuilder {
413 type Parameters = ExternalNodeBuilderParams;
414 type Strategy = proptest::strategy::BoxedStrategy<Self>;
415
416 fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
417 use proptest::prelude::*;
418
419 (
420 any::<[u64; 4]>().prop_map(|[a, b, c, d]| {
421 miden_crypto::Word::new([
422 miden_crypto::Felt::new(a),
423 miden_crypto::Felt::new(b),
424 miden_crypto::Felt::new(c),
425 miden_crypto::Felt::new(d),
426 ])
427 }),
428 proptest::collection::vec(
429 super::arbitrary::decorator_id_strategy(params.max_decorator_id_u32),
430 0..=params.max_decorators,
431 ),
432 proptest::collection::vec(
433 super::arbitrary::decorator_id_strategy(params.max_decorator_id_u32),
434 0..=params.max_decorators,
435 ),
436 )
437 .prop_map(|(digest, before_enter, after_exit)| {
438 Self::new(digest).with_before_enter(before_enter).with_after_exit(after_exit)
439 })
440 .boxed()
441 }
442}
443
444#[cfg(any(test, feature = "arbitrary"))]
446#[derive(Clone, Debug)]
447pub struct ExternalNodeBuilderParams {
448 pub max_decorators: usize,
449 pub max_decorator_id_u32: u32,
450}
451
452#[cfg(any(test, feature = "arbitrary"))]
453impl Default for ExternalNodeBuilderParams {
454 fn default() -> Self {
455 Self {
456 max_decorators: 4,
457 max_decorator_id_u32: 10,
458 }
459 }
460}