Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 */
8 #ifndef SkTDynamicHash_DEFINED
9 #define SkTDynamicHash_DEFINED
11 #include "SkMath.h"
12 #include "SkTemplates.h"
13 #include "SkTypes.h"
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 }
27 ~SkTDynamicHash() {
28 sk_free(fArray);
29 }
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 }
51 private:
52 T* current() const { return fHash->fArray[fCurrentIndex]; }
54 SkTDynamicHash* fHash;
55 int fCurrentIndex;
56 };
58 int count() const { return fCount; }
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 }
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 }
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 }
92 protected:
93 // These methods are used by tests only.
95 int capacity() const { return fCapacity; }
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 }
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.
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.
120 // O(1) checks, always done.
121 // Is capacity sane?
122 SKTDYNAMICHASH_CHECK(SkIsPow2(fCapacity));
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 }
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 }
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 }
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 }
195 void maybeGrow() {
196 if (100 * (fCount + fDeleted + 1) > fCapacity * kGrowPercent) {
197 this->resize(fCapacity > 0 ? fCapacity * 2 : 4);
198 }
199 }
201 void resize(int newCapacity) {
202 SkDEBUGCODE(int oldCount = fCount;)
203 int oldCapacity = fCapacity;
204 SkAutoTMalloc<T*> oldArray(fArray);
206 fCount = fDeleted = 0;
207 fCapacity = newCapacity;
208 fArray = (T**)sk_calloc_throw(sizeof(T*) * fCapacity);
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 }
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; }
222 int firstIndex(const Key& key) const {
223 return Hash(key) & this->hashMask();
224 }
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 }
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 };
238 #endif