Bug 1059550 - Add an iterator to PLDHashtable, r=froydnj

This commit is contained in:
Joshua Cranmer 2014-08-28 11:29:23 -05:00
parent 4cf96ddcba
commit 095e8ec91b
2 changed files with 115 additions and 1 deletions

View File

@ -697,6 +697,8 @@ PLDHashTable::Enumerate(PLDHashEnumerator aEtor, void* aArg)
{
INCREMENT_RECURSION_LEVEL(this);
// Please keep this method in sync with the PLDHashTable::Iterator constructor
// and ::NextEntry methods below in this file.
char* entryAddr = mEntryStore;
uint32_t capacity = Capacity();
uint32_t tableSize = capacity * mEntrySize;
@ -835,6 +837,97 @@ PL_DHashTableSizeOfIncludingThis(
aMallocSizeOf, aArg);
}
PLDHashTable::Iterator::Iterator(const PLDHashTable* aTable)
: mTable(aTable),
mEntryAddr(mTable->mEntryStore),
mEntryOffset(0)
{
// Make sure that modifications can't simultaneously happen while the iterator
// is active.
INCREMENT_RECURSION_LEVEL(mTable);
// The following code is taken from, and should be kept in sync with, the
// PLDHashTable::Enumerate method above. The variables i and entryAddr (which
// vary over the course of the for loop) are converted into mEntryOffset and
// mEntryAddr, respectively.
uint32_t capacity = mTable->Capacity();
uint32_t tableSize = capacity * mTable->EntrySize();
char* entryLimit = mEntryAddr + tableSize;
if (ChaosMode::isActive()) {
// Start iterating at a random point in the hashtable. It would be
// even more chaotic to iterate in fully random order, but that's a lot
// more work.
mEntryAddr += ChaosMode::randomUint32LessThan(capacity) * mTable->mEntrySize;
if (mEntryAddr >= entryLimit) {
mEntryAddr -= tableSize;
}
}
}
PLDHashTable::Iterator::Iterator(const Iterator& aIterator)
: mTable(aIterator.mTable),
mEntryAddr(aIterator.mEntryAddr),
mEntryOffset(aIterator.mEntryOffset)
{
// We need the copy constructor only so that we can keep the recursion level
// consistent.
INCREMENT_RECURSION_LEVEL(mTable);
}
PLDHashTable::Iterator::~Iterator()
{
DECREMENT_RECURSION_LEVEL(mTable);
}
bool PLDHashTable::Iterator::HasMoreEntries() const
{
// Check the number of live entries seen, not the total number of entries
// seen. To see why, consider what happens if the last entry is not live: we
// would have to iterate after returning an entry to see if more live entries
// exist.
return mEntryOffset < mTable->EntryCount();
}
PLDHashEntryHdr* PLDHashTable::Iterator::NextEntry()
{
MOZ_ASSERT(HasMoreEntries());
// The following code is taken from, and should be kept in sync with, the
// PLDHashTable::Enumerate method above. The variables i and entryAddr (which
// vary over the course of the for loop) are converted into mEntryOffset and
// mEntryAddr, respectively.
uint32_t capacity = mTable->Capacity();
uint32_t tableSize = capacity * mTable->mEntrySize;
char* entryLimit = mEntryAddr + tableSize;
// Strictly speaking, we don't need to iterate over the full capacity each
// time. However, it is simpler to do so rather than unnecessarily track the
// current number of entries checked as opposed to only live entries. If debug
// checks pass, then this method will only iterate through the full capacity
// once. If they fail, then this loop may end up returning the early entries
// more than once.
for (uint32_t e = 0; e < capacity; ++e) {
PLDHashEntryHdr* entry = (PLDHashEntryHdr*)mEntryAddr;
// Increment the count before returning so we don't keep returning the same
// address. This may wrap around if ChaosMode is enabled.
mEntryAddr += mTable->mEntrySize;
if (mEntryAddr >= entryLimit) {
mEntryAddr -= tableSize;
}
if (ENTRY_IS_LIVE(entry)) {
++mEntryOffset;
return entry;
}
}
// If the debug checks pass, then the above loop should always find a live
// entry. If those checks are disabled, then it may be possible to reach this
// if the table is empty and this method is called.
MOZ_CRASH("Flagrant misuse of hashtable iterators not caught by checks.");
}
#ifdef DEBUG
MOZ_ALWAYS_INLINE void
PLDHashTable::MarkImmutable()

View File

@ -194,7 +194,7 @@ private:
* non-DEBUG components. (Actually, even if it were removed,
* sizeof(PLDHashTable) wouldn't change, due to struct padding.)
*/
uint16_t mRecursionLevel;/* used to detect unsafe re-entry */
mutable uint16_t mRecursionLevel;/* used to detect unsafe re-entry */
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 */
@ -272,6 +272,27 @@ public:
void DumpMeter(PLDHashEnumerator aDump, FILE* aFp);
#endif
/**
* This is an iterator that works over the elements of PLDHashtable. It is not
* safe to modify the hashtable while it is being iterated over; on debug
* builds, attempting to do so will result in an assertion failure.
*/
class Iterator {
public:
Iterator(const PLDHashTable* aTable);
Iterator(const Iterator& aIterator);
~Iterator();
bool HasMoreEntries() const;
PLDHashEntryHdr* NextEntry();
private:
const PLDHashTable* mTable; /* Main table pointer */
char* mEntryAddr; /* Pointer to the next entry to check */
uint32_t mEntryOffset; /* The number of the elements returned */
};
Iterator Iterate() const { return Iterator(this); }
private:
PLDHashEntryHdr* PL_DHASH_FASTCALL
SearchTable(const void* aKey, PLDHashNumber aKeyHash, PLDHashOperator aOp);