mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1173600 (part 3) - Add PLDHashTable::RemovingIterator. r=froydnj.
This commit is contained in:
parent
b0a39a3a7c
commit
a2d83b8f85
@ -978,6 +978,39 @@ PLDHashTable::Iterator::Next()
|
||||
} while (IsOnNonLiveEntry());
|
||||
}
|
||||
|
||||
PLDHashTable::RemovingIterator::RemovingIterator(RemovingIterator&& aOther)
|
||||
: Iterator(mozilla::Move(aOther.mTable))
|
||||
, mHaveRemoved(aOther.mHaveRemoved)
|
||||
{
|
||||
}
|
||||
|
||||
PLDHashTable::RemovingIterator::RemovingIterator(PLDHashTable* aTable)
|
||||
: Iterator(aTable)
|
||||
, mHaveRemoved(false)
|
||||
{
|
||||
}
|
||||
|
||||
PLDHashTable::RemovingIterator::~RemovingIterator()
|
||||
{
|
||||
if (mHaveRemoved) {
|
||||
// Why is this cast needed? In Iterator, |mTable| is const. In
|
||||
// RemovingIterator it should be non-const, but it inherits from Iterator
|
||||
// so that's not possible. But it's ok because RemovingIterator's
|
||||
// constructor takes a pointer to a non-const table in the first place.
|
||||
const_cast<PLDHashTable*>(mTable)->ShrinkIfAppropriate();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PLDHashTable::RemovingIterator::Remove()
|
||||
{
|
||||
METER(mStats.mRemoveEnums++);
|
||||
|
||||
// This cast is needed for the same reason as the one in the destructor.
|
||||
const_cast<PLDHashTable*>(mTable)->RawRemove(Get());
|
||||
mHaveRemoved = true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
MOZ_ALWAYS_INLINE void
|
||||
PLDHashTable::MarkImmutable()
|
||||
|
@ -323,8 +323,10 @@ public:
|
||||
PLDHashEntryHdr* Get() const; // Get the current entry.
|
||||
void Next(); // Advance to the next entry.
|
||||
|
||||
private:
|
||||
protected:
|
||||
const PLDHashTable* mTable; // Main table pointer.
|
||||
|
||||
private:
|
||||
char* mCurrent; // Pointer to the current entry.
|
||||
char* mLimit; // One past the last entry.
|
||||
|
||||
@ -338,6 +340,35 @@ public:
|
||||
|
||||
Iterator Iter() const { return Iterator(this); }
|
||||
|
||||
// This is an iterator that allows elements to be removed during iteration.
|
||||
// If any elements are removed, the table may be resized once iteration ends.
|
||||
// Its usage is similar to that of Iterator, with the addition that Remove()
|
||||
// can be called once per element.
|
||||
class RemovingIterator : public Iterator
|
||||
{
|
||||
public:
|
||||
explicit RemovingIterator(PLDHashTable* aTable);
|
||||
RemovingIterator(RemovingIterator&& aOther);
|
||||
~RemovingIterator();
|
||||
|
||||
// Remove the current entry. Must only be called once per entry, and Get()
|
||||
// must not be called on that entry afterwards.
|
||||
void Remove();
|
||||
|
||||
private:
|
||||
bool mHaveRemoved; // Have any elements been removed?
|
||||
|
||||
RemovingIterator() = delete;
|
||||
RemovingIterator(const RemovingIterator&) = delete;
|
||||
RemovingIterator& operator=(const RemovingIterator&) = delete;
|
||||
RemovingIterator& operator=(const RemovingIterator&&) = delete;
|
||||
};
|
||||
|
||||
RemovingIterator RemovingIter() const
|
||||
{
|
||||
return RemovingIterator(const_cast<PLDHashTable*>(this));
|
||||
}
|
||||
|
||||
private:
|
||||
static bool EntryIsFree(PLDHashEntryHdr* aEntry);
|
||||
|
||||
|
@ -231,6 +231,72 @@ static bool test_pldhash_Iterator()
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool test_pldhash_RemovingIterator()
|
||||
{
|
||||
PLDHashTable t(&trivialOps, sizeof(PLDHashEntryStub));
|
||||
|
||||
// Explicitly test the move constructor. We do this because, due to copy
|
||||
// elision, compilers might optimize away move constructor calls for normal
|
||||
// iterator use.
|
||||
{
|
||||
PLDHashTable::Iterator iter1(&t);
|
||||
PLDHashTable::Iterator iter2(mozilla::Move(iter1));
|
||||
}
|
||||
|
||||
// First, we insert 64 items, which results in a capacity of 128, and a load
|
||||
// factor of 50%.
|
||||
for (intptr_t i = 0; i < 64; i++) {
|
||||
PL_DHashTableAdd(&t, (const void*)i);
|
||||
}
|
||||
if (t.EntryCount() != 64 || t.Capacity() != 128) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The first removing iterator does no removing; capacity and entry count are
|
||||
// unchanged.
|
||||
for (PLDHashTable::RemovingIterator iter(&t); !iter.Done(); iter.Next()) {
|
||||
(void) iter.Get();
|
||||
}
|
||||
if (t.EntryCount() != 64 || t.Capacity() != 128) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The second removing iterator removes 16 items. This reduces the load
|
||||
// factor to 37.5% (48 / 128), which isn't low enough to shrink the table.
|
||||
for (auto iter = t.RemovingIter(); !iter.Done(); iter.Next()) {
|
||||
auto entry = static_cast<PLDHashEntryStub*>(iter.Get());
|
||||
if ((intptr_t)(entry->key) % 4 == 0) {
|
||||
iter.Remove();
|
||||
}
|
||||
}
|
||||
if (t.EntryCount() != 48 || t.Capacity() != 128) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The third removing iterator removes another 16 items. This reduces
|
||||
// the load factor to 25% (32 / 128), so the table is shrunk.
|
||||
for (auto iter = t.RemovingIter(); !iter.Done(); iter.Next()) {
|
||||
auto entry = static_cast<PLDHashEntryStub*>(iter.Get());
|
||||
if ((intptr_t)(entry->key) % 2 == 0) {
|
||||
iter.Remove();
|
||||
}
|
||||
}
|
||||
if (t.EntryCount() != 32 || t.Capacity() != 64) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The fourth removing iterator removes all remaining items. This reduces
|
||||
// the capacity to the minimum.
|
||||
for (auto iter = t.RemovingIter(); !iter.Done(); iter.Next()) {
|
||||
iter.Remove();
|
||||
}
|
||||
if (t.EntryCount() != 0 || t.Capacity() != PL_DHASH_MIN_CAPACITY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// See bug 931062, we skip this test on Android due to OOM.
|
||||
#ifndef MOZ_WIDGET_ANDROID
|
||||
static bool test_pldhash_grow_to_max_capacity()
|
||||
@ -274,6 +340,7 @@ static const struct Test {
|
||||
DECL_TEST(test_pldhash_move_semantics),
|
||||
DECL_TEST(test_pldhash_Clear),
|
||||
DECL_TEST(test_pldhash_Iterator),
|
||||
DECL_TEST(test_pldhash_RemovingIterator),
|
||||
// See bug 931062, we skip this test on Android due to OOM. Also, it's slow,
|
||||
// and so should always be last.
|
||||
#ifndef MOZ_WIDGET_ANDROID
|
||||
|
Loading…
Reference in New Issue
Block a user