| // 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_ |