Chromium Code Reviews| Index: src/core/SkTDynamicHash.h |
| diff --git a/src/core/SkTDynamicHash.h b/src/core/SkTDynamicHash.h |
| index 4cb44204c85f61130354d25c78c7924249fe66f8..9b1dbf75fce3862eb46cd03188d0007c3f6c982d 100644 |
| --- a/src/core/SkTDynamicHash.h |
| +++ b/src/core/SkTDynamicHash.h |
| @@ -11,6 +11,9 @@ |
| #include "SkTypes.h" |
| #include "SkMath.h" |
| +/** SkTDynamicHash is a set of pointers of T. The elements can be looked up from the set by |
| + * key Key. Duplicate keys are allowed. |
| + */ |
| template <typename T, |
| typename Key, |
| const Key& (GetKey)(const T&), |
| @@ -23,7 +26,6 @@ class SkTDynamicHash { |
| public: |
| SkTDynamicHash(int initialCapacity=64/sizeof(T*)) { |
| this->reset(SkNextPow2(initialCapacity > kMinCapacity ? initialCapacity : kMinCapacity)); |
| - SkASSERT(this->validate()); |
|
mtklein
2013/11/27 14:21:54
Please put validate back to how it was. SkASSERT
Kimmo Kinnunen
2013/11/28 06:40:08
The commit msg actually said that when it is *run*
|
| } |
| ~SkTDynamicHash() { |
| @@ -32,78 +34,50 @@ public: |
| int count() const { return fCount; } |
| - // Return the entry with this key if we have it, otherwise NULL. |
| + struct Any { |
| + // Return the first entry that matches the key. |
| + bool operator()(const T*) const { return true; } |
|
mtklein
2013/11/27 14:21:54
I'm not a fan of this API. I know it's replicatin
Kimmo Kinnunen
2013/11/28 06:40:08
Yeah.. Though, in this stage of this usage, I don'
|
| + }; |
| + |
| + // Return an entry with passed key if the collection has it, otherwise NULL. |
| T* find(const Key& key) const { |
| + return this->find(key, Any()); |
| + } |
| + |
| + // Return an entry with the passed key if the collection has it and if filter returns true for |
| + // the entry. Otherwise return NULL. |
| + template <typename Filter> |
| + T* find(const Key& key, Filter filter) const { |
| int index = this->firstIndex(key); |
| for (int round = 0; round < fCapacity; round++) { |
| T* candidate = fArray[index]; |
| if (Empty() == candidate) { |
| return NULL; |
| } |
| - if (Deleted() != candidate && Equal(*candidate, key)) { |
| + if (Deleted() != candidate && Equal(*candidate, key) && filter(fArray[index])) { |
| return candidate; |
| } |
| index = this->nextIndex(index, round); |
| } |
| - SkASSERT(0); // find: should be unreachable |
| return NULL; |
| } |
| - // Add an entry with this key. We require that no entry with newEntry's key is already present. |
| + // Add an entry with this key. |
| void add(T* newEntry) { |
| - SkASSERT(NULL == this->find(GetKey(*newEntry))); |
| this->maybeGrow(); |
| - SkASSERT(this->validate()); |
| this->innerAdd(newEntry); |
| - SkASSERT(this->validate()); |
| } |
| - // Remove the entry with this key. We reqire that an entry with this key is present. |
| - void remove(const Key& key) { |
| - SkASSERT(NULL != this->find(key)); |
| - this->innerRemove(key); |
| - SkASSERT(this->validate()); |
| + // Remove the entry with this key. We require that the entry has been added before. |
| + void remove(const T* entry) { |
|
mtklein
2013/11/27 14:21:54
Generally, these should be const T&. It's neither
Kimmo Kinnunen
2013/11/28 06:40:08
A) This is set that stores pointers
B) Pointers ar
bsalomon
2013/12/02 14:18:16
If the function (or something it calls) will retai
reed1
2013/12/02 16:20:40
agreed -- passing a ptr is clearer (to me) that th
|
| + SkASSERT(NULL != this->find(GetKey(*entry))); |
| + this->innerRemove(entry); |
| this->maybeShrink(); |
| - SkASSERT(this->validate()); |
| } |
| -protected: |
| - // These methods are used by tests only. |
| - |
| - int capacity() const { return fCapacity; } |
| - |
| - // How many collisions do we go through before finding where this entry should be inserted? |
| - int countCollisions(const Key& key) const { |
| - int index = this->firstIndex(key); |
| - for (int round = 0; round < fCapacity; round++) { |
| - const T* candidate = fArray[index]; |
| - if (Empty() == candidate || Deleted() == candidate || Equal(*candidate, key)) { |
| - return round; |
| - } |
| - index = this->nextIndex(index, round); |
| - } |
| - SkASSERT(0); // countCollisions: should be unreachable |
| - return -1; |
| - } |
| - |
| -private: |
| - // We have two special values to indicate an empty or deleted entry. |
| - static T* Empty() { return reinterpret_cast<T*>(0); } // i.e. NULL |
| - static T* Deleted() { return reinterpret_cast<T*>(1); } // Also an invalid pointer. |
| - |
| - static T** AllocArray(int capacity) { |
| - return (T**)sk_calloc_throw(sizeof(T*) * capacity); // All cells == Empty(). |
| - } |
| - |
| - void reset(int capacity) { |
| - fCount = 0; |
| - fDeleted = 0; |
| - fCapacity = capacity; |
| - fArray = AllocArray(fCapacity); |
| - } |
| - |
| - bool validate() const { |
| - #define SKTDYNAMICHASH_CHECK(x) SkASSERT((x)); if (!(x)) return false |
| +#ifdef SK_DEBUG |
| + void validate() const { |
| + #define SKTDYNAMICHASH_CHECK(x) SkASSERT((x)); if (!(x)) return; |
| // Is capacity sane? |
| SKTDYNAMICHASH_CHECK(SkIsPow2(fCapacity)); |
| @@ -145,12 +119,47 @@ private: |
| continue; |
| } |
| SKTDYNAMICHASH_CHECK(fArray[i] != fArray[j]); |
| - SKTDYNAMICHASH_CHECK(!Equal(*fArray[i], GetKey(*fArray[j]))); |
| - SKTDYNAMICHASH_CHECK(!Equal(*fArray[j], GetKey(*fArray[i]))); |
| } |
| } |
| #undef SKTDYNAMICHASH_CHECK |
| - return true; |
| + } |
| +#else |
| + void validate() const { } |
| +#endif |
| + |
| +protected: |
| + // These methods are used by tests only. |
| + |
| + int capacity() const { return fCapacity; } |
| + |
| + // How many collisions do we go through before finding where this entry should be inseretd? |
| + int countCollisions(const Key& key) const { |
| + int index = this->firstIndex(key); |
| + for (int round = 0; round < fCapacity; round++) { |
| + const T* candidate = fArray[index]; |
| + if (Empty() == candidate || Deleted() == candidate || Equal(*candidate, key)) { |
| + return round; |
| + } |
| + index = this->nextIndex(index, round); |
| + } |
| + SkASSERT(0); // countCollisions: should be unreachable |
| + return -1; |
| + } |
| + |
| +private: |
| + // We have two special values to indicate an empty or deleted entry. |
| + static T* Empty() { return reinterpret_cast<T*>(0); } // i.e. NULL |
| + static T* Deleted() { return reinterpret_cast<T*>(1); } // Also an invalid pointer. |
| + |
| + static T** AllocArray(int capacity) { |
| + return (T**)sk_calloc_throw(sizeof(T*) * capacity); // All cells == Empty(). |
| + } |
| + |
| + void reset(int capacity) { |
| + fCount = 0; |
| + fDeleted = 0; |
| + fCapacity = capacity; |
| + fArray = AllocArray(fCapacity); |
| } |
| void innerAdd(T* newEntry) { |
| @@ -171,12 +180,13 @@ private: |
| SkASSERT(0); // add: should be unreachable |
| } |
| - void innerRemove(const Key& key) { |
| + void innerRemove(const T* entry) { |
| + const Key& key = GetKey(*entry); |
| const int firstIndex = this->firstIndex(key); |
| int index = firstIndex; |
| for (int round = 0; round < fCapacity; round++) { |
| const T* candidate = fArray[index]; |
| - if (Deleted() != candidate && Equal(*candidate, key)) { |
| + if (Deleted() != candidate && Equal(*candidate, key) && fArray[index] == entry) { |
| fDeleted++; |
| fCount--; |
| fArray[index] = Deleted(); |