Bug 1168007 (part 1) - Add PLDHashTable::{Clear,ClearAndPrepareForLength}(). r=froydnj.

This commit is contained in:
Nicholas Nethercote 2015-05-18 19:16:06 -07:00
parent 53a2fa35ef
commit f7e35f3076
3 changed files with 98 additions and 26 deletions

View File

@ -400,6 +400,25 @@ PLDHashTable2::~PLDHashTable2()
PLDHashTable::Finish();
}
void
PLDHashTable2::ClearAndPrepareForLength(uint32_t aLength)
{
MOZ_ASSERT(IsInitialized());
// Get these values before the call to Finish() clobbers them.
const PLDHashTableOps* ops = mOps;
uint32_t entrySize = mEntrySize;
PLDHashTable::Finish();
PLDHashTable::Init(ops, entrySize, aLength);
}
void
PLDHashTable2::Clear()
{
ClearAndPrepareForLength(PL_DHASH_DEFAULT_INITIAL_LENGTH);
}
// If |IsAdd| is true, the return value is always non-null and it may be a
// previously-removed entry. If |IsAdd| is false, the return value is null on a
// miss, and will never be a previously-removed entry on a hit. This

View File

@ -161,6 +161,8 @@ typedef size_t (*PLDHashSizeOfEntryExcludingThisFun)(
*/
class PLDHashTable
{
friend class PLDHashTable2;
private:
const PLDHashTableOps* mOps; /* Virtual operations; see below. */
int16_t mHashShift; /* multiplicative hash shift */
@ -370,6 +372,20 @@ public:
void Finish() { MOZ_CRASH("PLDHashTable2::Finish()"); }
// This function is equivalent to
// ClearAndPrepareForLength(PL_DHASH_DEFAULT_INITIAL_LENGTH).
void Clear();
// This function clears the table's contents and frees its entry storage,
// leaving it in a empty state ready to be used again. Afterwards, when the
// first element is added the entry storage that gets allocated will have a
// capacity large enough to fit |aLength| elements without rehashing.
//
// It's conceptually the same as calling the destructor and then re-calling
// the constructor with the original |aOps| and |aEntrySize| arguments, and
// a new |aLength| argument.
void ClearAndPrepareForLength(uint32_t aLength);
private:
PLDHashTable2(const PLDHashTable2& aOther) = delete;
PLDHashTable2& operator=(const PLDHashTable2& aOther) = delete;

View File

@ -109,28 +109,28 @@ static bool test_pldhash_lazy_storage()
return true;
}
// We insert the integers 0.., so this hash function is (a) as simple as
// possible, and (b) collision-free. Both of which are good, because we want
// this test to be as fast as possible.
// A trivial hash function is good enough here. It's also super-fast for
// test_pldhash_grow_to_max_capacity() because we insert the integers 0..,
// which means it's collision-free.
static PLDHashNumber
trivial_hash(PLDHashTable *table, const void *key)
{
return (PLDHashNumber)(size_t)key;
}
static const PLDHashTableOps trivialOps = {
trivial_hash,
PL_DHashMatchEntryStub,
PL_DHashMoveEntryStub,
PL_DHashClearEntryStub,
nullptr
};
static bool test_pldhash_move_semantics()
{
static const PLDHashTableOps ops = {
trivial_hash,
PL_DHashMatchEntryStub,
PL_DHashMoveEntryStub,
PL_DHashClearEntryStub,
nullptr
};
PLDHashTable2 t1(&ops, sizeof(PLDHashEntryStub));
PLDHashTable2 t1(&trivialOps, sizeof(PLDHashEntryStub));
PL_DHashTableAdd(&t1, (const void*)88);
PLDHashTable2 t2(&ops, sizeof(PLDHashEntryStub));
PLDHashTable2 t2(&trivialOps, sizeof(PLDHashEntryStub));
PL_DHashTableAdd(&t2, (const void*)99);
t1 = mozilla::Move(t1); // self-move
@ -138,7 +138,7 @@ static bool test_pldhash_move_semantics()
t1 = mozilla::Move(t2); // inited overwritten with inited
PLDHashTable t3, t4;
PL_DHashTableInit(&t3, &ops, sizeof(PLDHashEntryStub));
PL_DHashTableInit(&t3, &trivialOps, sizeof(PLDHashEntryStub));
PL_DHashTableAdd(&t3, (const void*)88);
t3 = mozilla::Move(t4); // inited overwritten with uninited
@ -147,7 +147,7 @@ static bool test_pldhash_move_semantics()
PL_DHashTableFinish(&t4);
PLDHashTable t5, t6;
PL_DHashTableInit(&t6, &ops, sizeof(PLDHashEntryStub));
PL_DHashTableInit(&t6, &trivialOps, sizeof(PLDHashEntryStub));
PL_DHashTableAdd(&t6, (const void*)88);
t5 = mozilla::Move(t6); // uninited overwritten with inited
@ -158,27 +158,63 @@ static bool test_pldhash_move_semantics()
PLDHashTable t7;
PLDHashTable t8(mozilla::Move(t7)); // new table constructed with uninited
PLDHashTable2 t9(&ops, sizeof(PLDHashEntryStub));
PLDHashTable2 t9(&trivialOps, sizeof(PLDHashEntryStub));
PL_DHashTableAdd(&t9, (const void*)88);
PLDHashTable t10(mozilla::Move(t9)); // new table constructed with inited
return true;
}
static bool test_pldhash_Clear()
{
PLDHashTable2 t1(&trivialOps, sizeof(PLDHashEntryStub));
t1.Clear();
if (t1.EntryCount() != 0) {
return false;
}
t1.ClearAndPrepareForLength(100);
if (t1.EntryCount() != 0) {
return false;
}
PL_DHashTableAdd(&t1, (const void*)77);
PL_DHashTableAdd(&t1, (const void*)88);
PL_DHashTableAdd(&t1, (const void*)99);
if (t1.EntryCount() != 3) {
return false;
}
t1.Clear();
if (t1.EntryCount() != 0) {
return false;
}
PL_DHashTableAdd(&t1, (const void*)55);
PL_DHashTableAdd(&t1, (const void*)66);
PL_DHashTableAdd(&t1, (const void*)77);
PL_DHashTableAdd(&t1, (const void*)88);
PL_DHashTableAdd(&t1, (const void*)99);
if (t1.EntryCount() != 5) {
return false;
}
t1.ClearAndPrepareForLength(8192);
if (t1.EntryCount() != 0) {
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()
{
static const PLDHashTableOps ops = {
trivial_hash,
PL_DHashMatchEntryStub,
PL_DHashMoveEntryStub,
PL_DHashClearEntryStub,
nullptr
};
// This is infallible.
PLDHashTable2* t = new PLDHashTable2(&ops, sizeof(PLDHashEntryStub), 128);
PLDHashTable2* t =
new PLDHashTable2(&trivialOps, sizeof(PLDHashEntryStub), 128);
// Check that New() sets |t->ops|.
if (!t->IsInitialized()) {
@ -219,6 +255,7 @@ static const struct Test {
DECL_TEST(test_pldhash_Init_capacity_ok),
DECL_TEST(test_pldhash_lazy_storage),
DECL_TEST(test_pldhash_move_semantics),
DECL_TEST(test_pldhash_Clear),
// See bug 931062, we skip this test on Android due to OOM.
#ifndef MOZ_WIDGET_ANDROID
DECL_TEST(test_pldhash_grow_to_max_capacity),
@ -235,7 +272,7 @@ int main(int argc, char *argv[])
bool success = true;
for (const Test* t = tests; t->name != nullptr; ++t) {
bool test_result = t->func();
printf("%25s : %s\n", t->name, test_result ? "SUCCESS" : "FAILURE");
printf("%35s : %s\n", t->name, test_result ? "SUCCESS" : "FAILURE");
if (!test_result)
success = false;
}