Bug 1187197 (part 3) - Convert pldhash.{cpp,h} to C++ style comments. r=froydnj.

--HG--
extra : rebase_source : 4615862ad586e24050a6f5571b8d21689ba3fdf3
This commit is contained in:
Nicholas Nethercote 2015-07-23 23:13:11 -07:00
Родитель 42e338fc52
Коммит 10da8efa1c
2 изменённых файлов: 132 добавлений и 176 удалений

Просмотреть файл

@ -4,9 +4,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Double hashing implementation.
*/
#include <new>
#include <stdio.h>
#include <stdlib.h>
@ -14,7 +11,6 @@
#include "pldhash.h"
#include "mozilla/HashFunctions.h"
#include "mozilla/MathAlgorithms.h"
#include "nsDebug.h" /* for PR_ASSERT */
#include "nsAlgorithm.h"
#include "mozilla/Likely.h"
#include "mozilla/MemoryReporting.h"
@ -95,7 +91,7 @@ PL_DHashMatchStringKey(PLDHashTable* aTable,
{
const PLDHashEntryStub* stub = (const PLDHashEntryStub*)aEntry;
/* XXX tolerate null keys on account of sloppy Mozilla callers. */
// XXX tolerate null keys on account of sloppy Mozilla callers.
return stub->key == aKey ||
(stub->key && aKey &&
strcmp((const char*)stub->key, (const char*)aKey) == 0);
@ -150,13 +146,11 @@ SizeOfEntryStore(uint32_t aCapacity, uint32_t aEntrySize, uint32_t* aNbytes)
return uint64_t(*aNbytes) == nbytes64; // returns false on overflow
}
/*
* Compute max and min load numbers (entry counts). We have a secondary max
* that allows us to overload a table reasonably if it cannot be grown further
* (i.e. if ChangeTable() fails). The table slows down drastically if the
* secondary max is too close to 1, but 0.96875 gives only a slight slowdown
* while allowing 1.3x more elements.
*/
// Compute max and min load numbers (entry counts). We have a secondary max
// that allows us to overload a table reasonably if it cannot be grown further
// (i.e. if ChangeTable() fails). The table slows down drastically if the
// secondary max is too close to 1, but 0.96875 gives only a slight slowdown
// while allowing 1.3x more elements.
static inline uint32_t
MaxLoad(uint32_t aCapacity)
{
@ -288,14 +282,12 @@ PLDHashTable::Hash2(PLDHashNumber aHash,
aSizeMaskOut = (PLDHashNumber(1) << sizeLog2) - 1;
}
/*
* Reserve mKeyHash 0 for free entries and 1 for removed-entry sentinels. Note
* that a removed-entry sentinel need be stored only if the removed entry had
* a colliding entry added after it. Therefore we can use 1 as the collision
* flag in addition to the removed-entry sentinel value. Multiplicative hash
* uses the high order bits of mKeyHash, so this least-significant reservation
* should not hurt the hash function's effectiveness much.
*/
// Reserve mKeyHash 0 for free entries and 1 for removed-entry sentinels. Note
// that a removed-entry sentinel need be stored only if the removed entry had
// a colliding entry added after it. Therefore we can use 1 as the collision
// flag in addition to the removed-entry sentinel value. Multiplicative hash
// uses the high order bits of mKeyHash, so this least-significant reservation
// should not hurt the hash function's effectiveness much.
/* static */ MOZ_ALWAYS_INLINE bool
PLDHashTable::EntryIsFree(PLDHashEntryHdr* aEntry)
@ -324,14 +316,14 @@ PLDHashTable::MarkEntryRemoved(PLDHashEntryHdr* aEntry)
aEntry->mKeyHash = 1;
}
/* Match an entry's mKeyHash against an unstored one computed from a key. */
// Match an entry's mKeyHash against an unstored one computed from a key.
/* static */ bool
PLDHashTable::MatchEntryKeyhash(PLDHashEntryHdr* aEntry, PLDHashNumber aKeyHash)
{
return (aEntry->mKeyHash & ~kCollisionFlag) == aKeyHash;
}
/* Compute the address of the indexed entry in table. */
// Compute the address of the indexed entry in table.
PLDHashEntryHdr*
PLDHashTable::AddressEntry(uint32_t aIndex)
{
@ -348,7 +340,7 @@ PLDHashTable::~PLDHashTable()
return;
}
/* Clear any remaining live entries. */
// Clear any remaining live entries.
char* entryAddr = mEntryStore;
char* entryLimit = entryAddr + Capacity() * mEntrySize;
while (entryAddr < entryLimit) {
@ -359,7 +351,7 @@ PLDHashTable::~PLDHashTable()
entryAddr += mEntrySize;
}
/* Free entry storage last. */
// Free entry storage last.
free(mEntryStore);
mEntryStore = nullptr;
}
@ -394,31 +386,29 @@ PLDHashTable::SearchTable(const void* aKey, PLDHashNumber aKeyHash)
NS_ASSERTION(!(aKeyHash & kCollisionFlag),
"!(aKeyHash & kCollisionFlag)");
/* Compute the primary hash address. */
// Compute the primary hash address.
PLDHashNumber hash1 = Hash1(aKeyHash);
PLDHashEntryHdr* entry = AddressEntry(hash1);
/* Miss: return space for a new entry. */
// Miss: return space for a new entry.
if (EntryIsFree(entry)) {
return (Reason == ForAdd) ? entry : nullptr;
}
/* Hit: return entry. */
// Hit: return entry.
PLDHashMatchEntry matchEntry = mOps->matchEntry;
if (MatchEntryKeyhash(entry, aKeyHash) &&
matchEntry(this, entry, aKey)) {
return entry;
}
/* Collision: double hash. */
// Collision: double hash.
PLDHashNumber hash2;
uint32_t sizeMask;
Hash2(aKeyHash, hash2, sizeMask);
/*
* Save the first removed entry pointer so Add() can recycle it. (Only used
* if Reason==ForAdd.)
*/
// Save the first removed entry pointer so Add() can recycle it. (Only used
// if Reason==ForAdd.)
PLDHashEntryHdr* firstRemoved = nullptr;
for (;;) {
@ -447,20 +437,17 @@ PLDHashTable::SearchTable(const void* aKey, PLDHashNumber aKeyHash)
}
}
/* NOTREACHED */
// NOTREACHED
return nullptr;
}
/*
* This is a copy of SearchTable, used by ChangeTable, hardcoded to
* 1. assume |aIsAdd| is true,
* 2. assume that |aKey| will never match an existing entry, and
* 3. assume that no entries have been removed from the current table
* structure.
* Avoiding the need for |aKey| means we can avoid needing a way to map
* entries to keys, which means callers can use complex key types more
* easily.
*/
// This is a copy of SearchTable(), used by ChangeTable(), hardcoded to
// 1. assume |aIsAdd| is true,
// 2. assume that |aKey| will never match an existing entry, and
// 3. assume that no entries have been removed from the current table
// structure.
// Avoiding the need for |aKey| means we can avoid needing a way to map entries
// to keys, which means callers can use complex key types more easily.
PLDHashEntryHdr* PL_DHASH_FASTCALL
PLDHashTable::FindFreeEntry(PLDHashNumber aKeyHash)
{
@ -468,16 +455,16 @@ PLDHashTable::FindFreeEntry(PLDHashNumber aKeyHash)
NS_ASSERTION(!(aKeyHash & kCollisionFlag),
"!(aKeyHash & kCollisionFlag)");
/* Compute the primary hash address. */
// Compute the primary hash address.
PLDHashNumber hash1 = Hash1(aKeyHash);
PLDHashEntryHdr* entry = AddressEntry(hash1);
/* Miss: return space for a new entry. */
// Miss: return space for a new entry.
if (EntryIsFree(entry)) {
return entry;
}
/* Collision: double hash. */
// Collision: double hash.
PLDHashNumber hash2;
uint32_t sizeMask;
Hash2(aKeyHash, hash2, sizeMask);
@ -496,7 +483,7 @@ PLDHashTable::FindFreeEntry(PLDHashNumber aKeyHash)
}
}
/* NOTREACHED */
// NOTREACHED
return nullptr;
}
@ -505,7 +492,7 @@ PLDHashTable::ChangeTable(int32_t aDeltaLog2)
{
MOZ_ASSERT(mEntryStore);
/* Look, but don't touch, until we succeed in getting new entry store. */
// Look, but don't touch, until we succeed in getting new entry store.
int32_t oldLog2 = kHashBits - mHashShift;
int32_t newLog2 = oldLog2 + aDeltaLog2;
uint32_t newCapacity = 1u << newLog2;
@ -523,12 +510,12 @@ PLDHashTable::ChangeTable(int32_t aDeltaLog2)
return false;
}
/* We can't fail from here on, so update table parameters. */
// We can't fail from here on, so update table parameters.
mHashShift = kHashBits - newLog2;
mRemovedCount = 0;
mGeneration++;
/* Assign the new entry store to table. */
// Assign the new entry store to table.
memset(newEntryStore, 0, nbytes);
char* oldEntryStore;
char* oldEntryAddr;
@ -536,7 +523,7 @@ PLDHashTable::ChangeTable(int32_t aDeltaLog2)
mEntryStore = newEntryStore;
PLDHashMoveEntry moveEntry = mOps->moveEntry;
/* Copy only live entries, leaving removed ones behind. */
// Copy only live entries, leaving removed ones behind.
uint32_t oldCapacity = 1u << oldLog2;
for (uint32_t i = 0; i < oldCapacity; ++i) {
PLDHashEntryHdr* oldEntry = (PLDHashEntryHdr*)oldEntryAddr;
@ -562,7 +549,7 @@ PLDHashTable::ComputeKeyHash(const void* aKey)
PLDHashNumber keyHash = mOps->hashKey(this, aKey);
keyHash *= kGoldenRatio;
/* Avoid 0 and 1 hash codes, they indicate free and removed entries. */
// Avoid 0 and 1 hash codes, they indicate free and removed entries.
if (keyHash < 2) {
keyHash -= 2;
}
@ -605,14 +592,12 @@ PLDHashTable::Add(const void* aKey, const mozilla::fallible_t&)
memset(mEntryStore, 0, nbytes);
}
/*
* If alpha is >= .75, grow or compress the table. If aKey is already
* in the table, we may grow once more than necessary, but only if we
* are on the edge of being overloaded.
*/
// If alpha is >= .75, grow or compress the table. If aKey is already in the
// table, we may grow once more than necessary, but only if we are on the
// edge of being overloaded.
uint32_t capacity = Capacity();
if (mEntryCount + mRemovedCount >= MaxLoad(capacity)) {
/* Compress if a quarter or more of all entries are removed. */
// Compress if a quarter or more of all entries are removed.
int deltaLog2;
if (mRemovedCount >= capacity >> 2) {
deltaLog2 = 0;
@ -620,25 +605,20 @@ PLDHashTable::Add(const void* aKey, const mozilla::fallible_t&)
deltaLog2 = 1;
}
/*
* Grow or compress the table. If ChangeTable() fails, allow
* overloading up to the secondary max. Once we hit the secondary
* max, return null.
*/
// Grow or compress the table. If ChangeTable() fails, allow overloading up
// to the secondary max. Once we hit the secondary max, return null.
if (!ChangeTable(deltaLog2) &&
mEntryCount + mRemovedCount >= MaxLoadOnGrowthFailure(capacity)) {
return nullptr;
}
}
/*
* Look for entry after possibly growing, so we don't have to add it,
* then skip it while growing the table and re-add it after.
*/
// Look for entry after possibly growing, so we don't have to add it,
// then skip it while growing the table and re-add it after.
PLDHashNumber keyHash = ComputeKeyHash(aKey);
PLDHashEntryHdr* entry = SearchTable<ForAdd>(aKey, keyHash);
if (!EntryIsLive(entry)) {
/* Initialize the entry, indicating that it's no longer free. */
// Initialize the entry, indicating that it's no longer free.
if (EntryIsRemoved(entry)) {
mRemovedCount--;
keyHash |= kCollisionFlag;
@ -685,10 +665,10 @@ PLDHashTable::Remove(const void* aKey)
mEntryStore ? SearchTable<ForSearchOrRemove>(aKey, ComputeKeyHash(aKey))
: nullptr;
if (entry) {
/* Clear this entry and mark it as "removed". */
// Clear this entry and mark it as "removed".
RawRemove(entry);
/* Shrink if alpha is <= .25 and the table isn't too small already. */
// Shrink if alpha is <= .25 and the table isn't too small already.
uint32_t capacity = Capacity();
if (capacity > kMinCapacity &&
mEntryCount <= MinLoad(capacity)) {
@ -734,7 +714,7 @@ PLDHashTable::RawRemove(PLDHashEntryHdr* aEntry)
NS_ASSERTION(EntryIsLive(aEntry), "EntryIsLive(aEntry)");
/* Load keyHash first in case clearEntry() goofs it. */
// Load keyHash first in case clearEntry() goofs it.
PLDHashNumber keyHash = aEntry->mKeyHash;
mOps->clearEntry(this, aEntry);
if (keyHash & kCollisionFlag) {

Просмотреть файл

@ -6,9 +6,7 @@
#ifndef pldhash_h___
#define pldhash_h___
/*
* Double hashing, a la Knuth 6.
*/
#include "mozilla/Atomics.h"
#include "mozilla/Attributes.h" // for MOZ_ALWAYS_INLINE
#include "mozilla/fallible.h"
@ -30,24 +28,22 @@ typedef uint32_t PLDHashNumber;
class PLDHashTable;
struct PLDHashTableOps;
/*
* Table entry header structure.
*
* In order to allow in-line allocation of key and value, we do not declare
* either here. Instead, the API uses const void *key as a formal parameter.
* The key need not be stored in the entry; it may be part of the value, but
* need not be stored at all.
*
* Callback types are defined below and grouped into the PLDHashTableOps
* structure, for single static initialization per hash table sub-type.
*
* Each hash table sub-type should make its entry type a subclass of
* PLDHashEntryHdr. The mKeyHash member contains the result of multiplying the
* hash code returned from the hashKey callback (see below) by kGoldenRatio,
* then constraining the result to avoid the magic 0 and 1 values. The stored
* mKeyHash value is table size invariant, and it is maintained automatically
* -- users need never access it.
*/
// Table entry header structure.
//
// In order to allow in-line allocation of key and value, we do not declare
// either here. Instead, the API uses const void *key as a formal parameter.
// The key need not be stored in the entry; it may be part of the value, but
// need not be stored at all.
//
// Callback types are defined below and grouped into the PLDHashTableOps
// structure, for single static initialization per hash table sub-type.
//
// Each hash table sub-type should make its entry type a subclass of
// PLDHashEntryHdr. The mKeyHash member contains the result of multiplying the
// hash code returned from the hashKey callback (see below) by kGoldenRatio,
// then constraining the result to avoid the magic 0 and 1 values. The stored
// mKeyHash value is table size invariant, and it is maintained automatically
// -- users need never access it.
struct PLDHashEntryHdr
{
private:
@ -206,31 +202,29 @@ private:
};
#endif
/*
* A PLDHashTable may be allocated on the stack or within another structure or
* class. No entry storage is allocated until the first element is added. This
* means that empty hash tables are cheap, which is good because they are
* common.
*
* There used to be a long, math-heavy comment here about the merits of
* double hashing vs. chaining; it was removed in bug 1058335. In short, double
* hashing is more space-efficient unless the element size gets large (in which
* case you should keep using double hashing but switch to using pointer
* elements). Also, with double hashing, you can't safely hold an entry pointer
* and use it after an ADD or REMOVE operation, unless you sample
* aTable->mGeneration before adding or removing, and compare the sample after,
* dereferencing the entry pointer only if aTable->mGeneration has not changed.
*/
// A PLDHashTable may be allocated on the stack or within another structure or
// class. No entry storage is allocated until the first element is added. This
// means that empty hash tables are cheap, which is good because they are
// common.
//
// There used to be a long, math-heavy comment here about the merits of
// double hashing vs. chaining; it was removed in bug 1058335. In short, double
// hashing is more space-efficient unless the element size gets large (in which
// case you should keep using double hashing but switch to using pointer
// elements). Also, with double hashing, you can't safely hold an entry pointer
// and use it after an ADD or REMOVE operation, unless you sample
// aTable->mGeneration before adding or removing, and compare the sample after,
// dereferencing the entry pointer only if aTable->mGeneration has not changed.
class PLDHashTable
{
private:
const PLDHashTableOps* const mOps; /* Virtual operations; see below. */
int16_t mHashShift; /* multiplicative hash shift */
const uint32_t mEntrySize; /* number of bytes in an entry */
uint32_t mEntryCount; /* number of entries in table */
uint32_t mRemovedCount; /* removed entry sentinels in table */
uint32_t mGeneration; /* entry storage generation number */
char* mEntryStore; /* entry storage; allocated lazily */
const PLDHashTableOps* const mOps; // Virtual operations; see below.
int16_t mHashShift; // Multiplicative hash shift.
const uint32_t mEntrySize; // Number of bytes in an entry.
uint32_t mEntryCount; // Number of entries in table.
uint32_t mRemovedCount; // Removed entry sentinels in table.
uint32_t mGeneration; // Entry storage generation number.
char* mEntryStore; // Entry storage; allocated lazily.
#ifdef DEBUG
mutable Checker mChecker;
@ -287,11 +281,9 @@ public:
// This should be used rarely.
const PLDHashTableOps* const Ops() { return mOps; }
/*
* Size in entries (gross, not net of free and removed sentinels) for table.
* This can be zero if no elements have been added yet, in which case the
* entry storage will not have yet been allocated.
*/
// Size in entries (gross, not net of free and removed sentinels) for table.
// This can be zero if no elements have been added yet, in which case the
// entry storage will not have yet been allocated.
uint32_t Capacity() const
{
return mEntryStore ? CapacityFromHashShift() : 0;
@ -495,88 +487,74 @@ private:
PLDHashTable& operator=(const PLDHashTable& aOther) = delete;
};
/*
* Compute the hash code for a given key to be looked up, added, or removed
* from aTable. A hash code may have any PLDHashNumber value.
*/
// Compute the hash code for a given key to be looked up, added, or removed
// from aTable. A hash code may have any PLDHashNumber value.
typedef PLDHashNumber (*PLDHashHashKey)(PLDHashTable* aTable,
const void* aKey);
/*
* Compare the key identifying aEntry in aTable with the provided key parameter.
* Return true if keys match, false otherwise.
*/
// Compare the key identifying aEntry in aTable with the provided key parameter.
// Return true if keys match, false otherwise.
typedef bool (*PLDHashMatchEntry)(PLDHashTable* aTable,
const PLDHashEntryHdr* aEntry,
const void* aKey);
/*
* Copy the data starting at aFrom to the new entry storage at aTo. Do not add
* reference counts for any strong references in the entry, however, as this
* is a "move" operation: the old entry storage at from will be freed without
* any reference-decrementing callback shortly.
*/
// Copy the data starting at aFrom to the new entry storage at aTo. Do not add
// reference counts for any strong references in the entry, however, as this
// is a "move" operation: the old entry storage at from will be freed without
// any reference-decrementing callback shortly.
typedef void (*PLDHashMoveEntry)(PLDHashTable* aTable,
const PLDHashEntryHdr* aFrom,
PLDHashEntryHdr* aTo);
/*
* Clear the entry and drop any strong references it holds. This callback is
* invoked by Remove(), but only if the given key is found in the table.
*/
// Clear the entry and drop any strong references it holds. This callback is
// invoked by Remove(), but only if the given key is found in the table.
typedef void (*PLDHashClearEntry)(PLDHashTable* aTable,
PLDHashEntryHdr* aEntry);
/*
* Initialize a new entry, apart from mKeyHash. This function is called when
* Add() finds no existing entry for the given key, and must add a new one. At
* that point, |aEntry->mKeyHash| is not set yet, to avoid claiming the last
* free entry in a severely overloaded table.
*/
// Initialize a new entry, apart from mKeyHash. This function is called when
// Add() finds no existing entry for the given key, and must add a new one. At
// that point, |aEntry->mKeyHash| is not set yet, to avoid claiming the last
// free entry in a severely overloaded table.
typedef void (*PLDHashInitEntry)(PLDHashEntryHdr* aEntry, const void* aKey);
/*
* Finally, the "vtable" structure for PLDHashTable. The first four hooks
* must be provided by implementations; they're called unconditionally by the
* generic pldhash.c code. Hooks after these may be null.
*
* Summary of allocation-related hook usage with C++ placement new emphasis:
* initEntry Call placement new using default key-based ctor.
* moveEntry Call placement new using copy ctor, run dtor on old
* entry storage.
* clearEntry Run dtor on entry.
*
* Note the reason why initEntry is optional: the default hooks (stubs) clear
* entry storage: On successful Add(tbl, key), the returned entry pointer
* addresses an entry struct whose mKeyHash member has been set non-zero, but
* all other entry members are still clear (null). Add() callers can test such
* members to see whether the entry was newly created by the Add() call that
* just succeeded. If placement new or similar initialization is required,
* define an |initEntry| hook. Of course, the |clearEntry| hook must zero or
* null appropriately.
*
* XXX assumes 0 is null for pointer types.
*/
// Finally, the "vtable" structure for PLDHashTable. The first four hooks
// must be provided by implementations; they're called unconditionally by the
// generic pldhash.c code. Hooks after these may be null.
//
// Summary of allocation-related hook usage with C++ placement new emphasis:
// initEntry Call placement new using default key-based ctor.
// moveEntry Call placement new using copy ctor, run dtor on old
// entry storage.
// clearEntry Run dtor on entry.
//
// Note the reason why initEntry is optional: the default hooks (stubs) clear
// entry storage: On successful Add(tbl, key), the returned entry pointer
// addresses an entry struct whose mKeyHash member has been set non-zero, but
// all other entry members are still clear (null). Add() callers can test such
// members to see whether the entry was newly created by the Add() call that
// just succeeded. If placement new or similar initialization is required,
// define an |initEntry| hook. Of course, the |clearEntry| hook must zero or
// null appropriately.
//
// XXX assumes 0 is null for pointer types.
struct PLDHashTableOps
{
/* Mandatory hooks. All implementations must provide these. */
// Mandatory hooks. All implementations must provide these.
PLDHashHashKey hashKey;
PLDHashMatchEntry matchEntry;
PLDHashMoveEntry moveEntry;
PLDHashClearEntry clearEntry;
/* Optional hooks start here. If null, these are not called. */
// Optional hooks start here. If null, these are not called.
PLDHashInitEntry initEntry;
};
/*
* Default implementations for the above mOps.
*/
// Default implementations for the above mOps.
PLDHashNumber
PL_DHashStringKey(PLDHashTable* aTable, const void* aKey);
/* A minimal entry is a subclass of PLDHashEntryHdr and has void key pointer. */
// A minimal entry is a subclass of PLDHashEntryHdr and has void key pointer.
struct PLDHashEntryStub : public PLDHashEntryHdr
{
const void* key;
@ -603,11 +581,9 @@ PL_DHashMoveEntryStub(PLDHashTable* aTable,
void
PL_DHashClearEntryStub(PLDHashTable* aTable, PLDHashEntryHdr* aEntry);
/*
* If you use PLDHashEntryStub or a subclass of it as your entry struct, and
* if your entries move via memcpy and clear via memset(0), you can use these
* stub operations.
*/
// If you use PLDHashEntryStub or a subclass of it as your entry struct, and
// if your entries move via memcpy and clear via memset(0), you can use these
// stub operations.
const PLDHashTableOps*
PL_DHashGetStubOps(void);