/* * Copyright 2012 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SkTInternalLList_DEFINED #define SkTInternalLList_DEFINED #include "SkTypes.h" /** * Helper class to automatically initialize the doubly linked list created pointers. */ template <typename T> class SkPtrWrapper { public: SkPtrWrapper() : fPtr(nullptr) {} SkPtrWrapper& operator =(T* ptr) { fPtr = ptr; return *this; } operator T*() const { return fPtr; } T* operator->() { return fPtr; } private: T* fPtr; }; /** * This macro creates the member variables required by the SkTInternalLList class. It should be * placed in the private section of any class that will be stored in a double linked list. */ #define SK_DECLARE_INTERNAL_LLIST_INTERFACE(ClassName) \ friend class SkTInternalLList<ClassName>; \ /* back pointer to the owning list - for debugging */ \ SkDEBUGCODE(SkPtrWrapper<SkTInternalLList<ClassName> > fList;) \ SkPtrWrapper<ClassName> fPrev; \ SkPtrWrapper<ClassName> fNext /** * This class implements a templated internal doubly linked list data structure. */ template <class T> class SkTInternalLList : SkNoncopyable { public: SkTInternalLList() : fHead(nullptr) , fTail(nullptr) { } void reset() { fHead = nullptr; fTail = nullptr; } void remove(T* entry) { SkASSERT(fHead && fTail); SkASSERT(this->isInList(entry)); T* prev = entry->fPrev; T* next = entry->fNext; if (prev) { prev->fNext = next; } else { fHead = next; } if (next) { next->fPrev = prev; } else { fTail = prev; } entry->fPrev = nullptr; entry->fNext = nullptr; #ifdef SK_DEBUG entry->fList = nullptr; #endif } void addToHead(T* entry) { SkASSERT(nullptr == entry->fPrev && nullptr == entry->fNext); SkASSERT(nullptr == entry->fList); entry->fPrev = nullptr; entry->fNext = fHead; if (fHead) { fHead->fPrev = entry; } fHead = entry; if (nullptr == fTail) { fTail = entry; } #ifdef SK_DEBUG entry->fList = this; #endif } void addToTail(T* entry) { SkASSERT(nullptr == entry->fPrev && nullptr == entry->fNext); SkASSERT(nullptr == entry->fList); entry->fPrev = fTail; entry->fNext = nullptr; if (fTail) { fTail->fNext = entry; } fTail = entry; if (nullptr == fHead) { fHead = entry; } #ifdef SK_DEBUG entry->fList = this; #endif } /** * Inserts a new list entry before an existing list entry. The new entry must not already be * a member of this or any other list. If existingEntry is NULL then the new entry is added * at the tail. */ void addBefore(T* newEntry, T* existingEntry) { SkASSERT(newEntry); if (nullptr == existingEntry) { this->addToTail(newEntry); return; } SkASSERT(this->isInList(existingEntry)); newEntry->fNext = existingEntry; T* prev = existingEntry->fPrev; existingEntry->fPrev = newEntry; newEntry->fPrev = prev; if (nullptr == prev) { SkASSERT(fHead == existingEntry); fHead = newEntry; } else { prev->fNext = newEntry; } #ifdef SK_DEBUG newEntry->fList = this; #endif } /** * Inserts a new list entry after an existing list entry. The new entry must not already be * a member of this or any other list. If existingEntry is NULL then the new entry is added * at the head. */ void addAfter(T* newEntry, T* existingEntry) { SkASSERT(newEntry); if (nullptr == existingEntry) { this->addToHead(newEntry); return; } SkASSERT(this->isInList(existingEntry)); newEntry->fPrev = existingEntry; T* next = existingEntry->fNext; existingEntry->fNext = newEntry; newEntry->fNext = next; if (nullptr == next) { SkASSERT(fTail == existingEntry); fTail = newEntry; } else { next->fPrev = newEntry; } #ifdef SK_DEBUG newEntry->fList = this; #endif } void concat(SkTInternalLList&& list) { if (list.isEmpty()) { return; } list.fHead->fPrev = fTail; if (!fHead) { SkASSERT(!list.fHead->fPrev); fHead = list.fHead; } else { SkASSERT(fTail); fTail->fNext = list.fHead; } fTail = list.fTail; #ifdef SK_DEBUG for (T* node = list.fHead; node; node = node->fNext) { SkASSERT(node->fList == &list); node->fList = this; } #endif list.fHead = list.fTail = nullptr; } bool isEmpty() const { SkASSERT(SkToBool(fHead) == SkToBool(fTail)); return !fHead; } T* head() { return fHead; } T* tail() { return fTail; } class Iter { public: enum IterStart { kHead_IterStart, kTail_IterStart }; Iter() : fCurr(nullptr) {} Iter(const Iter& iter) : fCurr(iter.fCurr) {} Iter& operator= (const Iter& iter) { fCurr = iter.fCurr; return *this; } T* init(const SkTInternalLList& list, IterStart startLoc) { if (kHead_IterStart == startLoc) { fCurr = list.fHead; } else { SkASSERT(kTail_IterStart == startLoc); fCurr = list.fTail; } return fCurr; } T* get() { return fCurr; } /** * Return the next/previous element in the list or NULL if at the end. */ T* next() { if (nullptr == fCurr) { return nullptr; } fCurr = fCurr->fNext; return fCurr; } T* prev() { if (nullptr == fCurr) { return nullptr; } fCurr = fCurr->fPrev; return fCurr; } private: T* fCurr; }; #ifdef SK_DEBUG void validate() const { SkASSERT(!fHead == !fTail); Iter iter; for (T* item = iter.init(*this, Iter::kHead_IterStart); item; item = iter.next()) { SkASSERT(this->isInList(item)); if (nullptr == item->fPrev) { SkASSERT(fHead == item); } else { SkASSERT(item->fPrev->fNext == item); } if (nullptr == item->fNext) { SkASSERT(fTail == item); } else { SkASSERT(item->fNext->fPrev == item); } } } /** * Debugging-only method that uses the list back pointer to check if 'entry' is indeed in 'this' * list. */ bool isInList(const T* entry) const { return entry->fList == this; } /** * Debugging-only method that laboriously counts the list entries. */ int countEntries() const { int count = 0; for (T* entry = fHead; entry; entry = entry->fNext) { ++count; } return count; } #endif // SK_DEBUG private: T* fHead; T* fTail; typedef SkNoncopyable INHERITED; }; #endif