Skip to content

Commit 1b5be2b

Browse files
committed
Bug 1575479 - Add support for STL iterators and range-based for to nsBaseHashtable. r=froydnj
Differential Revision: https://phabricator.services.mozilla.com/D44982 --HG-- extra : moz-landing-system : lando
1 parent dda41fa commit 1b5be2b

File tree

4 files changed

+234
-1
lines changed

4 files changed

+234
-1
lines changed

xpcom/ds/PLDHashTable.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,36 @@ PLDHashTable::Iterator::Iterator(PLDHashTable* aTable)
729729
}
730730
}
731731

732+
PLDHashTable::Iterator::Iterator(PLDHashTable* aTable, EndIteratorTag aTag)
733+
: mTable(aTable),
734+
mCurrent(mTable->mEntryStore.SlotForIndex(0, mTable->mEntrySize,
735+
mTable->Capacity())),
736+
mNexts(mTable->EntryCount()),
737+
mNextsLimit(mTable->EntryCount()),
738+
mHaveRemoved(false),
739+
mEntrySize(aTable->mEntrySize) {
740+
#ifdef DEBUG
741+
mTable->mChecker.StartReadOp();
742+
#endif
743+
744+
MOZ_ASSERT(Done());
745+
}
746+
747+
PLDHashTable::Iterator::Iterator(const Iterator& aOther)
748+
: mTable(aOther.mTable),
749+
mCurrent(aOther.mCurrent),
750+
mNexts(aOther.mNexts),
751+
mNextsLimit(aOther.mNextsLimit),
752+
mHaveRemoved(aOther.mHaveRemoved),
753+
mEntrySize(aOther.mEntrySize) {
754+
// TODO: Is this necessary?
755+
MOZ_ASSERT(!mHaveRemoved);
756+
757+
#ifdef DEBUG
758+
mTable->mChecker.StartReadOp();
759+
#endif
760+
}
761+
732762
PLDHashTable::Iterator::~Iterator() {
733763
if (mTable) {
734764
if (mHaveRemoved) {

xpcom/ds/PLDHashTable.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,8 @@ class PLDHashTable {
571571
class Iterator {
572572
public:
573573
explicit Iterator(PLDHashTable* aTable);
574+
struct EndIteratorTag {};
575+
Iterator(PLDHashTable* aTable, EndIteratorTag aTag);
574576
Iterator(Iterator&& aOther);
575577
~Iterator();
576578

@@ -591,6 +593,13 @@ class PLDHashTable {
591593
// must not be called on that entry afterwards.
592594
void Remove();
593595

596+
bool operator==(const Iterator& aOther) const {
597+
MOZ_ASSERT(mTable == aOther.mTable);
598+
return mNexts == aOther.mNexts;
599+
}
600+
601+
Iterator Clone() const { return {*this}; }
602+
594603
protected:
595604
PLDHashTable* mTable; // Main table pointer.
596605

@@ -607,7 +616,7 @@ class PLDHashTable {
607616
void MoveToNextLiveEntry();
608617

609618
Iterator() = delete;
610-
Iterator(const Iterator&) = delete;
619+
Iterator(const Iterator&);
611620
Iterator& operator=(const Iterator&) = delete;
612621
Iterator& operator=(const Iterator&&) = delete;
613622
};

xpcom/ds/nsBaseHashtable.h

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,91 @@ class nsBaseHashtable
396396
return Iterator(const_cast<nsBaseHashtable*>(this));
397397
}
398398

399+
// STL-style iterators to allow the use in range-based for loops, e.g.
400+
template <typename T>
401+
class base_iterator
402+
: public std::iterator<std::forward_iterator_tag, T, int32_t> {
403+
public:
404+
using typename std::iterator<std::forward_iterator_tag, T,
405+
int32_t>::value_type;
406+
using typename std::iterator<std::forward_iterator_tag, T,
407+
int32_t>::difference_type;
408+
409+
using iterator_type = base_iterator;
410+
using const_iterator_type = base_iterator<const T>;
411+
412+
using EndIteratorTag = PLDHashTable::Iterator::EndIteratorTag;
413+
414+
base_iterator(base_iterator&& aOther) = default;
415+
416+
base_iterator& operator=(base_iterator&& aOther) {
417+
// User-defined because the move assignment operator is deleted in
418+
// PLDHashtable::Iterator.
419+
return operator=(static_cast<const base_iterator&>(aOther));
420+
}
421+
422+
base_iterator(const base_iterator& aOther)
423+
: mIterator{aOther.mIterator.Clone()} {}
424+
base_iterator& operator=(const base_iterator& aOther) {
425+
// Since PLDHashTable::Iterator has no assignment operator, we destroy and
426+
// recreate mIterator.
427+
mIterator.~Iterator();
428+
new (&mIterator) PLDHashTable::Iterator(aOther.mIterator.Clone());
429+
return *this;
430+
}
431+
432+
explicit base_iterator(PLDHashTable::Iterator aFrom)
433+
: mIterator{std::move(aFrom)} {}
434+
435+
explicit base_iterator(const nsBaseHashtable* aTable)
436+
: mIterator{&const_cast<nsBaseHashtable*>(aTable)->mTable} {}
437+
438+
base_iterator(const nsBaseHashtable* aTable, EndIteratorTag aTag)
439+
: mIterator{&const_cast<nsBaseHashtable*>(aTable)->mTable, aTag} {}
440+
441+
bool operator==(const iterator_type& aRhs) const {
442+
return mIterator == aRhs.mIterator;
443+
}
444+
bool operator!=(const iterator_type& aRhs) const {
445+
return !(*this == aRhs);
446+
}
447+
448+
value_type* operator->() const {
449+
return static_cast<value_type*>(mIterator.Get());
450+
}
451+
value_type& operator*() const {
452+
return *static_cast<value_type*>(mIterator.Get());
453+
}
454+
455+
iterator_type& operator++() {
456+
mIterator.Next();
457+
return *this;
458+
}
459+
iterator_type operator++(int) {
460+
iterator_type it = *this;
461+
++*this;
462+
return it;
463+
}
464+
465+
operator const_iterator_type() const {
466+
return const_iterator_type{mIterator.Clone()};
467+
}
468+
469+
private:
470+
PLDHashTable::Iterator mIterator;
471+
};
472+
using const_iterator = base_iterator<const EntryType>;
473+
using iterator = base_iterator<EntryType>;
474+
475+
iterator begin() { return iterator{this}; }
476+
const_iterator begin() const { return const_iterator{this}; }
477+
const_iterator cbegin() const { return begin(); }
478+
iterator end() { return iterator{this, typename iterator::EndIteratorTag{}}; }
479+
const_iterator end() const {
480+
return const_iterator{this, typename const_iterator::EndIteratorTag{}};
481+
}
482+
const_iterator cend() const { return end(); }
483+
399484
/**
400485
* reset the hashtable, removing all entries
401486
*/

xpcom/tests/gtest/TestHashtables.cpp

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
#include "gtest/gtest.h"
1919

20+
#include <numeric>
21+
2022
namespace TestHashtables {
2123

2224
class TestUniChar // for nsClassHashtable
@@ -35,6 +37,11 @@ class TestUniChar // for nsClassHashtable
3537
struct EntityNode {
3638
const char* mStr; // never owns buffer
3739
uint32_t mUnicode;
40+
41+
bool operator<(const EntityNode& aOther) const {
42+
return mUnicode < aOther.mUnicode ||
43+
(mUnicode == aOther.mUnicode && strcmp(mStr, aOther.mStr) < 0);
44+
}
3845
};
3946

4047
static const EntityNode gEntities[] = {
@@ -286,6 +293,68 @@ TEST(Hashtables, DataHashtable)
286293
ASSERT_EQ(count, uint32_t(0));
287294
}
288295

296+
TEST(Hashtables, DataHashtable_STLIterators)
297+
{
298+
nsDataHashtable<nsUint32HashKey, const char*> UniToEntity(ENTITY_COUNT);
299+
300+
for (auto& entity : gEntities) {
301+
UniToEntity.Put(entity.mUnicode, entity.mStr);
302+
}
303+
304+
// operators, including conversion from iterator to const_iterator
305+
nsDataHashtable<nsUint32HashKey, const char*>::const_iterator ci =
306+
UniToEntity.begin();
307+
++ci;
308+
ASSERT_EQ(1, std::distance(UniToEntity.cbegin(), ci++));
309+
ASSERT_EQ(2, std::distance(UniToEntity.cbegin(), ci));
310+
ASSERT_TRUE(ci == ci);
311+
auto otherCi = ci;
312+
++otherCi;
313+
++ci;
314+
ASSERT_TRUE(&*ci == &*otherCi);
315+
316+
// STL algorithms (just to check that the iterator sufficiently conforms with
317+
// the actual syntactical requirements of those algorithms).
318+
std::for_each(UniToEntity.cbegin(), UniToEntity.cend(),
319+
[](const auto& entry) {});
320+
std::find_if(UniToEntity.cbegin(), UniToEntity.cend(),
321+
[](const auto& entry) { return entry.GetKey() == 42; });
322+
std::accumulate(
323+
UniToEntity.cbegin(), UniToEntity.cend(), 0u,
324+
[](size_t sum, const auto& entry) { return sum + entry.GetKey(); });
325+
std::any_of(UniToEntity.cbegin(), UniToEntity.cend(),
326+
[](const auto& entry) { return entry.GetKey() == 42; });
327+
std::max_element(UniToEntity.cbegin(), UniToEntity.cend(),
328+
[](const auto& lhs, const auto& rhs) {
329+
return lhs.GetKey() > rhs.GetKey();
330+
});
331+
332+
// const range-based for
333+
{
334+
std::set<EntityNode> entities(gEntities, gEntities + ENTITY_COUNT);
335+
for (const auto& entity :
336+
const_cast<const nsDataHashtable<nsUint32HashKey, const char*>&>(
337+
UniToEntity)) {
338+
ASSERT_EQ(1u,
339+
entities.erase(EntityNode{entity.GetData(), entity.GetKey()}));
340+
}
341+
ASSERT_TRUE(entities.empty());
342+
}
343+
344+
// non-const range-based for
345+
{
346+
std::set<EntityNode> entities(gEntities, gEntities + ENTITY_COUNT);
347+
for (auto& entity : UniToEntity) {
348+
ASSERT_EQ(1u,
349+
entities.erase(EntityNode{entity.GetData(), entity.GetKey()}));
350+
351+
entity.SetData(nullptr);
352+
ASSERT_EQ(nullptr, entity.GetData());
353+
}
354+
ASSERT_TRUE(entities.empty());
355+
}
356+
}
357+
289358
TEST(Hashtables, ClassHashtable)
290359
{
291360
// check a class-hashtable
@@ -319,6 +388,46 @@ TEST(Hashtables, ClassHashtable)
319388
ASSERT_EQ(count, uint32_t(0));
320389
}
321390

391+
TEST(Hashtables, ClassHashtable_RangeBasedFor)
392+
{
393+
// check a class-hashtable
394+
nsClassHashtable<nsCStringHashKey, TestUniChar> EntToUniClass(ENTITY_COUNT);
395+
396+
for (auto& entity : gEntities) {
397+
auto* temp = new TestUniChar(entity.mUnicode);
398+
EntToUniClass.Put(nsDependentCString(entity.mStr), temp);
399+
}
400+
401+
// const range-based for
402+
{
403+
std::set<EntityNode> entities(gEntities, gEntities + ENTITY_COUNT);
404+
for (const auto& entity :
405+
const_cast<const nsClassHashtable<nsCStringHashKey, TestUniChar>&>(
406+
EntToUniClass)) {
407+
const char* str;
408+
entity.GetKey().GetData(&str);
409+
ASSERT_EQ(1u,
410+
entities.erase(EntityNode{str, entity.GetData()->GetChar()}));
411+
}
412+
ASSERT_TRUE(entities.empty());
413+
}
414+
415+
// non-const range-based for
416+
{
417+
std::set<EntityNode> entities(gEntities, gEntities + ENTITY_COUNT);
418+
for (auto& entity : EntToUniClass) {
419+
const char* str;
420+
entity.GetKey().GetData(&str);
421+
ASSERT_EQ(1u,
422+
entities.erase(EntityNode{str, entity.GetData()->GetChar()}));
423+
424+
entity.SetData(nsAutoPtr<TestUniChar>{});
425+
ASSERT_EQ(nullptr, entity.GetData());
426+
}
427+
ASSERT_TRUE(entities.empty());
428+
}
429+
}
430+
322431
TEST(Hashtables, DataHashtableWithInterfaceKey)
323432
{
324433
// check a data-hashtable with an interface key

0 commit comments

Comments
 (0)