File indexing completed on 2024-05-12 15:43:39
0001 /* 0002 * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. 0003 * 0004 * This library is free software; you can redistribute it and/or 0005 * modify it under the terms of the GNU Library General Public 0006 * License as published by the Free Software Foundation; either 0007 * version 2 of the License, or (at your option) any later version. 0008 * 0009 * This library is distributed in the hope that it will be useful, 0010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 * Library General Public License for more details. 0013 * 0014 * You should have received a copy of the GNU Library General Public License 0015 * along with this library; see the file COPYING.LIB. If not, write to 0016 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0017 * Boston, MA 02110-1301, USA. 0018 * 0019 */ 0020 0021 #ifndef WTF_HashTable_h 0022 #define WTF_HashTable_h 0023 0024 #include <wtf/FastMalloc.h> 0025 #include <wtf/HashTraits.h> 0026 #include <wtf/Assertions.h> 0027 0028 namespace WTF 0029 { 0030 0031 #define DUMP_HASHTABLE_STATS 0 0032 #define CHECK_HASHTABLE_CONSISTENCY 0 0033 0034 // The Apple tree triggers this based on debug or not 0035 // We can't do this, since it would make the two builds BIC! 0036 #define CHECK_HASHTABLE_ITERATORS 0 0037 #define CHECK_HASHTABLE_USE_AFTER_DESTRUCTION 0 0038 0039 #if DUMP_HASHTABLE_STATS 0040 0041 struct HashTableStats { 0042 ~HashTableStats(); 0043 static int numAccesses; 0044 static int numCollisions; 0045 static int collisionGraph[4096]; 0046 static int maxCollisions; 0047 static int numRehashes; 0048 static int numRemoves; 0049 static int numReinserts; 0050 static void recordCollisionAtCount(int count); 0051 }; 0052 0053 #endif 0054 0055 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0056 class HashTable; 0057 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0058 class HashTableIterator; 0059 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0060 class HashTableConstIterator; 0061 0062 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0063 void addIterator(const HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> *, 0064 HashTableConstIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> *); 0065 0066 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0067 void removeIterator(HashTableConstIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> *); 0068 0069 #if !CHECK_HASHTABLE_ITERATORS 0070 0071 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0072 inline void addIterator(const HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> *, 0073 HashTableConstIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> *) { } 0074 0075 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0076 inline void removeIterator(HashTableConstIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> *) { } 0077 0078 #endif 0079 0080 typedef enum { HashItemKnownGood } HashItemKnownGoodTag; 0081 0082 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0083 class HashTableConstIterator 0084 { 0085 private: 0086 typedef HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> HashTableType; 0087 typedef HashTableIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> iterator; 0088 typedef HashTableConstIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> const_iterator; 0089 typedef Value ValueType; 0090 typedef const ValueType &ReferenceType; 0091 typedef const ValueType *PointerType; 0092 0093 friend class HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>; 0094 friend class HashTableIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>; 0095 0096 void skipEmptyBuckets() 0097 { 0098 while (m_position != m_endPosition && HashTableType::isEmptyOrDeletedBucket(*m_position)) { 0099 ++m_position; 0100 } 0101 } 0102 0103 HashTableConstIterator(const HashTableType *table, PointerType position, PointerType endPosition) 0104 : m_position(position), m_endPosition(endPosition) 0105 { 0106 addIterator(table, this); 0107 skipEmptyBuckets(); 0108 } 0109 0110 HashTableConstIterator(const HashTableType *table, PointerType position, PointerType endPosition, HashItemKnownGoodTag) 0111 : m_position(position), m_endPosition(endPosition) 0112 { 0113 addIterator(table, this); 0114 } 0115 0116 public: 0117 HashTableConstIterator() 0118 { 0119 addIterator(0, this); 0120 } 0121 0122 // default copy, assignment and destructor are OK if CHECK_HASHTABLE_ITERATORS is 0 0123 0124 #if CHECK_HASHTABLE_ITERATORS 0125 ~HashTableConstIterator() 0126 { 0127 removeIterator(this); 0128 } 0129 0130 HashTableConstIterator(const const_iterator &other) 0131 : m_position(other.m_position), m_endPosition(other.m_endPosition) 0132 { 0133 addIterator(other.m_table, this); 0134 } 0135 0136 const_iterator &operator=(const const_iterator &other) 0137 { 0138 m_position = other.m_position; 0139 m_endPosition = other.m_endPosition; 0140 0141 removeIterator(this); 0142 addIterator(other.m_table, this); 0143 0144 return *this; 0145 } 0146 #endif 0147 0148 PointerType get() const 0149 { 0150 checkValidity(); 0151 return m_position; 0152 } 0153 ReferenceType operator*() const 0154 { 0155 return *get(); 0156 } 0157 PointerType operator->() const 0158 { 0159 return get(); 0160 } 0161 0162 const_iterator &operator++() 0163 { 0164 checkValidity(); 0165 ASSERT(m_position != m_endPosition); 0166 ++m_position; 0167 skipEmptyBuckets(); 0168 return *this; 0169 } 0170 0171 // postfix ++ intentionally omitted 0172 0173 // Comparison. 0174 bool operator==(const const_iterator &other) const 0175 { 0176 checkValidity(other); 0177 return m_position == other.m_position; 0178 } 0179 bool operator!=(const const_iterator &other) const 0180 { 0181 checkValidity(other); 0182 return m_position != other.m_position; 0183 } 0184 0185 private: 0186 void checkValidity() const 0187 { 0188 #if CHECK_HASHTABLE_ITERATORS 0189 ASSERT(m_table); 0190 #endif 0191 } 0192 0193 #if CHECK_HASHTABLE_ITERATORS 0194 void checkValidity(const const_iterator &other) const 0195 { 0196 ASSERT(m_table); 0197 ASSERT(other.m_table); 0198 ASSERT(m_table == other.m_table); 0199 } 0200 #else 0201 void checkValidity(const const_iterator &) const { } 0202 #endif 0203 0204 PointerType m_position; 0205 PointerType m_endPosition; 0206 0207 #if CHECK_HASHTABLE_ITERATORS 0208 public: 0209 mutable const HashTableType *m_table; 0210 mutable const_iterator *m_next; 0211 mutable const_iterator *m_previous; 0212 #endif 0213 }; 0214 0215 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0216 class HashTableIterator 0217 { 0218 private: 0219 typedef HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> HashTableType; 0220 typedef HashTableIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> iterator; 0221 typedef HashTableConstIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> const_iterator; 0222 typedef Value ValueType; 0223 typedef ValueType &ReferenceType; 0224 typedef ValueType *PointerType; 0225 0226 friend class HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>; 0227 0228 HashTableIterator(HashTableType *table, PointerType pos, PointerType end) : m_iterator(table, pos, end) { } 0229 HashTableIterator(HashTableType *table, PointerType pos, PointerType end, HashItemKnownGoodTag tag) : m_iterator(table, pos, end, tag) { } 0230 0231 public: 0232 HashTableIterator() { } 0233 0234 // default copy, assignment and destructor are OK 0235 0236 PointerType get() const 0237 { 0238 return const_cast<PointerType>(m_iterator.get()); 0239 } 0240 ReferenceType operator*() const 0241 { 0242 return *get(); 0243 } 0244 PointerType operator->() const 0245 { 0246 return get(); 0247 } 0248 0249 iterator &operator++() 0250 { 0251 ++m_iterator; 0252 return *this; 0253 } 0254 0255 // postfix ++ intentionally omitted 0256 0257 // Comparison. 0258 bool operator==(const iterator &other) const 0259 { 0260 return m_iterator == other.m_iterator; 0261 } 0262 bool operator!=(const iterator &other) const 0263 { 0264 return m_iterator != other.m_iterator; 0265 } 0266 0267 operator const_iterator() const 0268 { 0269 return m_iterator; 0270 } 0271 0272 private: 0273 const_iterator m_iterator; 0274 }; 0275 0276 using std::swap; 0277 0278 // Work around MSVC's standard library, whose swap for pairs does not swap by component. 0279 template<typename T> inline void hashTableSwap(T &a, T &b) 0280 { 0281 swap(a, b); 0282 } 0283 0284 template<typename T, typename U> inline void hashTableSwap(pair<T, U> &a, pair<T, U> &b) 0285 { 0286 swap(a.first, b.first); 0287 swap(a.second, b.second); 0288 } 0289 0290 template<typename T, bool useSwap> struct Mover; 0291 template<typename T> struct Mover<T, true> { 0292 static void move(T &from, T &to) 0293 { 0294 hashTableSwap(from, to); 0295 } 0296 }; 0297 template<typename T> struct Mover<T, false> { 0298 static void move(T &from, T &to) 0299 { 0300 to = from; 0301 } 0302 }; 0303 0304 template<typename Key, typename Value, typename HashFunctions> class IdentityHashTranslator 0305 { 0306 public: 0307 static unsigned hash(const Key &key) 0308 { 0309 return HashFunctions::hash(key); 0310 } 0311 static bool equal(const Key &a, const Key &b) 0312 { 0313 return HashFunctions::equal(a, b); 0314 } 0315 static void translate(Value &location, const Key &, const Value &value) 0316 { 0317 location = value; 0318 } 0319 }; 0320 0321 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0322 class HashTable 0323 { 0324 public: 0325 typedef HashTableIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> iterator; 0326 typedef HashTableConstIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> const_iterator; 0327 typedef Traits ValueTraits; 0328 typedef Key KeyType; 0329 typedef Value ValueType; 0330 typedef IdentityHashTranslator<Key, Value, HashFunctions> IdentityTranslatorType; 0331 0332 HashTable(); 0333 ~HashTable() 0334 { 0335 invalidateIterators(); 0336 deallocateTable(m_table, m_tableSize); 0337 #if CHECK_HASHTABLE_USE_AFTER_DESTRUCTION 0338 m_table = (ValueType *)(uintptr_t)0xbbadbeef; 0339 #endif 0340 } 0341 0342 HashTable(const HashTable &); 0343 void swap(HashTable &); 0344 HashTable &operator=(const HashTable &); 0345 0346 // When the hash table is empty, just return the same iterator for end as for begin. 0347 // This is more efficient because we don't have to skip all the empty and deleted 0348 // buckets, and iterating an empty table is a common case that's worth optimizing. 0349 iterator begin() 0350 { 0351 return isEmpty() ? end() : makeIterator(m_table); 0352 } 0353 iterator end() 0354 { 0355 return makeKnownGoodIterator(m_table + m_tableSize); 0356 } 0357 const_iterator begin() const 0358 { 0359 return isEmpty() ? end() : makeConstIterator(m_table); 0360 } 0361 const_iterator end() const 0362 { 0363 return makeKnownGoodConstIterator(m_table + m_tableSize); 0364 } 0365 0366 int size() const 0367 { 0368 return m_keyCount; 0369 } 0370 int capacity() const 0371 { 0372 return m_tableSize; 0373 } 0374 bool isEmpty() const 0375 { 0376 return !m_keyCount; 0377 } 0378 0379 pair<iterator, bool> add(const ValueType &value) 0380 { 0381 return add<KeyType, ValueType, IdentityTranslatorType>(Extractor::extract(value), value); 0382 } 0383 0384 // A special version of add() that finds the object by hashing and comparing 0385 // with some other type, to avoid the cost of type conversion if the object is already 0386 // in the table. 0387 template<typename T, typename Extra, typename HashTranslator> pair<iterator, bool> add(const T &key, const Extra &); 0388 template<typename T, typename Extra, typename HashTranslator> pair<iterator, bool> addPassingHashCode(const T &key, const Extra &); 0389 0390 iterator find(const KeyType &key) 0391 { 0392 return find<KeyType, IdentityTranslatorType>(key); 0393 } 0394 const_iterator find(const KeyType &key) const 0395 { 0396 return find<KeyType, IdentityTranslatorType>(key); 0397 } 0398 bool contains(const KeyType &key) const 0399 { 0400 return contains<KeyType, IdentityTranslatorType>(key); 0401 } 0402 0403 template <typename T, typename HashTranslator> iterator find(const T &); 0404 template <typename T, typename HashTranslator> const_iterator find(const T &) const; 0405 template <typename T, typename HashTranslator> bool contains(const T &) const; 0406 0407 void remove(const KeyType &); 0408 void remove(iterator); 0409 void removeWithoutEntryConsistencyCheck(iterator); 0410 void clear(); 0411 0412 static bool isEmptyBucket(const ValueType &value) 0413 { 0414 return Extractor::extract(value) == KeyTraits::emptyValue(); 0415 } 0416 static bool isDeletedBucket(const ValueType &value) 0417 { 0418 return KeyTraits::isDeletedValue(Extractor::extract(value)); 0419 } 0420 static bool isEmptyOrDeletedBucket(const ValueType &value) 0421 { 0422 return isEmptyBucket(value) || isDeletedBucket(value); 0423 } 0424 0425 ValueType *lookup(const Key &key) 0426 { 0427 return lookup<Key, IdentityTranslatorType>(key); 0428 } 0429 template<typename T, typename HashTranslator> ValueType *lookup(const T &); 0430 0431 #if CHECK_HASHTABLE_CONSISTENCY 0432 void checkTableConsistency() const; 0433 #else 0434 static void checkTableConsistency() { } 0435 #endif 0436 0437 private: 0438 static ValueType *allocateTable(int size); 0439 static void deallocateTable(ValueType *table, int size); 0440 0441 typedef pair<ValueType *, bool> LookupType; 0442 typedef pair<LookupType, unsigned> FullLookupType; 0443 0444 LookupType lookupForWriting(const Key &key) 0445 { 0446 return lookupForWriting<Key, IdentityTranslatorType>(key); 0447 }; 0448 template<typename T, typename HashTranslator> FullLookupType fullLookupForWriting(const T &); 0449 template<typename T, typename HashTranslator> LookupType lookupForWriting(const T &); 0450 0451 template<typename T, typename HashTranslator> void checkKey(const T &); 0452 0453 void removeAndInvalidateWithoutEntryConsistencyCheck(ValueType *); 0454 void removeAndInvalidate(ValueType *); 0455 void remove(ValueType *); 0456 0457 bool shouldExpand() const 0458 { 0459 return (m_keyCount + m_deletedCount) * m_maxLoad >= m_tableSize; 0460 } 0461 bool mustRehashInPlace() const 0462 { 0463 return m_keyCount * m_minLoad < m_tableSize * 2; 0464 } 0465 bool shouldShrink() const 0466 { 0467 return m_keyCount * m_minLoad < m_tableSize && m_tableSize > m_minTableSize; 0468 } 0469 void expand(); 0470 void shrink() 0471 { 0472 rehash(m_tableSize / 2); 0473 } 0474 0475 void rehash(int newTableSize); 0476 void reinsert(ValueType &); 0477 0478 static void initializeBucket(ValueType &bucket) 0479 { 0480 new(&bucket) ValueType(Traits::emptyValue()); 0481 } 0482 static void deleteBucket(ValueType &bucket) 0483 { 0484 bucket.~ValueType(); 0485 Traits::constructDeletedValue(&bucket); 0486 } 0487 0488 FullLookupType makeLookupResult(ValueType *position, bool found, unsigned hash) 0489 { 0490 return FullLookupType(LookupType(position, found), hash); 0491 } 0492 0493 iterator makeIterator(ValueType *pos) 0494 { 0495 return iterator(this, pos, m_table + m_tableSize); 0496 } 0497 const_iterator makeConstIterator(ValueType *pos) const 0498 { 0499 return const_iterator(this, pos, m_table + m_tableSize); 0500 } 0501 iterator makeKnownGoodIterator(ValueType *pos) 0502 { 0503 return iterator(this, pos, m_table + m_tableSize, HashItemKnownGood); 0504 } 0505 const_iterator makeKnownGoodConstIterator(ValueType *pos) const 0506 { 0507 return const_iterator(this, pos, m_table + m_tableSize, HashItemKnownGood); 0508 } 0509 0510 #if CHECK_HASHTABLE_CONSISTENCY 0511 void checkTableConsistencyExceptSize() const; 0512 #else 0513 static void checkTableConsistencyExceptSize() { } 0514 #endif 0515 0516 #if CHECK_HASHTABLE_ITERATORS 0517 void invalidateIterators(); 0518 #else 0519 static void invalidateIterators() { } 0520 #endif 0521 0522 static const int m_minTableSize = 64; 0523 static const int m_maxLoad = 2; 0524 static const int m_minLoad = 6; 0525 0526 ValueType *m_table; 0527 int m_tableSize; 0528 int m_tableSizeMask; 0529 int m_keyCount; 0530 int m_deletedCount; 0531 0532 #if CHECK_HASHTABLE_ITERATORS 0533 public: 0534 mutable const_iterator *m_iterators; 0535 #endif 0536 }; 0537 0538 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0539 inline HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::HashTable() 0540 : m_table(nullptr) 0541 , m_tableSize(0) 0542 , m_tableSizeMask(0) 0543 , m_keyCount(0) 0544 , m_deletedCount(0) 0545 #if CHECK_HASHTABLE_ITERATORS 0546 , m_iterators(0) 0547 #endif 0548 { 0549 } 0550 0551 static inline unsigned doubleHash(unsigned key) 0552 { 0553 key = ~key + (key >> 23); 0554 key ^= (key << 12); 0555 key ^= (key >> 7); 0556 key ^= (key << 2); 0557 key ^= (key >> 20); 0558 return key; 0559 } 0560 0561 #if ASSERT_DISABLED 0562 0563 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0564 template<typename T, typename HashTranslator> 0565 inline void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::checkKey(const T &) 0566 { 0567 } 0568 0569 #else 0570 0571 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0572 template<typename T, typename HashTranslator> 0573 void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::checkKey(const T &key) 0574 { 0575 if (!HashFunctions::safeToCompareToEmptyOrDeleted) { 0576 return; 0577 } 0578 ASSERT(!HashTranslator::equal(KeyTraits::emptyValue(), key)); 0579 ValueType deletedValue = Traits::emptyValue(); 0580 deletedValue.~ValueType(); 0581 Traits::constructDeletedValue(&deletedValue); 0582 ASSERT(!HashTranslator::equal(Extractor::extract(deletedValue), key)); 0583 new(&deletedValue) ValueType(Traits::emptyValue()); 0584 } 0585 0586 #endif 0587 0588 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0589 template<typename T, typename HashTranslator> 0590 inline Value *HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::lookup(const T &key) 0591 { 0592 checkKey<T, HashTranslator>(key); 0593 0594 int k = 0; 0595 int sizeMask = m_tableSizeMask; 0596 ValueType *table = m_table; 0597 unsigned h = HashTranslator::hash(key); 0598 int i = h & sizeMask; 0599 0600 if (!table) { 0601 return nullptr; 0602 } 0603 0604 #if DUMP_HASHTABLE_STATS 0605 ++HashTableStats::numAccesses; 0606 int probeCount = 0; 0607 #endif 0608 0609 while (1) { 0610 ValueType *entry = table + i; 0611 0612 // we count on the compiler to optimize out this branch 0613 if (HashFunctions::safeToCompareToEmptyOrDeleted) { 0614 if (HashTranslator::equal(Extractor::extract(*entry), key)) { 0615 return entry; 0616 } 0617 0618 if (isEmptyBucket(*entry)) { 0619 return nullptr; 0620 } 0621 } else { 0622 if (isEmptyBucket(*entry)) { 0623 return nullptr; 0624 } 0625 0626 if (!isDeletedBucket(*entry) && HashTranslator::equal(Extractor::extract(*entry), key)) { 0627 return entry; 0628 } 0629 } 0630 #if DUMP_HASHTABLE_STATS 0631 ++probeCount; 0632 HashTableStats::recordCollisionAtCount(probeCount); 0633 #endif 0634 if (k == 0) { 0635 k = 1 | doubleHash(h); 0636 } 0637 i = (i + k) & sizeMask; 0638 } 0639 } 0640 0641 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0642 template<typename T, typename HashTranslator> 0643 inline typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::LookupType HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::lookupForWriting(const T &key) 0644 { 0645 ASSERT(m_table); 0646 checkKey<T, HashTranslator>(key); 0647 0648 int k = 0; 0649 ValueType *table = m_table; 0650 int sizeMask = m_tableSizeMask; 0651 unsigned h = HashTranslator::hash(key); 0652 int i = h & sizeMask; 0653 0654 #if DUMP_HASHTABLE_STATS 0655 ++HashTableStats::numAccesses; 0656 int probeCount = 0; 0657 #endif 0658 0659 ValueType *deletedEntry = nullptr; 0660 0661 while (1) { 0662 ValueType *entry = table + i; 0663 0664 // we count on the compiler to optimize out this branch 0665 if (HashFunctions::safeToCompareToEmptyOrDeleted) { 0666 if (isEmptyBucket(*entry)) { 0667 return LookupType(deletedEntry ? deletedEntry : entry, false); 0668 } 0669 0670 if (HashTranslator::equal(Extractor::extract(*entry), key)) { 0671 return LookupType(entry, true); 0672 } 0673 0674 if (isDeletedBucket(*entry)) { 0675 deletedEntry = entry; 0676 } 0677 } else { 0678 if (isEmptyBucket(*entry)) { 0679 return LookupType(deletedEntry ? deletedEntry : entry, false); 0680 } 0681 0682 if (isDeletedBucket(*entry)) { 0683 deletedEntry = entry; 0684 } else if (HashTranslator::equal(Extractor::extract(*entry), key)) { 0685 return LookupType(entry, true); 0686 } 0687 } 0688 #if DUMP_HASHTABLE_STATS 0689 ++probeCount; 0690 HashTableStats::recordCollisionAtCount(probeCount); 0691 #endif 0692 if (k == 0) { 0693 k = 1 | doubleHash(h); 0694 } 0695 i = (i + k) & sizeMask; 0696 } 0697 } 0698 0699 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0700 template<typename T, typename HashTranslator> 0701 inline typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::FullLookupType HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::fullLookupForWriting(const T &key) 0702 { 0703 ASSERT(m_table); 0704 checkKey<T, HashTranslator>(key); 0705 0706 int k = 0; 0707 ValueType *table = m_table; 0708 int sizeMask = m_tableSizeMask; 0709 unsigned h = HashTranslator::hash(key); 0710 int i = h & sizeMask; 0711 0712 #if DUMP_HASHTABLE_STATS 0713 ++HashTableStats::numAccesses; 0714 int probeCount = 0; 0715 #endif 0716 0717 ValueType *deletedEntry = nullptr; 0718 0719 while (1) { 0720 ValueType *entry = table + i; 0721 0722 // we count on the compiler to optimize out this branch 0723 if (HashFunctions::safeToCompareToEmptyOrDeleted) { 0724 if (isEmptyBucket(*entry)) { 0725 return makeLookupResult(deletedEntry ? deletedEntry : entry, false, h); 0726 } 0727 0728 if (HashTranslator::equal(Extractor::extract(*entry), key)) { 0729 return makeLookupResult(entry, true, h); 0730 } 0731 0732 if (isDeletedBucket(*entry)) { 0733 deletedEntry = entry; 0734 } 0735 } else { 0736 if (isEmptyBucket(*entry)) { 0737 return makeLookupResult(deletedEntry ? deletedEntry : entry, false, h); 0738 } 0739 0740 if (isDeletedBucket(*entry)) { 0741 deletedEntry = entry; 0742 } else if (HashTranslator::equal(Extractor::extract(*entry), key)) { 0743 return makeLookupResult(entry, true, h); 0744 } 0745 } 0746 #if DUMP_HASHTABLE_STATS 0747 ++probeCount; 0748 HashTableStats::recordCollisionAtCount(probeCount); 0749 #endif 0750 if (k == 0) { 0751 k = 1 | doubleHash(h); 0752 } 0753 i = (i + k) & sizeMask; 0754 } 0755 } 0756 0757 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0758 template<typename T, typename Extra, typename HashTranslator> 0759 inline pair<typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::iterator, bool> HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::add(const T &key, const Extra &extra) 0760 { 0761 checkKey<T, HashTranslator>(key); 0762 0763 invalidateIterators(); 0764 0765 if (!m_table) { 0766 expand(); 0767 } 0768 0769 checkTableConsistency(); 0770 0771 ASSERT(m_table); 0772 0773 int k = 0; 0774 ValueType *table = m_table; 0775 int sizeMask = m_tableSizeMask; 0776 unsigned h = HashTranslator::hash(key); 0777 int i = h & sizeMask; 0778 0779 #if DUMP_HASHTABLE_STATS 0780 ++HashTableStats::numAccesses; 0781 int probeCount = 0; 0782 #endif 0783 0784 ValueType *deletedEntry = nullptr; 0785 ValueType *entry; 0786 while (1) { 0787 entry = table + i; 0788 0789 // we count on the compiler to optimize out this branch 0790 if (HashFunctions::safeToCompareToEmptyOrDeleted) { 0791 if (isEmptyBucket(*entry)) { 0792 break; 0793 } 0794 0795 if (HashTranslator::equal(Extractor::extract(*entry), key)) { 0796 return std::make_pair(makeKnownGoodIterator(entry), false); 0797 } 0798 0799 if (isDeletedBucket(*entry)) { 0800 deletedEntry = entry; 0801 } 0802 } else { 0803 if (isEmptyBucket(*entry)) { 0804 break; 0805 } 0806 0807 if (isDeletedBucket(*entry)) { 0808 deletedEntry = entry; 0809 } else if (HashTranslator::equal(Extractor::extract(*entry), key)) { 0810 return std::make_pair(makeKnownGoodIterator(entry), false); 0811 } 0812 } 0813 #if DUMP_HASHTABLE_STATS 0814 ++probeCount; 0815 HashTableStats::recordCollisionAtCount(probeCount); 0816 #endif 0817 if (k == 0) { 0818 k = 1 | doubleHash(h); 0819 } 0820 i = (i + k) & sizeMask; 0821 } 0822 0823 if (deletedEntry) { 0824 initializeBucket(*deletedEntry); 0825 entry = deletedEntry; 0826 --m_deletedCount; 0827 } 0828 0829 HashTranslator::translate(*entry, key, extra); 0830 0831 ++m_keyCount; 0832 0833 if (shouldExpand()) { 0834 // FIXME: This makes an extra copy on expand. Probably not that bad since 0835 // expand is rare, but would be better to have a version of expand that can 0836 // follow a pivot entry and return the new position. 0837 KeyType enteredKey = Extractor::extract(*entry); 0838 expand(); 0839 return std::make_pair(find(enteredKey), true); 0840 } 0841 0842 checkTableConsistency(); 0843 0844 return std::make_pair(makeKnownGoodIterator(entry), true); 0845 } 0846 0847 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0848 template<typename T, typename Extra, typename HashTranslator> 0849 inline pair<typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::iterator, bool> HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::addPassingHashCode(const T &key, const Extra &extra) 0850 { 0851 checkKey<T, HashTranslator>(key); 0852 0853 invalidateIterators(); 0854 0855 if (!m_table) { 0856 expand(); 0857 } 0858 0859 checkTableConsistency(); 0860 0861 FullLookupType lookupResult = fullLookupForWriting<T, HashTranslator>(key); 0862 0863 ValueType *entry = lookupResult.first.first; 0864 bool found = lookupResult.first.second; 0865 unsigned h = lookupResult.second; 0866 0867 if (found) { 0868 return std::make_pair(makeKnownGoodIterator(entry), false); 0869 } 0870 0871 if (isDeletedBucket(*entry)) { 0872 initializeBucket(*entry); 0873 --m_deletedCount; 0874 } 0875 0876 HashTranslator::translate(*entry, key, extra, h); 0877 ++m_keyCount; 0878 if (shouldExpand()) { 0879 // FIXME: This makes an extra copy on expand. Probably not that bad since 0880 // expand is rare, but would be better to have a version of expand that can 0881 // follow a pivot entry and return the new position. 0882 KeyType enteredKey = Extractor::extract(*entry); 0883 expand(); 0884 return std::make_pair(find(enteredKey), true); 0885 } 0886 0887 checkTableConsistency(); 0888 0889 return std::make_pair(makeKnownGoodIterator(entry), true); 0890 } 0891 0892 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0893 inline void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::reinsert(ValueType &entry) 0894 { 0895 ASSERT(m_table); 0896 ASSERT(!lookupForWriting(Extractor::extract(entry)).second); 0897 #if DUMP_HASHTABLE_STATS 0898 ++HashTableStats::numReinserts; 0899 #endif 0900 0901 Mover<ValueType, Traits::needsDestruction>::move(entry, *lookupForWriting(Extractor::extract(entry)).first); 0902 } 0903 0904 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0905 template <typename T, typename HashTranslator> 0906 typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::iterator HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::find(const T &key) 0907 { 0908 if (!m_table) { 0909 return end(); 0910 } 0911 0912 ValueType *entry = lookup<T, HashTranslator>(key); 0913 if (!entry) { 0914 return end(); 0915 } 0916 0917 return makeKnownGoodIterator(entry); 0918 } 0919 0920 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0921 template <typename T, typename HashTranslator> 0922 typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::const_iterator HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::find(const T &key) const 0923 { 0924 if (!m_table) { 0925 return end(); 0926 } 0927 0928 ValueType *entry = const_cast<HashTable *>(this)->lookup<T, HashTranslator>(key); 0929 if (!entry) { 0930 return end(); 0931 } 0932 0933 return makeKnownGoodConstIterator(entry); 0934 } 0935 0936 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0937 template <typename T, typename HashTranslator> 0938 bool HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::contains(const T &key) const 0939 { 0940 if (!m_table) { 0941 return false; 0942 } 0943 0944 return const_cast<HashTable *>(this)->lookup<T, HashTranslator>(key); 0945 } 0946 0947 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0948 void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::removeAndInvalidateWithoutEntryConsistencyCheck(ValueType *pos) 0949 { 0950 invalidateIterators(); 0951 remove(pos); 0952 } 0953 0954 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0955 void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::removeAndInvalidate(ValueType *pos) 0956 { 0957 invalidateIterators(); 0958 checkTableConsistency(); 0959 remove(pos); 0960 } 0961 0962 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0963 void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::remove(ValueType *pos) 0964 { 0965 #if DUMP_HASHTABLE_STATS 0966 ++HashTableStats::numRemoves; 0967 #endif 0968 0969 deleteBucket(*pos); 0970 ++m_deletedCount; 0971 --m_keyCount; 0972 0973 if (shouldShrink()) { 0974 shrink(); 0975 } 0976 0977 checkTableConsistency(); 0978 } 0979 0980 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0981 inline void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::remove(iterator it) 0982 { 0983 if (it == end()) { 0984 return; 0985 } 0986 0987 removeAndInvalidate(const_cast<ValueType *>(it.m_iterator.m_position)); 0988 } 0989 0990 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 0991 inline void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::removeWithoutEntryConsistencyCheck(iterator it) 0992 { 0993 if (it == end()) { 0994 return; 0995 } 0996 0997 removeAndInvalidateWithoutEntryConsistencyCheck(const_cast<ValueType *>(it.m_iterator.m_position)); 0998 } 0999 1000 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 1001 inline void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::remove(const KeyType &key) 1002 { 1003 remove(find(key)); 1004 } 1005 1006 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 1007 Value *HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::allocateTable(int size) 1008 { 1009 // would use a template member function with explicit specializations here, but 1010 // gcc doesn't appear to support that 1011 if (Traits::emptyValueIsZero) { 1012 return static_cast<ValueType *>(fastZeroedMalloc(size * sizeof(ValueType))); 1013 } 1014 ValueType *result = static_cast<ValueType *>(fastMalloc(size * sizeof(ValueType))); 1015 for (int i = 0; i < size; i++) { 1016 initializeBucket(result[i]); 1017 } 1018 return result; 1019 } 1020 1021 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 1022 void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::deallocateTable(ValueType *table, int size) 1023 { 1024 if (Traits::needsDestruction) { 1025 for (int i = 0; i < size; ++i) { 1026 if (!isDeletedBucket(table[i])) { 1027 table[i].~ValueType(); 1028 } 1029 } 1030 } 1031 fastFree(table); 1032 } 1033 1034 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 1035 void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::expand() 1036 { 1037 int newSize; 1038 if (m_tableSize == 0) { 1039 newSize = m_minTableSize; 1040 } else if (mustRehashInPlace()) { 1041 newSize = m_tableSize; 1042 } else { 1043 newSize = m_tableSize * 2; 1044 } 1045 1046 rehash(newSize); 1047 } 1048 1049 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 1050 void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::rehash(int newTableSize) 1051 { 1052 checkTableConsistencyExceptSize(); 1053 1054 int oldTableSize = m_tableSize; 1055 ValueType *oldTable = m_table; 1056 1057 #if DUMP_HASHTABLE_STATS 1058 if (oldTableSize != 0) { 1059 ++HashTableStats::numRehashes; 1060 } 1061 #endif 1062 1063 m_tableSize = newTableSize; 1064 m_tableSizeMask = newTableSize - 1; 1065 m_table = allocateTable(newTableSize); 1066 1067 for (int i = 0; i != oldTableSize; ++i) 1068 if (!isEmptyOrDeletedBucket(oldTable[i])) { 1069 reinsert(oldTable[i]); 1070 } 1071 1072 m_deletedCount = 0; 1073 1074 deallocateTable(oldTable, oldTableSize); 1075 1076 checkTableConsistency(); 1077 } 1078 1079 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 1080 void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::clear() 1081 { 1082 invalidateIterators(); 1083 deallocateTable(m_table, m_tableSize); 1084 m_table = nullptr; 1085 m_tableSize = 0; 1086 m_tableSizeMask = 0; 1087 m_keyCount = 0; 1088 } 1089 1090 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 1091 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::HashTable(const HashTable &other) 1092 : m_table(nullptr) 1093 , m_tableSize(0) 1094 , m_tableSizeMask(0) 1095 , m_keyCount(0) 1096 , m_deletedCount(0) 1097 #if CHECK_HASHTABLE_ITERATORS 1098 , m_iterators(0) 1099 #endif 1100 { 1101 // Copy the hash table the dumb way, by adding each element to the new table. 1102 // It might be more efficient to copy the table slots, but it's not clear that efficiency is needed. 1103 const_iterator end = other.end(); 1104 for (const_iterator it = other.begin(); it != end; ++it) { 1105 add(*it); 1106 } 1107 } 1108 1109 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 1110 void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::swap(HashTable &other) 1111 { 1112 invalidateIterators(); 1113 other.invalidateIterators(); 1114 1115 ValueType *tmp_table = m_table; 1116 m_table = other.m_table; 1117 other.m_table = tmp_table; 1118 1119 int tmp_tableSize = m_tableSize; 1120 m_tableSize = other.m_tableSize; 1121 other.m_tableSize = tmp_tableSize; 1122 1123 int tmp_tableSizeMask = m_tableSizeMask; 1124 m_tableSizeMask = other.m_tableSizeMask; 1125 other.m_tableSizeMask = tmp_tableSizeMask; 1126 1127 int tmp_keyCount = m_keyCount; 1128 m_keyCount = other.m_keyCount; 1129 other.m_keyCount = tmp_keyCount; 1130 1131 int tmp_deletedCount = m_deletedCount; 1132 m_deletedCount = other.m_deletedCount; 1133 other.m_deletedCount = tmp_deletedCount; 1134 } 1135 1136 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 1137 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> &HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::operator=(const HashTable &other) 1138 { 1139 HashTable tmp(other); 1140 swap(tmp); 1141 return *this; 1142 } 1143 1144 #if CHECK_HASHTABLE_CONSISTENCY 1145 1146 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 1147 void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::checkTableConsistency() const 1148 { 1149 checkTableConsistencyExceptSize(); 1150 ASSERT(!shouldExpand()); 1151 ASSERT(!shouldShrink()); 1152 } 1153 1154 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 1155 void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::checkTableConsistencyExceptSize() const 1156 { 1157 if (!m_table) { 1158 return; 1159 } 1160 1161 int count = 0; 1162 int deletedCount = 0; 1163 for (int j = 0; j < m_tableSize; ++j) { 1164 ValueType *entry = m_table + j; 1165 if (isEmptyBucket(*entry)) { 1166 continue; 1167 } 1168 1169 if (isDeletedBucket(*entry)) { 1170 ++deletedCount; 1171 continue; 1172 } 1173 1174 const_iterator it = find(Extractor::extract(*entry)); 1175 ASSERT(entry == it.m_position); 1176 ++count; 1177 } 1178 1179 ASSERT(count == m_keyCount); 1180 ASSERT(deletedCount == m_deletedCount); 1181 ASSERT(m_tableSize >= m_minTableSize); 1182 ASSERT(m_tableSizeMask); 1183 ASSERT(m_tableSize == m_tableSizeMask + 1); 1184 } 1185 1186 #endif // CHECK_HASHTABLE_CONSISTENCY 1187 1188 #if CHECK_HASHTABLE_ITERATORS 1189 1190 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 1191 void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::invalidateIterators() 1192 { 1193 const_iterator *next; 1194 for (const_iterator *p = m_iterators; p; p = next) { 1195 next = p->m_next; 1196 p->m_table = nullptr; 1197 p->m_next = nullptr; 1198 p->m_previous = nullptr; 1199 } 1200 m_iterators = 0; 1201 } 1202 1203 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 1204 void addIterator(const HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> *table, 1205 HashTableConstIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> *it) 1206 { 1207 it->m_table = table; 1208 it->m_previous = nullptr; 1209 1210 // Insert iterator at head of doubly-linked list of iterators. 1211 if (!table) { 1212 it->m_next = nullptr; 1213 } else { 1214 ASSERT(table->m_iterators != it); 1215 it->m_next = table->m_iterators; 1216 table->m_iterators = it; 1217 if (it->m_next) { 1218 ASSERT(!it->m_next->m_previous); 1219 it->m_next->m_previous = it; 1220 } 1221 } 1222 } 1223 1224 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 1225 void removeIterator(HashTableConstIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> *it) 1226 { 1227 typedef HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> HashTableType; 1228 typedef HashTableConstIterator<Key, Value, Extractor, HashFunctions, Traits, KeyTraits> const_iterator; 1229 1230 // Delete iterator from doubly-linked list of iterators. 1231 if (!it->m_table) { 1232 ASSERT(!it->m_next); 1233 ASSERT(!it->m_previous); 1234 } else { 1235 if (it->m_next) { 1236 ASSERT(it->m_next->m_previous == it); 1237 it->m_next->m_previous = it->m_previous; 1238 } 1239 if (it->m_previous) { 1240 ASSERT(it->m_table->m_iterators != it); 1241 ASSERT(it->m_previous->m_next == it); 1242 it->m_previous->m_next = it->m_next; 1243 } else { 1244 ASSERT(it->m_table->m_iterators == it); 1245 it->m_table->m_iterators = it->m_next; 1246 } 1247 } 1248 1249 it->m_table = nullptr; 1250 it->m_next = nullptr; 1251 it->m_previous = nullptr; 1252 } 1253 1254 #endif // CHECK_HASHTABLE_ITERATORS 1255 1256 // iterator adapters 1257 1258 template<typename HashTableType, typename ValueType> struct HashTableConstIteratorAdapter { 1259 HashTableConstIteratorAdapter(const typename HashTableType::const_iterator &impl) : m_impl(impl) {} 1260 1261 const ValueType *get() const 1262 { 1263 return (const ValueType *)m_impl.get(); 1264 } 1265 const ValueType &operator*() const 1266 { 1267 return *get(); 1268 } 1269 const ValueType *operator->() const 1270 { 1271 return get(); 1272 } 1273 1274 HashTableConstIteratorAdapter &operator++() 1275 { 1276 ++m_impl; 1277 return *this; 1278 } 1279 // postfix ++ intentionally omitted 1280 1281 typename HashTableType::const_iterator m_impl; 1282 }; 1283 1284 template<typename HashTableType, typename ValueType> struct HashTableIteratorAdapter { 1285 HashTableIteratorAdapter(const typename HashTableType::iterator &impl) : m_impl(impl) {} 1286 1287 ValueType *get() const 1288 { 1289 return (ValueType *)m_impl.get(); 1290 } 1291 ValueType &operator*() const 1292 { 1293 return *get(); 1294 } 1295 ValueType *operator->() const 1296 { 1297 return get(); 1298 } 1299 1300 HashTableIteratorAdapter &operator++() 1301 { 1302 ++m_impl; 1303 return *this; 1304 } 1305 // postfix ++ intentionally omitted 1306 1307 operator HashTableConstIteratorAdapter<HashTableType, ValueType>() 1308 { 1309 typename HashTableType::const_iterator i = m_impl; 1310 return i; 1311 } 1312 1313 typename HashTableType::iterator m_impl; 1314 }; 1315 1316 template<typename T, typename U> 1317 inline bool operator==(const HashTableConstIteratorAdapter<T, U> &a, const HashTableConstIteratorAdapter<T, U> &b) 1318 { 1319 return a.m_impl == b.m_impl; 1320 } 1321 1322 template<typename T, typename U> 1323 inline bool operator!=(const HashTableConstIteratorAdapter<T, U> &a, const HashTableConstIteratorAdapter<T, U> &b) 1324 { 1325 return a.m_impl != b.m_impl; 1326 } 1327 1328 template<typename T, typename U> 1329 inline bool operator==(const HashTableIteratorAdapter<T, U> &a, const HashTableIteratorAdapter<T, U> &b) 1330 { 1331 return a.m_impl == b.m_impl; 1332 } 1333 1334 template<typename T, typename U> 1335 inline bool operator!=(const HashTableIteratorAdapter<T, U> &a, const HashTableIteratorAdapter<T, U> &b) 1336 { 1337 return a.m_impl != b.m_impl; 1338 } 1339 1340 } // namespace WTF 1341 1342 #include <wtf/HashIterators.h> 1343 1344 #endif // WTF_HashTable_h