2017-12-04 14:36:05 +00:00
|
|
|
// 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 LIBSPIRV_OPT_TREE_ITERATOR_H_
|
|
|
|
#define LIBSPIRV_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_;
|
|
|
|
};
|
|
|
|
|
2017-12-21 14:47:25 +00:00
|
|
|
// 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_;
|
|
|
|
};
|
|
|
|
|
2017-12-04 14:36:05 +00:00
|
|
|
} // namespace opt
|
|
|
|
} // namespace spvtools
|
|
|
|
|
|
|
|
#endif // LIBSPIRV_OPT_TREE_ITERATOR_H_
|