github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/ledger/complete/mtrie/flattener/iterator.go (about) 1 package flattener 2 3 import ( 4 "github.com/onflow/flow-go/ledger" 5 "github.com/onflow/flow-go/ledger/complete/mtrie/node" 6 ) 7 8 // NodeIterator is an iterator over the nodes in a trie. 9 // It guarantees a DESCENDANTS-FIRST-RELATIONSHIP in the sequence of nodes it generates: 10 // - Consider the sequence of nodes, in the order they are generated by NodeIterator. 11 // Let `node[k]` denote the node with index `k` in this sequence. 12 // - Descendents-First-Relationship means that for any `node[k]`, all its descendents 13 // have indices strictly smaller than k in the iterator's sequence. 14 // 15 // The Descendents-First-Relationship has the following important property: 16 // When re-building the Trie from the sequence of nodes, one can build the trie on the fly, 17 // as for each node, the children have been previously encountered. 18 type NodeIterator struct { 19 // NodeIterator internal implementation 20 // NodeIterator is initialized with an empty stack and the trie's root node assigned to 21 // unprocessedRoot. On the FIRST call of Next(), the NodeIterator will traverse the trie 22 // starting from the root in a depth-first search (DFS) order (prioritizing the left child 23 // over the right, when descending). It pushed the nodes it encounters on the stack, 24 // until it hits a leaf node (which then forms the head of the stack). 25 // On each subsequent call of Next(), the NodeIterator always pops the head of the stack. 26 // Let `n` be the node which was popped from the stack. 27 // If the `n` has a parent, denominated as `p`, the parent is now the head of the stack. 28 // Parent `p` can either have one or two children. 29 // * If the parent `p` has only one child, there is no other child of `p` to enumerate. 30 // * If the parent has two children: 31 // - if `n` is the left child, we haven't searched through `p.RightChild()` 32 // (as priority is given to the left child) 33 // => we search p.RightChild() and push nodes in DFS manner on the stack 34 // until we hit the first leaf node again 35 // By induction, it follows that the head of the stack always contains a node, 36 // whose descendents have already been recalled: 37 // * after the initial call of Next(), the head of the stack is a leaf node, which has 38 // no children, it can be recalled without restriction. 39 // * When popping node `n` from the stack, its parent `p` (if it exists) is now the 40 // head of the stack. 41 // - If `p` has only one child, this child must be `n`. 42 // Therefore, by recalling `n`, we have recalled all ancestors of `p`. 43 // - If `n` is the right child, we haven already searched through all of `p` 44 // descendents (as the `p.LeftChild` must have been searched before) 45 // Therefore, by recalling `n`, we have recalled all ancestors of `p` 46 // Hence, it follows that the head of the stack always satisfies the 47 // Descendents-First-Relationship. As we search the trie in DFS manner, each 48 // node of the trie is recalled (once). Hence, the algorithm iterates all 49 // nodes of the MTrie while guaranteeing Descendents-First-Relationship. 50 51 // unprocessedRoot contains the trie's root before the first call of Next(). 52 // Thereafter, it is set to nil (which prevents repeated iteration through the trie). 53 // This has the advantage, that we gracefully handle tries whose root node is nil. 54 unprocessedRoot *node.Node 55 stack []*node.Node 56 // visitedNodes are nodes that were visited and can be skipped during 57 // traversal through dig(). visitedNodes is used to optimize node traveral 58 // IN FOREST by skipping nodes in shared sub-tries after they are visited, 59 // because sub-tries are shared between tries (original MTrie before register updates 60 // and updated MTrie after register writes). 61 // NodeIterator only uses visitedNodes for read operation. 62 // No special handling is needed if visitedNodes is nil. 63 // WARNING: visitedNodes is not safe for concurrent use. 64 visitedNodes map[*node.Node]uint64 65 } 66 67 // NewNodeIterator returns a node NodeIterator, which iterates through all nodes 68 // comprising the MTrie. The Iterator guarantees a DESCENDANTS-FIRST-RELATIONSHIP in 69 // the sequence of nodes it generates: 70 // - Consider the sequence of nodes, in the order they are generated by NodeIterator. 71 // Let `node[k]` denote the node with index `k` in this sequence. 72 // - Descendents-First-Relationship means that for any `node[k]`, all its descendents 73 // have indices strictly smaller than k in the iterator's sequence. 74 // 75 // The Descendents-First-Relationship has the following important property: 76 // When re-building the Trie from the sequence of nodes, one can build the trie on the fly, 77 // as for each node, the children have been previously encountered. 78 // NodeIterator created by NewNodeIterator is safe for concurrent use 79 // because visitedNodes is always nil in this case. 80 func NewNodeIterator(n *node.Node) *NodeIterator { 81 return NewUniqueNodeIterator(n, nil) 82 } 83 84 // NewUniqueNodeIterator returns a node NodeIterator, which iterates through all unique nodes 85 // that weren't visited. This should be used for forest node iteration to avoid repeatedly 86 // traversing shared sub-tries. 87 // The Iterator guarantees a DESCENDANTS-FIRST-RELATIONSHIP in the sequence of nodes it generates: 88 // - Consider the sequence of nodes, in the order they are generated by NodeIterator. 89 // Let `node[k]` denote the node with index `k` in this sequence. 90 // - Descendents-First-Relationship means that for any `node[k]`, all its descendents 91 // have indices strictly smaller than k in the iterator's sequence. 92 // 93 // The Descendents-First-Relationship has the following important property: 94 // When re-building the Trie from the sequence of nodes, one can build the trie on the fly, 95 // as for each node, the children have been previously encountered. 96 // WARNING: visitedNodes is not safe for concurrent use. 97 func NewUniqueNodeIterator(n *node.Node, visitedNodes map[*node.Node]uint64) *NodeIterator { 98 // For a Trie with height H (measured by number of edges), the longest possible path 99 // contains H+1 vertices. 100 stackSize := ledger.NodeMaxHeight + 1 101 i := &NodeIterator{ 102 stack: make([]*node.Node, 0, stackSize), 103 visitedNodes: visitedNodes, 104 } 105 i.unprocessedRoot = n 106 return i 107 } 108 109 // Next moves the cursor to the next node in order for Value method to return it. 110 // It returns true if there is a next node to iterate, in which case the Value method will return the node. 111 // It returns false if there is no more node to iterate, in which case the Value method will return nil. 112 func (i *NodeIterator) Next() bool { 113 if i.unprocessedRoot != nil { 114 // initial call to Next() for a non-empty trie 115 i.dig(i.unprocessedRoot) 116 i.unprocessedRoot = nil 117 return len(i.stack) > 0 118 } 119 120 // the current head of the stack, `n`, has been recalled 121 // we now inspect n's parent and dig into the parent's right child, if necessary 122 n := i.pop() 123 if len(i.stack) > 0 { 124 // If there are more elements on the stack, the next element on the stack is n's parent `p`. 125 // Before we can recall `p`, we need to dig into the parent's right child, if we haven't 126 // done so already. As we decent into the left child with priority, the only case where 127 // we still need to dig into the right child is, if n is p's left child. 128 parent := i.peek() 129 if parent.LeftChild() == n { 130 i.dig(parent.RightChild()) 131 } 132 return true 133 } 134 return false // as len(i.stack) == 0, i.e. there are no more elements to recall 135 } 136 137 // Value will return the current node at the cursor. 138 // Note: you should call Next() before calling 139 func (i *NodeIterator) Value() *node.Node { 140 if len(i.stack) == 0 { 141 return nil 142 } 143 return i.peek() 144 } 145 146 func (i *NodeIterator) pop() *node.Node { 147 if len(i.stack) == 0 { 148 return nil 149 } 150 headIdx := len(i.stack) - 1 151 head := i.stack[headIdx] 152 i.stack = i.stack[:headIdx] 153 return head 154 } 155 156 func (i *NodeIterator) peek() *node.Node { 157 return i.stack[len(i.stack)-1] 158 } 159 160 func (i *NodeIterator) dig(n *node.Node) { 161 if n == nil { 162 return 163 } 164 if _, found := i.visitedNodes[n]; found { 165 return 166 } 167 for { 168 i.stack = append(i.stack, n) 169 if lChild := n.LeftChild(); lChild != nil { 170 if _, found := i.visitedNodes[lChild]; !found { 171 n = lChild 172 continue 173 } 174 } 175 if rChild := n.RightChild(); rChild != nil { 176 if _, found := i.visitedNodes[rChild]; !found { 177 n = rChild 178 continue 179 } 180 } 181 return 182 } 183 }