blob: 9837b09b32b4eca9ab40105a07f26af2d61bcfef [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 SOURCE_UTIL_ILIST_H_
#define SOURCE_UTIL_ILIST_H_
#include <cassert>
#include <memory>
#include <type_traits>
#include <vector>
#include "source/util/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.
virtual ~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);
}
// Moves the nodes in |list| to the list that |this| points to. The
// positions of the nodes will be immediately before the element pointed to
// by the iterator. The return value will be an iterator pointing to the
// first of the newly inserted elements.
iterator_template MoveBefore(IntrusiveList* list) {
if (list->empty()) return *this;
NodeType* first_node = list->sentinel_.next_node_;
NodeType* last_node = list->sentinel_.previous_node_;
this->node_->previous_node_->next_node_ = first_node;
first_node->previous_node_ = this->node_->previous_node_;
last_node->next_node_ = this->node_;
this->node_->previous_node_ = last_node;
list->sentinel_.next_node_ = &list->sentinel_;
list->sentinel_.previous_node_ = &list->sentinel_;
return iterator(first_node);
}
// Define standard iterator types needs so this class can be
// used with <algorithms>.
using iterator_category = std::bidirectional_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using size_type = size_t;
protected:
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);
// Returns true if the list is empty.
bool empty() const;
// Makes the current list empty.
inline void clear();
// Returns references to the first or last element in the list. It is an
// error to call these functions on an empty list.
NodeType& front();
NodeType& back();
const NodeType& front() const;
const NodeType& back() const;
// Transfers [|first|, |last|) from |other| into the list at |where|.
//
// If |other| is |this|, no change is made.
void Splice(iterator where, IntrusiveList<NodeType>* other, iterator first,
iterator last);
protected:
// 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;
// This function will assert if it finds the list containing |node| is not in
// a valid state.
static void Check(NodeType* node);
// 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) : sentinel_() {
sentinel_.next_node_ = &sentinel_;
sentinel_.previous_node_ = &sentinel_;
sentinel_.is_sentinel_ = true;
list.sentinel_.ReplaceWith(&sentinel_);
}
template <class NodeType>
IntrusiveList<NodeType>::~IntrusiveList() {
clear();
}
template <class NodeType>
IntrusiveList<NodeType>& IntrusiveList<NodeType>::operator=(
IntrusiveList<NodeType>&& list) {
list.sentinel_.ReplaceWith(&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_);
}
template <class NodeType>
bool IntrusiveList<NodeType>::empty() const {
return sentinel_.NextNode() == nullptr;
}
template <class NodeType>
void IntrusiveList<NodeType>::clear() {
while (!empty()) {
front().RemoveFromList();
}
}
template <class NodeType>
NodeType& IntrusiveList<NodeType>::front() {
NodeType* node = sentinel_.NextNode();
assert(node != nullptr && "Can't get the front of an empty list.");
return *node;
}
template <class NodeType>
NodeType& IntrusiveList<NodeType>::back() {
NodeType* node = sentinel_.PreviousNode();
assert(node != nullptr && "Can't get the back of an empty list.");
return *node;
}
template <class NodeType>
const NodeType& IntrusiveList<NodeType>::front() const {
NodeType* node = sentinel_.NextNode();
assert(node != nullptr && "Can't get the front of an empty list.");
return *node;
}
template <class NodeType>
const NodeType& IntrusiveList<NodeType>::back() const {
NodeType* node = sentinel_.PreviousNode();
assert(node != nullptr && "Can't get the back of an empty list.");
return *node;
}
template <class NodeType>
void IntrusiveList<NodeType>::Splice(iterator where,
IntrusiveList<NodeType>* other,
iterator first, iterator last) {
if (first == last) return;
if (other == this) return;
NodeType* first_prev = first.node_->previous_node_;
NodeType* where_next = where.node_->next_node_;
// Attach first.
where.node_->next_node_ = first.node_;
first.node_->previous_node_ = where.node_;
// Attach last.
where_next->previous_node_ = last.node_->previous_node_;
last.node_->previous_node_->next_node_ = where_next;
// Fixup other.
first_prev->next_node_ = last.node_;
last.node_->previous_node_ = first_prev;
}
template <class NodeType>
void IntrusiveList<NodeType>::Check(NodeType* start) {
int sentinel_count = 0;
NodeType* p = start;
do {
assert(p != nullptr);
assert(p->next_node_->previous_node_ == p);
assert(p->previous_node_->next_node_ == p);
if (p->is_sentinel_) sentinel_count++;
p = p->next_node_;
} while (p != start);
assert(sentinel_count == 1 && "List should have exactly 1 sentinel node.");
p = start;
do {
assert(p != nullptr);
assert(p->previous_node_->next_node_ == p);
assert(p->next_node_->previous_node_ == p);
if (p->is_sentinel_) sentinel_count++;
p = p->previous_node_;
} while (p != start);
}
} // namespace utils
} // namespace spvtools
#endif // SOURCE_UTIL_ILIST_H_