LCOV - code coverage report
Current view: top level - xpcom/ds - PLDHashTable.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 289 327 88.4 %
Date: 2017-07-14 16:53:18 Functions: 47 50 94.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include <new>
       8             : #include <stdio.h>
       9             : #include <stdlib.h>
      10             : #include <string.h>
      11             : #include "PLDHashTable.h"
      12             : #include "mozilla/HashFunctions.h"
      13             : #include "mozilla/MathAlgorithms.h"
      14             : #include "mozilla/OperatorNewExtensions.h"
      15             : #include "nsAlgorithm.h"
      16             : #include "mozilla/Likely.h"
      17             : #include "mozilla/MemoryReporting.h"
      18             : #include "mozilla/ChaosMode.h"
      19             : 
      20             : using namespace mozilla;
      21             : 
      22             : #ifdef DEBUG
      23             : 
      24             : class AutoReadOp
      25             : {
      26             :   Checker& mChk;
      27             : public:
      28      239615 :   explicit AutoReadOp(Checker& aChk) : mChk(aChk) { mChk.StartReadOp(); }
      29      239610 :   ~AutoReadOp() { mChk.EndReadOp(); }
      30             : };
      31             : 
      32             : class AutoWriteOp
      33             : {
      34             :   Checker& mChk;
      35             : public:
      36      211259 :   explicit AutoWriteOp(Checker& aChk) : mChk(aChk) { mChk.StartWriteOp(); }
      37      211258 :   ~AutoWriteOp() { mChk.EndWriteOp(); }
      38             : };
      39             : 
      40             : class AutoIteratorRemovalOp
      41             : {
      42             :   Checker& mChk;
      43             : public:
      44             :   explicit AutoIteratorRemovalOp(Checker& aChk)
      45             :     : mChk(aChk)
      46             :   {
      47             :     mChk.StartIteratorRemovalOp();
      48             :   }
      49             :   ~AutoIteratorRemovalOp() { mChk.EndIteratorRemovalOp(); }
      50             : };
      51             : 
      52             : class AutoDestructorOp
      53             : {
      54             :   Checker& mChk;
      55             : public:
      56        5582 :   explicit AutoDestructorOp(Checker& aChk)
      57        5582 :     : mChk(aChk)
      58             :   {
      59        5582 :     mChk.StartDestructorOp();
      60        5583 :   }
      61        5583 :   ~AutoDestructorOp() { mChk.EndDestructorOp(); }
      62             : };
      63             : 
      64             : #endif
      65             : 
      66             : /* static */ PLDHashNumber
      67       31000 : PLDHashTable::HashStringKey(const void* aKey)
      68             : {
      69       31000 :   return HashString(static_cast<const char*>(aKey));
      70             : }
      71             : 
      72             : /* static */ PLDHashNumber
      73       86339 : PLDHashTable::HashVoidPtrKeyStub(const void* aKey)
      74             : {
      75       86339 :   return uintptr_t(aKey) >> 2;
      76             : }
      77             : 
      78             : /* static */ bool
      79       24302 : PLDHashTable::MatchEntryStub(const PLDHashEntryHdr* aEntry, const void* aKey)
      80             : {
      81       24302 :   const PLDHashEntryStub* stub = (const PLDHashEntryStub*)aEntry;
      82             : 
      83       24302 :   return stub->key == aKey;
      84             : }
      85             : 
      86             : /* static */ bool
      87         148 : PLDHashTable::MatchStringKey(const PLDHashEntryHdr* aEntry, const void* aKey)
      88             : {
      89         148 :   const PLDHashEntryStub* stub = (const PLDHashEntryStub*)aEntry;
      90             : 
      91             :   // XXX tolerate null keys on account of sloppy Mozilla callers.
      92         444 :   return stub->key == aKey ||
      93         444 :          (stub->key && aKey &&
      94         296 :           strcmp((const char*)stub->key, (const char*)aKey) == 0);
      95             : }
      96             : 
      97             : /* static */ void
      98       60998 : PLDHashTable::MoveEntryStub(PLDHashTable* aTable,
      99             :                             const PLDHashEntryHdr* aFrom,
     100             :                             PLDHashEntryHdr* aTo)
     101             : {
     102       60998 :   memcpy(aTo, aFrom, aTable->mEntrySize);
     103       60998 : }
     104             : 
     105             : /* static */ void
     106        2627 : PLDHashTable::ClearEntryStub(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
     107             : {
     108        2627 :   memset(aEntry, 0, aTable->mEntrySize);
     109        2627 : }
     110             : 
     111             : static const PLDHashTableOps gStubOps = {
     112             :   PLDHashTable::HashVoidPtrKeyStub,
     113             :   PLDHashTable::MatchEntryStub,
     114             :   PLDHashTable::MoveEntryStub,
     115             :   PLDHashTable::ClearEntryStub,
     116             :   nullptr
     117             : };
     118             : 
     119             : /* static */ const PLDHashTableOps*
     120         673 : PLDHashTable::StubOps()
     121             : {
     122         673 :   return &gStubOps;
     123             : }
     124             : 
     125             : static bool
     126       18394 : SizeOfEntryStore(uint32_t aCapacity, uint32_t aEntrySize, uint32_t* aNbytes)
     127             : {
     128       18394 :   uint64_t nbytes64 = uint64_t(aCapacity) * uint64_t(aEntrySize);
     129       18394 :   *aNbytes = aCapacity * aEntrySize;
     130       18394 :   return uint64_t(*aNbytes) == nbytes64;   // returns false on overflow
     131             : }
     132             : 
     133             : // Compute max and min load numbers (entry counts). We have a secondary max
     134             : // that allows us to overload a table reasonably if it cannot be grown further
     135             : // (i.e. if ChangeTable() fails). The table slows down drastically if the
     136             : // secondary max is too close to 1, but 0.96875 gives only a slight slowdown
     137             : // while allowing 1.3x more elements.
     138             : static inline uint32_t
     139      200036 : MaxLoad(uint32_t aCapacity)
     140             : {
     141      200036 :   return aCapacity - (aCapacity >> 2);  // == aCapacity * 0.75
     142             : }
     143             : static inline uint32_t
     144           0 : MaxLoadOnGrowthFailure(uint32_t aCapacity)
     145             : {
     146           0 :   return aCapacity - (aCapacity >> 5);  // == aCapacity * 0.96875
     147             : }
     148             : static inline uint32_t
     149        9961 : MinLoad(uint32_t aCapacity)
     150             : {
     151        9961 :   return aCapacity >> 2;                // == aCapacity * 0.25
     152             : }
     153             : 
     154             : // Compute the minimum capacity (and the Log2 of that capacity) for a table
     155             : // containing |aLength| elements while respecting the following contraints:
     156             : // - table must be at most 75% full;
     157             : // - capacity must be a power of two;
     158             : // - capacity cannot be too small.
     159             : static inline void
     160       13606 : BestCapacity(uint32_t aLength, uint32_t* aCapacityOut,
     161             :              uint32_t* aLog2CapacityOut)
     162             : {
     163             :   // Compute the smallest capacity allowing |aLength| elements to be inserted
     164             :   // without rehashing.
     165       13606 :   uint32_t capacity = (aLength * 4 + (3 - 1)) / 3; // == ceil(aLength * 4 / 3)
     166       13606 :   if (capacity < PLDHashTable::kMinCapacity) {
     167       12774 :     capacity = PLDHashTable::kMinCapacity;
     168             :   }
     169             : 
     170             :   // Round up capacity to next power-of-two.
     171       13606 :   uint32_t log2 = CeilingLog2(capacity);
     172       13606 :   capacity = 1u << log2;
     173       13606 :   MOZ_ASSERT(capacity <= PLDHashTable::kMaxCapacity);
     174             : 
     175       13606 :   *aCapacityOut = capacity;
     176       13606 :   *aLog2CapacityOut = log2;
     177       13606 : }
     178             : 
     179             : /* static */ MOZ_ALWAYS_INLINE uint32_t
     180       13559 : PLDHashTable::HashShift(uint32_t aEntrySize, uint32_t aLength)
     181             : {
     182       13559 :   if (aLength > kMaxInitialLength) {
     183           0 :     MOZ_CRASH("Initial length is too large");
     184             :   }
     185             : 
     186             :   uint32_t capacity, log2;
     187       13559 :   BestCapacity(aLength, &capacity, &log2);
     188             : 
     189             :   uint32_t nbytes;
     190       13559 :   if (!SizeOfEntryStore(capacity, aEntrySize, &nbytes)) {
     191           0 :     MOZ_CRASH("Initial entry store size is too large");
     192             :   }
     193             : 
     194             :   // Compute the hashShift value.
     195       13559 :   return kHashBits - log2;
     196             : }
     197             : 
     198       13559 : PLDHashTable::PLDHashTable(const PLDHashTableOps* aOps, uint32_t aEntrySize,
     199       13559 :                            uint32_t aLength)
     200             :   : mOps(aOps)
     201       13559 :   , mHashShift(HashShift(aEntrySize, aLength))
     202             :   , mEntrySize(aEntrySize)
     203             :   , mEntryCount(0)
     204             :   , mRemovedCount(0)
     205             :   , mEntryStore()
     206             : #ifdef DEBUG
     207       27118 :   , mChecker()
     208             : #endif
     209             : {
     210       13559 : }
     211             : 
     212             : PLDHashTable&
     213           3 : PLDHashTable::operator=(PLDHashTable&& aOther)
     214             : {
     215           3 :   if (this == &aOther) {
     216           0 :     return *this;
     217             :   }
     218             : 
     219             :   // Destruct |this|.
     220           3 :   this->~PLDHashTable();
     221             : 
     222             :   // |mOps| and |mEntrySize| are const so we can't assign them. Instead, we
     223             :   // require that they are equal. The justification for this is that they're
     224             :   // conceptually part of the type -- indeed, if PLDHashTable was a templated
     225             :   // type like nsTHashtable, they *would* be part of the type -- so it only
     226             :   // makes sense to assign in cases where they match.
     227           3 :   MOZ_RELEASE_ASSERT(mOps == aOther.mOps);
     228           3 :   MOZ_RELEASE_ASSERT(mEntrySize == aOther.mEntrySize);
     229             : 
     230             :   // Move non-const pieces over.
     231           3 :   mHashShift = Move(aOther.mHashShift);
     232           3 :   mEntryCount = Move(aOther.mEntryCount);
     233           3 :   mRemovedCount = Move(aOther.mRemovedCount);
     234           3 :   mEntryStore = Move(aOther.mEntryStore);
     235             : #ifdef DEBUG
     236           3 :   mChecker = Move(aOther.mChecker);
     237             : #endif
     238             : 
     239             :   // Clear up |aOther| so its destruction will be a no-op.
     240             :   {
     241             : #ifdef DEBUG
     242           6 :     AutoDestructorOp op(mChecker);
     243             : #endif
     244           3 :     aOther.mEntryStore.Set(nullptr);
     245             :   }
     246             : 
     247           3 :   return *this;
     248             : }
     249             : 
     250             : PLDHashNumber
     251      495325 : PLDHashTable::Hash1(PLDHashNumber aHash0)
     252             : {
     253      495325 :   return aHash0 >> mHashShift;
     254             : }
     255             : 
     256             : void
     257      146186 : PLDHashTable::Hash2(PLDHashNumber aHash0,
     258             :                     size_t& aHash2Out, size_t& aSizeMaskOut)
     259             : {
     260      146186 :   size_t sizeLog2 = kHashBits - mHashShift;
     261      146186 :   size_t sizeMask = (PLDHashNumber(1) << sizeLog2) - 1;
     262      146186 :   aSizeMaskOut = sizeMask;
     263             : 
     264             :   // The incoming aHash0 always has the low bit unset (since we leave it
     265             :   // free for the collision flag), and should have reasonably random
     266             :   // data in the other 31 bits.  We used the high bits of aHash0 for
     267             :   // Hash1, so we use the low bits here.  If the table size is large,
     268             :   // the bits we use may overlap, but that's still more random than
     269             :   // filling with 0s.
     270             :   //
     271             :   // Double hashing needs the second hash code to be relatively prime to table
     272             :   // size, so we simply make hash2 odd.
     273             :   //
     274             :   // This also conveniently covers up the fact that we have the low bit
     275             :   // unset since aHash0 has the low bit unset.
     276      146186 :   aHash2Out = (aHash0 & sizeMask) | 1;
     277      146186 : }
     278             : 
     279             : // Reserve mKeyHash 0 for free entries and 1 for removed-entry sentinels. Note
     280             : // that a removed-entry sentinel need be stored only if the removed entry had
     281             : // a colliding entry added after it. Therefore we can use 1 as the collision
     282             : // flag in addition to the removed-entry sentinel value. Multiplicative hash
     283             : // uses the high order bits of mKeyHash, so this least-significant reservation
     284             : // should not hurt the hash function's effectiveness much.
     285             : 
     286             : // Match an entry's mKeyHash against an unstored one computed from a key.
     287             : /* static */ bool
     288      505357 : PLDHashTable::MatchEntryKeyhash(PLDHashEntryHdr* aEntry, PLDHashNumber aKeyHash)
     289             : {
     290      505357 :   return (aEntry->mKeyHash & ~kCollisionFlag) == aKeyHash;
     291             : }
     292             : 
     293             : // Compute the address of the indexed entry in table.
     294             : PLDHashEntryHdr*
     295      785600 : PLDHashTable::AddressEntry(size_t aIndex)
     296             : {
     297             :   return reinterpret_cast<PLDHashEntryHdr*>(
     298      785600 :     mEntryStore.Get() + aIndex * mEntrySize);
     299             : }
     300             : 
     301       11159 : PLDHashTable::~PLDHashTable()
     302             : {
     303             : #ifdef DEBUG
     304        6893 :   AutoDestructorOp op(mChecker);
     305             : #endif
     306             : 
     307        5580 :   if (!mEntryStore.Get()) {
     308        4266 :     return;
     309             :   }
     310             : 
     311             :   // Clear any remaining live entries.
     312        1314 :   char* entryAddr = mEntryStore.Get();
     313        1314 :   char* entryLimit = entryAddr + Capacity() * mEntrySize;
     314       36642 :   while (entryAddr < entryLimit) {
     315       17664 :     PLDHashEntryHdr* entry = (PLDHashEntryHdr*)entryAddr;
     316       17664 :     if (EntryIsLive(entry)) {
     317        4819 :       mOps->clearEntry(this, entry);
     318             :     }
     319       17664 :     entryAddr += mEntrySize;
     320             :   }
     321             : 
     322             :   // Entry storage is freed last, by ~EntryStore().
     323        5580 : }
     324             : 
     325             : void
     326         771 : PLDHashTable::ClearAndPrepareForLength(uint32_t aLength)
     327             : {
     328             :   // Get these values before the destructor clobbers them.
     329         771 :   const PLDHashTableOps* ops = mOps;
     330         771 :   uint32_t entrySize = mEntrySize;
     331             : 
     332         771 :   this->~PLDHashTable();
     333         771 :   new (KnownNotNull, this) PLDHashTable(ops, entrySize, aLength);
     334         771 : }
     335             : 
     336             : void
     337         770 : PLDHashTable::Clear()
     338             : {
     339         770 :   ClearAndPrepareForLength(kDefaultInitialLength);
     340         770 : }
     341             : 
     342             : // If |Reason| is |ForAdd|, the return value is always non-null and it may be
     343             : // a previously-removed entry. If |Reason| is |ForSearchOrRemove|, the return
     344             : // value is null on a miss, and will never be a previously-removed entry on a
     345             : // hit. This distinction is a bit grotty but this function is hot enough that
     346             : // these differences are worthwhile.
     347             : template <PLDHashTable::SearchReason Reason>
     348             : PLDHashEntryHdr* NS_FASTCALL
     349      423052 : PLDHashTable::SearchTable(const void* aKey, PLDHashNumber aKeyHash)
     350             : {
     351      423052 :   MOZ_ASSERT(mEntryStore.Get());
     352      423044 :   NS_ASSERTION(!(aKeyHash & kCollisionFlag),
     353             :                "!(aKeyHash & kCollisionFlag)");
     354             : 
     355             :   // Compute the primary hash address.
     356      423044 :   PLDHashNumber hash1 = Hash1(aKeyHash);
     357      423044 :   PLDHashEntryHdr* entry = AddressEntry(hash1);
     358             : 
     359             :   // Miss: return space for a new entry.
     360      423042 :   if (EntryIsFree(entry)) {
     361      111018 :     return (Reason == ForAdd) ? entry : nullptr;
     362             :   }
     363             : 
     364             :   // Hit: return entry.
     365      312013 :   PLDHashMatchEntry matchEntry = mOps->matchEntry;
     366      491282 :   if (MatchEntryKeyhash(entry, aKeyHash) &&
     367      179270 :       matchEntry(entry, aKey)) {
     368      179264 :     return entry;
     369             :   }
     370             : 
     371             :   // Collision: double hash.
     372             :   PLDHashNumber hash2;
     373             :   size_t sizeMask;
     374      132754 :   Hash2(aKeyHash, hash2, sizeMask);
     375             : 
     376             :   // Save the first removed entry pointer so Add() can recycle it. (Only used
     377             :   // if Reason==ForAdd.)
     378      132751 :   PLDHashEntryHdr* firstRemoved = nullptr;
     379             : 
     380             :   for (;;) {
     381      272747 :     if (Reason == ForAdd && !firstRemoved) {
     382      131534 :       if (MOZ_UNLIKELY(EntryIsRemoved(entry))) {
     383        1694 :         firstRemoved = entry;
     384             :       } else {
     385      129840 :         entry->mKeyHash |= kCollisionFlag;
     386             :       }
     387             :     }
     388             : 
     389      272253 :     hash1 -= hash2;
     390      272253 :     hash1 &= sizeMask;
     391             : 
     392      272253 :     entry = AddressEntry(hash1);
     393      272254 :     if (EntryIsFree(entry)) {
     394             :       return (Reason == ForAdd) ? (firstRemoved ? firstRemoved : entry)
     395       78887 :                                 : nullptr;
     396             :     }
     397             : 
     398      247242 :     if (MatchEntryKeyhash(entry, aKeyHash) &&
     399       53872 :         matchEntry(entry, aKey)) {
     400       53872 :       return entry;
     401             :     }
     402             :   }
     403             : 
     404             :   // NOTREACHED
     405             :   return nullptr;
     406             : }
     407             : 
     408             : // This is a copy of SearchTable(), used by ChangeTable(), hardcoded to
     409             : //   1. assume |Reason| is |ForAdd|,
     410             : //   2. assume that |aKey| will never match an existing entry, and
     411             : //   3. assume that no entries have been removed from the current table
     412             : //      structure.
     413             : // Avoiding the need for |aKey| means we can avoid needing a way to map entries
     414             : // to keys, which means callers can use complex key types more easily.
     415             : MOZ_ALWAYS_INLINE PLDHashEntryHdr*
     416       72296 : PLDHashTable::FindFreeEntry(PLDHashNumber aKeyHash)
     417             : {
     418       72296 :   MOZ_ASSERT(mEntryStore.Get());
     419       72296 :   NS_ASSERTION(!(aKeyHash & kCollisionFlag),
     420             :                "!(aKeyHash & kCollisionFlag)");
     421             : 
     422             :   // Compute the primary hash address.
     423       72296 :   PLDHashNumber hash1 = Hash1(aKeyHash);
     424       72296 :   PLDHashEntryHdr* entry = AddressEntry(hash1);
     425             : 
     426             :   // Miss: return space for a new entry.
     427       72296 :   if (EntryIsFree(entry)) {
     428       58863 :     return entry;
     429             :   }
     430             : 
     431             :   // Collision: double hash.
     432             :   PLDHashNumber hash2;
     433             :   size_t sizeMask;
     434       13433 :   Hash2(aKeyHash, hash2, sizeMask);
     435             : 
     436        4613 :   for (;;) {
     437       18046 :     NS_ASSERTION(!EntryIsRemoved(entry),
     438             :                  "!EntryIsRemoved(entry)");
     439       18046 :     entry->mKeyHash |= kCollisionFlag;
     440             : 
     441       18046 :     hash1 -= hash2;
     442       18046 :     hash1 &= sizeMask;
     443             : 
     444       18046 :     entry = AddressEntry(hash1);
     445       18046 :     if (EntryIsFree(entry)) {
     446       13433 :       return entry;
     447             :     }
     448             :   }
     449             : 
     450             :   // NOTREACHED
     451             : }
     452             : 
     453             : bool
     454        1393 : PLDHashTable::ChangeTable(int32_t aDeltaLog2)
     455             : {
     456        1393 :   MOZ_ASSERT(mEntryStore.Get());
     457             : 
     458             :   // Look, but don't touch, until we succeed in getting new entry store.
     459        1393 :   int32_t oldLog2 = kHashBits - mHashShift;
     460        1393 :   int32_t newLog2 = oldLog2 + aDeltaLog2;
     461        1393 :   uint32_t newCapacity = 1u << newLog2;
     462        1393 :   if (newCapacity > kMaxCapacity) {
     463           0 :     return false;
     464             :   }
     465             : 
     466             :   uint32_t nbytes;
     467        1393 :   if (!SizeOfEntryStore(newCapacity, mEntrySize, &nbytes)) {
     468           0 :     return false;   // overflowed
     469             :   }
     470             : 
     471        1393 :   char* newEntryStore = (char*)malloc(nbytes);
     472        1393 :   if (!newEntryStore) {
     473           0 :     return false;
     474             :   }
     475             : 
     476             :   // We can't fail from here on, so update table parameters.
     477        1393 :   mHashShift = kHashBits - newLog2;
     478        1393 :   mRemovedCount = 0;
     479             : 
     480             :   // Assign the new entry store to table.
     481        1393 :   memset(newEntryStore, 0, nbytes);
     482             :   char* oldEntryStore;
     483             :   char* oldEntryAddr;
     484        1393 :   oldEntryAddr = oldEntryStore = mEntryStore.Get();
     485        1393 :   mEntryStore.Set(newEntryStore);
     486        1393 :   PLDHashMoveEntry moveEntry = mOps->moveEntry;
     487             : 
     488             :   // Copy only live entries, leaving removed ones behind.
     489        1393 :   uint32_t oldCapacity = 1u << oldLog2;
     490       98937 :   for (uint32_t i = 0; i < oldCapacity; ++i) {
     491       97544 :     PLDHashEntryHdr* oldEntry = (PLDHashEntryHdr*)oldEntryAddr;
     492       97544 :     if (EntryIsLive(oldEntry)) {
     493       72296 :       oldEntry->mKeyHash &= ~kCollisionFlag;
     494       72296 :       PLDHashEntryHdr* newEntry = FindFreeEntry(oldEntry->mKeyHash);
     495       72296 :       NS_ASSERTION(EntryIsFree(newEntry), "EntryIsFree(newEntry)");
     496       72296 :       moveEntry(this, oldEntry, newEntry);
     497       72296 :       newEntry->mKeyHash = oldEntry->mKeyHash;
     498             :     }
     499       97544 :     oldEntryAddr += mEntrySize;
     500             :   }
     501             : 
     502        1393 :   free(oldEntryStore);
     503        1393 :   return true;
     504             : }
     505             : 
     506             : MOZ_ALWAYS_INLINE PLDHashNumber
     507      423045 : PLDHashTable::ComputeKeyHash(const void* aKey)
     508             : {
     509      423045 :   MOZ_ASSERT(mEntryStore.Get());
     510             : 
     511      423042 :   PLDHashNumber keyHash = mOps->hashKey(aKey);
     512      423042 :   keyHash *= kGoldenRatio;
     513             : 
     514             :   // Avoid 0 and 1 hash codes, they indicate free and removed entries.
     515      423042 :   if (keyHash < 2) {
     516        9469 :     keyHash -= 2;
     517             :   }
     518      423042 :   keyHash &= ~kCollisionFlag;
     519             : 
     520      423042 :   return keyHash;
     521             : }
     522             : 
     523             : PLDHashEntryHdr*
     524      239441 : PLDHashTable::Search(const void* aKey)
     525             : {
     526             : #ifdef DEBUG
     527      478877 :   AutoReadOp op(mChecker);
     528             : #endif
     529             : 
     530      239453 :   PLDHashEntryHdr* entry = mEntryStore.Get()
     531      239453 :                          ? SearchTable<ForSearchOrRemove>(aKey,
     532             :                                                           ComputeKeyHash(aKey))
     533      239436 :                          : nullptr;
     534      478890 :   return entry;
     535             : }
     536             : 
     537             : PLDHashEntryHdr*
     538      200036 : PLDHashTable::Add(const void* aKey, const mozilla::fallible_t&)
     539             : {
     540             : #ifdef DEBUG
     541      400071 :   AutoWriteOp op(mChecker);
     542             : #endif
     543             : 
     544             :   // Allocate the entry storage if it hasn't already been allocated.
     545      200036 :   if (!mEntryStore.Get()) {
     546             :     uint32_t nbytes;
     547             :     // We already checked this in the constructor, so it must still be true.
     548        3442 :     MOZ_RELEASE_ASSERT(SizeOfEntryStore(CapacityFromHashShift(), mEntrySize,
     549             :                                         &nbytes));
     550        3442 :     mEntryStore.Set((char*)malloc(nbytes));
     551        3442 :     if (!mEntryStore.Get()) {
     552           0 :       return nullptr;
     553             :     }
     554        3442 :     memset(mEntryStore.Get(), 0, nbytes);
     555             :   }
     556             : 
     557             :   // If alpha is >= .75, grow or compress the table. If aKey is already in the
     558             :   // table, we may grow once more than necessary, but only if we are on the
     559             :   // edge of being overloaded.
     560      200036 :   uint32_t capacity = Capacity();
     561      200036 :   if (mEntryCount + mRemovedCount >= MaxLoad(capacity)) {
     562             :     // Compress if a quarter or more of all entries are removed.
     563             :     int deltaLog2;
     564        1346 :     if (mRemovedCount >= capacity >> 2) {
     565           0 :       deltaLog2 = 0;
     566             :     } else {
     567        1346 :       deltaLog2 = 1;
     568             :     }
     569             : 
     570             :     // Grow or compress the table. If ChangeTable() fails, allow overloading up
     571             :     // to the secondary max. Once we hit the secondary max, return null.
     572        1346 :     if (!ChangeTable(deltaLog2) &&
     573           0 :         mEntryCount + mRemovedCount >= MaxLoadOnGrowthFailure(capacity)) {
     574           0 :       return nullptr;
     575             :     }
     576             :   }
     577             : 
     578             :   // Look for entry after possibly growing, so we don't have to add it,
     579             :   // then skip it while growing the table and re-add it after.
     580      200036 :   PLDHashNumber keyHash = ComputeKeyHash(aKey);
     581      200036 :   PLDHashEntryHdr* entry = SearchTable<ForAdd>(aKey, keyHash);
     582      200036 :   if (!EntryIsLive(entry)) {
     583             :     // Initialize the entry, indicating that it's no longer free.
     584      113636 :     if (EntryIsRemoved(entry)) {
     585        1677 :       mRemovedCount--;
     586        1677 :       keyHash |= kCollisionFlag;
     587             :     }
     588      113636 :     if (mOps->initEntry) {
     589       86578 :       mOps->initEntry(entry, aKey);
     590             :     }
     591      113635 :     entry->mKeyHash = keyHash;
     592      113635 :     mEntryCount++;
     593             :   }
     594             : 
     595      200035 :   return entry;
     596             : }
     597             : 
     598             : PLDHashEntryHdr*
     599       82329 : PLDHashTable::Add(const void* aKey)
     600             : {
     601       82329 :   PLDHashEntryHdr* entry = Add(aKey, fallible);
     602       82329 :   if (!entry) {
     603           0 :     if (!mEntryStore.Get()) {
     604             :       // We OOM'd while allocating the initial entry storage.
     605             :       uint32_t nbytes;
     606           0 :       (void) SizeOfEntryStore(CapacityFromHashShift(), mEntrySize, &nbytes);
     607           0 :       NS_ABORT_OOM(nbytes);
     608             :     } else {
     609             :       // We failed to resize the existing entry storage, either due to OOM or
     610             :       // because we exceeded the maximum table capacity or size; report it as
     611             :       // an OOM. The multiplication by 2 gets us the size we tried to allocate,
     612             :       // which is double the current size.
     613           0 :       NS_ABORT_OOM(2 * EntrySize() * EntryCount());
     614             :     }
     615             :   }
     616       82329 :   return entry;
     617             : }
     618             : 
     619             : void
     620        9311 : PLDHashTable::Remove(const void* aKey)
     621             : {
     622             : #ifdef DEBUG
     623       18622 :   AutoWriteOp op(mChecker);
     624             : #endif
     625             : 
     626        9311 :   PLDHashEntryHdr* entry = mEntryStore.Get()
     627        9311 :                          ? SearchTable<ForSearchOrRemove>(aKey,
     628             :                                                           ComputeKeyHash(aKey))
     629        9311 :                          : nullptr;
     630        9311 :   if (entry) {
     631        9118 :     RawRemove(entry);
     632        9118 :     ShrinkIfAppropriate();
     633             :   }
     634        9311 : }
     635             : 
     636             : void
     637        1912 : PLDHashTable::RemoveEntry(PLDHashEntryHdr* aEntry)
     638             : {
     639             : #ifdef DEBUG
     640        3824 :   AutoWriteOp op(mChecker);
     641             : #endif
     642             : 
     643        1912 :   RawRemove(aEntry);
     644        1912 :   ShrinkIfAppropriate();
     645        1912 : }
     646             : 
     647             : void
     648       11181 : PLDHashTable::RawRemove(PLDHashEntryHdr* aEntry)
     649             : {
     650             :   // Unfortunately, we can only do weak checking here. That's because
     651             :   // RawRemove() can be called legitimately while an Enumerate() call is
     652             :   // active, which doesn't fit well into how Checker's mState variable works.
     653       11181 :   MOZ_ASSERT(mChecker.IsWritable());
     654             : 
     655       11181 :   MOZ_ASSERT(mEntryStore.Get());
     656             : 
     657       11181 :   MOZ_ASSERT(EntryIsLive(aEntry), "EntryIsLive(aEntry)");
     658             : 
     659             :   // Load keyHash first in case clearEntry() goofs it.
     660       11181 :   PLDHashNumber keyHash = aEntry->mKeyHash;
     661       11181 :   mOps->clearEntry(this, aEntry);
     662       11181 :   if (keyHash & kCollisionFlag) {
     663        2709 :     MarkEntryRemoved(aEntry);
     664        2709 :     mRemovedCount++;
     665             :   } else {
     666        8472 :     MarkEntryFree(aEntry);
     667             :   }
     668       11181 :   mEntryCount--;
     669       11181 : }
     670             : 
     671             : // Shrink or compress if a quarter or more of all entries are removed, or if the
     672             : // table is underloaded according to the minimum alpha, and is not minimal-size
     673             : // already.
     674             : void
     675       11096 : PLDHashTable::ShrinkIfAppropriate()
     676             : {
     677       11096 :   uint32_t capacity = Capacity();
     678       22226 :   if (mRemovedCount >= capacity >> 2 ||
     679       21044 :       (capacity > kMinCapacity && mEntryCount <= MinLoad(capacity))) {
     680             :     uint32_t log2;
     681          47 :     BestCapacity(mEntryCount, &capacity, &log2);
     682             : 
     683          47 :     int32_t deltaLog2 = log2 - (kHashBits - mHashShift);
     684          47 :     MOZ_ASSERT(deltaLog2 <= 0);
     685             : 
     686          47 :     (void) ChangeTable(deltaLog2);
     687             :   }
     688       11096 : }
     689             : 
     690             : size_t
     691         174 : PLDHashTable::ShallowSizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
     692             : {
     693             : #ifdef DEBUG
     694         348 :   AutoReadOp op(mChecker);
     695             : #endif
     696             : 
     697         348 :   return aMallocSizeOf(mEntryStore.Get());
     698             : }
     699             : 
     700             : size_t
     701           0 : PLDHashTable::ShallowSizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
     702             : {
     703           0 :   return aMallocSizeOf(this) + ShallowSizeOfExcludingThis(aMallocSizeOf);
     704             : }
     705             : 
     706           0 : PLDHashTable::Iterator::Iterator(Iterator&& aOther)
     707           0 :   : mTable(aOther.mTable)
     708           0 :   , mStart(aOther.mStart)
     709           0 :   , mLimit(aOther.mLimit)
     710           0 :   , mCurrent(aOther.mCurrent)
     711           0 :   , mNexts(aOther.mNexts)
     712           0 :   , mNextsLimit(aOther.mNextsLimit)
     713           0 :   , mHaveRemoved(aOther.mHaveRemoved)
     714             : {
     715             :   // No need to change |mChecker| here.
     716           0 :   aOther.mTable = nullptr;
     717           0 :   aOther.mStart = nullptr;
     718           0 :   aOther.mLimit = nullptr;
     719           0 :   aOther.mCurrent = nullptr;
     720           0 :   aOther.mNexts = 0;
     721           0 :   aOther.mNextsLimit = 0;
     722           0 :   aOther.mHaveRemoved = false;
     723           0 : }
     724             : 
     725        4639 : PLDHashTable::Iterator::Iterator(PLDHashTable* aTable)
     726             :   : mTable(aTable)
     727        4639 :   , mStart(mTable->mEntryStore.Get())
     728        4639 :   , mLimit(mTable->mEntryStore.Get() + mTable->Capacity() * mTable->mEntrySize)
     729        4639 :   , mCurrent(mTable->mEntryStore.Get())
     730             :   , mNexts(0)
     731        4639 :   , mNextsLimit(mTable->EntryCount())
     732       23195 :   , mHaveRemoved(false)
     733             : {
     734             : #ifdef DEBUG
     735        4639 :   mTable->mChecker.StartReadOp();
     736             : #endif
     737             : 
     738        4639 :   if (ChaosMode::isActive(ChaosFeature::HashTableIteration) &&
     739           0 :       mTable->Capacity() > 0) {
     740             :     // Start iterating at a random entry. It would be even more chaotic to
     741             :     // iterate in fully random order, but that's harder.
     742           0 :     mCurrent += ChaosMode::randomUint32LessThan(mTable->Capacity()) *
     743           0 :                 mTable->mEntrySize;
     744             :   }
     745             : 
     746             :   // Advance to the first live entry, if there is one.
     747        4639 :   if (!Done()) {
     748       17122 :     while (IsOnNonLiveEntry()) {
     749        7143 :       MoveToNextEntry();
     750             :     }
     751             :   }
     752        4639 : }
     753             : 
     754        9278 : PLDHashTable::Iterator::~Iterator()
     755             : {
     756        4639 :   if (mTable) {
     757        4639 :     if (mHaveRemoved) {
     758          66 :       mTable->ShrinkIfAppropriate();
     759             :     }
     760             : #ifdef DEBUG
     761        4639 :     mTable->mChecker.EndReadOp();
     762             : #endif
     763             :   }
     764        4639 : }
     765             : 
     766             : MOZ_ALWAYS_INLINE bool
     767      197595 : PLDHashTable::Iterator::IsOnNonLiveEntry() const
     768             : {
     769      197595 :   MOZ_ASSERT(!Done());
     770      197595 :   return !EntryIsLive(reinterpret_cast<PLDHashEntryHdr*>(mCurrent));
     771             : }
     772             : 
     773             : MOZ_ALWAYS_INLINE void
     774      194759 : PLDHashTable::Iterator::MoveToNextEntry()
     775             : {
     776      194759 :   mCurrent += mTable->mEntrySize;
     777      194759 :   if (mCurrent == mLimit) {
     778           0 :     mCurrent = mStart;  // Wrap-around. Possible due to Chaos Mode.
     779             :   }
     780      194759 : }
     781             : 
     782             : void
     783      123576 : PLDHashTable::Iterator::Next()
     784             : {
     785      123576 :   MOZ_ASSERT(!Done());
     786             : 
     787      123576 :   mNexts++;
     788             : 
     789             :   // Advance to the next live entry, if there is one.
     790      123576 :   if (!Done()) {
     791      187616 :     do {
     792      187616 :       MoveToNextEntry();
     793             :     } while (IsOnNonLiveEntry());
     794             :   }
     795      123576 : }
     796             : 
     797             : void
     798         148 : PLDHashTable::Iterator::Remove()
     799             : {
     800             :   // This cast is needed for the same reason as the one in the destructor.
     801         148 :   mTable->RawRemove(Get());
     802         148 :   mHaveRemoved = true;
     803         148 : }
     804             : 
     805             : #ifdef DEBUG
     806             : void
     807          32 : PLDHashTable::MarkImmutable()
     808             : {
     809          32 :   mChecker.SetNonWritable();
     810          32 : }
     811             : #endif

Generated by: LCOV version 1.13