tree_iterator.h 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  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. #ifndef SOURCE_OPT_TREE_ITERATOR_H_
  15. #define SOURCE_OPT_TREE_ITERATOR_H_
  16. #include <stack>
  17. #include <type_traits>
  18. #include <utility>
  19. namespace spvtools {
  20. namespace opt {
  21. // Helper class to iterate over a tree in a depth first order.
  22. // The class assumes the data structure is a tree, tree node type implements a
  23. // forward iterator.
  24. // At each step, the iterator holds the pointer to the current node and state of
  25. // the walk.
  26. // The state is recorded by stacking the iteration position of the node
  27. // children. To move to the next node, the iterator:
  28. // - Looks at the top of the stack;
  29. // - Sets the node behind the iterator as the current node;
  30. // - Increments the iterator if it has more children to visit, pops otherwise;
  31. // - If the current node has children, the children iterator is pushed into the
  32. // stack.
  33. template <typename NodeTy>
  34. class TreeDFIterator {
  35. static_assert(!std::is_pointer<NodeTy>::value &&
  36. !std::is_reference<NodeTy>::value,
  37. "NodeTy should be a class");
  38. // Type alias to keep track of the const qualifier.
  39. using NodeIterator =
  40. typename std::conditional<std::is_const<NodeTy>::value,
  41. typename NodeTy::const_iterator,
  42. typename NodeTy::iterator>::type;
  43. // Type alias to keep track of the const qualifier.
  44. using NodePtr = NodeTy*;
  45. public:
  46. // Standard iterator interface.
  47. using reference = NodeTy&;
  48. using value_type = NodeTy;
  49. explicit inline TreeDFIterator(NodePtr top_node) : current_(top_node) {
  50. if (current_ && current_->begin() != current_->end())
  51. parent_iterators_.emplace(make_pair(current_, current_->begin()));
  52. }
  53. // end() iterator.
  54. inline TreeDFIterator() : TreeDFIterator(nullptr) {}
  55. bool operator==(const TreeDFIterator& x) const {
  56. return current_ == x.current_;
  57. }
  58. bool operator!=(const TreeDFIterator& x) const { return !(*this == x); }
  59. reference operator*() const { return *current_; }
  60. NodePtr operator->() const { return current_; }
  61. TreeDFIterator& operator++() {
  62. MoveToNextNode();
  63. return *this;
  64. }
  65. TreeDFIterator operator++(int) {
  66. TreeDFIterator tmp = *this;
  67. ++*this;
  68. return tmp;
  69. }
  70. private:
  71. // Moves the iterator to the next node in the tree.
  72. // If we are at the end, do nothing, otherwise
  73. // if our current node has children, use the children iterator and push the
  74. // current node into the stack.
  75. // If we reach the end of the local iterator, pop it.
  76. inline void MoveToNextNode() {
  77. if (!current_) return;
  78. if (parent_iterators_.empty()) {
  79. current_ = nullptr;
  80. return;
  81. }
  82. std::pair<NodePtr, NodeIterator>& next_it = parent_iterators_.top();
  83. // Set the new node.
  84. current_ = *next_it.second;
  85. // Update the iterator for the next child.
  86. ++next_it.second;
  87. // If we finished with node, pop it.
  88. if (next_it.first->end() == next_it.second) parent_iterators_.pop();
  89. // If our current node is not a leaf, store the iteration state for later.
  90. if (current_->begin() != current_->end())
  91. parent_iterators_.emplace(make_pair(current_, current_->begin()));
  92. }
  93. // The current node of the tree.
  94. NodePtr current_;
  95. // State of the tree walk: each pair contains the parent node (which has been
  96. // already visited) and the iterator of the next children to visit.
  97. // When all the children has been visited, we pop the entry, get the next
  98. // child and push back the pair if the children iterator is not end().
  99. std::stack<std::pair<NodePtr, NodeIterator>> parent_iterators_;
  100. };
  101. // Helper class to iterate over a tree in a depth first post-order.
  102. // The class assumes the data structure is a tree, tree node type implements a
  103. // forward iterator.
  104. // At each step, the iterator holds the pointer to the current node and state of
  105. // the walk.
  106. // The state is recorded by stacking the iteration position of the node
  107. // children. To move to the next node, the iterator:
  108. // - Looks at the top of the stack;
  109. // - If the children iterator has reach the end, then the node become the
  110. // current one and we pop the stack;
  111. // - Otherwise, we save the child and increment the iterator;
  112. // - We walk the child sub-tree until we find a leaf, stacking all non-leaves
  113. // states (pair of node pointer and child iterator) as we walk it.
  114. template <typename NodeTy>
  115. class PostOrderTreeDFIterator {
  116. static_assert(!std::is_pointer<NodeTy>::value &&
  117. !std::is_reference<NodeTy>::value,
  118. "NodeTy should be a class");
  119. // Type alias to keep track of the const qualifier.
  120. using NodeIterator =
  121. typename std::conditional<std::is_const<NodeTy>::value,
  122. typename NodeTy::const_iterator,
  123. typename NodeTy::iterator>::type;
  124. // Type alias to keep track of the const qualifier.
  125. using NodePtr = NodeTy*;
  126. public:
  127. // Standard iterator interface.
  128. using reference = NodeTy&;
  129. using value_type = NodeTy;
  130. static inline PostOrderTreeDFIterator begin(NodePtr top_node) {
  131. return PostOrderTreeDFIterator(top_node);
  132. }
  133. static inline PostOrderTreeDFIterator end(NodePtr sentinel_node) {
  134. return PostOrderTreeDFIterator(sentinel_node, false);
  135. }
  136. bool operator==(const PostOrderTreeDFIterator& x) const {
  137. return current_ == x.current_;
  138. }
  139. bool operator!=(const PostOrderTreeDFIterator& x) const {
  140. return !(*this == x);
  141. }
  142. reference operator*() const { return *current_; }
  143. NodePtr operator->() const { return current_; }
  144. PostOrderTreeDFIterator& operator++() {
  145. MoveToNextNode();
  146. return *this;
  147. }
  148. PostOrderTreeDFIterator operator++(int) {
  149. PostOrderTreeDFIterator tmp = *this;
  150. ++*this;
  151. return tmp;
  152. }
  153. private:
  154. explicit inline PostOrderTreeDFIterator(NodePtr top_node)
  155. : current_(top_node) {
  156. if (current_) WalkToLeaf();
  157. }
  158. // Constructor for the "end()" iterator.
  159. // |end_sentinel| is the value that acts as end value (can be null). The bool
  160. // parameters is to distinguish from the start() Ctor.
  161. inline PostOrderTreeDFIterator(NodePtr sentinel_node, bool)
  162. : current_(sentinel_node) {}
  163. // Moves the iterator to the next node in the tree.
  164. // If we are at the end, do nothing, otherwise
  165. // if our current node has children, use the children iterator and push the
  166. // current node into the stack.
  167. // If we reach the end of the local iterator, pop it.
  168. inline void MoveToNextNode() {
  169. if (!current_) return;
  170. if (parent_iterators_.empty()) {
  171. current_ = nullptr;
  172. return;
  173. }
  174. std::pair<NodePtr, NodeIterator>& next_it = parent_iterators_.top();
  175. // If we visited all children, the current node is the top of the stack.
  176. if (next_it.second == next_it.first->end()) {
  177. // Set the new node.
  178. current_ = next_it.first;
  179. parent_iterators_.pop();
  180. return;
  181. }
  182. // We have more children to visit, set the current node to the first child
  183. // and dive to leaf.
  184. current_ = *next_it.second;
  185. // Update the iterator for the next child (avoid unneeded pop).
  186. ++next_it.second;
  187. WalkToLeaf();
  188. }
  189. // Moves the iterator to the next node in the tree.
  190. // If we are at the end, do nothing, otherwise
  191. // if our current node has children, use the children iterator and push the
  192. // current node into the stack.
  193. // If we reach the end of the local iterator, pop it.
  194. inline void WalkToLeaf() {
  195. while (current_->begin() != current_->end()) {
  196. NodeIterator next = ++current_->begin();
  197. parent_iterators_.emplace(make_pair(current_, next));
  198. // Set the first child as the new node.
  199. current_ = *current_->begin();
  200. }
  201. }
  202. // The current node of the tree.
  203. NodePtr current_;
  204. // State of the tree walk: each pair contains the parent node and the iterator
  205. // of the next children to visit.
  206. // When all the children has been visited, we pop the first entry and the
  207. // parent node become the current node.
  208. std::stack<std::pair<NodePtr, NodeIterator>> parent_iterators_;
  209. };
  210. } // namespace opt
  211. } // namespace spvtools
  212. #endif // SOURCE_OPT_TREE_ITERATOR_H_