diff options
Diffstat (limited to 'xpcom/ds/nsCheapSets.h')
-rw-r--r-- | xpcom/ds/nsCheapSets.h | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/xpcom/ds/nsCheapSets.h b/xpcom/ds/nsCheapSets.h new file mode 100644 index 0000000000..3d09ccfdb7 --- /dev/null +++ b/xpcom/ds/nsCheapSets.h @@ -0,0 +1,155 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __nsCheapSets_h__ +#define __nsCheapSets_h__ + +#include "nsTHashtable.h" +#include <stdint.h> + +enum nsCheapSetOperator { + OpNext = 0, // enumerator says continue + OpRemove = 1, // enumerator says remove and continue +}; + +/** + * A set that takes up minimal size when there are 0 or 1 entries in the set. + * Use for cases where sizes of 0 and 1 are even slightly common. + */ +template <typename EntryType> +class nsCheapSet { + public: + typedef typename EntryType::KeyType KeyType; + typedef nsCheapSetOperator (*Enumerator)(EntryType* aEntry, void* userArg); + + nsCheapSet() : mState(ZERO) { mUnion.table = nullptr; } + ~nsCheapSet() { Clear(); } + + /** + * Remove all entries. + */ + void Clear() { + switch (mState) { + case ZERO: + break; + case ONE: + GetSingleEntry()->~EntryType(); + break; + case MANY: + delete mUnion.table; + break; + default: + MOZ_ASSERT_UNREACHABLE("bogus state"); + break; + } + mState = ZERO; + } + + void Put(const KeyType aVal); + + void Remove(const KeyType aVal); + + bool Contains(const KeyType aVal) { + switch (mState) { + case ZERO: + return false; + case ONE: + return GetSingleEntry()->KeyEquals(EntryType::KeyToPointer(aVal)); + case MANY: + return !!mUnion.table->GetEntry(aVal); + default: + MOZ_ASSERT_UNREACHABLE("bogus state"); + return false; + } + } + + uint32_t EnumerateEntries(Enumerator aEnumFunc, void* aUserArg) { + switch (mState) { + case ZERO: + return 0; + case ONE: + if (aEnumFunc(GetSingleEntry(), aUserArg) == OpRemove) { + GetSingleEntry()->~EntryType(); + mState = ZERO; + } + return 1; + case MANY: { + uint32_t n = mUnion.table->Count(); + for (auto iter = mUnion.table->Iter(); !iter.Done(); iter.Next()) { + auto entry = static_cast<EntryType*>(iter.Get()); + if (aEnumFunc(entry, aUserArg) == OpRemove) { + iter.Remove(); + } + } + return n; + } + default: + MOZ_ASSERT_UNREACHABLE("bogus state"); + return 0; + } + } + + private: + EntryType* GetSingleEntry() { + return reinterpret_cast<EntryType*>(&mUnion.singleEntry[0]); + } + + enum SetState { ZERO, ONE, MANY }; + + union { + nsTHashtable<EntryType>* table; + char singleEntry[sizeof(EntryType)]; + } mUnion; + enum SetState mState; +}; + +template <typename EntryType> +void nsCheapSet<EntryType>::Put(const KeyType aVal) { + switch (mState) { + case ZERO: + new (GetSingleEntry()) EntryType(EntryType::KeyToPointer(aVal)); + mState = ONE; + return; + case ONE: { + nsTHashtable<EntryType>* table = new nsTHashtable<EntryType>(); + EntryType* entry = GetSingleEntry(); + table->PutEntry(entry->GetKey()); + entry->~EntryType(); + mUnion.table = table; + mState = MANY; + } + [[fallthrough]]; + + case MANY: + mUnion.table->PutEntry(aVal); + return; + default: + MOZ_ASSERT_UNREACHABLE("bogus state"); + return; + } +} + +template <typename EntryType> +void nsCheapSet<EntryType>::Remove(const KeyType aVal) { + switch (mState) { + case ZERO: + break; + case ONE: + if (Contains(aVal)) { + GetSingleEntry()->~EntryType(); + mState = ZERO; + } + break; + case MANY: + mUnion.table->RemoveEntry(aVal); + break; + default: + MOZ_ASSERT_UNREACHABLE("bogus state"); + break; + } +} + +#endif |