1 // Copyright (c) 2017 Google Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef SOURCE_OPT_TREE_ITERATOR_H_ 16 #define SOURCE_OPT_TREE_ITERATOR_H_ 17 18 #include <stack> 19 #include <type_traits> 20 #include <utility> 21 22 namespace spvtools { 23 namespace opt { 24 25 // Helper class to iterate over a tree in a depth first order. 26 // The class assumes the data structure is a tree, tree node type implements a 27 // forward iterator. 28 // At each step, the iterator holds the pointer to the current node and state of 29 // the walk. 30 // The state is recorded by stacking the iteration position of the node 31 // children. To move to the next node, the iterator: 32 // - Looks at the top of the stack; 33 // - Sets the node behind the iterator as the current node; 34 // - Increments the iterator if it has more children to visit, pops otherwise; 35 // - If the current node has children, the children iterator is pushed into the 36 // stack. 37 template <typename NodeTy> 38 class TreeDFIterator { 39 static_assert(!std::is_pointer<NodeTy>::value && 40 !std::is_reference<NodeTy>::value, 41 "NodeTy should be a class"); 42 // Type alias to keep track of the const qualifier. 43 using NodeIterator = 44 typename std::conditional<std::is_const<NodeTy>::value, 45 typename NodeTy::const_iterator, 46 typename NodeTy::iterator>::type; 47 48 // Type alias to keep track of the const qualifier. 49 using NodePtr = NodeTy*; 50 51 public: 52 // Standard iterator interface. 53 using reference = NodeTy&; 54 using value_type = NodeTy; 55 TreeDFIterator(NodePtr top_node)56 explicit inline TreeDFIterator(NodePtr top_node) : current_(top_node) { 57 if (current_ && current_->begin() != current_->end()) 58 parent_iterators_.emplace(make_pair(current_, current_->begin())); 59 } 60 61 // end() iterator. TreeDFIterator()62 inline TreeDFIterator() : TreeDFIterator(nullptr) {} 63 64 bool operator==(const TreeDFIterator& x) const { 65 return current_ == x.current_; 66 } 67 68 bool operator!=(const TreeDFIterator& x) const { return !(*this == x); } 69 70 reference operator*() const { return *current_; } 71 72 NodePtr operator->() const { return current_; } 73 74 TreeDFIterator& operator++() { 75 MoveToNextNode(); 76 return *this; 77 } 78 79 TreeDFIterator operator++(int) { 80 TreeDFIterator tmp = *this; 81 ++*this; 82 return tmp; 83 } 84 85 private: 86 // Moves the iterator to the next node in the tree. 87 // If we are at the end, do nothing, otherwise 88 // if our current node has children, use the children iterator and push the 89 // current node into the stack. 90 // If we reach the end of the local iterator, pop it. MoveToNextNode()91 inline void MoveToNextNode() { 92 if (!current_) return; 93 if (parent_iterators_.empty()) { 94 current_ = nullptr; 95 return; 96 } 97 std::pair<NodePtr, NodeIterator>& next_it = parent_iterators_.top(); 98 // Set the new node. 99 current_ = *next_it.second; 100 // Update the iterator for the next child. 101 ++next_it.second; 102 // If we finished with node, pop it. 103 if (next_it.first->end() == next_it.second) parent_iterators_.pop(); 104 // If our current node is not a leaf, store the iteration state for later. 105 if (current_->begin() != current_->end()) 106 parent_iterators_.emplace(make_pair(current_, current_->begin())); 107 } 108 109 // The current node of the tree. 110 NodePtr current_; 111 // State of the tree walk: each pair contains the parent node (which has been 112 // already visited) and the iterator of the next children to visit. 113 // When all the children has been visited, we pop the entry, get the next 114 // child and push back the pair if the children iterator is not end(). 115 std::stack<std::pair<NodePtr, NodeIterator>> parent_iterators_; 116 }; 117 118 // Helper class to iterate over a tree in a depth first post-order. 119 // The class assumes the data structure is a tree, tree node type implements a 120 // forward iterator. 121 // At each step, the iterator holds the pointer to the current node and state of 122 // the walk. 123 // The state is recorded by stacking the iteration position of the node 124 // children. To move to the next node, the iterator: 125 // - Looks at the top of the stack; 126 // - If the children iterator has reach the end, then the node become the 127 // current one and we pop the stack; 128 // - Otherwise, we save the child and increment the iterator; 129 // - We walk the child sub-tree until we find a leaf, stacking all non-leaves 130 // states (pair of node pointer and child iterator) as we walk it. 131 template <typename NodeTy> 132 class PostOrderTreeDFIterator { 133 static_assert(!std::is_pointer<NodeTy>::value && 134 !std::is_reference<NodeTy>::value, 135 "NodeTy should be a class"); 136 // Type alias to keep track of the const qualifier. 137 using NodeIterator = 138 typename std::conditional<std::is_const<NodeTy>::value, 139 typename NodeTy::const_iterator, 140 typename NodeTy::iterator>::type; 141 142 // Type alias to keep track of the const qualifier. 143 using NodePtr = NodeTy*; 144 145 public: 146 // Standard iterator interface. 147 using reference = NodeTy&; 148 using value_type = NodeTy; 149 begin(NodePtr top_node)150 static inline PostOrderTreeDFIterator begin(NodePtr top_node) { 151 return PostOrderTreeDFIterator(top_node); 152 } 153 end(NodePtr sentinel_node)154 static inline PostOrderTreeDFIterator end(NodePtr sentinel_node) { 155 return PostOrderTreeDFIterator(sentinel_node, false); 156 } 157 158 bool operator==(const PostOrderTreeDFIterator& x) const { 159 return current_ == x.current_; 160 } 161 162 bool operator!=(const PostOrderTreeDFIterator& x) const { 163 return !(*this == x); 164 } 165 166 reference operator*() const { return *current_; } 167 168 NodePtr operator->() const { return current_; } 169 170 PostOrderTreeDFIterator& operator++() { 171 MoveToNextNode(); 172 return *this; 173 } 174 175 PostOrderTreeDFIterator operator++(int) { 176 PostOrderTreeDFIterator tmp = *this; 177 ++*this; 178 return tmp; 179 } 180 181 private: PostOrderTreeDFIterator(NodePtr top_node)182 explicit inline PostOrderTreeDFIterator(NodePtr top_node) 183 : current_(top_node) { 184 if (current_) WalkToLeaf(); 185 } 186 187 // Constructor for the "end()" iterator. 188 // |end_sentinel| is the value that acts as end value (can be null). The bool 189 // parameters is to distinguish from the start() Ctor. PostOrderTreeDFIterator(NodePtr sentinel_node,bool)190 inline PostOrderTreeDFIterator(NodePtr sentinel_node, bool) 191 : current_(sentinel_node) {} 192 193 // Moves the iterator to the next node in the tree. 194 // If we are at the end, do nothing, otherwise 195 // if our current node has children, use the children iterator and push the 196 // current node into the stack. 197 // If we reach the end of the local iterator, pop it. MoveToNextNode()198 inline void MoveToNextNode() { 199 if (!current_) return; 200 if (parent_iterators_.empty()) { 201 current_ = nullptr; 202 return; 203 } 204 std::pair<NodePtr, NodeIterator>& next_it = parent_iterators_.top(); 205 // If we visited all children, the current node is the top of the stack. 206 if (next_it.second == next_it.first->end()) { 207 // Set the new node. 208 current_ = next_it.first; 209 parent_iterators_.pop(); 210 return; 211 } 212 // We have more children to visit, set the current node to the first child 213 // and dive to leaf. 214 current_ = *next_it.second; 215 // Update the iterator for the next child (avoid unneeded pop). 216 ++next_it.second; 217 WalkToLeaf(); 218 } 219 220 // Moves the iterator to the next node in the tree. 221 // If we are at the end, do nothing, otherwise 222 // if our current node has children, use the children iterator and push the 223 // current node into the stack. 224 // If we reach the end of the local iterator, pop it. WalkToLeaf()225 inline void WalkToLeaf() { 226 while (current_->begin() != current_->end()) { 227 NodeIterator next = ++current_->begin(); 228 parent_iterators_.emplace(make_pair(current_, next)); 229 // Set the first child as the new node. 230 current_ = *current_->begin(); 231 } 232 } 233 234 // The current node of the tree. 235 NodePtr current_; 236 // State of the tree walk: each pair contains the parent node and the iterator 237 // of the next children to visit. 238 // When all the children has been visited, we pop the first entry and the 239 // parent node become the current node. 240 std::stack<std::pair<NodePtr, NodeIterator>> parent_iterators_; 241 }; 242 243 } // namespace opt 244 } // namespace spvtools 245 246 #endif // SOURCE_OPT_TREE_ITERATOR_H_ 247