blob: 91c726474e1920c444ecbb09c1fa7b024a4cf5db [file] [log] [blame]
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_BASE_THREADED_LIST_H_
#define V8_BASE_THREADED_LIST_H_
#include <iterator>
#include "src/base/compiler-specific.h"
#include "src/base/macros.h"
namespace v8 {
namespace base {
template <typename T>
struct ThreadedListTraits {
static T** next(T* t) { return t->next(); }
static T** start(T** t) { return t; }
static T* const* start(T* const* t) { return t; }
};
// Represents a linked list that threads through the nodes in the linked list.
// Entries in the list are pointers to nodes. By default nodes need to have a
// T** next() method that returns the location where the next value is stored.
// The default can be overwritten by providing a ThreadedTraits class.
template <typename T, typename BaseClass,
typename TLTraits = ThreadedListTraits<T>>
class ThreadedListBase final : public BaseClass {
public:
ThreadedListBase() : head_(nullptr), tail_(&head_) {}
ThreadedListBase(const ThreadedListBase&) = delete;
ThreadedListBase& operator=(const ThreadedListBase&) = delete;
void Add(T* v) {
DCHECK_NULL(*tail_);
DCHECK_NULL(*TLTraits::next(v));
*tail_ = v;
tail_ = TLTraits::next(v);
// Check that only one element was added (and that hasn't created a cycle).
DCHECK_NULL(*tail_);
}
void AddFront(T* v) {
DCHECK_NULL(*TLTraits::next(v));
DCHECK_NOT_NULL(v);
T** const next = TLTraits::next(v);
*next = head_;
if (head_ == nullptr) tail_ = next;
head_ = v;
}
void DropHead() {
DCHECK_NOT_NULL(head_);
T* old_head = head_;
head_ = *TLTraits::next(head_);
if (head_ == nullptr) tail_ = &head_;
*TLTraits::next(old_head) = nullptr;
}
bool Contains(T* v) {
for (Iterator it = begin(); it != end(); ++it) {
if (*it == v) return true;
}
return false;
}
void Append(ThreadedListBase&& list) {
if (list.is_empty()) return;
*tail_ = list.head_;
tail_ = list.tail_;
list.Clear();
}
void Prepend(ThreadedListBase&& list) {
if (list.head_ == nullptr) return;
T* new_head = list.head_;
*list.tail_ = head_;
if (head_ == nullptr) {
tail_ = list.tail_;
}
head_ = new_head;
list.Clear();
}
void Clear() {
head_ = nullptr;
tail_ = &head_;
}
ThreadedListBase& operator=(ThreadedListBase&& other) V8_NOEXCEPT {
head_ = other.head_;
tail_ = other.head_ ? other.tail_ : &head_;
#ifdef DEBUG
other.Clear();
#endif
return *this;
}
ThreadedListBase(ThreadedListBase&& other) V8_NOEXCEPT
: head_(other.head_),
tail_(other.head_ ? other.tail_ : &head_) {
#ifdef DEBUG
other.Clear();
#endif
}
bool Remove(T* v) {
T* current = first();
if (current == v) {
DropHead();
return true;
}
while (current != nullptr) {
T* next = *TLTraits::next(current);
if (next == v) {
*TLTraits::next(current) = *TLTraits::next(next);
*TLTraits::next(next) = nullptr;
if (TLTraits::next(next) == tail_) {
tail_ = TLTraits::next(current);
}
return true;
}
current = next;
}
return false;
}
class Iterator final {
public:
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T*;
using reference = value_type;
using pointer = value_type*;
public:
Iterator& operator++() {
entry_ = TLTraits::next(*entry_);
return *this;
}
bool operator==(const Iterator& other) const {
return entry_ == other.entry_;
}
bool operator!=(const Iterator& other) const {
return entry_ != other.entry_;
}
T*& operator*() { return *entry_; }
T* operator->() { return *entry_; }
Iterator& operator=(T* entry) {
T* next = *TLTraits::next(*entry_);
*TLTraits::next(entry) = next;
*entry_ = entry;
return *this;
}
Iterator() : entry_(nullptr) {}
private:
explicit Iterator(T** entry) : entry_(entry) {}
T** entry_;
friend class ThreadedListBase;
};
class ConstIterator final {
public:
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T*;
using reference = const value_type;
using pointer = const value_type*;
public:
ConstIterator& operator++() {
entry_ = TLTraits::next(*entry_);
return *this;
}
bool operator==(const ConstIterator& other) const {
return entry_ == other.entry_;
}
bool operator!=(const ConstIterator& other) const {
return entry_ != other.entry_;
}
const T* operator*() const { return *entry_; }
private:
explicit ConstIterator(T* const* entry) : entry_(entry) {}
T* const* entry_;
friend class ThreadedListBase;
};
Iterator begin() { return Iterator(TLTraits::start(&head_)); }
Iterator end() { return Iterator(tail_); }
ConstIterator begin() const { return ConstIterator(TLTraits::start(&head_)); }
ConstIterator end() const { return ConstIterator(tail_); }
// Rewinds the list's tail to the reset point, i.e., cutting of the rest of
// the list, including the reset_point.
void Rewind(Iterator reset_point) {
tail_ = reset_point.entry_;
*tail_ = nullptr;
}
// Moves the tail of the from_list, starting at the from_location, to the end
// of this list.
void MoveTail(ThreadedListBase* from_list, Iterator from_location) {
if (from_list->end() != from_location) {
DCHECK_NULL(*tail_);
*tail_ = *from_location;
tail_ = from_list->tail_;
from_list->Rewind(from_location);
}
}
bool is_empty() const { return head_ == nullptr; }
T* first() const { return head_; }
// Slow. For testing purposes.
int LengthForTest() {
int result = 0;
for (Iterator t = begin(); t != end(); ++t) ++result;
return result;
}
T* AtForTest(int i) {
Iterator t = begin();
while (i-- > 0) ++t;
return *t;
}
bool Verify() {
T* last = this->first();
if (last == nullptr) {
CHECK_EQ(&head_, tail_);
} else {
while (*TLTraits::next(last) != nullptr) {
last = *TLTraits::next(last);
}
CHECK_EQ(TLTraits::next(last), tail_);
}
return true;
}
private:
T* head_;
T** tail_;
};
struct EmptyBase {};
template <typename T, typename TLTraits = ThreadedListTraits<T>>
using ThreadedList = ThreadedListBase<T, EmptyBase, TLTraits>;
} // namespace base
} // namespace v8
#endif // V8_BASE_THREADED_LIST_H_