|
1 |
|
2 /* |
|
3 * Copyright 2010 Google Inc. |
|
4 * |
|
5 * Use of this source code is governed by a BSD-style license that can be |
|
6 * found in the LICENSE file. |
|
7 */ |
|
8 |
|
9 |
|
10 |
|
11 #ifndef GrTHashTable_DEFINED |
|
12 #define GrTHashTable_DEFINED |
|
13 |
|
14 #include "GrTypes.h" |
|
15 #include "SkTDArray.h" |
|
16 |
|
17 /** |
|
18 * Key needs |
|
19 * static bool Equals(const Entry&, const Key&); |
|
20 * static bool LessThan(const Entry&, const Key&); |
|
21 * static bool Equals(const Entry&, const Entry&); for SK_DEBUG if GrTHashTable::validate() is called |
|
22 * static bool LessThan(const Entry&, const Entry&); for SK_DEBUG if GrTHashTable::validate() is called |
|
23 * uint32_t getHash() const; |
|
24 * |
|
25 * Allows duplicate key entries but on find you may get |
|
26 * any of the duplicate entries returned. |
|
27 */ |
|
28 template <typename T, typename Key, size_t kHashBits> class GrTHashTable { |
|
29 public: |
|
30 GrTHashTable() { this->clearHash(); } |
|
31 ~GrTHashTable() {} |
|
32 |
|
33 int count() const { return fSorted.count(); } |
|
34 |
|
35 struct Any { |
|
36 // Return the first resource that matches the key. |
|
37 bool operator()(const T*) const { return true; } |
|
38 }; |
|
39 |
|
40 T* find(const Key& key) const { return this->find(key, Any()); } |
|
41 template <typename Filter> T* find(const Key&, Filter filter) const; |
|
42 |
|
43 // return true if key was unique when inserted. |
|
44 bool insert(const Key&, T*); |
|
45 void remove(const Key&, const T*); |
|
46 |
|
47 void deleteAll(); |
|
48 |
|
49 #ifdef SK_DEBUG |
|
50 void validate() const; |
|
51 bool contains(T*) const; |
|
52 #endif |
|
53 |
|
54 // testing |
|
55 const SkTDArray<T*>& getArray() const { return fSorted; } |
|
56 SkTDArray<T*>& getArray() { return fSorted; } |
|
57 private: |
|
58 void clearHash() { sk_bzero(fHash, sizeof(fHash)); } |
|
59 |
|
60 enum { |
|
61 kHashCount = 1 << kHashBits, |
|
62 kHashMask = kHashCount - 1 |
|
63 }; |
|
64 static unsigned hash2Index(uint32_t hash) { |
|
65 hash ^= hash >> 16; |
|
66 if (kHashBits <= 8) { |
|
67 hash ^= hash >> 8; |
|
68 } |
|
69 return hash & kHashMask; |
|
70 } |
|
71 |
|
72 mutable T* fHash[kHashCount]; |
|
73 SkTDArray<T*> fSorted; |
|
74 |
|
75 // search fSorted, and return the found index, or ~index of where it |
|
76 // should be inserted |
|
77 int searchArray(const Key&) const; |
|
78 }; |
|
79 |
|
80 /////////////////////////////////////////////////////////////////////////////// |
|
81 |
|
82 template <typename T, typename Key, size_t kHashBits> |
|
83 int GrTHashTable<T, Key, kHashBits>::searchArray(const Key& key) const { |
|
84 int count = fSorted.count(); |
|
85 if (0 == count) { |
|
86 // we should insert it at 0 |
|
87 return ~0; |
|
88 } |
|
89 |
|
90 const T* const* array = fSorted.begin(); |
|
91 int high = count - 1; |
|
92 int low = 0; |
|
93 while (high > low) { |
|
94 int index = (low + high) >> 1; |
|
95 if (Key::LessThan(*array[index], key)) { |
|
96 low = index + 1; |
|
97 } else { |
|
98 high = index; |
|
99 } |
|
100 } |
|
101 |
|
102 // check if we found it |
|
103 if (Key::Equals(*array[high], key)) { |
|
104 // above search should have found the first occurrence if there |
|
105 // are multiple. |
|
106 SkASSERT(0 == high || Key::LessThan(*array[high - 1], key)); |
|
107 return high; |
|
108 } |
|
109 |
|
110 // now return the ~ of where we should insert it |
|
111 if (Key::LessThan(*array[high], key)) { |
|
112 high += 1; |
|
113 } |
|
114 return ~high; |
|
115 } |
|
116 |
|
117 template <typename T, typename Key, size_t kHashBits> |
|
118 template <typename Filter> |
|
119 T* GrTHashTable<T, Key, kHashBits>::find(const Key& key, Filter filter) const { |
|
120 |
|
121 int hashIndex = hash2Index(key.getHash()); |
|
122 T* elem = fHash[hashIndex]; |
|
123 |
|
124 if (NULL != elem && Key::Equals(*elem, key) && filter(elem)) { |
|
125 return elem; |
|
126 } |
|
127 |
|
128 // bsearch for the key in our sorted array |
|
129 int index = this->searchArray(key); |
|
130 if (index < 0) { |
|
131 return NULL; |
|
132 } |
|
133 |
|
134 const T* const* array = fSorted.begin(); |
|
135 |
|
136 // above search should have found the first occurrence if there |
|
137 // are multiple. |
|
138 SkASSERT(0 == index || Key::LessThan(*array[index - 1], key)); |
|
139 |
|
140 for ( ; index < count() && Key::Equals(*array[index], key); ++index) { |
|
141 if (filter(fSorted[index])) { |
|
142 // update the hash |
|
143 fHash[hashIndex] = fSorted[index]; |
|
144 return fSorted[index]; |
|
145 } |
|
146 } |
|
147 |
|
148 return NULL; |
|
149 } |
|
150 |
|
151 template <typename T, typename Key, size_t kHashBits> |
|
152 bool GrTHashTable<T, Key, kHashBits>::insert(const Key& key, T* elem) { |
|
153 int index = this->searchArray(key); |
|
154 bool first = index < 0; |
|
155 if (first) { |
|
156 // turn it into the actual index |
|
157 index = ~index; |
|
158 } |
|
159 // add it to our array |
|
160 *fSorted.insert(index) = elem; |
|
161 // update our hash table (overwrites any dupe's position in the hash) |
|
162 fHash[hash2Index(key.getHash())] = elem; |
|
163 return first; |
|
164 } |
|
165 |
|
166 template <typename T, typename Key, size_t kHashBits> |
|
167 void GrTHashTable<T, Key, kHashBits>::remove(const Key& key, const T* elem) { |
|
168 int index = hash2Index(key.getHash()); |
|
169 if (fHash[index] == elem) { |
|
170 fHash[index] = NULL; |
|
171 } |
|
172 |
|
173 // remove from our sorted array |
|
174 index = this->searchArray(key); |
|
175 SkASSERT(index >= 0); |
|
176 // if there are multiple matches searchArray will give us the first match |
|
177 // march forward until we find elem. |
|
178 while (elem != fSorted[index]) { |
|
179 ++index; |
|
180 SkASSERT(index < fSorted.count()); |
|
181 } |
|
182 SkASSERT(elem == fSorted[index]); |
|
183 fSorted.remove(index); |
|
184 } |
|
185 |
|
186 template <typename T, typename Key, size_t kHashBits> |
|
187 void GrTHashTable<T, Key, kHashBits>::deleteAll() { |
|
188 fSorted.deleteAll(); |
|
189 this->clearHash(); |
|
190 } |
|
191 |
|
192 #ifdef SK_DEBUG |
|
193 template <typename T, typename Key, size_t kHashBits> |
|
194 void GrTHashTable<T, Key, kHashBits>::validate() const { |
|
195 int count = fSorted.count(); |
|
196 for (int i = 1; i < count; i++) { |
|
197 SkASSERT(Key::LessThan(*fSorted[i - 1], *fSorted[i]) || |
|
198 Key::Equals(*fSorted[i - 1], *fSorted[i])); |
|
199 } |
|
200 } |
|
201 |
|
202 template <typename T, typename Key, size_t kHashBits> |
|
203 bool GrTHashTable<T, Key, kHashBits>::contains(T* elem) const { |
|
204 int index = fSorted.find(elem); |
|
205 return index >= 0; |
|
206 } |
|
207 |
|
208 #endif |
|
209 |
|
210 #endif |