'<(skia_include_path)/core/SkTDArray.h',
'<(skia_include_path)/core/SkTDStack.h',
'<(skia_include_path)/core/SkTDict.h',
+ '<(skia_include_path)/core/SkTDLinkedList.h',
'<(skia_include_path)/core/SkTRegistry.h',
'<(skia_include_path)/core/SkTScopedPtr.h',
'<(skia_include_path)/core/SkTSearch.h',
'../tests/SrcOverTest.cpp',
'../tests/StreamTest.cpp',
'../tests/StringTest.cpp',
+ '../tests/TDLinkedListTest.cpp',
'../tests/Test.cpp',
'../tests/Test.h',
'../tests/TestSize.cpp',
--- /dev/null
+/*
+ * 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 SkTDLinkedList_DEFINED
+#define SkTDLinkedList_DEFINED
+
+#include "SkTypes.h"
+
+/**
+ * Helper class to automatically initialize the doubly linked list
+ * created pointers.
+ */
+template <typename T> class SkPtrWrapper {
+ public:
+ SkPtrWrapper() : fPtr(NULL) {}
+ 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 SkTDLinkedList class. It should be placed in the private section
+ * of any class that will be stored in a double linked list.
+ */
+#define SK_DEFINE_DLINKEDLIST_INTERFACE(ClassName) \
+ friend class SkTDLinkedList<ClassName>; \
+ /* back pointer to the owning list - for debugging */ \
+ SkDEBUGCODE(SkPtrWrapper<SkTDLinkedList<ClassName> > fList;)\
+ SkPtrWrapper<ClassName> fPrev; \
+ SkPtrWrapper<ClassName> fNext;
+
+/**
+ * This class implements a templated internal doubly linked list data structure.
+ */
+template <class T> class SkTDLinkedList : public SkNoncopyable {
+public:
+ SkTDLinkedList()
+ : fHead(NULL)
+ , fTail(NULL) {
+ }
+
+ void remove(T* entry) {
+ SkASSERT(this->isInList(entry));
+
+ T* prev = entry->fPrev;
+ T* next = entry->fNext;
+
+ if (NULL != prev) {
+ prev->fNext = next;
+ } else {
+ fHead = next;
+ }
+ if (NULL != next) {
+ next->fPrev = prev;
+ } else {
+ fTail = prev;
+ }
+
+ entry->fPrev = NULL;
+ entry->fNext = NULL;
+
+#if SK_DEBUG
+ entry->fList = NULL;
+#endif
+ }
+
+ void addToHead(T* entry) {
+ SkASSERT(NULL == entry->fPrev && NULL == entry->fNext);
+ SkASSERT(NULL == entry->fList);
+
+ entry->fPrev = NULL;
+ entry->fNext = fHead;
+ if (NULL != fHead) {
+ fHead->fPrev = entry;
+ }
+ fHead = entry;
+ if (NULL == fTail) {
+ fTail = entry;
+ }
+
+#if SK_DEBUG
+ entry->fList = this;
+#endif
+ }
+
+ bool isEmpty() const {
+ return NULL == fHead && NULL == fTail;
+ }
+
+ class Iter {
+ public:
+ enum IterStart {
+ kHead_IterStart,
+ kTail_IterStart
+ };
+
+ Iter() : fCur(NULL) {}
+
+ T* init(SkTDLinkedList& list, IterStart startLoc) {
+ if (kHead_IterStart == startLoc) {
+ fCur = list.fHead;
+ } else {
+ SkASSERT(kTail_IterStart == startLoc);
+ fCur = list.fTail;
+ }
+
+ return fCur;
+ }
+
+ /**
+ * Return the next/previous element in the list or NULL if at the end.
+ */
+ T* next() {
+ if (NULL == fCur) {
+ return NULL;
+ }
+
+ fCur = fCur->fNext;
+ return fCur;
+ }
+
+ T* prev() {
+ if (NULL == fCur) {
+ return NULL;
+ }
+
+ fCur = fCur->fPrev;
+ return fCur;
+ }
+
+ private:
+ T* fCur;
+ };
+
+#if SK_DEBUG
+ void validate() const {
+ GrAssert(!fHead == !fTail);
+ }
+
+ /**
+ * 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; NULL != entry; entry = entry->fNext) {
+ ++count;
+ }
+ return count;
+ }
+#endif // SK_DEBUG
+
+private:
+ T* fHead;
+ T* fTail;
+
+ typedef SkNoncopyable INHERITED;
+};
+
+#endif // SkTDLinkedList_DEFINED
GrResourceEntry::GrResourceEntry(const GrResourceKey& key, GrResource* resource)
: fKey(key), fResource(resource) {
fLockCount = 0;
- fPrev = fNext = NULL;
// we assume ownership of the resource, and will unref it when we die
GrAssert(resource);
fPurging = true;
bool withinBudget = false;
do {
- GrResourceEntry* entry = fList.fTail;
+ SkTDLinkedList<GrResourceEntry>::Iter iter;
+
+ // Note: the following code relies on the fact that the
+ // doubly linked list doesn't invalidate its data/pointers
+ // outside of the specific area where a deletion occurs (e.g.,
+ // in internalDetach)
+ GrResourceEntry* entry = iter.init(fList,
+ SkTDLinkedList<GrResourceEntry>::Iter::kTail_IterStart);
+
while (entry && fUnlockedEntryCount) {
GrAutoResourceCacheValidate atcv(this);
if (fEntryCount <= fMaxCount && fEntryBytes <= fMaxBytes) {
break;
}
- GrResourceEntry* prev = entry->fPrev;
+ GrResourceEntry* prev = iter.prev();
if (!entry->isLocked()) {
// remove from our cache
fCache.remove(entry->fKey, entry);
#if GR_DEBUG
GrAssert(fExclusiveList.countEntries() == fClientDetachedCount);
- GrAssert(fExclusiveList.countBytes() == fClientDetachedBytes);
+ GrAssert(countBytes(fExclusiveList) == fClientDetachedBytes);
GrAssert(!fUnlockedEntryCount);
if (!fCache.count()) {
// Items may have been detached from the cache (such as the backing
///////////////////////////////////////////////////////////////////////////////
#if GR_DEBUG
-static int countMatches(const GrResourceEntry* head, const GrResourceEntry* target) {
- const GrResourceEntry* entry = head;
- int count = 0;
- while (entry) {
- if (target == entry) {
- count += 1;
- }
- entry = entry->next();
+size_t GrResourceCache::countBytes(const SkTDLinkedList<GrResourceEntry>& list) {
+ size_t bytes = 0;
+
+ SkTDLinkedList<GrResourceEntry>::Iter iter;
+
+ const GrResourceEntry* entry = iter.init(
+ const_cast<SkTDLinkedList<GrResourceEntry>&>(list),
+ SkTDLinkedList<GrResourceEntry>::Iter::kTail_IterStart);
+
+ for ( ; NULL != entry; entry = iter.prev()) {
+ bytes += entry->resource()->sizeInBytes();
}
- return count;
+ return bytes;
}
static bool both_zero_or_nonzero(int count, size_t bytes) {
}
void GrResourceCache::validate() const {
- GrAssert(!fList.fHead == !fList.fTail);
+ fList.validate();
+ fExclusiveList.validate();
GrAssert(both_zero_or_nonzero(fEntryCount, fEntryBytes));
GrAssert(both_zero_or_nonzero(fClientDetachedCount, fClientDetachedBytes));
GrAssert(fClientDetachedBytes <= fEntryBytes);
fCache.validate();
- GrResourceEntry* entry = fList.fHead;
int count = 0;
int unlockCount = 0;
- while (entry) {
+
+ SkTDLinkedList<GrResourceEntry>::Iter iter;
+
+ const GrResourceEntry* entry = iter.init(
+ const_cast<SkTDLinkedList<GrResourceEntry>&>(fList),
+ SkTDLinkedList<GrResourceEntry>::Iter::kHead_IterStart);
+
+ for ( ; NULL != entry; entry = iter.next()) {
entry->validate();
GrAssert(fCache.find(entry->key()));
count += 1;
if (!entry->isLocked()) {
unlockCount += 1;
}
- entry = entry->fNext;
}
GrAssert(count == fEntryCount - fClientDetachedCount);
- size_t bytes = fList.countBytes();
+ size_t bytes = countBytes(fList);
GrAssert(bytes == fEntryBytes - fClientDetachedBytes);
- bytes = fExclusiveList.countBytes();
+ bytes = countBytes(fExclusiveList);
GrAssert(bytes == fClientDetachedBytes);
GrAssert(unlockCount == fUnlockedEntryCount);
GrAssert(fList.countEntries() == fEntryCount - fClientDetachedCount);
GrAssert(fExclusiveList.countEntries() == fClientDetachedCount);
-
- for (int i = 0; i < count; i++) {
- int matches = countMatches(fList.fHead, fCache.getArray()[i]);
- GrAssert(1 == matches);
- }
}
void GrResourceCache::printStats() const {
#endif
///////////////////////////////////////////////////////////////////////////////
-void GrDLinkedList::remove(GrResourceEntry* entry) {
- GrAssert(this->isInList(entry));
-
- GrResourceEntry* prev = entry->fPrev;
- GrResourceEntry* next = entry->fNext;
-
- if (prev) {
- prev->fNext = next;
- } else {
- fHead = next;
- }
- if (next) {
- next->fPrev = prev;
- } else {
- fTail = prev;
- }
-
- entry->fPrev = NULL;
- entry->fNext = NULL;
-}
-
-void GrDLinkedList::addToHead(GrResourceEntry* entry) {
- GrAssert(NULL == entry->fPrev && NULL == entry->fNext);
-
- entry->fPrev = NULL;
- entry->fNext = fHead;
- if (fHead) {
- fHead->fPrev = entry;
- }
- fHead = entry;
- if (NULL == fTail) {
- fTail = entry;
- }
-}
-
-#if GR_DEBUG
-
-bool GrDLinkedList::isInList(const GrResourceEntry* entry) const {
-
- for (GrResourceEntry* cur = fHead; NULL != cur; cur = cur->fNext) {
- if (entry == cur) {
- return true;
- }
- }
-
- return false;
-}
-
-int GrDLinkedList::countEntries() const {
- int count = 0;
- for (GrResourceEntry* entry = fTail; NULL != entry; entry = entry->fPrev) {
- ++count;
- }
- return count;
-}
-
-size_t GrDLinkedList::countBytes() const {
- size_t bytes = 0;
- for (GrResourceEntry* entry = fTail; NULL != entry; entry = entry->fPrev) {
- bytes += entry->resource()->sizeInBytes();
- }
- return bytes;
-}
-
-#endif // GR_DEBUG
#include "GrTypes.h"
#include "GrTHashCache.h"
+#include "SkTDLinkedList.h"
class GrResource;
GrResource* resource() const { return fResource; }
const GrResourceKey& key() const { return fKey; }
-#if GR_DEBUG
- GrResourceEntry* next() const { return fNext; }
- GrResourceEntry* prev() const { return fPrev; }
-#endif
-
#if GR_DEBUG
void validate() const;
#else
int fLockCount;
// we're a dlinklist
- GrResourceEntry* fPrev;
- GrResourceEntry* fNext;
+ SK_DEFINE_DLINKEDLIST_INTERFACE(GrResourceEntry);
friend class GrResourceCache;
friend class GrDLinkedList;
#include "GrTHashCache.h"
-class GrDLinkedList {
-public:
- GrDLinkedList()
- : fHead(NULL)
- , fTail(NULL) {
- }
-
- void remove(GrResourceEntry* entry);
- void addToHead(GrResourceEntry* entry);
-
-#ifdef GR_DEBUG
- bool isInList(const GrResourceEntry* entry) const;
- bool isEmpty() const { return NULL == fHead && NULL == fTail; }
- int countEntries() const;
- size_t countBytes() const;
-#endif
-
- GrResourceEntry* fHead;
- GrResourceEntry* fTail;
-};
-
/**
* Cache of GrResource objects.
*
GrTHashTable<GrResourceEntry, Key, 8> fCache;
// manage the dlink list
- GrDLinkedList fList;
+ SkTDLinkedList<GrResourceEntry> fList;
#if GR_DEBUG
// These objects cannot be returned by a search
- GrDLinkedList fExclusiveList;
+ SkTDLinkedList<GrResourceEntry> fExclusiveList;
#endif
// our budget, used in purgeAsNeeded()
bool lock,
bool clientReattach);
+#if GR_DEBUG
+ static size_t GrResourceCache::countBytes(
+ const SkTDLinkedList<GrResourceEntry>& list);
+#endif
};
///////////////////////////////////////////////////////////////////////////////
--- /dev/null
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+#include "SkTDLinkedList.h"
+
+class ListElement {
+public:
+ ListElement(int id) : fID(id) {
+ }
+
+ int fID;
+
+private:
+ SK_DEFINE_DLINKEDLIST_INTERFACE(ListElement);
+};
+
+static void CheckList(const SkTDLinkedList<ListElement>& list,
+ skiatest::Reporter* reporter,
+ bool empty,
+ int numElements,
+ bool in0, bool in1, bool in2, bool in3,
+ ListElement elements[4]) {
+
+ REPORTER_ASSERT(reporter, empty == list.isEmpty());
+#if SK_DEBUG
+ REPORTER_ASSERT(reporter, numElements == list.countEntries());
+ REPORTER_ASSERT(reporter, in0 == list.isInList(&elements[0]));
+ REPORTER_ASSERT(reporter, in1 == list.isInList(&elements[1]));
+ REPORTER_ASSERT(reporter, in2 == list.isInList(&elements[2]));
+ REPORTER_ASSERT(reporter, in3 == list.isInList(&elements[3]));
+#endif
+}
+
+static void TestTDLinkedList(skiatest::Reporter* reporter) {
+ SkTDLinkedList<ListElement> list;
+ ListElement elements[4] = {
+ ListElement(0),
+ ListElement(1),
+ ListElement(2),
+ ListElement(3),
+ };
+
+ // list should be empty to start with
+ CheckList(list, reporter, true, 0, false, false, false, false, elements);
+
+ list.addToHead(&elements[0]);
+
+ CheckList(list, reporter, false, 1, true, false, false, false, elements);
+
+ list.addToHead(&elements[1]);
+ list.addToHead(&elements[2]);
+ list.addToHead(&elements[3]);
+
+ CheckList(list, reporter, false, 4, true, true, true, true, elements);
+
+ // test out iterators
+ SkTDLinkedList<ListElement>::Iter iter;
+
+ ListElement* cur = iter.init(list, SkTDLinkedList<ListElement>::Iter::kHead_IterStart);
+ for (int i = 0; NULL != cur; ++i, cur = iter.next()) {
+ REPORTER_ASSERT(reporter, cur->fID == 3-i);
+ }
+
+ cur = iter.init(list, SkTDLinkedList<ListElement>::Iter::kTail_IterStart);
+ for (int i = 0; NULL != cur; ++i, cur = iter.prev()) {
+ REPORTER_ASSERT(reporter, cur->fID == i);
+ }
+
+ // remove middle, frontmost then backmost
+ list.remove(&elements[1]);
+ list.remove(&elements[3]);
+ list.remove(&elements[0]);
+
+ CheckList(list, reporter, false, 1, false, false, true, false, elements);
+
+ // remove last element
+ list.remove(&elements[2]);
+
+ // list should be empty again
+ CheckList(list, reporter, true, 0, false, false, false, false, elements);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("TDLinkedList", TestTDLinkedListClass, TestTDLinkedList)