blob: accb398ba3246f44558976ed219d52c0e9720725 [file] [log] [blame]
// 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_ILIST_H_
#define LIBSPIRV_OPT_ILIST_H_
#include <cassert>
#include <type_traits>
#include "ilist_node.h"
namespace spvtools {
namespace utils {
// An IntrusiveList is a generic implementation of a doubly-linked list. The
// intended convention for using this container is:
//
// class Node : public IntrusiveNodeBase<Node> {
// // Note that "Node", the class being defined is the template.
// // Must have a default constructor accessible to List.
// // Add whatever data is needed in the node
// };
//
// using List = IntrusiveList<Node>;
//
// You can also inherit from IntrusiveList instead of a typedef if you want to
// add more functionality.
//
// The condition on the template for IntrusiveNodeBase is there to add some type
// checking to the container. The compiler will still allow inserting elements
// of type IntrusiveNodeBase<Node>, but that would be an error. This assumption
// allows NextNode and PreviousNode to return pointers to Node, and casting will
// not be required by the user.
template <class NodeType>
class IntrusiveList {
public:
static_assert(
std::is_base_of<IntrusiveNodeBase<NodeType>, NodeType>::value,
"The type from the node must be derived from IntrusiveNodeBase, with "
"itself in the template.");
// Creates an empty list.
inline IntrusiveList();
// Moves the contents of the given list to the list being constructed.
IntrusiveList(IntrusiveList&&);
// Destorys the list. Note that the elements of the list will not be deleted,
// but they will be removed from the list.
~IntrusiveList();
// Moves all of the elements in the list on the RHS to the list on the LHS.
IntrusiveList& operator=(IntrusiveList&&);
// Basetype for iterators so an IntrusiveList can be traversed like STL
// containers.
template <class T>
class iterator_template {
public:
iterator_template(const iterator_template& i) : node_(i.node_) {}
iterator_template& operator++() {
node_ = node_->next_node_;
return *this;
}
iterator_template& operator--() {
node_ = node_->previous_node_;
return *this;
}
iterator_template& operator=(const iterator_template& i) {
node_ = i.node_;
return *this;
}
T& operator*() const { return *node_; }
T* operator->() const { return node_; }
friend inline bool operator==(const iterator_template& lhs,
const iterator_template& rhs) {
return lhs.node_ == rhs.node_;
}
friend inline bool operator!=(const iterator_template& lhs,
const iterator_template& rhs) {
return !(lhs == rhs);
}
private:
iterator_template() = delete;
inline iterator_template(T* node) { node_ = node; }
T* node_;
friend IntrusiveList;
};
using iterator = iterator_template<NodeType>;
using const_iterator = iterator_template<const NodeType>;
// Various types of iterators for the start (begin) and one past the end (end)
// of the list.
//
// Decrementing |end()| iterator will give and iterator pointing to the last
// element in the list, if one exists.
//
// Incrementing |end()| iterator will give |begin()|.
//
// Decrementing |begin()| will give |end()|.
//
// TODO: Not marking these functions as noexcept because Visual Studio 2013
// does not support it. When we no longer care about that compiler, we should
// mark these as noexcept.
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
const_iterator cbegin() const;
const_iterator cend() const;
// Appends |node| to the end of the list. If |node| is already in a list, it
// will be removed from that list first.
void push_back(NodeType* node);
private:
// Doing a deep copy of the list does not make sense if the list does not own
// the data. It is not clear who will own the newly created data. Making
// copies illegal for that reason.
IntrusiveList(const IntrusiveList&) = delete;
IntrusiveList& operator=(const IntrusiveList&) = delete;
// A special node used to represent both the start and end of the list,
// without being part of the list.
NodeType sentinel_;
};
// Implementation of IntrusiveList
template <class NodeType>
inline IntrusiveList<NodeType>::IntrusiveList() : sentinel_() {
sentinel_.next_node_ = &sentinel_;
sentinel_.previous_node_ = &sentinel_;
sentinel_.is_sentinel_ = true;
}
template <class NodeType>
IntrusiveList<NodeType>::IntrusiveList(IntrusiveList&& list) {
this->sentinel_ = list.sentinel_;
this->sentinel_.next_node_->previous_node_ = &this->sentinel_;
this->sentinel_.previous_node_->next_node_ = &this->sentinel_;
list.sentinel_.next_node_ = &list.sentinel_;
list.sentinel_.previous_node_ = &list.sentinel_;
}
template <class NodeType>
IntrusiveList<NodeType>::~IntrusiveList() {
for (auto i : *this) i.RemoveFromList();
}
template <class NodeType>
IntrusiveList<NodeType>& IntrusiveList<NodeType>::operator=(
IntrusiveList<NodeType>&& list) {
this->sentinel_ = list.sentinel_;
this->sentinel_.next_node_->previous_node_ = &this->sentinel_;
this->sentinel_.previous_node_->next_node_ = &this->sentinel_;
list.sentinel_.next_node_ = &list.sentinel_;
list.sentinel_.previous_node_ = &list.sentinel_;
return *this;
}
template <class NodeType>
inline typename IntrusiveList<NodeType>::iterator
IntrusiveList<NodeType>::begin() {
return iterator(sentinel_.next_node_);
}
template <class NodeType>
inline typename IntrusiveList<NodeType>::iterator
IntrusiveList<NodeType>::end() {
return iterator(&sentinel_);
}
template <class NodeType>
inline typename IntrusiveList<NodeType>::const_iterator
IntrusiveList<NodeType>::begin() const {
return const_iterator(sentinel_.next_node_);
}
template <class NodeType>
inline typename IntrusiveList<NodeType>::const_iterator
IntrusiveList<NodeType>::end() const {
return const_iterator(&sentinel_);
}
template <class NodeType>
inline typename IntrusiveList<NodeType>::const_iterator
IntrusiveList<NodeType>::cbegin() const {
return const_iterator(sentinel_.next_node_);
}
template <class NodeType>
inline typename IntrusiveList<NodeType>::const_iterator
IntrusiveList<NodeType>::cend() const {
return const_iterator(&sentinel_);
}
template <class NodeType>
void IntrusiveList<NodeType>::push_back(NodeType* node) {
node->InsertBefore(&sentinel_);
}
} // namespace utils
} // namespace spvtools
#endif // LIBSPIRV_OPT_ILIST_H_