OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2005, 2006, 2007, 2008, 2011, 2012 Apple Inc. All rights reserv
ed. | |
3 * Copyright (C) 2008 David Levin <levin@chromium.org> | |
4 * | |
5 * This library is free software; you can redistribute it and/or | |
6 * modify it under the terms of the GNU Library General Public | |
7 * License as published by the Free Software Foundation; either | |
8 * version 2 of the License, or (at your option) any later version. | |
9 * | |
10 * This library is distributed in the hope that it will be useful, | |
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 * Library General Public License for more details. | |
14 * | |
15 * You should have received a copy of the GNU Library General Public License | |
16 * along with this library; see the file COPYING.LIB. If not, write to | |
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | |
18 * Boston, MA 02110-1301, USA. | |
19 * | |
20 */ | |
21 | |
22 #ifndef WTF_HashTable_h | |
23 #define WTF_HashTable_h | |
24 | |
25 #include <wtf/Alignment.h> | |
26 #include <wtf/Assertions.h> | |
27 #include <wtf/FastMalloc.h> | |
28 #include <wtf/HashTraits.h> | |
29 #include <string.h> | |
30 | |
31 #define DUMP_HASHTABLE_STATS 0 | |
32 #define DUMP_HASHTABLE_STATS_PER_TABLE 0 | |
33 | |
34 // Enables internal WTF consistency checks that are invoked automatically. Non-W
TF callers can call checkTableConsistency() even if internal checks are disabled
. | |
35 #define CHECK_HASHTABLE_CONSISTENCY 0 | |
36 | |
37 #ifdef NDEBUG | |
38 #define CHECK_HASHTABLE_ITERATORS 0 | |
39 #define CHECK_HASHTABLE_USE_AFTER_DESTRUCTION 0 | |
40 #else | |
41 #define CHECK_HASHTABLE_ITERATORS 1 | |
42 #define CHECK_HASHTABLE_USE_AFTER_DESTRUCTION 1 | |
43 #endif | |
44 | |
45 #if CHECK_HASHTABLE_ITERATORS | |
46 // Required for CHECK_HASHTABLE_ITERATORS. | |
47 #include <wtf/OwnPtr.h> | |
48 #include <wtf/PassOwnPtr.h> | |
49 #include <wtf/Threading.h> | |
50 #endif | |
51 | |
52 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
53 #include <wtf/DataLog.h> | |
54 #endif | |
55 | |
56 #if !ASSERT_DISABLED | |
57 #include <wtf/ValueCheck.h> | |
58 #endif | |
59 | |
60 namespace WTF { | |
61 | |
62 #if DUMP_HASHTABLE_STATS | |
63 | |
64 struct HashTableStats { | |
65 // The following variables are all atomically incremented when modified. | |
66 WTF_EXPORTDATA static int numAccesses; | |
67 WTF_EXPORTDATA static int numRehashes; | |
68 WTF_EXPORTDATA static int numRemoves; | |
69 WTF_EXPORTDATA static int numReinserts; | |
70 | |
71 // The following variables are only modified in the recordCollisionAtCou
nt method within a mutex. | |
72 WTF_EXPORTDATA static int maxCollisions; | |
73 WTF_EXPORTDATA static int numCollisions; | |
74 WTF_EXPORTDATA static int collisionGraph[4096]; | |
75 | |
76 WTF_EXPORT_PRIVATE static void recordCollisionAtCount(int count); | |
77 WTF_EXPORT_PRIVATE static void dumpStats(); | |
78 }; | |
79 | |
80 #endif | |
81 | |
82 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
83 class HashTable; | |
84 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
85 class HashTableIterator; | |
86 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
87 class HashTableConstIterator; | |
88 | |
89 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
90 void addIterator(const HashTable<Key, Value, Extractor, HashFunctions, Trait
s, KeyTraits>*, | |
91 HashTableConstIterator<Key, Value, Extractor, HashFunctions, Traits, Key
Traits>*); | |
92 | |
93 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
94 void removeIterator(HashTableConstIterator<Key, Value, Extractor, HashFuncti
ons, Traits, KeyTraits>*); | |
95 | |
96 #if !CHECK_HASHTABLE_ITERATORS | |
97 | |
98 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
99 inline void addIterator(const HashTable<Key, Value, Extractor, HashFunctions
, Traits, KeyTraits>*, | |
100 HashTableConstIterator<Key, Value, Extractor, HashFunctions, Traits, Key
Traits>*) { } | |
101 | |
102 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
103 inline void removeIterator(HashTableConstIterator<Key, Value, Extractor, Has
hFunctions, Traits, KeyTraits>*) { } | |
104 | |
105 #endif | |
106 | |
107 typedef enum { HashItemKnownGood } HashItemKnownGoodTag; | |
108 | |
109 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
110 class HashTableConstIterator { | |
111 private: | |
112 typedef HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTrait
s> HashTableType; | |
113 typedef HashTableIterator<Key, Value, Extractor, HashFunctions, Traits,
KeyTraits> iterator; | |
114 typedef HashTableConstIterator<Key, Value, Extractor, HashFunctions, Tra
its, KeyTraits> const_iterator; | |
115 typedef Value ValueType; | |
116 typedef const ValueType& ReferenceType; | |
117 typedef const ValueType* PointerType; | |
118 | |
119 friend class HashTable<Key, Value, Extractor, HashFunctions, Traits, Key
Traits>; | |
120 friend class HashTableIterator<Key, Value, Extractor, HashFunctions, Tra
its, KeyTraits>; | |
121 | |
122 void skipEmptyBuckets() | |
123 { | |
124 while (m_position != m_endPosition && HashTableType::isEmptyOrDelete
dBucket(*m_position)) | |
125 ++m_position; | |
126 } | |
127 | |
128 HashTableConstIterator(const HashTableType* table, PointerType position,
PointerType endPosition) | |
129 : m_position(position), m_endPosition(endPosition) | |
130 { | |
131 addIterator(table, this); | |
132 skipEmptyBuckets(); | |
133 } | |
134 | |
135 HashTableConstIterator(const HashTableType* table, PointerType position,
PointerType endPosition, HashItemKnownGoodTag) | |
136 : m_position(position), m_endPosition(endPosition) | |
137 { | |
138 addIterator(table, this); | |
139 } | |
140 | |
141 public: | |
142 HashTableConstIterator() | |
143 { | |
144 addIterator(static_cast<const HashTableType*>(0), this); | |
145 } | |
146 | |
147 // default copy, assignment and destructor are OK if CHECK_HASHTABLE_ITE
RATORS is 0 | |
148 | |
149 #if CHECK_HASHTABLE_ITERATORS | |
150 ~HashTableConstIterator() | |
151 { | |
152 removeIterator(this); | |
153 } | |
154 | |
155 HashTableConstIterator(const const_iterator& other) | |
156 : m_position(other.m_position), m_endPosition(other.m_endPosition) | |
157 { | |
158 addIterator(other.m_table, this); | |
159 } | |
160 | |
161 const_iterator& operator=(const const_iterator& other) | |
162 { | |
163 m_position = other.m_position; | |
164 m_endPosition = other.m_endPosition; | |
165 | |
166 removeIterator(this); | |
167 addIterator(other.m_table, this); | |
168 | |
169 return *this; | |
170 } | |
171 #endif | |
172 | |
173 PointerType get() const | |
174 { | |
175 checkValidity(); | |
176 return m_position; | |
177 } | |
178 ReferenceType operator*() const { return *get(); } | |
179 PointerType operator->() const { return get(); } | |
180 | |
181 const_iterator& operator++() | |
182 { | |
183 checkValidity(); | |
184 ASSERT(m_position != m_endPosition); | |
185 ++m_position; | |
186 skipEmptyBuckets(); | |
187 return *this; | |
188 } | |
189 | |
190 // postfix ++ intentionally omitted | |
191 | |
192 // Comparison. | |
193 bool operator==(const const_iterator& other) const | |
194 { | |
195 checkValidity(other); | |
196 return m_position == other.m_position; | |
197 } | |
198 bool operator!=(const const_iterator& other) const | |
199 { | |
200 checkValidity(other); | |
201 return m_position != other.m_position; | |
202 } | |
203 bool operator==(const iterator& other) const | |
204 { | |
205 return *this == static_cast<const_iterator>(other); | |
206 } | |
207 bool operator!=(const iterator& other) const | |
208 { | |
209 return *this != static_cast<const_iterator>(other); | |
210 } | |
211 | |
212 private: | |
213 void checkValidity() const | |
214 { | |
215 #if CHECK_HASHTABLE_ITERATORS | |
216 ASSERT(m_table); | |
217 #endif | |
218 } | |
219 | |
220 | |
221 #if CHECK_HASHTABLE_ITERATORS | |
222 void checkValidity(const const_iterator& other) const | |
223 { | |
224 ASSERT(m_table); | |
225 ASSERT_UNUSED(other, other.m_table); | |
226 ASSERT(m_table == other.m_table); | |
227 } | |
228 #else | |
229 void checkValidity(const const_iterator&) const { } | |
230 #endif | |
231 | |
232 PointerType m_position; | |
233 PointerType m_endPosition; | |
234 | |
235 #if CHECK_HASHTABLE_ITERATORS | |
236 public: | |
237 // Any modifications of the m_next or m_previous of an iterator that is
in a linked list of a HashTable::m_iterator, | |
238 // should be guarded with m_table->m_mutex. | |
239 mutable const HashTableType* m_table; | |
240 mutable const_iterator* m_next; | |
241 mutable const_iterator* m_previous; | |
242 #endif | |
243 }; | |
244 | |
245 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
246 class HashTableIterator { | |
247 private: | |
248 typedef HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTrait
s> HashTableType; | |
249 typedef HashTableIterator<Key, Value, Extractor, HashFunctions, Traits,
KeyTraits> iterator; | |
250 typedef HashTableConstIterator<Key, Value, Extractor, HashFunctions, Tra
its, KeyTraits> const_iterator; | |
251 typedef Value ValueType; | |
252 typedef ValueType& ReferenceType; | |
253 typedef ValueType* PointerType; | |
254 | |
255 friend class HashTable<Key, Value, Extractor, HashFunctions, Traits, Key
Traits>; | |
256 | |
257 HashTableIterator(HashTableType* table, PointerType pos, PointerType end
) : m_iterator(table, pos, end) { } | |
258 HashTableIterator(HashTableType* table, PointerType pos, PointerType end
, HashItemKnownGoodTag tag) : m_iterator(table, pos, end, tag) { } | |
259 | |
260 public: | |
261 HashTableIterator() { } | |
262 | |
263 // default copy, assignment and destructor are OK | |
264 | |
265 PointerType get() const { return const_cast<PointerType>(m_iterator.get(
)); } | |
266 ReferenceType operator*() const { return *get(); } | |
267 PointerType operator->() const { return get(); } | |
268 | |
269 iterator& operator++() { ++m_iterator; return *this; } | |
270 | |
271 // postfix ++ intentionally omitted | |
272 | |
273 // Comparison. | |
274 bool operator==(const iterator& other) const { return m_iterator == othe
r.m_iterator; } | |
275 bool operator!=(const iterator& other) const { return m_iterator != othe
r.m_iterator; } | |
276 bool operator==(const const_iterator& other) const { return m_iterator =
= other; } | |
277 bool operator!=(const const_iterator& other) const { return m_iterator !
= other; } | |
278 | |
279 operator const_iterator() const { return m_iterator; } | |
280 | |
281 private: | |
282 const_iterator m_iterator; | |
283 }; | |
284 | |
285 using std::swap; | |
286 | |
287 // Work around MSVC's standard library, whose swap for pairs does not swap b
y component. | |
288 template<typename T> inline void hashTableSwap(T& a, T& b) | |
289 { | |
290 swap(a, b); | |
291 } | |
292 | |
293 template<typename T, typename U> inline void hashTableSwap(KeyValuePair<T, U
>& a, KeyValuePair<T, U>& b) | |
294 { | |
295 swap(a.key, b.key); | |
296 swap(a.value, b.value); | |
297 } | |
298 | |
299 template<typename T, bool useSwap> struct Mover; | |
300 template<typename T> struct Mover<T, true> { static void move(T& from, T& to
) { hashTableSwap(from, to); } }; | |
301 template<typename T> struct Mover<T, false> { static void move(T& from, T& t
o) { to = from; } }; | |
302 | |
303 template<typename HashFunctions> class IdentityHashTranslator { | |
304 public: | |
305 template<typename T> static unsigned hash(const T& key) { return HashFun
ctions::hash(key); } | |
306 template<typename T> static bool equal(const T& a, const T& b) { return
HashFunctions::equal(a, b); } | |
307 template<typename T, typename U> static void translate(T& location, cons
t U&, const T& value) { location = value; } | |
308 }; | |
309 | |
310 template<typename IteratorType> struct HashTableAddResult { | |
311 HashTableAddResult(IteratorType iter, bool isNewEntry) : iterator(iter),
isNewEntry(isNewEntry) { } | |
312 IteratorType iterator; | |
313 bool isNewEntry; | |
314 }; | |
315 | |
316 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
317 class HashTable { | |
318 public: | |
319 typedef HashTableIterator<Key, Value, Extractor, HashFunctions, Traits,
KeyTraits> iterator; | |
320 typedef HashTableConstIterator<Key, Value, Extractor, HashFunctions, Tra
its, KeyTraits> const_iterator; | |
321 typedef Traits ValueTraits; | |
322 typedef Key KeyType; | |
323 typedef Value ValueType; | |
324 typedef IdentityHashTranslator<HashFunctions> IdentityTranslatorType; | |
325 typedef HashTableAddResult<iterator> AddResult; | |
326 | |
327 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
328 struct Stats { | |
329 Stats() | |
330 : numAccesses(0) | |
331 , numRehashes(0) | |
332 , numRemoves(0) | |
333 , numReinserts(0) | |
334 , maxCollisions(0) | |
335 , numCollisions(0) | |
336 , collisionGraph() | |
337 { | |
338 } | |
339 | |
340 int numAccesses; | |
341 int numRehashes; | |
342 int numRemoves; | |
343 int numReinserts; | |
344 | |
345 int maxCollisions; | |
346 int numCollisions; | |
347 int collisionGraph[4096]; | |
348 | |
349 void recordCollisionAtCount(int count) | |
350 { | |
351 if (count > maxCollisions) | |
352 maxCollisions = count; | |
353 numCollisions++; | |
354 collisionGraph[count]++; | |
355 } | |
356 | |
357 void dumpStats() | |
358 { | |
359 dataLogF("\nWTF::HashTable::Stats dump\n\n"); | |
360 dataLogF("%d accesses\n", numAccesses); | |
361 dataLogF("%d total collisions, average %.2f probes per access\n"
, numCollisions, 1.0 * (numAccesses + numCollisions) / numAccesses); | |
362 dataLogF("longest collision chain: %d\n", maxCollisions); | |
363 for (int i = 1; i <= maxCollisions; i++) { | |
364 dataLogF(" %d lookups with exactly %d collisions (%.2f%% ,
%.2f%% with this many or more)\n", collisionGraph[i], i, 100.0 * (collisionGraph
[i] - collisionGraph[i+1]) / numAccesses, 100.0 * collisionGraph[i] / numAccesse
s); | |
365 } | |
366 dataLogF("%d rehashes\n", numRehashes); | |
367 dataLogF("%d reinserts\n", numReinserts); | |
368 } | |
369 }; | |
370 #endif | |
371 | |
372 HashTable(); | |
373 ~HashTable() | |
374 { | |
375 invalidateIterators(); | |
376 if (m_table) | |
377 deallocateTable(m_table, m_tableSize); | |
378 #if CHECK_HASHTABLE_USE_AFTER_DESTRUCTION | |
379 m_table = (ValueType*)(uintptr_t)0xbbadbeef; | |
380 #endif | |
381 } | |
382 | |
383 HashTable(const HashTable&); | |
384 void swap(HashTable&); | |
385 HashTable& operator=(const HashTable&); | |
386 | |
387 // When the hash table is empty, just return the same iterator for end a
s for begin. | |
388 // This is more efficient because we don't have to skip all the empty an
d deleted | |
389 // buckets, and iterating an empty table is a common case that's worth o
ptimizing. | |
390 iterator begin() { return isEmpty() ? end() : makeIterator(m_table); } | |
391 iterator end() { return makeKnownGoodIterator(m_table + m_tableSize); } | |
392 const_iterator begin() const { return isEmpty() ? end() : makeConstItera
tor(m_table); } | |
393 const_iterator end() const { return makeKnownGoodConstIterator(m_table +
m_tableSize); } | |
394 | |
395 int size() const { return m_keyCount; } | |
396 int capacity() const { return m_tableSize; } | |
397 bool isEmpty() const { return !m_keyCount; } | |
398 | |
399 AddResult add(const ValueType& value) { return add<IdentityTranslatorTyp
e>(Extractor::extract(value), value); } | |
400 | |
401 // A special version of add() that finds the object by hashing and compa
ring | |
402 // with some other type, to avoid the cost of type conversion if the obj
ect is already | |
403 // in the table. | |
404 template<typename HashTranslator, typename T, typename Extra> AddResult
add(const T& key, const Extra&); | |
405 template<typename HashTranslator, typename T, typename Extra> AddResult
addPassingHashCode(const T& key, const Extra&); | |
406 | |
407 iterator find(const KeyType& key) { return find<IdentityTranslatorType>(
key); } | |
408 const_iterator find(const KeyType& key) const { return find<IdentityTran
slatorType>(key); } | |
409 bool contains(const KeyType& key) const { return contains<IdentityTransl
atorType>(key); } | |
410 | |
411 template<typename HashTranslator, typename T> iterator find(const T&); | |
412 template<typename HashTranslator, typename T> const_iterator find(const
T&) const; | |
413 template<typename HashTranslator, typename T> bool contains(const T&) co
nst; | |
414 | |
415 void remove(const KeyType&); | |
416 void remove(iterator); | |
417 void removeWithoutEntryConsistencyCheck(iterator); | |
418 void removeWithoutEntryConsistencyCheck(const_iterator); | |
419 void clear(); | |
420 | |
421 static bool isEmptyBucket(const ValueType& value) { return isHashTraitsE
mptyValue<KeyTraits>(Extractor::extract(value)); } | |
422 static bool isDeletedBucket(const ValueType& value) { return KeyTraits::
isDeletedValue(Extractor::extract(value)); } | |
423 static bool isEmptyOrDeletedBucket(const ValueType& value) { return isEm
ptyBucket(value) || isDeletedBucket(value); } | |
424 | |
425 ValueType* lookup(const Key& key) { return lookup<IdentityTranslatorType
>(key); } | |
426 template<typename HashTranslator, typename T> ValueType* lookup(const T&
); | |
427 | |
428 #if !ASSERT_DISABLED | |
429 void checkTableConsistency() const; | |
430 #else | |
431 static void checkTableConsistency() { } | |
432 #endif | |
433 #if CHECK_HASHTABLE_CONSISTENCY | |
434 void internalCheckTableConsistency() const { checkTableConsistency(); } | |
435 void internalCheckTableConsistencyExceptSize() const { checkTableConsist
encyExceptSize(); } | |
436 #else | |
437 static void internalCheckTableConsistencyExceptSize() { } | |
438 static void internalCheckTableConsistency() { } | |
439 #endif | |
440 | |
441 private: | |
442 static ValueType* allocateTable(int size); | |
443 static void deallocateTable(ValueType* table, int size); | |
444 | |
445 typedef std::pair<ValueType*, bool> LookupType; | |
446 typedef std::pair<LookupType, unsigned> FullLookupType; | |
447 | |
448 LookupType lookupForWriting(const Key& key) { return lookupForWriting<Id
entityTranslatorType>(key); }; | |
449 template<typename HashTranslator, typename T> FullLookupType fullLookupF
orWriting(const T&); | |
450 template<typename HashTranslator, typename T> LookupType lookupForWritin
g(const T&); | |
451 | |
452 template<typename HashTranslator, typename T> void checkKey(const T&); | |
453 | |
454 void removeAndInvalidateWithoutEntryConsistencyCheck(ValueType*); | |
455 void removeAndInvalidate(ValueType*); | |
456 void remove(ValueType*); | |
457 | |
458 bool shouldExpand() const { return (m_keyCount + m_deletedCount) * m_max
Load >= m_tableSize; } | |
459 bool mustRehashInPlace() const { return m_keyCount * m_minLoad < m_table
Size * 2; } | |
460 bool shouldShrink() const { return m_keyCount * m_minLoad < m_tableSize
&& m_tableSize > KeyTraits::minimumTableSize; } | |
461 void expand(); | |
462 void shrink() { rehash(m_tableSize / 2); } | |
463 | |
464 void rehash(int newTableSize); | |
465 void reinsert(ValueType&); | |
466 | |
467 static void initializeBucket(ValueType& bucket); | |
468 static void deleteBucket(ValueType& bucket) { bucket.~ValueType(); Trait
s::constructDeletedValue(bucket); } | |
469 | |
470 FullLookupType makeLookupResult(ValueType* position, bool found, unsigne
d hash) | |
471 { return FullLookupType(LookupType(position, found), hash); } | |
472 | |
473 iterator makeIterator(ValueType* pos) { return iterator(this, pos, m_tab
le + m_tableSize); } | |
474 const_iterator makeConstIterator(ValueType* pos) const { return const_it
erator(this, pos, m_table + m_tableSize); } | |
475 iterator makeKnownGoodIterator(ValueType* pos) { return iterator(this, p
os, m_table + m_tableSize, HashItemKnownGood); } | |
476 const_iterator makeKnownGoodConstIterator(ValueType* pos) const { return
const_iterator(this, pos, m_table + m_tableSize, HashItemKnownGood); } | |
477 | |
478 #if !ASSERT_DISABLED | |
479 void checkTableConsistencyExceptSize() const; | |
480 #else | |
481 static void checkTableConsistencyExceptSize() { } | |
482 #endif | |
483 | |
484 #if CHECK_HASHTABLE_ITERATORS | |
485 void invalidateIterators(); | |
486 #else | |
487 static void invalidateIterators() { } | |
488 #endif | |
489 | |
490 static const int m_maxLoad = 2; | |
491 static const int m_minLoad = 6; | |
492 | |
493 ValueType* m_table; | |
494 int m_tableSize; | |
495 int m_tableSizeMask; | |
496 int m_keyCount; | |
497 int m_deletedCount; | |
498 | |
499 #if CHECK_HASHTABLE_ITERATORS | |
500 public: | |
501 // All access to m_iterators should be guarded with m_mutex. | |
502 mutable const_iterator* m_iterators; | |
503 // Use OwnPtr so HashTable can still be memmove'd or memcpy'ed. | |
504 mutable OwnPtr<Mutex> m_mutex; | |
505 #endif | |
506 | |
507 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
508 public: | |
509 mutable OwnPtr<Stats> m_stats; | |
510 #endif | |
511 }; | |
512 | |
513 // Set all the bits to one after the most significant bit: 00110101010 -> 00
111111111. | |
514 template<unsigned size> struct OneifyLowBits; | |
515 template<> | |
516 struct OneifyLowBits<0> { | |
517 static const unsigned value = 0; | |
518 }; | |
519 template<unsigned number> | |
520 struct OneifyLowBits { | |
521 static const unsigned value = number | OneifyLowBits<(number >> 1)>::val
ue; | |
522 }; | |
523 // Compute the first power of two integer that is an upper bound of the para
meter 'number'. | |
524 template<unsigned number> | |
525 struct UpperPowerOfTwoBound { | |
526 static const unsigned value = (OneifyLowBits<number - 1>::value + 1) * 2
; | |
527 }; | |
528 | |
529 // Because power of two numbers are the limit of maxLoad, their capacity is
twice the | |
530 // UpperPowerOfTwoBound, or 4 times their values. | |
531 template<unsigned size, bool isPowerOfTwo> struct HashTableCapacityForSizeSp
litter; | |
532 template<unsigned size> | |
533 struct HashTableCapacityForSizeSplitter<size, true> { | |
534 static const unsigned value = size * 4; | |
535 }; | |
536 template<unsigned size> | |
537 struct HashTableCapacityForSizeSplitter<size, false> { | |
538 static const unsigned value = UpperPowerOfTwoBound<size>::value; | |
539 }; | |
540 | |
541 // HashTableCapacityForSize computes the upper power of two capacity to hold
the size parameter. | |
542 // This is done at compile time to initialize the HashTraits. | |
543 template<unsigned size> | |
544 struct HashTableCapacityForSize { | |
545 static const unsigned value = HashTableCapacityForSizeSplitter<size, !(s
ize & (size - 1))>::value; | |
546 COMPILE_ASSERT(size > 0, HashTableNonZeroMinimumCapacity); | |
547 COMPILE_ASSERT(!static_cast<int>(value >> 31), HashTableNoCapacityOverfl
ow); | |
548 COMPILE_ASSERT(value > (2 * size), HashTableCapacityHoldsContentSize); | |
549 }; | |
550 | |
551 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
552 inline HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::H
ashTable() | |
553 : m_table(0) | |
554 , m_tableSize(0) | |
555 , m_tableSizeMask(0) | |
556 , m_keyCount(0) | |
557 , m_deletedCount(0) | |
558 #if CHECK_HASHTABLE_ITERATORS | |
559 , m_iterators(0) | |
560 , m_mutex(adoptPtr(new Mutex)) | |
561 #endif | |
562 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
563 , m_stats(adoptPtr(new Stats)) | |
564 #endif | |
565 { | |
566 } | |
567 | |
568 inline unsigned doubleHash(unsigned key) | |
569 { | |
570 key = ~key + (key >> 23); | |
571 key ^= (key << 12); | |
572 key ^= (key >> 7); | |
573 key ^= (key << 2); | |
574 key ^= (key >> 20); | |
575 return key; | |
576 } | |
577 | |
578 #if ASSERT_DISABLED | |
579 | |
580 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
581 template<typename HashTranslator, typename T> | |
582 inline void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTrait
s>::checkKey(const T&) | |
583 { | |
584 } | |
585 | |
586 #else | |
587 | |
588 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
589 template<typename HashTranslator, typename T> | |
590 void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::che
ckKey(const T& key) | |
591 { | |
592 if (!HashFunctions::safeToCompareToEmptyOrDeleted) | |
593 return; | |
594 ASSERT(!HashTranslator::equal(KeyTraits::emptyValue(), key)); | |
595 AlignedBuffer<sizeof(ValueType), WTF_ALIGN_OF(ValueType)> deletedValueBu
ffer; | |
596 ValueType* deletedValuePtr = reinterpret_cast_ptr<ValueType*>(deletedVal
ueBuffer.buffer); | |
597 ValueType& deletedValue = *deletedValuePtr; | |
598 Traits::constructDeletedValue(deletedValue); | |
599 ASSERT(!HashTranslator::equal(Extractor::extract(deletedValue), key)); | |
600 } | |
601 | |
602 #endif | |
603 | |
604 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
605 template<typename HashTranslator, typename T> | |
606 inline Value* HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTra
its>::lookup(const T& key) | |
607 { | |
608 checkKey<HashTranslator>(key); | |
609 | |
610 int k = 0; | |
611 int sizeMask = m_tableSizeMask; | |
612 ValueType* table = m_table; | |
613 unsigned h = HashTranslator::hash(key); | |
614 int i = h & sizeMask; | |
615 | |
616 if (!table) | |
617 return 0; | |
618 | |
619 #if DUMP_HASHTABLE_STATS | |
620 atomicIncrement(&HashTableStats::numAccesses); | |
621 int probeCount = 0; | |
622 #endif | |
623 | |
624 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
625 ++m_stats->numAccesses; | |
626 int perTableProbeCount = 0; | |
627 #endif | |
628 | |
629 while (1) { | |
630 ValueType* entry = table + i; | |
631 | |
632 // we count on the compiler to optimize out this branch | |
633 if (HashFunctions::safeToCompareToEmptyOrDeleted) { | |
634 if (HashTranslator::equal(Extractor::extract(*entry), key)) | |
635 return entry; | |
636 | |
637 if (isEmptyBucket(*entry)) | |
638 return 0; | |
639 } else { | |
640 if (isEmptyBucket(*entry)) | |
641 return 0; | |
642 | |
643 if (!isDeletedBucket(*entry) && HashTranslator::equal(Extractor:
:extract(*entry), key)) | |
644 return entry; | |
645 } | |
646 #if DUMP_HASHTABLE_STATS | |
647 ++probeCount; | |
648 HashTableStats::recordCollisionAtCount(probeCount); | |
649 #endif | |
650 | |
651 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
652 ++perTableProbeCount; | |
653 m_stats->recordCollisionAtCount(perTableProbeCount); | |
654 #endif | |
655 | |
656 if (k == 0) | |
657 k = 1 | doubleHash(h); | |
658 i = (i + k) & sizeMask; | |
659 } | |
660 } | |
661 | |
662 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
663 template<typename HashTranslator, typename T> | |
664 inline typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyT
raits>::LookupType HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTr
aits>::lookupForWriting(const T& key) | |
665 { | |
666 ASSERT(m_table); | |
667 checkKey<HashTranslator>(key); | |
668 | |
669 int k = 0; | |
670 ValueType* table = m_table; | |
671 int sizeMask = m_tableSizeMask; | |
672 unsigned h = HashTranslator::hash(key); | |
673 int i = h & sizeMask; | |
674 | |
675 #if DUMP_HASHTABLE_STATS | |
676 atomicIncrement(&HashTableStats::numAccesses); | |
677 int probeCount = 0; | |
678 #endif | |
679 | |
680 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
681 ++m_stats->numAccesses; | |
682 int perTableProbeCount = 0; | |
683 #endif | |
684 | |
685 ValueType* deletedEntry = 0; | |
686 | |
687 while (1) { | |
688 ValueType* entry = table + i; | |
689 | |
690 // we count on the compiler to optimize out this branch | |
691 if (HashFunctions::safeToCompareToEmptyOrDeleted) { | |
692 if (isEmptyBucket(*entry)) | |
693 return LookupType(deletedEntry ? deletedEntry : entry, false
); | |
694 | |
695 if (HashTranslator::equal(Extractor::extract(*entry), key)) | |
696 return LookupType(entry, true); | |
697 | |
698 if (isDeletedBucket(*entry)) | |
699 deletedEntry = entry; | |
700 } else { | |
701 if (isEmptyBucket(*entry)) | |
702 return LookupType(deletedEntry ? deletedEntry : entry, false
); | |
703 | |
704 if (isDeletedBucket(*entry)) | |
705 deletedEntry = entry; | |
706 else if (HashTranslator::equal(Extractor::extract(*entry), key)) | |
707 return LookupType(entry, true); | |
708 } | |
709 #if DUMP_HASHTABLE_STATS | |
710 ++probeCount; | |
711 HashTableStats::recordCollisionAtCount(probeCount); | |
712 #endif | |
713 | |
714 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
715 ++perTableProbeCount; | |
716 m_stats->recordCollisionAtCount(perTableProbeCount); | |
717 #endif | |
718 | |
719 if (k == 0) | |
720 k = 1 | doubleHash(h); | |
721 i = (i + k) & sizeMask; | |
722 } | |
723 } | |
724 | |
725 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
726 template<typename HashTranslator, typename T> | |
727 inline typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyT
raits>::FullLookupType HashTable<Key, Value, Extractor, HashFunctions, Traits, K
eyTraits>::fullLookupForWriting(const T& key) | |
728 { | |
729 ASSERT(m_table); | |
730 checkKey<HashTranslator>(key); | |
731 | |
732 int k = 0; | |
733 ValueType* table = m_table; | |
734 int sizeMask = m_tableSizeMask; | |
735 unsigned h = HashTranslator::hash(key); | |
736 int i = h & sizeMask; | |
737 | |
738 #if DUMP_HASHTABLE_STATS | |
739 atomicIncrement(&HashTableStats::numAccesses); | |
740 int probeCount = 0; | |
741 #endif | |
742 | |
743 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
744 ++m_stats->numAccesses; | |
745 int perTableProbeCount = 0; | |
746 #endif | |
747 | |
748 ValueType* deletedEntry = 0; | |
749 | |
750 while (1) { | |
751 ValueType* entry = table + i; | |
752 | |
753 // we count on the compiler to optimize out this branch | |
754 if (HashFunctions::safeToCompareToEmptyOrDeleted) { | |
755 if (isEmptyBucket(*entry)) | |
756 return makeLookupResult(deletedEntry ? deletedEntry : entry,
false, h); | |
757 | |
758 if (HashTranslator::equal(Extractor::extract(*entry), key)) | |
759 return makeLookupResult(entry, true, h); | |
760 | |
761 if (isDeletedBucket(*entry)) | |
762 deletedEntry = entry; | |
763 } else { | |
764 if (isEmptyBucket(*entry)) | |
765 return makeLookupResult(deletedEntry ? deletedEntry : entry,
false, h); | |
766 | |
767 if (isDeletedBucket(*entry)) | |
768 deletedEntry = entry; | |
769 else if (HashTranslator::equal(Extractor::extract(*entry), key)) | |
770 return makeLookupResult(entry, true, h); | |
771 } | |
772 #if DUMP_HASHTABLE_STATS | |
773 ++probeCount; | |
774 HashTableStats::recordCollisionAtCount(probeCount); | |
775 #endif | |
776 | |
777 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
778 ++perTableProbeCount; | |
779 m_stats->recordCollisionAtCount(perTableProbeCount); | |
780 #endif | |
781 | |
782 if (k == 0) | |
783 k = 1 | doubleHash(h); | |
784 i = (i + k) & sizeMask; | |
785 } | |
786 } | |
787 | |
788 template<bool emptyValueIsZero> struct HashTableBucketInitializer; | |
789 | |
790 template<> struct HashTableBucketInitializer<false> { | |
791 template<typename Traits, typename Value> static void initialize(Value&
bucket) | |
792 { | |
793 new (NotNull, &bucket) Value(Traits::emptyValue()); | |
794 } | |
795 }; | |
796 | |
797 template<> struct HashTableBucketInitializer<true> { | |
798 template<typename Traits, typename Value> static void initialize(Value&
bucket) | |
799 { | |
800 // This initializes the bucket without copying the empty value. | |
801 // That makes it possible to use this with types that don't support
copying. | |
802 // The memset to 0 looks like a slow operation but is optimized by t
he compilers. | |
803 memset(&bucket, 0, sizeof(bucket)); | |
804 } | |
805 }; | |
806 | |
807 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
808 inline void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTrait
s>::initializeBucket(ValueType& bucket) | |
809 { | |
810 HashTableBucketInitializer<Traits::emptyValueIsZero>::template initializ
e<Traits>(bucket); | |
811 } | |
812 | |
813 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
814 template<typename HashTranslator, typename T, typename Extra> | |
815 inline typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyT
raits>::AddResult HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTra
its>::add(const T& key, const Extra& extra) | |
816 { | |
817 checkKey<HashTranslator>(key); | |
818 | |
819 invalidateIterators(); | |
820 | |
821 if (!m_table) | |
822 expand(); | |
823 | |
824 internalCheckTableConsistency(); | |
825 | |
826 ASSERT(m_table); | |
827 | |
828 int k = 0; | |
829 ValueType* table = m_table; | |
830 int sizeMask = m_tableSizeMask; | |
831 unsigned h = HashTranslator::hash(key); | |
832 int i = h & sizeMask; | |
833 | |
834 #if DUMP_HASHTABLE_STATS | |
835 atomicIncrement(&HashTableStats::numAccesses); | |
836 int probeCount = 0; | |
837 #endif | |
838 | |
839 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
840 ++m_stats->numAccesses; | |
841 int perTableProbeCount = 0; | |
842 #endif | |
843 | |
844 ValueType* deletedEntry = 0; | |
845 ValueType* entry; | |
846 while (1) { | |
847 entry = table + i; | |
848 | |
849 // we count on the compiler to optimize out this branch | |
850 if (HashFunctions::safeToCompareToEmptyOrDeleted) { | |
851 if (isEmptyBucket(*entry)) | |
852 break; | |
853 | |
854 if (HashTranslator::equal(Extractor::extract(*entry), key)) | |
855 return AddResult(makeKnownGoodIterator(entry), false); | |
856 | |
857 if (isDeletedBucket(*entry)) | |
858 deletedEntry = entry; | |
859 } else { | |
860 if (isEmptyBucket(*entry)) | |
861 break; | |
862 | |
863 if (isDeletedBucket(*entry)) | |
864 deletedEntry = entry; | |
865 else if (HashTranslator::equal(Extractor::extract(*entry), key)) | |
866 return AddResult(makeKnownGoodIterator(entry), false); | |
867 } | |
868 #if DUMP_HASHTABLE_STATS | |
869 ++probeCount; | |
870 HashTableStats::recordCollisionAtCount(probeCount); | |
871 #endif | |
872 | |
873 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
874 ++perTableProbeCount; | |
875 m_stats->recordCollisionAtCount(perTableProbeCount); | |
876 #endif | |
877 | |
878 if (k == 0) | |
879 k = 1 | doubleHash(h); | |
880 i = (i + k) & sizeMask; | |
881 } | |
882 | |
883 if (deletedEntry) { | |
884 initializeBucket(*deletedEntry); | |
885 entry = deletedEntry; | |
886 --m_deletedCount; | |
887 } | |
888 | |
889 HashTranslator::translate(*entry, key, extra); | |
890 | |
891 ++m_keyCount; | |
892 | |
893 if (shouldExpand()) { | |
894 // FIXME: This makes an extra copy on expand. Probably not that bad
since | |
895 // expand is rare, but would be better to have a version of expand t
hat can | |
896 // follow a pivot entry and return the new position. | |
897 KeyType enteredKey = Extractor::extract(*entry); | |
898 expand(); | |
899 AddResult result(find(enteredKey), true); | |
900 ASSERT(result.iterator != end()); | |
901 return result; | |
902 } | |
903 | |
904 internalCheckTableConsistency(); | |
905 | |
906 return AddResult(makeKnownGoodIterator(entry), true); | |
907 } | |
908 | |
909 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
910 template<typename HashTranslator, typename T, typename Extra> | |
911 inline typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyT
raits>::AddResult HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTra
its>::addPassingHashCode(const T& key, const Extra& extra) | |
912 { | |
913 checkKey<HashTranslator>(key); | |
914 | |
915 invalidateIterators(); | |
916 | |
917 if (!m_table) | |
918 expand(); | |
919 | |
920 internalCheckTableConsistency(); | |
921 | |
922 FullLookupType lookupResult = fullLookupForWriting<HashTranslator>(key); | |
923 | |
924 ValueType* entry = lookupResult.first.first; | |
925 bool found = lookupResult.first.second; | |
926 unsigned h = lookupResult.second; | |
927 | |
928 if (found) | |
929 return AddResult(makeKnownGoodIterator(entry), false); | |
930 | |
931 if (isDeletedBucket(*entry)) { | |
932 initializeBucket(*entry); | |
933 --m_deletedCount; | |
934 } | |
935 | |
936 HashTranslator::translate(*entry, key, extra, h); | |
937 ++m_keyCount; | |
938 if (shouldExpand()) { | |
939 // FIXME: This makes an extra copy on expand. Probably not that bad
since | |
940 // expand is rare, but would be better to have a version of expand t
hat can | |
941 // follow a pivot entry and return the new position. | |
942 KeyType enteredKey = Extractor::extract(*entry); | |
943 expand(); | |
944 AddResult result(find(enteredKey), true); | |
945 ASSERT(result.iterator != end()); | |
946 return result; | |
947 } | |
948 | |
949 internalCheckTableConsistency(); | |
950 | |
951 return AddResult(makeKnownGoodIterator(entry), true); | |
952 } | |
953 | |
954 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
955 inline void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTrait
s>::reinsert(ValueType& entry) | |
956 { | |
957 ASSERT(m_table); | |
958 ASSERT(!lookupForWriting(Extractor::extract(entry)).second); | |
959 ASSERT(!isDeletedBucket(*(lookupForWriting(Extractor::extract(entry)).fi
rst))); | |
960 #if DUMP_HASHTABLE_STATS | |
961 atomicIncrement(&HashTableStats::numReinserts); | |
962 #endif | |
963 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
964 ++m_stats->numReinserts; | |
965 #endif | |
966 | |
967 Mover<ValueType, Traits::needsDestruction>::move(entry, *lookupForWritin
g(Extractor::extract(entry)).first); | |
968 } | |
969 | |
970 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
971 template <typename HashTranslator, typename T> | |
972 typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>:
:iterator HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::fi
nd(const T& key) | |
973 { | |
974 if (!m_table) | |
975 return end(); | |
976 | |
977 ValueType* entry = lookup<HashTranslator>(key); | |
978 if (!entry) | |
979 return end(); | |
980 | |
981 return makeKnownGoodIterator(entry); | |
982 } | |
983 | |
984 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
985 template <typename HashTranslator, typename T> | |
986 typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>:
:const_iterator HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTrait
s>::find(const T& key) const | |
987 { | |
988 if (!m_table) | |
989 return end(); | |
990 | |
991 ValueType* entry = const_cast<HashTable*>(this)->lookup<HashTranslator>(
key); | |
992 if (!entry) | |
993 return end(); | |
994 | |
995 return makeKnownGoodConstIterator(entry); | |
996 } | |
997 | |
998 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
999 template <typename HashTranslator, typename T> | |
1000 bool HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::con
tains(const T& key) const | |
1001 { | |
1002 if (!m_table) | |
1003 return false; | |
1004 | |
1005 return const_cast<HashTable*>(this)->lookup<HashTranslator>(key); | |
1006 } | |
1007 | |
1008 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
1009 void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::rem
oveAndInvalidateWithoutEntryConsistencyCheck(ValueType* pos) | |
1010 { | |
1011 invalidateIterators(); | |
1012 remove(pos); | |
1013 } | |
1014 | |
1015 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
1016 void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::rem
oveAndInvalidate(ValueType* pos) | |
1017 { | |
1018 invalidateIterators(); | |
1019 internalCheckTableConsistency(); | |
1020 remove(pos); | |
1021 } | |
1022 | |
1023 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
1024 void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::rem
ove(ValueType* pos) | |
1025 { | |
1026 #if DUMP_HASHTABLE_STATS | |
1027 atomicIncrement(&HashTableStats::numRemoves); | |
1028 #endif | |
1029 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
1030 ++m_stats->numRemoves; | |
1031 #endif | |
1032 | |
1033 deleteBucket(*pos); | |
1034 ++m_deletedCount; | |
1035 --m_keyCount; | |
1036 | |
1037 if (shouldShrink()) | |
1038 shrink(); | |
1039 | |
1040 internalCheckTableConsistency(); | |
1041 } | |
1042 | |
1043 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
1044 inline void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTrait
s>::remove(iterator it) | |
1045 { | |
1046 if (it == end()) | |
1047 return; | |
1048 | |
1049 removeAndInvalidate(const_cast<ValueType*>(it.m_iterator.m_position)); | |
1050 } | |
1051 | |
1052 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
1053 inline void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTrait
s>::removeWithoutEntryConsistencyCheck(iterator it) | |
1054 { | |
1055 if (it == end()) | |
1056 return; | |
1057 | |
1058 removeAndInvalidateWithoutEntryConsistencyCheck(const_cast<ValueType*>(i
t.m_iterator.m_position)); | |
1059 } | |
1060 | |
1061 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
1062 inline void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTrait
s>::removeWithoutEntryConsistencyCheck(const_iterator it) | |
1063 { | |
1064 if (it == end()) | |
1065 return; | |
1066 | |
1067 removeAndInvalidateWithoutEntryConsistencyCheck(const_cast<ValueType*>(i
t.m_position)); | |
1068 } | |
1069 | |
1070 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
1071 inline void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTrait
s>::remove(const KeyType& key) | |
1072 { | |
1073 remove(find(key)); | |
1074 } | |
1075 | |
1076 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
1077 Value* HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::a
llocateTable(int size) | |
1078 { | |
1079 // would use a template member function with explicit specializations he
re, but | |
1080 // gcc doesn't appear to support that | |
1081 if (Traits::emptyValueIsZero) | |
1082 return static_cast<ValueType*>(fastZeroedMalloc(size * sizeof(ValueT
ype))); | |
1083 ValueType* result = static_cast<ValueType*>(fastMalloc(size * sizeof(Val
ueType))); | |
1084 for (int i = 0; i < size; i++) | |
1085 initializeBucket(result[i]); | |
1086 return result; | |
1087 } | |
1088 | |
1089 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
1090 void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::dea
llocateTable(ValueType* table, int size) | |
1091 { | |
1092 if (Traits::needsDestruction) { | |
1093 for (int i = 0; i < size; ++i) { | |
1094 if (!isDeletedBucket(table[i])) | |
1095 table[i].~ValueType(); | |
1096 } | |
1097 } | |
1098 fastFree(table); | |
1099 } | |
1100 | |
1101 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
1102 void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::exp
and() | |
1103 { | |
1104 int newSize; | |
1105 if (m_tableSize == 0) | |
1106 newSize = KeyTraits::minimumTableSize; | |
1107 else if (mustRehashInPlace()) | |
1108 newSize = m_tableSize; | |
1109 else | |
1110 newSize = m_tableSize * 2; | |
1111 | |
1112 rehash(newSize); | |
1113 } | |
1114 | |
1115 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
1116 void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::reh
ash(int newTableSize) | |
1117 { | |
1118 internalCheckTableConsistencyExceptSize(); | |
1119 | |
1120 int oldTableSize = m_tableSize; | |
1121 ValueType* oldTable = m_table; | |
1122 | |
1123 #if DUMP_HASHTABLE_STATS | |
1124 if (oldTableSize != 0) | |
1125 atomicIncrement(&HashTableStats::numRehashes); | |
1126 #endif | |
1127 | |
1128 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
1129 if (oldTableSize != 0) | |
1130 ++m_stats->numRehashes; | |
1131 #endif | |
1132 | |
1133 m_tableSize = newTableSize; | |
1134 m_tableSizeMask = newTableSize - 1; | |
1135 m_table = allocateTable(newTableSize); | |
1136 | |
1137 for (int i = 0; i != oldTableSize; ++i) | |
1138 if (!isEmptyOrDeletedBucket(oldTable[i])) | |
1139 reinsert(oldTable[i]); | |
1140 | |
1141 m_deletedCount = 0; | |
1142 | |
1143 deallocateTable(oldTable, oldTableSize); | |
1144 | |
1145 internalCheckTableConsistency(); | |
1146 } | |
1147 | |
1148 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
1149 void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::cle
ar() | |
1150 { | |
1151 invalidateIterators(); | |
1152 if (!m_table) | |
1153 return; | |
1154 | |
1155 deallocateTable(m_table, m_tableSize); | |
1156 m_table = 0; | |
1157 m_tableSize = 0; | |
1158 m_tableSizeMask = 0; | |
1159 m_keyCount = 0; | |
1160 } | |
1161 | |
1162 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
1163 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::HashTabl
e(const HashTable& other) | |
1164 : m_table(0) | |
1165 , m_tableSize(0) | |
1166 , m_tableSizeMask(0) | |
1167 , m_keyCount(0) | |
1168 , m_deletedCount(0) | |
1169 #if CHECK_HASHTABLE_ITERATORS | |
1170 , m_iterators(0) | |
1171 , m_mutex(adoptPtr(new Mutex)) | |
1172 #endif | |
1173 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
1174 , m_stats(adoptPtr(new Stats(*other.m_stats))) | |
1175 #endif | |
1176 { | |
1177 // Copy the hash table the dumb way, by adding each element to the new t
able. | |
1178 // It might be more efficient to copy the table slots, but it's not clea
r that efficiency is needed. | |
1179 const_iterator end = other.end(); | |
1180 for (const_iterator it = other.begin(); it != end; ++it) | |
1181 add(*it); | |
1182 } | |
1183 | |
1184 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
1185 void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::swa
p(HashTable& other) | |
1186 { | |
1187 invalidateIterators(); | |
1188 other.invalidateIterators(); | |
1189 | |
1190 ValueType* tmp_table = m_table; | |
1191 m_table = other.m_table; | |
1192 other.m_table = tmp_table; | |
1193 | |
1194 int tmp_tableSize = m_tableSize; | |
1195 m_tableSize = other.m_tableSize; | |
1196 other.m_tableSize = tmp_tableSize; | |
1197 | |
1198 int tmp_tableSizeMask = m_tableSizeMask; | |
1199 m_tableSizeMask = other.m_tableSizeMask; | |
1200 other.m_tableSizeMask = tmp_tableSizeMask; | |
1201 | |
1202 int tmp_keyCount = m_keyCount; | |
1203 m_keyCount = other.m_keyCount; | |
1204 other.m_keyCount = tmp_keyCount; | |
1205 | |
1206 int tmp_deletedCount = m_deletedCount; | |
1207 m_deletedCount = other.m_deletedCount; | |
1208 other.m_deletedCount = tmp_deletedCount; | |
1209 | |
1210 #if DUMP_HASHTABLE_STATS_PER_TABLE | |
1211 m_stats.swap(other.m_stats); | |
1212 #endif | |
1213 } | |
1214 | |
1215 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
1216 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>& HashTabl
e<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::operator=(const Hash
Table& other) | |
1217 { | |
1218 HashTable tmp(other); | |
1219 swap(tmp); | |
1220 return *this; | |
1221 } | |
1222 | |
1223 #if !ASSERT_DISABLED | |
1224 | |
1225 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
1226 void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::che
ckTableConsistency() const | |
1227 { | |
1228 checkTableConsistencyExceptSize(); | |
1229 ASSERT(!m_table || !shouldExpand()); | |
1230 ASSERT(!shouldShrink()); | |
1231 } | |
1232 | |
1233 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
1234 void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::che
ckTableConsistencyExceptSize() const | |
1235 { | |
1236 if (!m_table) | |
1237 return; | |
1238 | |
1239 int count = 0; | |
1240 int deletedCount = 0; | |
1241 for (int j = 0; j < m_tableSize; ++j) { | |
1242 ValueType* entry = m_table + j; | |
1243 if (isEmptyBucket(*entry)) | |
1244 continue; | |
1245 | |
1246 if (isDeletedBucket(*entry)) { | |
1247 ++deletedCount; | |
1248 continue; | |
1249 } | |
1250 | |
1251 const_iterator it = find(Extractor::extract(*entry)); | |
1252 ASSERT(entry == it.m_position); | |
1253 ++count; | |
1254 | |
1255 ValueCheck<Key>::checkConsistency(it->key); | |
1256 } | |
1257 | |
1258 ASSERT(count == m_keyCount); | |
1259 ASSERT(deletedCount == m_deletedCount); | |
1260 ASSERT(m_tableSize >= KeyTraits::minimumTableSize); | |
1261 ASSERT(m_tableSizeMask); | |
1262 ASSERT(m_tableSize == m_tableSizeMask + 1); | |
1263 } | |
1264 | |
1265 #endif // ASSERT_DISABLED | |
1266 | |
1267 #if CHECK_HASHTABLE_ITERATORS | |
1268 | |
1269 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
1270 void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::inv
alidateIterators() | |
1271 { | |
1272 MutexLocker lock(*m_mutex); | |
1273 const_iterator* next; | |
1274 for (const_iterator* p = m_iterators; p; p = next) { | |
1275 next = p->m_next; | |
1276 p->m_table = 0; | |
1277 p->m_next = 0; | |
1278 p->m_previous = 0; | |
1279 } | |
1280 m_iterators = 0; | |
1281 } | |
1282 | |
1283 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
1284 void addIterator(const HashTable<Key, Value, Extractor, HashFunctions, Trait
s, KeyTraits>* table, | |
1285 HashTableConstIterator<Key, Value, Extractor, HashFunctions, Traits, Key
Traits>* it) | |
1286 { | |
1287 it->m_table = table; | |
1288 it->m_previous = 0; | |
1289 | |
1290 // Insert iterator at head of doubly-linked list of iterators. | |
1291 if (!table) { | |
1292 it->m_next = 0; | |
1293 } else { | |
1294 MutexLocker lock(*table->m_mutex); | |
1295 ASSERT(table->m_iterators != it); | |
1296 it->m_next = table->m_iterators; | |
1297 table->m_iterators = it; | |
1298 if (it->m_next) { | |
1299 ASSERT(!it->m_next->m_previous); | |
1300 it->m_next->m_previous = it; | |
1301 } | |
1302 } | |
1303 } | |
1304 | |
1305 template<typename Key, typename Value, typename Extractor, typename HashFunc
tions, typename Traits, typename KeyTraits> | |
1306 void removeIterator(HashTableConstIterator<Key, Value, Extractor, HashFuncti
ons, Traits, KeyTraits>* it) | |
1307 { | |
1308 typedef HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTrait
s> HashTableType; | |
1309 typedef HashTableConstIterator<Key, Value, Extractor, HashFunctions, Tra
its, KeyTraits> const_iterator; | |
1310 | |
1311 // Delete iterator from doubly-linked list of iterators. | |
1312 if (!it->m_table) { | |
1313 ASSERT(!it->m_next); | |
1314 ASSERT(!it->m_previous); | |
1315 } else { | |
1316 MutexLocker lock(*it->m_table->m_mutex); | |
1317 if (it->m_next) { | |
1318 ASSERT(it->m_next->m_previous == it); | |
1319 it->m_next->m_previous = it->m_previous; | |
1320 } | |
1321 if (it->m_previous) { | |
1322 ASSERT(it->m_table->m_iterators != it); | |
1323 ASSERT(it->m_previous->m_next == it); | |
1324 it->m_previous->m_next = it->m_next; | |
1325 } else { | |
1326 ASSERT(it->m_table->m_iterators == it); | |
1327 it->m_table->m_iterators = it->m_next; | |
1328 } | |
1329 } | |
1330 | |
1331 it->m_table = 0; | |
1332 it->m_next = 0; | |
1333 it->m_previous = 0; | |
1334 } | |
1335 | |
1336 #endif // CHECK_HASHTABLE_ITERATORS | |
1337 | |
1338 // iterator adapters | |
1339 | |
1340 template<typename HashTableType, typename ValueType> struct HashTableConstIt
eratorAdapter { | |
1341 HashTableConstIteratorAdapter() {} | |
1342 HashTableConstIteratorAdapter(const typename HashTableType::const_iterat
or& impl) : m_impl(impl) {} | |
1343 | |
1344 const ValueType* get() const { return (const ValueType*)m_impl.get(); } | |
1345 const ValueType& operator*() const { return *get(); } | |
1346 const ValueType* operator->() const { return get(); } | |
1347 | |
1348 HashTableConstIteratorAdapter& operator++() { ++m_impl; return *this; } | |
1349 // postfix ++ intentionally omitted | |
1350 | |
1351 typename HashTableType::const_iterator m_impl; | |
1352 }; | |
1353 | |
1354 template<typename HashTableType, typename ValueType> struct HashTableIterato
rAdapter { | |
1355 HashTableIteratorAdapter() {} | |
1356 HashTableIteratorAdapter(const typename HashTableType::iterator& impl) :
m_impl(impl) {} | |
1357 | |
1358 ValueType* get() const { return (ValueType*)m_impl.get(); } | |
1359 ValueType& operator*() const { return *get(); } | |
1360 ValueType* operator->() const { return get(); } | |
1361 | |
1362 HashTableIteratorAdapter& operator++() { ++m_impl; return *this; } | |
1363 // postfix ++ intentionally omitted | |
1364 | |
1365 operator HashTableConstIteratorAdapter<HashTableType, ValueType>() { | |
1366 typename HashTableType::const_iterator i = m_impl; | |
1367 return i; | |
1368 } | |
1369 | |
1370 typename HashTableType::iterator m_impl; | |
1371 }; | |
1372 | |
1373 template<typename T, typename U> | |
1374 inline bool operator==(const HashTableConstIteratorAdapter<T, U>& a, const H
ashTableConstIteratorAdapter<T, U>& b) | |
1375 { | |
1376 return a.m_impl == b.m_impl; | |
1377 } | |
1378 | |
1379 template<typename T, typename U> | |
1380 inline bool operator!=(const HashTableConstIteratorAdapter<T, U>& a, const H
ashTableConstIteratorAdapter<T, U>& b) | |
1381 { | |
1382 return a.m_impl != b.m_impl; | |
1383 } | |
1384 | |
1385 template<typename T, typename U> | |
1386 inline bool operator==(const HashTableIteratorAdapter<T, U>& a, const HashTa
bleIteratorAdapter<T, U>& b) | |
1387 { | |
1388 return a.m_impl == b.m_impl; | |
1389 } | |
1390 | |
1391 template<typename T, typename U> | |
1392 inline bool operator!=(const HashTableIteratorAdapter<T, U>& a, const HashTa
bleIteratorAdapter<T, U>& b) | |
1393 { | |
1394 return a.m_impl != b.m_impl; | |
1395 } | |
1396 | |
1397 // All 4 combinations of ==, != and Const,non const. | |
1398 template<typename T, typename U> | |
1399 inline bool operator==(const HashTableConstIteratorAdapter<T, U>& a, const H
ashTableIteratorAdapter<T, U>& b) | |
1400 { | |
1401 return a.m_impl == b.m_impl; | |
1402 } | |
1403 | |
1404 template<typename T, typename U> | |
1405 inline bool operator!=(const HashTableConstIteratorAdapter<T, U>& a, const H
ashTableIteratorAdapter<T, U>& b) | |
1406 { | |
1407 return a.m_impl != b.m_impl; | |
1408 } | |
1409 | |
1410 template<typename T, typename U> | |
1411 inline bool operator==(const HashTableIteratorAdapter<T, U>& a, const HashTa
bleConstIteratorAdapter<T, U>& b) | |
1412 { | |
1413 return a.m_impl == b.m_impl; | |
1414 } | |
1415 | |
1416 template<typename T, typename U> | |
1417 inline bool operator!=(const HashTableIteratorAdapter<T, U>& a, const HashTa
bleConstIteratorAdapter<T, U>& b) | |
1418 { | |
1419 return a.m_impl != b.m_impl; | |
1420 } | |
1421 | |
1422 } // namespace WTF | |
1423 | |
1424 #include <wtf/HashIterators.h> | |
1425 | |
1426 #endif // WTF_HashTable_h | |
OLD | NEW |