367 lines
11 KiB
C++
367 lines
11 KiB
C++
// 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&&);
|
|
|
|
// Destroys 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.");
|
|
(void)sentinel_count;
|
|
|
|
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_
|