// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #ifndef RUNTIME_VM_INTRUSIVE_DLIST_H_ #define RUNTIME_VM_INTRUSIVE_DLIST_H_ #include "platform/assert.h" #include "platform/globals.h" namespace dart { // Abstraction for "intrusive" doubly-linked list in a type-safe way in C++. // // By "intrusive" we mean that a class Item containing various field members // has also next/previous pointers to the next/previous Item. // // To change a class Item to be part of such a doubly-linked list, one only // needs to inherit from IntrusiveDListEntry (in addition to the normal // base class). // // To change a class Item to be part of multiple doubly-linked lists, one can // inherit multiple times from IntrusiveDListEntry, each time with a // different value for . // // To insert an object into a list, one uses a IntrusiveDList. It // provides support for insertion/iteration/deletion. // // Usage example: // // class Base { // // }; // // class Item : public Base, // public IntrusiveDListEntry, // public IntrusiveDListEntry { // // }; // // Item a1, a2, a3; // // IntrusiveDList all; // all.Append(a2); // all.Prepend(a1); // all.Append(a3); // // IntrusiveDList idle, ready; // idle.Append(a1); // ready.Append(a2); // ready.Append(a3); // template class IntrusiveDList; template class IntrusiveDListEntry { public: IntrusiveDListEntry() {} ~IntrusiveDListEntry() { // Either this is an unlinked entry or a head/anchor. ASSERT((next_ == nullptr && prev_ == nullptr) || (next_ == this && prev_ == this)); } private: void MakeHead() { ASSERT(next_ == nullptr && prev_ == nullptr); next_ = prev_ = this; } // This uses the C++ compiler's ability to convert between classes inside a // (possibly multi-) inheritance hierarchy. // // The non-typesafe C equivalent of this is: // // ((uint8*)this) - offsetof(ContainerType, list_entry); // T* container() { return static_cast(this); } void Append(IntrusiveDListEntry* entry) { ASSERT(entry->next_ == nullptr && entry->prev_ == nullptr); entry->next_ = next_; entry->prev_ = this; next_ = entry; entry->next_->prev_ = entry; } void Prepend(IntrusiveDListEntry* entry) { ASSERT(entry->next_ == nullptr && entry->prev_ == nullptr); entry->next_ = this; entry->prev_ = prev_; prev_ = entry; entry->prev_->next_ = entry; } void Remove() { ASSERT(prev_->next_ == this); ASSERT(next_->prev_ == this); ASSERT(prev_ != this && next_ != this); prev_->next_ = next_; next_->prev_ = prev_; next_ = nullptr; prev_ = nullptr; } bool IsEmpty() const { bool result = next_ == this; ASSERT(result == (prev_ == this)); return result; } bool IsLinked() const { ASSERT((next_ == nullptr) == (prev_ == nullptr)); return next_ != nullptr; } IntrusiveDListEntry* Prev() const { return prev_; } IntrusiveDListEntry* Next() const { return next_; } friend class IntrusiveDList; IntrusiveDListEntry* next_ = nullptr; IntrusiveDListEntry* prev_ = nullptr; DISALLOW_COPY_AND_ASSIGN(IntrusiveDListEntry); }; template class IntrusiveDList { public: typedef IntrusiveDListEntry Entry; template class Iterator { public: Iterator(IntrusiveDList* head, IntrusiveDListEntry* entry) : head_(head), entry_(entry) {} inline ContainerType* operator->() const { return entry_->container(); } inline ContainerType* operator*() const { return entry_->container(); } inline bool operator==(const Iterator& other) const { return entry_ == other.entry_; } inline bool operator!=(const Iterator& other) const { return !(*this == other); } inline Iterator& operator++() { entry_ = entry_->Next(); return *this; } inline Iterator& operator--() { entry_ = entry_->Prev(); return *this; } private: friend IntrusiveDList; IntrusiveDList* head_; IntrusiveDListEntry* entry_; }; inline IntrusiveDList() { head_.MakeHead(); } inline void Append(T* a) { head_.Prepend(convert(a)); } inline void Prepend(T* a) { head_.Append(convert(a)); } // NOTE: This function only checks whether [a] is linked inside *a* // [IntrusiveDList]. inline bool IsInList(T* a) const { return convert(a)->IsLinked(); } inline void Remove(T* a) { convert(a)->Remove(); } inline bool IsEmpty() const { return head_.IsEmpty(); } inline T* First() const { ASSERT(!IsEmpty()); return head_.Next()->container(); } inline T* Last() const { ASSERT(!IsEmpty()); return head_.Prev()->container(); } inline T* RemoveFirst() { ASSERT(!IsEmpty()); auto entry = head_.Next(); T* container = entry->container(); entry->Remove(); return container; } inline T* RemoveLast() { ASSERT(!IsEmpty()); auto entry = head_.Prev(); T* container = entry->container(); entry->Remove(); return container; } inline Iterator Begin() { return Iterator(this, head_.Next()); } inline Iterator End() { return Iterator(this, &head_); } inline Iterator begin() { return Begin(); } inline Iterator end() { return End(); } inline Iterator Erase(const Iterator& iterator) { ASSERT(iterator.head_ == this); Iterator next(this, iterator.entry_->Next()); iterator.entry_->Remove(); return next; } bool ContainsForDebugging(const T* a) { for (auto entry : *this) { if (entry == a) return true; } return false; } void AppendList(IntrusiveDList* other) { if (other->IsEmpty()) return; auto other_first = other->head_.Next(); auto other_last = other->head_.Prev(); other->head_.next_ = &other->head_; other->head_.prev_ = &other->head_; auto prev = head_.prev_; prev->next_ = other_first; other_first->prev_ = prev; other_last->next_ = &head_; head_.prev_ = other_last; } private: Entry head_; Entry* convert(T* entry) const { return static_cast(entry); } }; } // namespace dart. #endif // RUNTIME_VM_INTRUSIVE_DLIST_H_