Changeset 27176 in webkit
- Timestamp:
- Oct 28, 2007, 1:17:39 AM (17 years ago)
- Location:
- trunk/JavaScriptCore
- Files:
-
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/JavaScriptCore/ChangeLog
r27164 r27176 1 2007-10-27 Maciej Stachowiak <mjs@apple.com> 2 3 Reviewed by Oliver. 4 5 - numerous HashTable performance improvements 6 7 This does not quite add up to a measurable win on SunSpider, but it allows a 8 follow-on > 3% improvement and probably helps WebCore too. 9 10 I made the following improvements, among others: 11 12 - Made HashFunctions note whether it is ok to compare a real value with the equal() function 13 to the empty or deleted value, and used this to optimize the comparisons done in hash lookup. 14 15 - Specialized lookup so it doesn't have to do so many extra branches and build so many extra 16 std::pairs for cases that don't need them. There are now four versions, one for read-only access, 17 two for writing, and one folded directly into add() (these all were improvments). 18 19 - Made HashMap::get() use lookup() directly instead of find() to avoid having to build iterators. 20 21 - Made a special constructor for iterators that knows it points to 22 a valid filled cell and so skips updating itself. 23 24 - Reordered memory accesses in the various lookup functions for better codegetion 25 26 - Made simple translators avoid passing a hash code around 27 28 - Other minor tweaks 29 30 * wtf/HashTable.h: 31 (WTF::): 32 (WTF::HashTableConstIterator::HashTableConstIterator): 33 (WTF::HashTableIterator::HashTableIterator): 34 (WTF::IdentityHashTranslator::translate): 35 (WTF::HashTable::end): 36 (WTF::HashTable::lookup): 37 (WTF::HashTable::lookupForWriting): 38 (WTF::HashTable::makeKnownGoodIterator): 39 (WTF::HashTable::makeKnownGoodConstIterator): 40 (WTF::::lookup): 41 (WTF::::lookupForWriting): 42 (WTF::::fullLookupForWriting): 43 (WTF::::add): 44 (WTF::::addPassingHashCode): 45 (WTF::::reinsert): 46 (WTF::::find): 47 (WTF::::contains): 48 * kjs/identifier.cpp: 49 (WTF::): 50 * wtf/HashFunctions.h: 51 (WTF::): 52 * wtf/HashMap.h: 53 (WTF::): 54 (WTF::::get): 55 * wtf/HashSet.h: 56 (WTF::): 57 (WTF::::add): 58 * wtf/ListHashSet.h: 59 (WTF::ListHashSetTranslator::translate): 60 1 61 2007-10-27 Darin Adler <darin@apple.com> 2 62 -
trunk/JavaScriptCore/kjs/identifier.cpp
r27153 r27176 39 39 static unsigned hash(const KJS::UString::Rep *key) { return key->hash(); } 40 40 static bool equal(const KJS::UString::Rep *a, const KJS::UString::Rep *b) { return KJS::Identifier::equal(a, b); } 41 static const bool safeToCompareToEmptyOrDeleted = false; 41 42 }; 42 43 -
trunk/JavaScriptCore/kjs/nodes.h
r27134 r27176 1258 1258 1259 1259 // Properties that will go into the ActivationImp's local storage. (Used for initializing the ActivationImp.) 1260 1261 1262 1260 DeclarationStacks::VarStack m_varStack; 1261 DeclarationStacks::FunctionStack m_functionStack; 1262 Vector<Identifier> m_parameters; 1263 1263 1264 1264 // Mapping from property name -> local storage index. (Used once to transform the AST, and subsequently for residual slow case lookups.) -
trunk/JavaScriptCore/wtf/HashFunctions.h
r26417 r27176 66 66 static unsigned hash(T key) { return intHash(static_cast<typename IntTypes<sizeof(T)>::UnsignedType>(key)); } 67 67 static bool equal(T a, T b) { return a == b; } 68 static const bool safeToCompareToEmptyOrDeleted = true; 68 69 }; 69 70 … … 71 72 static unsigned hash(T key) { return intHash(*reinterpret_cast<typename IntTypes<sizeof(T)>::UnsignedType*>(&key)); } 72 73 static bool equal(T a, T b) { return a == b; } 74 static const bool safeToCompareToEmptyOrDeleted = true; 73 75 }; 74 76 … … 88 90 } 89 91 static bool equal(T a, T b) { return a == b; } 92 static const bool safeToCompareToEmptyOrDeleted = true; 90 93 }; 91 94 template<typename P> struct PtrHash<RefPtr<P> > { 92 95 static unsigned hash(const RefPtr<P>& key) { return PtrHash<P*>::hash(key.get()); } 93 96 static bool equal(const RefPtr<P>& a, const RefPtr<P>& b) { return a == b; } 97 static const bool safeToCompareToEmptyOrDeleted = true; 94 98 }; 95 99 -
trunk/JavaScriptCore/wtf/HashMap.h
r25365 r27176 130 130 static unsigned hash(const KeyType& key) { return HashFunctions::hash(key); } 131 131 static bool equal(const KeyStorageType& a, const KeyType& b) { return HashFunctions::equal(*(KeyType*)&a, b); } 132 static void translate(ValueStorageType& location, const KeyType& key, const MappedType& mapped , unsigned)132 static void translate(ValueStorageType& location, const KeyType& key, const MappedType& mapped) 133 133 { 134 134 Assigner<KeyTraits::needsRef, KeyType, KeyStorageType, KeyTraits>::assign(key, location.first); … … 151 151 static unsigned hash(const KeyType& key) { return HashFunctions::hash(key); } 152 152 static bool equal(const KeyStorageType& a, const KeyType& b) { return HashFunctions::equal(*(KeyType*)&a, b); } 153 static void translate(ValueStorageType& location, const KeyType& key, const MappedType& mapped , unsigned)153 static void translate(ValueStorageType& location, const KeyType& key, const MappedType& mapped) 154 154 { 155 155 if (location.first == KeyStorageTraits::deletedValue()) … … 292 292 293 293 template<typename T, typename U, typename V, typename W, typename MappedTraits> 294 inlinetypename HashMap<T, U, V, W, MappedTraits>::MappedType294 typename HashMap<T, U, V, W, MappedTraits>::MappedType 295 295 HashMap<T, U, V, W, MappedTraits>::get(const KeyType& key) const 296 296 { 297 const_iterator it = find(key); 298 if (it == end()) 297 if (m_impl.isEmpty()) 299 298 return MappedTraits::emptyValue(); 300 return it->second; 299 ValueStorageType* entry = const_cast<HashTableType&>(m_impl).lookup(*(const KeyStorageType*)&key); 300 if (!entry) 301 return MappedTraits::emptyValue(); 302 return ((ValueType *)entry)->second; 301 303 } 302 304 -
trunk/JavaScriptCore/wtf/HashSet.h
r25365 r27176 112 112 static unsigned hash(const ValueType& key) { return HashFunctions::hash(key); } 113 113 static bool equal(const StorageType& a, const ValueType& b) { return HashFunctions::equal(*(const ValueType*)&a, b); } 114 static void translate(StorageType& location, const ValueType& key, const ValueType& , unsigned)114 static void translate(StorageType& location, const ValueType& key, const ValueType&) 115 115 { 116 116 Assigner<ValueTraits::needsRef, ValueType, StorageType, ValueTraits>::assign(key, location); … … 123 123 static unsigned hash(const ValueType& key) { return HashFunctions::hash(key); } 124 124 static bool equal(const StorageType& a, const ValueType& b) { return HashFunctions::equal(*(const ValueType*)&a, b); } 125 static void translate(StorageType& location, const ValueType& key, const ValueType& , unsigned)125 static void translate(StorageType& location, const ValueType& key, const ValueType&) 126 126 { 127 127 if (location == StorageTraits::deletedValue()) … … 277 277 const bool canReplaceDeletedValue = !ValueTraits::needsDestruction || StorageTraits::needsDestruction; 278 278 typedef HashSetTranslatorAdapter<canReplaceDeletedValue, ValueType, StorageTraits, T, Translator> Adapter; 279 return m_impl.template add <T, T, Adapter>(value, value);279 return m_impl.template addPassingHashCode<T, T, Adapter>(value, value); 280 280 } 281 281 -
trunk/JavaScriptCore/wtf/HashTable.h
r27093 r27176 78 78 #endif 79 79 80 typedef enum { HashItemKnownGood } HashItemKnownGoodTag; 81 82 80 83 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 81 84 class HashTableConstIterator { … … 104 107 } 105 108 109 HashTableConstIterator(const HashTableType* table, PointerType position, PointerType endPosition, HashItemKnownGoodTag) 110 : m_position(position), m_endPosition(endPosition) 111 { 112 addIterator(table, this); 113 } 114 106 115 public: 107 116 HashTableConstIterator() … … 211 220 212 221 HashTableIterator(HashTableType* table, PointerType pos, PointerType end) : m_iterator(table, pos, end) { } 222 HashTableIterator(HashTableType* table, PointerType pos, PointerType end, HashItemKnownGoodTag tag) : m_iterator(table, pos, end, tag) { } 213 223 214 224 public: … … 256 266 static unsigned hash(const Key& key) { return HashFunctions::hash(key); } 257 267 static bool equal(const Key& a, const Key& b) { return HashFunctions::equal(a, b); } 258 static void translate(Value& location, const Key&, const Value& value , unsigned) { location = value; }268 static void translate(Value& location, const Key&, const Value& value) { location = value; } 259 269 }; 260 270 … … 277 287 278 288 iterator begin() { return makeIterator(m_table); } 279 iterator end() { return make Iterator(m_table + m_tableSize); }289 iterator end() { return makeKnownGoodIterator(m_table + m_tableSize); } 280 290 const_iterator begin() const { return makeConstIterator(m_table); } 281 const_iterator end() const { return make ConstIterator(m_table + m_tableSize); }291 const_iterator end() const { return makeKnownGoodConstIterator(m_table + m_tableSize); } 282 292 283 293 int size() const { return m_keyCount; } … … 291 301 // in the table. 292 302 template<typename T, typename Extra, typename HashTranslator> pair<iterator, bool> add(const T& key, const Extra&); 303 template<typename T, typename Extra, typename HashTranslator> pair<iterator, bool> addPassingHashCode(const T& key, const Extra&); 293 304 294 305 iterator find(const KeyType& key) { return find<KeyType, IdentityTranslatorType>(key); } … … 308 319 static bool isEmptyOrDeletedBucket(const ValueType& value) { return isEmptyBucket(value) || isDeletedBucket(value); } 309 320 321 ValueType* lookup(const Key& key) { return lookup<Key, IdentityTranslatorType>(key); } 322 310 323 private: 311 324 static ValueType* allocateTable(int size); … … 315 328 typedef pair<LookupType, unsigned> FullLookupType; 316 329 317 LookupType lookup(const Key& key) { return lookup<Key, IdentityTranslatorType>(key).first; } 318 template<typename T, typename HashTranslator> FullLookupType lookup(const T&); 330 template<typename T, typename HashTranslator> ValueType* lookup(const T&); 331 LookupType lookupForWriting(const Key& key) { return lookupForWriting<Key, IdentityTranslatorType>(key); }; 332 template<typename T, typename HashTranslator> FullLookupType fullLookupForWriting(const T&); 333 template<typename T, typename HashTranslator> LookupType lookupForWriting(const T&); 319 334 320 335 void remove(ValueType*); … … 337 352 iterator makeIterator(ValueType* pos) { return iterator(this, pos, m_table + m_tableSize); } 338 353 const_iterator makeConstIterator(ValueType* pos) const { return const_iterator(this, pos, m_table + m_tableSize); } 354 iterator makeKnownGoodIterator(ValueType* pos) { return iterator(this, pos, m_table + m_tableSize, HashItemKnownGood); } 355 const_iterator makeKnownGoodConstIterator(ValueType* pos) const { return const_iterator(this, pos, m_table + m_tableSize, HashItemKnownGood); } 339 356 340 357 #if CHECK_HASHTABLE_CONSISTENCY … … 383 400 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 384 401 template<typename T, typename HashTranslator> 385 inline typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::FullLookupTypeHashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::lookup(const T& key)402 inline Value* HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::lookup(const T& key) 386 403 { 387 404 ASSERT(m_table); 388 405 406 int k = 0; 407 int sizeMask = m_tableSizeMask; 408 ValueType* table = m_table; 389 409 unsigned h = HashTranslator::hash(key); 390 int sizeMask = m_tableSizeMask;391 410 int i = h & sizeMask; 392 int k = 0;393 411 394 412 #if DUMP_HASHTABLE_STATS … … 397 415 #endif 398 416 399 ValueType* table = m_table;400 ValueType* deletedEntry = 0;401 402 417 while (1) { 403 418 ValueType* entry = table + i; 404 405 if (isEmptyBucket(*entry)) 406 return makeLookupResult(deletedEntry ? deletedEntry : entry, false, h); 407 408 if (isDeletedBucket(*entry)) 409 deletedEntry = entry; 410 else if (HashTranslator::equal(Extractor::extract(*entry), key)) 411 return makeLookupResult(entry, true, h); 412 419 420 // we count on the compiler to optimize out this branch 421 if (HashFunctions::safeToCompareToEmptyOrDeleted) { 422 if (HashTranslator::equal(Extractor::extract(*entry), key)) 423 return entry; 424 425 if (isEmptyBucket(*entry)) 426 return 0; 427 } else { 428 if (isEmptyBucket(*entry)) 429 return 0; 430 431 if (!isDeletedBucket(*entry) && HashTranslator::equal(Extractor::extract(*entry), key)) 432 return entry; 433 } 413 434 #if DUMP_HASHTABLE_STATS 414 435 ++probeCount; … … 422 443 423 444 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 445 template<typename T, typename HashTranslator> 446 inline typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::LookupType HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::lookupForWriting(const T& key) 447 { 448 ASSERT(m_table); 449 450 int k = 0; 451 ValueType* table = m_table; 452 int sizeMask = m_tableSizeMask; 453 unsigned h = HashTranslator::hash(key); 454 int i = h & sizeMask; 455 456 #if DUMP_HASHTABLE_STATS 457 ++HashTableStats::numAccesses; 458 int probeCount = 0; 459 #endif 460 461 ValueType* deletedEntry = 0; 462 463 while (1) { 464 ValueType* entry = table + i; 465 466 // we count on the compiler to optimize out this branch 467 if (HashFunctions::safeToCompareToEmptyOrDeleted) { 468 if (isEmptyBucket(*entry)) 469 return LookupType(deletedEntry ? deletedEntry : entry, false); 470 471 if (HashTranslator::equal(Extractor::extract(*entry), key)) 472 return LookupType(entry, true); 473 474 if (isDeletedBucket(*entry)) 475 deletedEntry = entry; 476 } else { 477 if (isEmptyBucket(*entry)) 478 return LookupType(deletedEntry ? deletedEntry : entry, false); 479 480 if (isDeletedBucket(*entry)) 481 deletedEntry = entry; 482 else if (HashTranslator::equal(Extractor::extract(*entry), key)) 483 return LookupType(entry, true); 484 } 485 #if DUMP_HASHTABLE_STATS 486 ++probeCount; 487 HashTableStats::recordCollisionAtCount(probeCount); 488 #endif 489 if (k == 0) 490 k = 1 | (h % sizeMask); 491 i = (i + k) & sizeMask; 492 } 493 } 494 495 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 496 template<typename T, typename HashTranslator> 497 inline typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::FullLookupType HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::fullLookupForWriting(const T& key) 498 { 499 ASSERT(m_table); 500 501 int k = 0; 502 ValueType* table = m_table; 503 int sizeMask = m_tableSizeMask; 504 unsigned h = HashTranslator::hash(key); 505 int i = h & sizeMask; 506 507 #if DUMP_HASHTABLE_STATS 508 ++HashTableStats::numAccesses; 509 int probeCount = 0; 510 #endif 511 512 ValueType* deletedEntry = 0; 513 514 while (1) { 515 ValueType* entry = table + i; 516 517 // we count on the compiler to optimize out this branch 518 if (HashFunctions::safeToCompareToEmptyOrDeleted) { 519 if (isEmptyBucket(*entry)) 520 return makeLookupResult(deletedEntry ? deletedEntry : entry, false, h); 521 522 if (HashTranslator::equal(Extractor::extract(*entry), key)) 523 return makeLookupResult(entry, true, h); 524 525 if (isDeletedBucket(*entry)) 526 deletedEntry = entry; 527 } else { 528 if (isEmptyBucket(*entry)) 529 return makeLookupResult(deletedEntry ? deletedEntry : entry, false, h); 530 531 if (isDeletedBucket(*entry)) 532 deletedEntry = entry; 533 else if (HashTranslator::equal(Extractor::extract(*entry), key)) 534 return makeLookupResult(entry, true, h); 535 } 536 #if DUMP_HASHTABLE_STATS 537 ++probeCount; 538 HashTableStats::recordCollisionAtCount(probeCount); 539 #endif 540 if (k == 0) 541 k = 1 | (h % sizeMask); 542 i = (i + k) & sizeMask; 543 } 544 } 545 546 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 424 547 template<typename T, typename Extra, typename HashTranslator> 425 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)548 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) 426 549 { 427 550 invalidateIterators(); … … 432 555 checkTableConsistency(); 433 556 434 FullLookupType lookupResult = lookup<T, HashTranslator>(key); 435 436 ValueType *entry = lookupResult.first.first; 437 bool found = lookupResult.first.second; 438 unsigned h = lookupResult.second; 439 440 if (found) 441 return std::make_pair(makeIterator(entry), false); 442 443 if (isDeletedBucket(*entry)) 444 --m_deletedCount; 445 446 HashTranslator::translate(*entry, key, extra, h); 557 ASSERT(m_table); 558 559 int k = 0; 560 ValueType* table = m_table; 561 int sizeMask = m_tableSizeMask; 562 unsigned h = HashTranslator::hash(key); 563 int i = h & sizeMask; 564 565 #if DUMP_HASHTABLE_STATS 566 ++HashTableStats::numAccesses; 567 int probeCount = 0; 568 #endif 569 570 ValueType* deletedEntry = 0; 571 ValueType* entry; 572 while (1) { 573 entry = table + i; 574 575 // we count on the compiler to optimize out this branch 576 if (HashFunctions::safeToCompareToEmptyOrDeleted) { 577 if (isEmptyBucket(*entry)) 578 break; 579 580 if (HashTranslator::equal(Extractor::extract(*entry), key)) 581 return std::make_pair(makeKnownGoodIterator(entry), false); 582 583 if (isDeletedBucket(*entry)) 584 deletedEntry = entry; 585 } else { 586 if (isEmptyBucket(*entry)) 587 break; 588 589 if (isDeletedBucket(*entry)) 590 deletedEntry = entry; 591 else if (HashTranslator::equal(Extractor::extract(*entry), key)) 592 return std::make_pair(makeKnownGoodIterator(entry), false); 593 } 594 #if DUMP_HASHTABLE_STATS 595 ++probeCount; 596 HashTableStats::recordCollisionAtCount(probeCount); 597 #endif 598 if (k == 0) 599 k = 1 | (h % sizeMask); 600 i = (i + k) & sizeMask; 601 } 602 603 if (deletedEntry) { 604 entry = deletedEntry; 605 --m_deletedCount; 606 } 607 608 HashTranslator::translate(*entry, key, extra); 609 447 610 ++m_keyCount; 448 611 449 612 if (shouldExpand()) { 450 613 // FIXME: this makes an extra copy on expand. Probably not that bad since … … 455 618 return std::make_pair(find(enteredKey), true); 456 619 } 457 620 458 621 checkTableConsistency(); 459 460 return std::make_pair(makeIterator(entry), true); 622 623 return std::make_pair(makeKnownGoodIterator(entry), true); 624 } 625 626 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 627 template<typename T, typename Extra, typename HashTranslator> 628 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) 629 { 630 invalidateIterators(); 631 632 if (!m_table) 633 expand(); 634 635 checkTableConsistency(); 636 637 FullLookupType lookupResult = fullLookupForWriting<T, HashTranslator>(key); 638 639 ValueType *entry = lookupResult.first.first; 640 bool found = lookupResult.first.second; 641 unsigned h = lookupResult.second; 642 643 if (found) 644 return std::make_pair(makeKnownGoodIterator(entry), false); 645 646 if (isDeletedBucket(*entry)) 647 --m_deletedCount; 648 649 HashTranslator::translate(*entry, key, extra, h); 650 ++m_keyCount; 651 if (shouldExpand()) { 652 // FIXME: this makes an extra copy on expand. Probably not that bad since 653 // expand is rare, but would be better to have a version of expand that can 654 // follow a pivot entry and return the new position 655 KeyType enteredKey = Extractor::extract(*entry); 656 expand(); 657 return std::make_pair(find(enteredKey), true); 658 } 659 660 checkTableConsistency(); 661 662 return std::make_pair(makeKnownGoodIterator(entry), true); 461 663 } 462 664 … … 465 667 { 466 668 ASSERT(m_table); 467 ASSERT(!lookup (Extractor::extract(entry)).second);468 ASSERT(!isDeletedBucket(*(lookup (Extractor::extract(entry)).first)));669 ASSERT(!lookupForWriting(Extractor::extract(entry)).second); 670 ASSERT(!isDeletedBucket(*(lookupForWriting(Extractor::extract(entry)).first))); 469 671 #if DUMP_HASHTABLE_STATS 470 672 ++HashTableStats::numReinserts; 471 673 #endif 472 674 473 Mover<ValueType, Traits::needsDestruction>::move(entry, *(lookup (Extractor::extract(entry)).first));675 Mover<ValueType, Traits::needsDestruction>::move(entry, *(lookupForWriting(Extractor::extract(entry)).first)); 474 676 } 475 677 … … 481 683 return end(); 482 684 483 LookupType result = lookup<T, HashTranslator>(key).first;484 if (! result.second)685 ValueType* entry = lookup<T, HashTranslator>(key); 686 if (!entry) 485 687 return end(); 486 return makeIterator(result.first); 688 689 return makeKnownGoodIterator(entry); 487 690 } 488 691 … … 494 697 return end(); 495 698 496 LookupType result = const_cast<HashTable *>(this)->lookup<T, HashTranslator>(key).first;497 if (! result.second)699 ValueType* entry = const_cast<HashTable *>(this)->lookup<T, HashTranslator>(key); 700 if (!entry) 498 701 return end(); 499 return makeConstIterator(result.first); 702 703 return makeKnownGoodConstIterator(entry); 500 704 } 501 705 … … 507 711 return false; 508 712 509 return const_cast<HashTable *>(this)->lookup<T, HashTranslator>(key) .first.second;713 return const_cast<HashTable *>(this)->lookup<T, HashTranslator>(key); 510 714 } 511 715 -
trunk/JavaScriptCore/wtf/ListHashSet.h
r21175 r27176 221 221 static unsigned hash(Node* const& key) { return HashArg::hash(key->m_value); } 222 222 static bool equal(Node* const& a, Node* const& b) { return HashArg::equal(a->m_value, b->m_value); } 223 static const bool safeToCompareToEmptyOrDeleted = false; 223 224 }; 224 225 … … 341 342 static unsigned hash(const ValueType& key) { return HashFunctions::hash(key); } 342 343 static bool equal(Node* const& a, const ValueType& b) { return HashFunctions::equal(a->m_value, b); } 343 static void translate(Node*& location, const ValueType& key, NodeAllocator* allocator , unsigned)344 static void translate(Node*& location, const ValueType& key, NodeAllocator* allocator) 344 345 { 345 346 location = new (allocator) Node(key);
Note:
See TracChangeset
for help on using the changeset viewer.