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