OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright 2013 Google Inc. | 2 * Copyright 2013 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #ifndef SkTDynamicHash_DEFINED | 8 #ifndef SkTDynamicHash_DEFINED |
9 #define SkTDynamicHash_DEFINED | 9 #define SkTDynamicHash_DEFINED |
10 | 10 |
11 #include "SkTypes.h" | 11 #include "SkTypes.h" |
12 #include "SkMath.h" | |
12 | 13 |
13 template <typename T, | 14 template <typename T, |
14 typename KEY, | 15 typename Key, |
15 const KEY& (KEY_FROM_T)(const T&), | 16 const Key& (GetKey)(const T&), |
16 uint32_t (HASH_FROM_KEY)(const KEY&), | 17 uint32_t (Hash)(const Key&), |
17 bool (EQ_T_KEY)(const T&, const KEY&)> | 18 bool (Equal)(const T&, const Key&)> |
18 class SkTDynamicHash { | 19 class SkTDynamicHash { |
19 private: | 20 public: |
20 T* fStorage[4]; // cheap storage for small arrays | 21 SkTDynamicHash(int initialCapacity=64/sizeof(T*)) |
reed1
2013/08/05 15:43:29
My scrape of the web sites showed a lot of font ca
mtklein
2013/08/05 18:14:46
Nope, was just trying to keep things simple with a
| |
21 T** fArray; | 22 : fCount(0) |
22 int fCapacity; // number of slots in fArray. Must be pow2 | 23 , fCapacity(SkNextPow2(initialCapacity)) |
23 int fCountUsed; // number of valid entries in fArray | 24 , fArray(AllocArray(fCapacity)) {} |
24 int fCountDeleted; // number of deletedValue() entries in fArray | |
25 | 25 |
26 // Need an illegal ptr value different from NULL (which we use to | 26 ~SkTDynamicHash() { |
27 // signal empty/unused. | 27 sk_free(fArray); |
28 const T* deletedValue() const { return reinterpret_cast<const T*>(-1); } | |
29 | |
30 // fCapacity is a pow2, so that minus one is a clean mask to grab | |
31 // the low bits of hash to use as an index. | |
32 uint32_t hashMask() const { return fCapacity - 1; } | |
33 | |
34 int hashToIndex(uint32_t hash) const { | |
35 // this 16bit fold may be overkill, if we trust that hash is good | |
36 return ((hash >> 16) ^ hash) & this->hashMask(); | |
37 } | 28 } |
38 | 29 |
39 public: | 30 // Return the entry with this key if we have it, otherwise NULL. |
40 SkTDynamicHash() { | 31 T* find(const Key& key) const { |
41 sk_bzero(fStorage, sizeof(fStorage)); | 32 int index = this->firstIndex(key); |
42 fArray = fStorage; | 33 for (int round = 0; round < fCapacity; round++) { |
43 fCapacity = SK_ARRAY_COUNT(fStorage); | 34 T* candidate = fArray[index]; |
44 fCountUsed = fCountDeleted = 0; | 35 if (candidate == Empty()) return NULL; |
reed1
2013/08/05 15:43:29
prolly need to follow skia's "always use { }" rule
mtklein
2013/08/05 18:14:46
ah, done. That habit is dying hard.
| |
45 } | 36 if (candidate != Deleted() && Equal(*candidate, key)) return candida te; |
46 ~SkTDynamicHash() { | 37 index = this->nextIndex(index, round); |
47 if (fArray != fStorage) { | |
48 sk_free(fArray); | |
49 } | 38 } |
50 } | 39 SkASSERT(!"find: should be unreachable"); |
51 | |
52 T* find(const KEY& key) { | |
53 const T* const deleted = this->deletedValue(); | |
54 const unsigned mask = this->hashMask(); | |
55 int index = this->hashToIndex(HASH_FROM_KEY(key)); | |
56 int delta = 1; | |
57 | |
58 do { | |
59 T* candidate = fArray[index]; | |
60 if (NULL == candidate) { | |
61 return NULL; | |
62 } | |
63 if (deleted != candidate && EQ_T_KEY(*candidate, key)) { | |
64 return candidate; | |
65 } | |
66 index = (index + delta) & mask; | |
67 delta <<= 1; | |
68 } while (delta <= fCapacity); | |
69 return NULL; | 40 return NULL; |
70 } | 41 } |
71 | 42 |
72 bool add(const KEY& key, T* newElement, bool autoGrow = true) { | 43 // Add an entry with this key. |
73 const T* const deleted = this->deletedValue(); | 44 void add(T* newEntry) { |
74 for (;;) { | 45 this->maybeGrow(); |
75 const unsigned mask = this->hashMask(); | |
76 int index = this->hashToIndex(HASH_FROM_KEY(key)); | |
77 int delta = 1; | |
78 | 46 |
79 do { | 47 const Key& key = GetKey(*newEntry); |
80 const T* candidate = fArray[index]; | 48 int index = this->firstIndex(key); |
81 if (NULL == candidate || deleted == candidate) { | 49 for (int round = 0; round < fCapacity; round++) { |
82 fArray[index] = newElement; | 50 T* candidate = fArray[index]; |
83 fCountUsed += 1; | 51 if (candidate == Empty() || candidate == Deleted()) { |
84 if (deleted == candidate) { | 52 fArray[index] = newEntry; |
85 SkASSERT(fCountDeleted > 0); | 53 fCount++; |
86 fCountDeleted -= 1; | 54 return; |
87 } | |
88 return true; | |
89 } | |
90 index = (index + delta) & mask; | |
91 delta <<= 1; | |
92 } while (delta <= fCapacity); | |
93 if (autoGrow) { | |
94 this->grow(); | |
95 } else { | |
96 return false; | |
97 } | 55 } |
56 if (Equal(*candidate, key)) { | |
57 fArray[index] = newEntry; | |
58 return; | |
59 } | |
60 index = this->nextIndex(index, round); | |
98 } | 61 } |
99 SkASSERT(!"never get here"); | 62 SkASSERT(!"add: should be unreachable"); |
100 return false; | |
101 } | 63 } |
102 | 64 |
103 void remove(const KEY& key) { | 65 // Remove entry with this key, if we have it. |
104 const T* const deleted = this->deletedValue(); | 66 void remove(const Key& key) { |
105 const unsigned mask = this->hashMask(); | 67 this->innerRemove(key); |
106 int index = this->hashToIndex(HASH_FROM_KEY(key)); | 68 this->maybeShrink(); |
107 int delta = 1; | |
108 | |
109 for (;;) { | |
110 const T* candidate = fArray[index]; | |
111 SkASSERT(candidate); | |
112 if (deleted != candidate && EQ_T_KEY(*candidate, key)) { | |
113 fArray[index] = const_cast<T*>(deleted); | |
114 fCountUsed -= 1; | |
115 fCountDeleted += 1; | |
116 break; | |
117 } | |
118 index = (index + delta) & mask; | |
119 delta <<= 1; | |
120 SkASSERT(delta <= fCapacity); | |
121 } | |
122 this->checkStrink(); | |
123 } | 69 } |
124 | 70 |
125 private: | 71 private: |
126 int countCollisions(const KEY& key) const { | 72 // We have two special values to indicate an empty or deleted entry. |
127 const T* const deleted = this->deletedValue(); | 73 static const T* Empty() { return reinterpret_cast<const T*>(0); } // i.e. NULL |
128 const unsigned mask = this->hashMask(); | 74 static const T* Deleted() { return reinterpret_cast<const T*>(1); } // Also an invalid pointer. |
129 int index = this->hashToIndex(HASH_FROM_KEY(key)); | |
130 int delta = 1; | |
131 int collisionCount = 0; | |
132 | 75 |
133 for (;;) { | 76 static T** AllocArray(int capacity) { |
134 const T* candidate = fArray[index]; | 77 T** array = (T**)sk_malloc_throw(sizeof(T*) * capacity); |
135 SkASSERT(candidate); | 78 sk_bzero(array, sizeof(T*) * capacity); // All cells == Empty(). |
136 if (deleted != candidate && EQ_T_KEY(*candidate, key)) { | 79 return array; |
137 break; | |
138 } | |
139 index = (index + delta) & mask; | |
140 delta <<= 1; | |
141 collisionCount += 1; | |
142 SkASSERT(delta <= fCapacity); | |
143 } | |
144 return collisionCount; | |
145 } | 80 } |
146 | 81 |
147 void grow() { | 82 void innerRemove(const Key& key) { |
148 const T* const deleted = this->deletedValue(); | 83 int index = this->firstIndex(key); |
149 #if 0 | 84 for (int round = 0; round < fCapacity; round++) { |
150 SkDebugf("growing from %d: used=%d\n", fCapacity, fCountUsed); | 85 const T* candidate = fArray[index]; |
151 for (int i = 0; i < fCapacity; ++i) { | 86 if (candidate == Empty()) return; |
152 T* elem = fArray[i]; | 87 if (candidate != Deleted() && Equal(*candidate, key)) { |
153 if (NULL == elem || deleted == elem) { | 88 fArray[index] = const_cast<T*>(Deleted()); |
154 continue; | 89 fCount--; |
90 return; | |
155 } | 91 } |
156 SkDebugf(" entry[%d] had %d collisions\n", i, countCollisions(KEY _FROM_T(*elem))); | 92 index = this->nextIndex(index, round); |
157 } | 93 } |
158 #endif | 94 SkASSERT(!"innerRemove: should be unreachable"); |
95 } | |
96 | |
97 void maybeGrow() { | |
98 if (fCount < fCapacity / 2) return; | |
99 | |
100 SkDEBUGCODE(int oldCount = fCount;) | |
159 int oldCapacity = fCapacity; | 101 int oldCapacity = fCapacity; |
160 T** oldArray = fArray; | 102 T** oldArray = fArray; |
161 | 103 |
162 int newCapacity = oldCapacity << 1; | 104 fCount = 0; |
163 T** newArray = (T**)sk_malloc_throw(sizeof(T*) * newCapacity); | 105 fCapacity *= 2; |
164 sk_bzero(newArray, sizeof(T*) * newCapacity); | 106 fArray = AllocArray(fCapacity); |
165 | 107 |
166 SkDEBUGCODE(int oldCountUsed = fCountUsed;) | 108 for (int i = 0; i < oldCapacity; i++) { |
167 fArray = newArray; | 109 T* entry = oldArray[i]; |
168 fCapacity = newCapacity; | 110 if (entry == Empty() || entry == Deleted()) continue; |
169 fCountUsed = 0; | 111 this->add(entry); |
170 fCountDeleted = 0; | 112 } |
113 SkASSERT(oldCount == fCount); | |
171 | 114 |
172 for (int i = 0; i < oldCapacity; ++i) { | 115 sk_free(oldArray); |
173 T* elem = oldArray[i]; | |
174 if (NULL == elem || deleted == elem) { | |
175 continue; | |
176 } | |
177 SkDEBUGCODE(bool success =) this->add(KEY_FROM_T(*elem), elem, false ); | |
178 SkASSERT(success); | |
179 } | |
180 SkASSERT(oldCountUsed == fCountUsed); | |
181 | |
182 if (oldArray != fStorage) { | |
183 sk_free(oldArray); | |
184 } | |
185 } | 116 } |
186 | 117 |
187 void checkStrink() { | 118 void maybeShrink() { |
188 // todo: based on density and deadspace (fCountDeleted), consider | 119 // TODO |
189 // shrinking fArray and repopulating it. | |
190 } | 120 } |
121 | |
122 // fCapacity is always a power of 2, so this masks the correct low bits to i ndex into our hash. | |
123 uint32_t hashMask() const { return fCapacity - 1; } | |
124 | |
125 int firstIndex(const Key& key) const { | |
126 return Hash(key) & this->hashMask(); | |
127 } | |
128 | |
129 // Given index at round N, what is the index to check at N+1? round should start at 0. | |
130 int nextIndex(int index, int round) const { | |
131 // This will search a power-of-two array fully without repeating an inde x. | |
132 return (index + round + 1) & this->hashMask(); | |
133 } | |
134 | |
135 int fCount; // Number of non-empty, non-deleted entries in fArray. | |
136 int fCapacity; // Number of entries in fArray. Always a power of 2. | |
137 T** fArray; | |
191 }; | 138 }; |
192 | 139 |
193 #endif | 140 #endif |
OLD | NEW |