From 249958c03db1f1348ddc1c64917bf42b67efbb07 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 18 May 2015 19:16:06 -0700 Subject: [PATCH] Bug 1168007 (part 1) - Add PLDHashTable::{Clear,ClearAndPrepareForLength}(). r=froydnj. --HG-- extra : rebase_source : 05445de53b178173bfbaed29095e0ca4c11639a2 --- xpcom/glue/pldhash.cpp | 19 ++++++++ xpcom/glue/pldhash.h | 16 +++++++ xpcom/tests/TestPLDHash.cpp | 89 ++++++++++++++++++++++++++----------- 3 files changed, 98 insertions(+), 26 deletions(-) diff --git a/xpcom/glue/pldhash.cpp b/xpcom/glue/pldhash.cpp index b1debe7586e2..db5bad383b27 100644 --- a/xpcom/glue/pldhash.cpp +++ b/xpcom/glue/pldhash.cpp @@ -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 diff --git a/xpcom/glue/pldhash.h b/xpcom/glue/pldhash.h index 736629a086c5..1d76a29c6639 100644 --- a/xpcom/glue/pldhash.h +++ b/xpcom/glue/pldhash.h @@ -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; diff --git a/xpcom/tests/TestPLDHash.cpp b/xpcom/tests/TestPLDHash.cpp index fd19bf4a01b2..38349e96cd5f 100644 --- a/xpcom/tests/TestPLDHash.cpp +++ b/xpcom/tests/TestPLDHash.cpp @@ -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; }