| // Copyright (c) 2017 Google Inc. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #ifndef SOURCE_OPT_TREE_ITERATOR_H_ |
| #define SOURCE_OPT_TREE_ITERATOR_H_ |
| |
| #include <stack> |
| #include <type_traits> |
| #include <utility> |
| |
| namespace spvtools { |
| namespace opt { |
| |
| // Helper class to iterate over a tree in a depth first order. |
| // The class assumes the data structure is a tree, tree node type implements a |
| // forward iterator. |
| // At each step, the iterator holds the pointer to the current node and state of |
| // the walk. |
| // The state is recorded by stacking the iteration position of the node |
| // children. To move to the next node, the iterator: |
| // - Looks at the top of the stack; |
| // - Sets the node behind the iterator as the current node; |
| // - Increments the iterator if it has more children to visit, pops otherwise; |
| // - If the current node has children, the children iterator is pushed into the |
| // stack. |
| template <typename NodeTy> |
| class TreeDFIterator { |
| static_assert(!std::is_pointer<NodeTy>::value && |
| !std::is_reference<NodeTy>::value, |
| "NodeTy should be a class"); |
| // Type alias to keep track of the const qualifier. |
| using NodeIterator = |
| typename std::conditional<std::is_const<NodeTy>::value, |
| typename NodeTy::const_iterator, |
| typename NodeTy::iterator>::type; |
| |
| // Type alias to keep track of the const qualifier. |
| using NodePtr = NodeTy*; |
| |
| public: |
| // Standard iterator interface. |
| using reference = NodeTy&; |
| using value_type = NodeTy; |
| |
| explicit inline TreeDFIterator(NodePtr top_node) : current_(top_node) { |
| if (current_ && current_->begin() != current_->end()) |
| parent_iterators_.emplace(make_pair(current_, current_->begin())); |
| } |
| |
| // end() iterator. |
| inline TreeDFIterator() : TreeDFIterator(nullptr) {} |
| |
| bool operator==(const TreeDFIterator& x) const { |
| return current_ == x.current_; |
| } |
| |
| bool operator!=(const TreeDFIterator& x) const { return !(*this == x); } |
| |
| reference operator*() const { return *current_; } |
| |
| NodePtr operator->() const { return current_; } |
| |
| TreeDFIterator& operator++() { |
| MoveToNextNode(); |
| return *this; |
| } |
| |
| TreeDFIterator operator++(int) { |
| TreeDFIterator tmp = *this; |
| ++*this; |
| return tmp; |
| } |
| |
| private: |
| // Moves the iterator to the next node in the tree. |
| // If we are at the end, do nothing, otherwise |
| // if our current node has children, use the children iterator and push the |
| // current node into the stack. |
| // If we reach the end of the local iterator, pop it. |
| inline void MoveToNextNode() { |
| if (!current_) return; |
| if (parent_iterators_.empty()) { |
| current_ = nullptr; |
| return; |
| } |
| std::pair<NodePtr, NodeIterator>& next_it = parent_iterators_.top(); |
| // Set the new node. |
| current_ = *next_it.second; |
| // Update the iterator for the next child. |
| ++next_it.second; |
| // If we finished with node, pop it. |
| if (next_it.first->end() == next_it.second) parent_iterators_.pop(); |
| // If our current node is not a leaf, store the iteration state for later. |
| if (current_->begin() != current_->end()) |
| parent_iterators_.emplace(make_pair(current_, current_->begin())); |
| } |
| |
| // The current node of the tree. |
| NodePtr current_; |
| // State of the tree walk: each pair contains the parent node (which has been |
| // already visited) and the iterator of the next children to visit. |
| // When all the children has been visited, we pop the entry, get the next |
| // child and push back the pair if the children iterator is not end(). |
| std::stack<std::pair<NodePtr, NodeIterator>> parent_iterators_; |
| }; |
| |
| // Helper class to iterate over a tree in a depth first post-order. |
| // The class assumes the data structure is a tree, tree node type implements a |
| // forward iterator. |
| // At each step, the iterator holds the pointer to the current node and state of |
| // the walk. |
| // The state is recorded by stacking the iteration position of the node |
| // children. To move to the next node, the iterator: |
| // - Looks at the top of the stack; |
| // - If the children iterator has reach the end, then the node become the |
| // current one and we pop the stack; |
| // - Otherwise, we save the child and increment the iterator; |
| // - We walk the child sub-tree until we find a leaf, stacking all non-leaves |
| // states (pair of node pointer and child iterator) as we walk it. |
| template <typename NodeTy> |
| class PostOrderTreeDFIterator { |
| static_assert(!std::is_pointer<NodeTy>::value && |
| !std::is_reference<NodeTy>::value, |
| "NodeTy should be a class"); |
| // Type alias to keep track of the const qualifier. |
| using NodeIterator = |
| typename std::conditional<std::is_const<NodeTy>::value, |
| typename NodeTy::const_iterator, |
| typename NodeTy::iterator>::type; |
| |
| // Type alias to keep track of the const qualifier. |
| using NodePtr = NodeTy*; |
| |
| public: |
| // Standard iterator interface. |
| using reference = NodeTy&; |
| using value_type = NodeTy; |
| |
| static inline PostOrderTreeDFIterator begin(NodePtr top_node) { |
| return PostOrderTreeDFIterator(top_node); |
| } |
| |
| static inline PostOrderTreeDFIterator end(NodePtr sentinel_node) { |
| return PostOrderTreeDFIterator(sentinel_node, false); |
| } |
| |
| bool operator==(const PostOrderTreeDFIterator& x) const { |
| return current_ == x.current_; |
| } |
| |
| bool operator!=(const PostOrderTreeDFIterator& x) const { |
| return !(*this == x); |
| } |
| |
| reference operator*() const { return *current_; } |
| |
| NodePtr operator->() const { return current_; } |
| |
| PostOrderTreeDFIterator& operator++() { |
| MoveToNextNode(); |
| return *this; |
| } |
| |
| PostOrderTreeDFIterator operator++(int) { |
| PostOrderTreeDFIterator tmp = *this; |
| ++*this; |
| return tmp; |
| } |
| |
| private: |
| explicit inline PostOrderTreeDFIterator(NodePtr top_node) |
| : current_(top_node) { |
| if (current_) WalkToLeaf(); |
| } |
| |
| // Constructor for the "end()" iterator. |
| // |end_sentinel| is the value that acts as end value (can be null). The bool |
| // parameters is to distinguish from the start() Ctor. |
| inline PostOrderTreeDFIterator(NodePtr sentinel_node, bool) |
| : current_(sentinel_node) {} |
| |
| // Moves the iterator to the next node in the tree. |
| // If we are at the end, do nothing, otherwise |
| // if our current node has children, use the children iterator and push the |
| // current node into the stack. |
| // If we reach the end of the local iterator, pop it. |
| inline void MoveToNextNode() { |
| if (!current_) return; |
| if (parent_iterators_.empty()) { |
| current_ = nullptr; |
| return; |
| } |
| std::pair<NodePtr, NodeIterator>& next_it = parent_iterators_.top(); |
| // If we visited all children, the current node is the top of the stack. |
| if (next_it.second == next_it.first->end()) { |
| // Set the new node. |
| current_ = next_it.first; |
| parent_iterators_.pop(); |
| return; |
| } |
| // We have more children to visit, set the current node to the first child |
| // and dive to leaf. |
| current_ = *next_it.second; |
| // Update the iterator for the next child (avoid unneeded pop). |
| ++next_it.second; |
| WalkToLeaf(); |
| } |
| |
| // Moves the iterator to the next node in the tree. |
| // If we are at the end, do nothing, otherwise |
| // if our current node has children, use the children iterator and push the |
| // current node into the stack. |
| // If we reach the end of the local iterator, pop it. |
| inline void WalkToLeaf() { |
| while (current_->begin() != current_->end()) { |
| NodeIterator next = ++current_->begin(); |
| parent_iterators_.emplace(make_pair(current_, next)); |
| // Set the first child as the new node. |
| current_ = *current_->begin(); |
| } |
| } |
| |
| // The current node of the tree. |
| NodePtr current_; |
| // State of the tree walk: each pair contains the parent node and the iterator |
| // of the next children to visit. |
| // When all the children has been visited, we pop the first entry and the |
| // parent node become the current node. |
| std::stack<std::pair<NodePtr, NodeIterator>> parent_iterators_; |
| }; |
| |
| } // namespace opt |
| } // namespace spvtools |
| |
| #endif // SOURCE_OPT_TREE_ITERATOR_H_ |