| 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 |