2 * Copyright 2013 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #ifndef SkTDynamicHash_DEFINED
9 #define SkTDynamicHash_DEFINED
12 #include "SkTemplates.h"
16 // static const Key& GetKey(const T&) { ... }
17 // static uint32_t Hash(const Key&) { ... }
18 // We'll look on T for these by default, or you can pass a custom Traits type.
22 int kGrowPercent = 75> // Larger -> more memory efficient, but slower.
23 class SkTDynamicHash {
25 SkTDynamicHash() : fCount(0), fDeleted(0), fCapacity(0), fArray(NULL) {
26 SkASSERT(this->validate());
35 explicit Iter(SkTDynamicHash* hash) : fHash(hash), fCurrentIndex(-1) {
40 SkASSERT(fCurrentIndex <= fHash->fCapacity);
41 return fCurrentIndex == fHash->fCapacity;
43 T& operator*() const {
44 SkASSERT(!this->done());
45 return *this->current();
50 } while (!this->done() && (this->current() == Empty() || this->current() == Deleted()));
54 T* current() const { return fHash->fArray[fCurrentIndex]; }
56 SkTDynamicHash* fHash;
62 explicit ConstIter(const SkTDynamicHash* hash) : fHash(hash), fCurrentIndex(-1) {
67 SkASSERT(fCurrentIndex <= fHash->fCapacity);
68 return fCurrentIndex == fHash->fCapacity;
70 const T& operator*() const {
71 SkASSERT(!this->done());
72 return *this->current();
77 } while (!this->done() && (this->current() == Empty() || this->current() == Deleted()));
81 const T* current() const { return fHash->fArray[fCurrentIndex]; }
83 const SkTDynamicHash* fHash;
87 int count() const { return fCount; }
89 // Return the entry with this key if we have it, otherwise NULL.
90 T* find(const Key& key) const {
91 int index = this->firstIndex(key);
92 for (int round = 0; round < fCapacity; round++) {
93 SkASSERT(index >= 0 && index < fCapacity);
94 T* candidate = fArray[index];
95 if (Empty() == candidate) {
98 if (Deleted() != candidate && GetKey(*candidate) == key) {
101 index = this->nextIndex(index, round);
103 SkASSERT(fCapacity == 0);
107 // Add an entry with this key. We require that no entry with newEntry's key is already present.
108 void add(T* newEntry) {
109 SkASSERT(NULL == this->find(GetKey(*newEntry)));
111 this->innerAdd(newEntry);
112 SkASSERT(this->validate());
115 // Remove the entry with this key. We require that an entry with this key is present.
116 void remove(const Key& key) {
117 SkASSERT(this->find(key));
118 this->innerRemove(key);
119 SkASSERT(this->validate());
124 sk_bzero(fArray, sizeof(T*)* fCapacity);
139 // These methods are used by tests only.
141 int capacity() const { return fCapacity; }
143 // How many collisions do we go through before finding where this entry should be inserted?
144 int countCollisions(const Key& key) const {
145 int index = this->firstIndex(key);
146 for (int round = 0; round < fCapacity; round++) {
147 SkASSERT(index >= 0 && index < fCapacity);
148 const T* candidate = fArray[index];
149 if (Empty() == candidate || Deleted() == candidate || GetKey(*candidate) == key) {
152 index = this->nextIndex(index, round);
154 SkASSERT(fCapacity == 0);
159 // We have two special values to indicate an empty or deleted entry.
160 static T* Empty() { return reinterpret_cast<T*>(0); } // i.e. NULL
161 static T* Deleted() { return reinterpret_cast<T*>(1); } // Also an invalid pointer.
163 bool validate() const {
164 #define SKTDYNAMICHASH_CHECK(x) SkASSERT((x)); if (!(x)) return false
165 static const int kLarge = 50; // Arbitrary, tweak to suit your patience.
167 // O(1) checks, always done.
169 SKTDYNAMICHASH_CHECK(SkIsPow2(fCapacity));
171 // O(N) checks, skipped when very large.
172 if (fCount < kLarge * kLarge) {
173 // Are fCount and fDeleted correct, and are all elements findable?
174 int count = 0, deleted = 0;
175 for (int i = 0; i < fCapacity; i++) {
176 if (Deleted() == fArray[i]) {
178 } else if (Empty() != fArray[i]) {
180 SKTDYNAMICHASH_CHECK(this->find(GetKey(*fArray[i])));
183 SKTDYNAMICHASH_CHECK(count == fCount);
184 SKTDYNAMICHASH_CHECK(deleted == fDeleted);
187 // O(N^2) checks, skipped when large.
188 if (fCount < kLarge) {
189 // Are all entries unique?
190 for (int i = 0; i < fCapacity; i++) {
191 if (Empty() == fArray[i] || Deleted() == fArray[i]) {
194 for (int j = i+1; j < fCapacity; j++) {
195 if (Empty() == fArray[j] || Deleted() == fArray[j]) {
198 SKTDYNAMICHASH_CHECK(fArray[i] != fArray[j]);
199 SKTDYNAMICHASH_CHECK(!(GetKey(*fArray[i]) == GetKey(*fArray[j])));
203 #undef SKTDYNAMICHASH_CHECK
207 void innerAdd(T* newEntry) {
208 const Key& key = GetKey(*newEntry);
209 int index = this->firstIndex(key);
210 for (int round = 0; round < fCapacity; round++) {
211 SkASSERT(index >= 0 && index < fCapacity);
212 const T* candidate = fArray[index];
213 if (Empty() == candidate || Deleted() == candidate) {
214 if (Deleted() == candidate) {
218 fArray[index] = newEntry;
221 index = this->nextIndex(index, round);
223 SkASSERT(fCapacity == 0);
226 void innerRemove(const Key& key) {
227 const int firstIndex = this->firstIndex(key);
228 int index = firstIndex;
229 for (int round = 0; round < fCapacity; round++) {
230 SkASSERT(index >= 0 && index < fCapacity);
231 const T* candidate = fArray[index];
232 if (Deleted() != candidate && GetKey(*candidate) == key) {
235 fArray[index] = Deleted();
238 index = this->nextIndex(index, round);
240 SkASSERT(fCapacity == 0);
244 if (100 * (fCount + fDeleted + 1) > fCapacity * kGrowPercent) {
245 this->resize(fCapacity > 0 ? fCapacity * 2 : 4);
249 void resize(int newCapacity) {
250 SkDEBUGCODE(int oldCount = fCount;)
251 int oldCapacity = fCapacity;
252 SkAutoTMalloc<T*> oldArray(fArray);
254 fCount = fDeleted = 0;
255 fCapacity = newCapacity;
256 fArray = (T**)sk_calloc_throw(sizeof(T*) * fCapacity);
258 for (int i = 0; i < oldCapacity; i++) {
259 T* entry = oldArray[i];
260 if (Empty() != entry && Deleted() != entry) {
261 this->innerAdd(entry);
264 SkASSERT(oldCount == fCount);
267 // fCapacity is always a power of 2, so this masks the correct low bits to index into our hash.
268 uint32_t hashMask() const { return fCapacity - 1; }
270 int firstIndex(const Key& key) const {
271 return Hash(key) & this->hashMask();
274 // Given index at round N, what is the index to check at N+1? round should start at 0.
275 int nextIndex(int index, int round) const {
276 // This will search a power-of-two array fully without repeating an index.
277 return (index + round + 1) & this->hashMask();
280 static const Key& GetKey(const T& t) { return Traits::GetKey(t); }
281 static uint32_t Hash(const Key& key) { return Traits::Hash(key); }
283 int fCount; // Number of non Empty(), non Deleted() entries in fArray.
284 int fDeleted; // Number of Deleted() entries in fArray.
285 int fCapacity; // Number of entries in fArray. Always a power of 2.