summaryrefslogtreecommitdiffstats
path: root/xpcom/ds/nsBaseHashtable.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--xpcom/ds/nsBaseHashtable.h499
1 files changed, 499 insertions, 0 deletions
diff --git a/xpcom/ds/nsBaseHashtable.h b/xpcom/ds/nsBaseHashtable.h
new file mode 100644
index 0000000000..6cd13c8e4d
--- /dev/null
+++ b/xpcom/ds/nsBaseHashtable.h
@@ -0,0 +1,499 @@
+/* -*- 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 nsBaseHashtable_h__
+#define nsBaseHashtable_h__
+
+#include <utility>
+
+#include "mozilla/MemoryReporting.h"
+#include "nsDebug.h"
+#include "nsTHashtable.h"
+
+template <class KeyClass, class DataType, class UserDataType, class Converter>
+class nsBaseHashtable; // forward declaration
+
+/**
+ * Data type conversion helper that is used to wrap and unwrap the specified
+ * DataType.
+ */
+template <class DataType, class UserDataType>
+class nsDefaultConverter {
+ public:
+ /**
+ * Maps the storage DataType to the exposed UserDataType.
+ */
+ static UserDataType Unwrap(DataType& src) { return UserDataType(src); }
+
+ /**
+ * Const ref variant used for example with nsCOMPtr wrappers.
+ */
+ static DataType Wrap(const UserDataType& src) { return DataType(src); }
+
+ /**
+ * Generic conversion, this is useful for things like already_AddRefed.
+ */
+ template <typename U>
+ static DataType Wrap(U&& src) {
+ return std::move(src);
+ }
+};
+
+/**
+ * the private nsTHashtable::EntryType class used by nsBaseHashtable
+ * @see nsTHashtable for the specification of this class
+ * @see nsBaseHashtable for template parameters
+ */
+template <class KeyClass, class DataType>
+class nsBaseHashtableET : public KeyClass {
+ public:
+ const DataType& GetData() const { return mData; }
+ DataType* GetModifiableData() { return &mData; }
+ template <typename U>
+ void SetData(U&& aData) {
+ mData = std::forward<U>(aData);
+ }
+
+ private:
+ DataType mData;
+ friend class nsTHashtable<nsBaseHashtableET<KeyClass, DataType>>;
+ template <typename KeyClassX, typename DataTypeX, typename UserDataTypeX,
+ typename ConverterX>
+ friend class nsBaseHashtable;
+
+ typedef typename KeyClass::KeyType KeyType;
+ typedef typename KeyClass::KeyTypePointer KeyTypePointer;
+
+ explicit nsBaseHashtableET(KeyTypePointer aKey);
+ nsBaseHashtableET(nsBaseHashtableET<KeyClass, DataType>&& aToMove);
+ ~nsBaseHashtableET() = default;
+};
+
+/**
+ * templated hashtable for simple data types
+ * This class manages simple data types that do not need construction or
+ * destruction.
+ *
+ * @param KeyClass a wrapper-class for the hashtable key, see nsHashKeys.h
+ * for a complete specification.
+ * @param DataType the datatype stored in the hashtable,
+ * for example, uint32_t or nsCOMPtr.
+ * @param UserDataType the user sees, for example uint32_t or nsISupports*
+ * @param Converter that can be used to map from DataType to UserDataType. A
+ * default converter is provided that assumes implicit conversion is an
+ * option.
+ */
+template <class KeyClass, class DataType, class UserDataType,
+ class Converter = nsDefaultConverter<DataType, UserDataType>>
+class nsBaseHashtable
+ : protected nsTHashtable<nsBaseHashtableET<KeyClass, DataType>> {
+ typedef mozilla::fallible_t fallible_t;
+
+ public:
+ typedef typename KeyClass::KeyType KeyType;
+ typedef nsBaseHashtableET<KeyClass, DataType> EntryType;
+
+ using nsTHashtable<EntryType>::Contains;
+ using nsTHashtable<EntryType>::GetGeneration;
+ using nsTHashtable<EntryType>::SizeOfExcludingThis;
+ using nsTHashtable<EntryType>::SizeOfIncludingThis;
+
+ nsBaseHashtable() = default;
+ explicit nsBaseHashtable(uint32_t aInitLength)
+ : nsTHashtable<EntryType>(aInitLength) {}
+
+ /**
+ * Return the number of entries in the table.
+ * @return number of entries
+ */
+ uint32_t Count() const { return nsTHashtable<EntryType>::Count(); }
+
+ /**
+ * Return whether the table is empty.
+ * @return whether empty
+ */
+ bool IsEmpty() const { return nsTHashtable<EntryType>::IsEmpty(); }
+
+ /**
+ * retrieve the value for a key.
+ * @param aKey the key to retreive
+ * @param aData data associated with this key will be placed at this
+ * pointer. If you only need to check if the key exists, aData
+ * may be null.
+ * @return true if the key exists. If key does not exist, aData is not
+ * modified.
+ */
+ bool Get(KeyType aKey, UserDataType* aData) const {
+ EntryType* ent = this->GetEntry(aKey);
+ if (!ent) {
+ return false;
+ }
+
+ if (aData) {
+ *aData = Converter::Unwrap(ent->mData);
+ }
+
+ return true;
+ }
+
+ /**
+ * Get the value, returning a zero-initialized POD or a default-initialized
+ * object if the entry is not present in the table.
+ *
+ * @param aKey the key to retrieve
+ * @return The found value, or UserDataType{} if no entry was found with the
+ * given key.
+ * @note If zero/default-initialized values are stored in the table, it is
+ * not possible to distinguish between such a value and a missing entry.
+ */
+ UserDataType Get(KeyType aKey) const {
+ EntryType* ent = this->GetEntry(aKey);
+ if (!ent) {
+ return UserDataType{};
+ }
+
+ return Converter::Unwrap(ent->mData);
+ }
+
+ /**
+ * Add key to the table if not already present, and return a reference to its
+ * value. If key is not already in the table then the value is default
+ * constructed.
+ */
+ DataType& GetOrInsert(const KeyType& aKey) {
+ EntryType* ent = this->PutEntry(aKey);
+ return ent->mData;
+ }
+
+ /**
+ * Put a new value for the associated key
+ * @param aKey the key to put
+ * @param aData the new data
+ */
+ void Put(KeyType aKey, const UserDataType& aData) {
+ if (!Put(aKey, aData, mozilla::fallible)) {
+ NS_ABORT_OOM(this->mTable.EntrySize() * this->mTable.EntryCount());
+ }
+ }
+
+ [[nodiscard]] bool Put(KeyType aKey, const UserDataType& aData,
+ const fallible_t&) {
+ EntryType* ent = this->PutEntry(aKey, mozilla::fallible);
+ if (!ent) {
+ return false;
+ }
+
+ ent->mData = Converter::Wrap(aData);
+
+ return true;
+ }
+
+ /**
+ * Put a new value for the associated key
+ * @param aKey the key to put
+ * @param aData the new data
+ */
+ void Put(KeyType aKey, UserDataType&& aData) {
+ if (!Put(aKey, std::move(aData), mozilla::fallible)) {
+ NS_ABORT_OOM(this->mTable.EntrySize() * this->mTable.EntryCount());
+ }
+ }
+
+ [[nodiscard]] bool Put(KeyType aKey, UserDataType&& aData,
+ const fallible_t&) {
+ EntryType* ent = this->PutEntry(aKey, mozilla::fallible);
+ if (!ent) {
+ return false;
+ }
+
+ ent->mData = Converter::Wrap(std::move(aData));
+
+ return true;
+ }
+
+ /**
+ * Remove the entry associated with aKey (if any), optionally _moving_ its
+ * current value into *aData. Return true if found.
+ * @param aKey the key to remove from the hashtable
+ * @param aData where to move the value (if non-null). If an entry is not
+ * found, *aData will be assigned a default-constructed value
+ * (i.e. reset to zero or nullptr for primitive types).
+ * @return true if an entry for aKey was found (and removed)
+ */
+ bool Remove(KeyType aKey, DataType* aData = nullptr) {
+ if (auto* ent = this->GetEntry(aKey)) {
+ if (aData) {
+ *aData = std::move(ent->mData);
+ }
+ this->RemoveEntry(ent);
+ return true;
+ }
+ if (aData) {
+ *aData = std::move(DataType());
+ }
+ return false;
+ }
+
+ struct LookupResult {
+ private:
+ EntryType* mEntry;
+ nsBaseHashtable& mTable;
+#ifdef DEBUG
+ uint32_t mTableGeneration;
+#endif
+
+ public:
+ LookupResult(EntryType* aEntry, nsBaseHashtable& aTable)
+ : mEntry(aEntry),
+ mTable(aTable)
+#ifdef DEBUG
+ ,
+ mTableGeneration(aTable.GetGeneration())
+#endif
+ {
+ }
+
+ // Is there something stored in the table?
+ explicit operator bool() const {
+ MOZ_ASSERT(mTableGeneration == mTable.GetGeneration());
+ return mEntry;
+ }
+
+ void Remove() {
+ if (!*this) {
+ return;
+ }
+ mTable.RemoveEntry(mEntry);
+ mEntry = nullptr;
+ }
+
+ [[nodiscard]] DataType& Data() {
+ MOZ_ASSERT(!!*this, "must have an entry to access its value");
+ return mEntry->mData;
+ }
+ };
+
+ /**
+ * Removes all entries matching a predicate.
+ *
+ * The predicate must be compatible with signature bool (const Iterator &).
+ */
+ template <typename Pred>
+ void RemoveIf(Pred&& aPred) {
+ for (auto iter = Iter(); !iter.Done(); iter.Next()) {
+ if (aPred(const_cast<std::add_const_t<decltype(iter)>&>(iter))) {
+ iter.Remove();
+ }
+ }
+ }
+
+ /**
+ * Looks up aKey in the hashtable and returns an object that allows you to
+ * read/modify the value of the entry, or remove the entry (if found).
+ *
+ * A typical usage of this API looks like this:
+ *
+ * if (auto entry = hashtable.Lookup(key)) {
+ * DoSomething(entry.Data());
+ * if (entry.Data() > 42) {
+ * entry.Remove();
+ * }
+ * } // else - an entry with the given key doesn't exist
+ *
+ * This is useful for cases where you want to read/write the value of an entry
+ * and (optionally) remove the entry without having to do multiple hashtable
+ * lookups. If you want to insert a new entry if one does not exist, then use
+ * LookupForAdd instead, see below.
+ */
+ [[nodiscard]] LookupResult Lookup(KeyType aKey) {
+ return LookupResult(this->GetEntry(aKey), *this);
+ }
+
+ struct EntryPtr {
+ private:
+ EntryType* mEntry;
+ bool mExistingEntry;
+ nsBaseHashtable& mTable;
+ // For debugging purposes
+#ifdef DEBUG
+ uint32_t mTableGeneration;
+ bool mDidInitNewEntry;
+#endif
+
+ public:
+ EntryPtr(nsBaseHashtable& aTable, EntryType* aEntry, bool aExistingEntry)
+ : mEntry(aEntry),
+ mExistingEntry(aExistingEntry),
+ mTable(aTable)
+#ifdef DEBUG
+ ,
+ mTableGeneration(aTable.GetGeneration()),
+ mDidInitNewEntry(false)
+#endif
+ {
+ }
+ ~EntryPtr() {
+ MOZ_ASSERT(mExistingEntry || mDidInitNewEntry || !mEntry,
+ "Forgot to call OrInsert() or OrRemove() on a new entry");
+ }
+
+ // Is there something stored in the table already?
+ explicit operator bool() const {
+ MOZ_ASSERT(mTableGeneration == mTable.GetGeneration());
+ return mExistingEntry;
+ }
+
+ template <class F>
+ DataType& OrInsert(F func) {
+ MOZ_ASSERT(mTableGeneration == mTable.GetGeneration());
+ MOZ_ASSERT(mEntry);
+ if (!mExistingEntry) {
+ mEntry->mData = Converter::Wrap(func());
+#ifdef DEBUG
+ mDidInitNewEntry = true;
+#endif
+ }
+ return mEntry->mData;
+ }
+
+ void OrRemove() {
+ MOZ_ASSERT(mTableGeneration == mTable.GetGeneration());
+ MOZ_ASSERT(mEntry);
+ mTable.RemoveEntry(mEntry);
+ mEntry = nullptr;
+ }
+
+ [[nodiscard]] DataType& Data() {
+ MOZ_ASSERT(mTableGeneration == mTable.GetGeneration());
+ MOZ_ASSERT(mEntry);
+ return mEntry->mData;
+ }
+ };
+
+ /**
+ * Looks up aKey in the hashtable and returns an object that allows you to
+ * insert a new entry into the hashtable for that key if an existing entry
+ * isn't found for it.
+ *
+ * A typical usage of this API looks like this:
+ *
+ * auto insertedValue = table.LookupForAdd(key).OrInsert([]() {
+ * return newValue;
+ * });
+ *
+ * auto p = table.LookupForAdd(key);
+ * if (p) {
+ * // The entry already existed in the table.
+ * DoSomething(p.Data());
+ * } else {
+ * // An existing entry wasn't found, store a new entry in the hashtable.
+ * p.OrInsert([]() { return newValue; });
+ * }
+ *
+ * We ensure that the hashtable isn't modified before EntryPtr method calls.
+ * This is useful for cases where you want to insert a new entry into the
+ * hashtable if one doesn't exist before but would like to avoid two hashtable
+ * lookups.
+ */
+ [[nodiscard]] EntryPtr LookupForAdd(KeyType aKey) {
+ auto count = Count();
+ EntryType* ent = this->PutEntry(aKey);
+ return EntryPtr(*this, ent, count == Count());
+ }
+
+ // This is an iterator that also allows entry removal. Example usage:
+ //
+ // for (auto iter = table.Iter(); !iter.Done(); iter.Next()) {
+ // const KeyType key = iter.Key();
+ // const UserDataType data = iter.UserData();
+ // // or
+ // const DataType& data = iter.Data();
+ // // ... do stuff with |key| and/or |data| ...
+ // // ... possibly call iter.Remove() once ...
+ // }
+ //
+ class Iterator : public PLDHashTable::Iterator {
+ public:
+ typedef PLDHashTable::Iterator Base;
+
+ explicit Iterator(nsBaseHashtable* aTable) : Base(&aTable->mTable) {}
+ Iterator(Iterator&& aOther) : Base(aOther.mTable) {}
+ ~Iterator() = default;
+
+ KeyType Key() const { return static_cast<EntryType*>(Get())->GetKey(); }
+ UserDataType UserData() const {
+ return Converter::Unwrap(static_cast<EntryType*>(Get())->mData);
+ }
+ DataType& Data() const { return static_cast<EntryType*>(Get())->mData; }
+
+ private:
+ Iterator() = delete;
+ Iterator(const Iterator&) = delete;
+ Iterator& operator=(const Iterator&) = delete;
+ Iterator& operator=(const Iterator&&) = delete;
+ };
+
+ Iterator Iter() { return Iterator(this); }
+
+ Iterator ConstIter() const {
+ return Iterator(const_cast<nsBaseHashtable*>(this));
+ }
+
+ using typename nsTHashtable<EntryType>::iterator;
+ using typename nsTHashtable<EntryType>::const_iterator;
+
+ using nsTHashtable<EntryType>::begin;
+ using nsTHashtable<EntryType>::end;
+ using nsTHashtable<EntryType>::cbegin;
+ using nsTHashtable<EntryType>::cend;
+
+ /**
+ * reset the hashtable, removing all entries
+ */
+ void Clear() { nsTHashtable<EntryType>::Clear(); }
+
+ /**
+ * Measure the size of the table's entry storage. The size of things pointed
+ * to by entries must be measured separately; hence the "Shallow" prefix.
+ *
+ * @param aMallocSizeOf the function used to measure heap-allocated blocks
+ * @return the summed size of the table's storage
+ */
+ size_t ShallowSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
+ return this->mTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ /**
+ * Like ShallowSizeOfExcludingThis, but includes sizeof(*this).
+ */
+ size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
+ return aMallocSizeOf(this) + ShallowSizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ /**
+ * Swap the elements in this hashtable with the elements in aOther.
+ */
+ void SwapElements(nsBaseHashtable& aOther) {
+ nsTHashtable<EntryType>::SwapElements(aOther);
+ }
+
+ using nsTHashtable<EntryType>::MarkImmutable;
+};
+
+//
+// nsBaseHashtableET definitions
+//
+
+template <class KeyClass, class DataType>
+nsBaseHashtableET<KeyClass, DataType>::nsBaseHashtableET(KeyTypePointer aKey)
+ : KeyClass(aKey), mData() {}
+
+template <class KeyClass, class DataType>
+nsBaseHashtableET<KeyClass, DataType>::nsBaseHashtableET(
+ nsBaseHashtableET<KeyClass, DataType>&& aToMove)
+ : KeyClass(std::move(aToMove)), mData(std::move(aToMove.mData)) {}
+
+#endif // nsBaseHashtable_h__