1 /* 2 * Copyright 2013 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #ifndef SkTDynamicHash_DEFINED 9 #define SkTDynamicHash_DEFINED 10 11 #include "SkTypes.h" 12 #include "SkMath.h" 13 14 template <typename T, 15 typename Key, 16 const Key& (GetKey)(const T&), 17 uint32_t (Hash)(const Key&), 18 bool (Equal)(const T&, const Key&), 19 int kGrowPercent = 75, // Larger -> more memory efficient, but slower. 20 int kShrinkPercent = 25> 21 class SkTDynamicHash { 22 static const int kMinCapacity = 4; // Smallest capacity we allow. 23 public: 24 SkTDynamicHash(int initialCapacity=64/sizeof(T*)) { 25 this->reset(SkNextPow2(initialCapacity > kMinCapacity ? initialCapacity : kMinCapacity)); 26 SkASSERT(this->validate()); 27 } 28 29 ~SkTDynamicHash() { 30 sk_free(fArray); 31 } 32 33 int count() const { return fCount; } 34 35 // Return the entry with this key if we have it, otherwise NULL. 36 T* find(const Key& key) const { 37 int index = this->firstIndex(key); 38 for (int round = 0; round < fCapacity; round++) { 39 T* candidate = fArray[index]; 40 if (Empty() == candidate) { 41 return NULL; 42 } 43 if (Deleted() != candidate && Equal(*candidate, key)) { 44 return candidate; 45 } 46 index = this->nextIndex(index, round); 47 } 48 SkASSERT(0); // find: should be unreachable 49 return NULL; 50 } 51 52 // Add an entry with this key. We require that no entry with newEntry's key is already present. 53 void add(T* newEntry) { 54 SkASSERT(NULL == this->find(GetKey(*newEntry))); 55 this->maybeGrow(); 56 SkASSERT(this->validate()); 57 this->innerAdd(newEntry); 58 SkASSERT(this->validate()); 59 } 60 61 // Remove the entry with this key. We reqire that an entry with this key is present. 62 void remove(const Key& key) { 63 SkASSERT(NULL != this->find(key)); 64 this->innerRemove(key); 65 SkASSERT(this->validate()); 66 this->maybeShrink(); 67 SkASSERT(this->validate()); 68 } 69 70 protected: 71 // These methods are used by tests only. 72 73 int capacity() const { return fCapacity; } 74 75 // How many collisions do we go through before finding where this entry should be inserted? 76 int countCollisions(const Key& key) const { 77 int index = this->firstIndex(key); 78 for (int round = 0; round < fCapacity; round++) { 79 const T* candidate = fArray[index]; 80 if (Empty() == candidate || Deleted() == candidate || Equal(*candidate, key)) { 81 return round; 82 } 83 index = this->nextIndex(index, round); 84 } 85 SkASSERT(0); // countCollisions: should be unreachable 86 return -1; 87 } 88 89 private: 90 // We have two special values to indicate an empty or deleted entry. 91 static T* Empty() { return reinterpret_cast<T*>(0); } // i.e. NULL 92 static T* Deleted() { return reinterpret_cast<T*>(1); } // Also an invalid pointer. 93 94 static T** AllocArray(int capacity) { 95 return (T**)sk_calloc_throw(sizeof(T*) * capacity); // All cells == Empty(). 96 } 97 98 void reset(int capacity) { 99 fCount = 0; 100 fDeleted = 0; 101 fCapacity = capacity; 102 fArray = AllocArray(fCapacity); 103 } 104 105 bool validate() const { 106 #define SKTDYNAMICHASH_CHECK(x) SkASSERT((x)); if (!(x)) return false 107 108 // Is capacity sane? 109 SKTDYNAMICHASH_CHECK(SkIsPow2(fCapacity)); 110 SKTDYNAMICHASH_CHECK(fCapacity >= kMinCapacity); 111 112 // Is fCount correct? 113 int count = 0; 114 for (int i = 0; i < fCapacity; i++) { 115 if (Empty() != fArray[i] && Deleted() != fArray[i]) { 116 count++; 117 } 118 } 119 SKTDYNAMICHASH_CHECK(count == fCount); 120 121 // Is fDeleted correct? 122 int deleted = 0; 123 for (int i = 0; i < fCapacity; i++) { 124 if (Deleted() == fArray[i]) { 125 deleted++; 126 } 127 } 128 SKTDYNAMICHASH_CHECK(deleted == fDeleted); 129 130 // Are all entries findable? 131 for (int i = 0; i < fCapacity; i++) { 132 if (Empty() == fArray[i] || Deleted() == fArray[i]) { 133 continue; 134 } 135 SKTDYNAMICHASH_CHECK(NULL != this->find(GetKey(*fArray[i]))); 136 } 137 138 // Are all entries unique? 139 for (int i = 0; i < fCapacity; i++) { 140 if (Empty() == fArray[i] || Deleted() == fArray[i]) { 141 continue; 142 } 143 for (int j = i+1; j < fCapacity; j++) { 144 if (Empty() == fArray[j] || Deleted() == fArray[j]) { 145 continue; 146 } 147 SKTDYNAMICHASH_CHECK(fArray[i] != fArray[j]); 148 SKTDYNAMICHASH_CHECK(!Equal(*fArray[i], GetKey(*fArray[j]))); 149 SKTDYNAMICHASH_CHECK(!Equal(*fArray[j], GetKey(*fArray[i]))); 150 } 151 } 152 #undef SKTDYNAMICHASH_CHECK 153 return true; 154 } 155 156 void innerAdd(T* newEntry) { 157 const Key& key = GetKey(*newEntry); 158 int index = this->firstIndex(key); 159 for (int round = 0; round < fCapacity; round++) { 160 const T* candidate = fArray[index]; 161 if (Empty() == candidate || Deleted() == candidate) { 162 if (Deleted() == candidate) { 163 fDeleted--; 164 } 165 fCount++; 166 fArray[index] = newEntry; 167 return; 168 } 169 index = this->nextIndex(index, round); 170 } 171 SkASSERT(0); // add: should be unreachable 172 } 173 174 void innerRemove(const Key& key) { 175 const int firstIndex = this->firstIndex(key); 176 int index = firstIndex; 177 for (int round = 0; round < fCapacity; round++) { 178 const T* candidate = fArray[index]; 179 if (Deleted() != candidate && Equal(*candidate, key)) { 180 fDeleted++; 181 fCount--; 182 fArray[index] = Deleted(); 183 return; 184 } 185 index = this->nextIndex(index, round); 186 } 187 SkASSERT(0); // innerRemove: should be unreachable 188 } 189 190 void maybeGrow() { 191 if (fCount + fDeleted + 1 > (fCapacity * kGrowPercent) / 100) { 192 resize(fCapacity * 2); 193 } 194 } 195 196 void maybeShrink() { 197 if (fCount < (fCapacity * kShrinkPercent) / 100 && fCapacity / 2 > kMinCapacity) { 198 resize(fCapacity / 2); 199 } 200 } 201 202 void resize(int newCapacity) { 203 SkDEBUGCODE(int oldCount = fCount;) 204 int oldCapacity = fCapacity; 205 T** oldArray = fArray; 206 207 reset(newCapacity); 208 209 for (int i = 0; i < oldCapacity; i++) { 210 T* entry = oldArray[i]; 211 if (Empty() != entry && Deleted() != entry) { 212 this->add(entry); 213 } 214 } 215 SkASSERT(oldCount == fCount); 216 217 sk_free(oldArray); 218 } 219 220 // fCapacity is always a power of 2, so this masks the correct low bits to index into our hash. 221 uint32_t hashMask() const { return fCapacity - 1; } 222 223 int firstIndex(const Key& key) const { 224 return Hash(key) & this->hashMask(); 225 } 226 227 // Given index at round N, what is the index to check at N+1? round should start at 0. 228 int nextIndex(int index, int round) const { 229 // This will search a power-of-two array fully without repeating an index. 230 return (index + round + 1) & this->hashMask(); 231 } 232 233 int fCount; // Number of non Empty(), non Deleted() entries in fArray. 234 int fDeleted; // Number of Deleted() entries in fArray. 235 int fCapacity; // Number of entries in fArray. Always a power of 2. 236 T** fArray; 237 }; 238 239 #endif 240