/* -*- 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 #include #include "mozilla/dom/SafeRefPtr.h" #include "mozilla/Maybe.h" #include "mozilla/MemoryReporting.h" #include "mozilla/RefPtr.h" #include "mozilla/Result.h" #include "mozilla/UniquePtr.h" #include "nsCOMPtr.h" #include "nsDebug.h" #include "nsHashtablesFwd.h" #include "nsTHashtable.h" namespace mozilla::detail { template struct SmartPtrTraits { static constexpr bool IsSmartPointer = false; static constexpr bool IsRefCounted = false; }; template struct SmartPtrTraits> { static constexpr bool IsSmartPointer = true; static constexpr bool IsRefCounted = false; using SmartPointerType = UniquePtr; using PointeeType = Pointee; using RawPointerType = Pointee*; template using OtherSmartPtrType = UniquePtr; template static SmartPointerType NewObject(Args&&... aConstructionArgs) { return mozilla::MakeUnique(std::forward(aConstructionArgs)...); } }; template struct SmartPtrTraits> { static constexpr bool IsSmartPointer = true; static constexpr bool IsRefCounted = true; using SmartPointerType = RefPtr; using PointeeType = Pointee; using RawPointerType = Pointee*; template using OtherSmartPtrType = RefPtr; template static SmartPointerType NewObject(Args&&... aConstructionArgs) { return MakeRefPtr(std::forward(aConstructionArgs)...); } }; template struct SmartPtrTraits> { static constexpr bool IsSmartPointer = true; static constexpr bool IsRefCounted = true; using SmartPointerType = SafeRefPtr; using PointeeType = Pointee; using RawPointerType = Pointee*; template using OtherSmartPtrType = SafeRefPtr; template static SmartPointerType NewObject(Args&&... aConstructionArgs) { return MakeSafeRefPtr(std::forward(aConstructionArgs)...); } }; template struct SmartPtrTraits> { static constexpr bool IsSmartPointer = true; static constexpr bool IsRefCounted = true; using SmartPointerType = nsCOMPtr; using PointeeType = Pointee; using RawPointerType = Pointee*; template using OtherSmartPtrType = nsCOMPtr; template static SmartPointerType NewObject(Args&&... aConstructionArgs) { return MakeRefPtr(std::forward(aConstructionArgs)...); } }; template T* PtrGetWeak(T* aPtr) { return aPtr; } template T* PtrGetWeak(const RefPtr& aPtr) { return aPtr.get(); } template T* PtrGetWeak(const SafeRefPtr& aPtr) { return aPtr.unsafeGetRawPtr(); } template T* PtrGetWeak(const nsCOMPtr& aPtr) { return aPtr.get(); } template T* PtrGetWeak(const UniquePtr& aPtr) { return aPtr.get(); } template class nsBaseHashtableValueIterator : public ::detail::nsTHashtableIteratorBase { // friend class nsTHashtable; public: using iterator_category = std::forward_iterator_tag; using value_type = const std::decay_t; using difference_type = int32_t; using pointer = value_type*; using reference = value_type&; using iterator_type = nsBaseHashtableValueIterator; using const_iterator_type = nsBaseHashtableValueIterator; using nsTHashtableIteratorBase::nsTHashtableIteratorBase; value_type* operator->() const { return &static_cast(mIterator.Get())->GetData(); } decltype(auto) operator*() const { return static_cast(mIterator.Get())->GetData(); } iterator_type& operator++() { mIterator.Next(); return *this; } iterator_type operator++(int) { iterator_type it = *this; ++*this; return it; } }; template class nsBaseHashtableValueRange { public: using IteratorType = nsBaseHashtableValueIterator; using iterator = IteratorType; explicit nsBaseHashtableValueRange(const PLDHashTable& aHashtable) : mHashtable{aHashtable} {} auto begin() const { return IteratorType{mHashtable}; } auto end() const { return IteratorType{mHashtable, typename IteratorType::EndIteratorTag{}}; } auto cbegin() const { return begin(); } auto cend() const { return end(); } uint32_t Count() const { return mHashtable.EntryCount(); } private: const PLDHashTable& mHashtable; }; template auto RangeSize(const detail::nsBaseHashtableValueRange& aRange) { return aRange.Count(); } } // namespace mozilla::detail /** * Data type conversion helper that is used to wrap and unwrap the specified * DataType. */ template 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 static DataType Wrap(U&& src) { return std::forward(src); } template static UserDataType Unwrap(U&& src) { return std::forward(src); } }; /** * the private nsTHashtable::EntryType class used by nsBaseHashtable * @see nsTHashtable for the specification of this class * @see nsBaseHashtable for template parameters */ template class nsBaseHashtableET : public KeyClass { public: using DataType = TDataType; const DataType& GetData() const { return mData; } DataType* GetModifiableData() { return &mData; } template void SetData(U&& aData) { mData = std::forward(aData); } decltype(auto) GetWeak() const { return mozilla::detail::PtrGetWeak(GetData()); } private: DataType mData; friend class nsTHashtable>; template friend class nsBaseHashtable; friend class ::detail::nsTHashtableKeyIterator< nsBaseHashtableET>; typedef typename KeyClass::KeyType KeyType; typedef typename KeyClass::KeyTypePointer KeyTypePointer; template explicit nsBaseHashtableET(KeyTypePointer aKey, Args&&... aArgs); nsBaseHashtableET(nsBaseHashtableET&& aToMove) = default; ~nsBaseHashtableET() = default; }; /** * Templated hashtable. Usually, this isn't instantiated directly but through * its sub-class templates nsInterfaceHashtable, nsClassHashtable, * nsRefPtrHashtable and nsTHashMap. * * Originally, UserDataType used to be the only type exposed to the user in the * public member function signatures (hence its name), but this has proven to * inadequate over time. Now, UserDataType is only exposed in by-value * getter member functions that are called *Get*. Member functions that provide * access to the DataType are called Lookup rather than Get. Note that this rule * does not apply to nsRefPtrHashtable and nsInterfaceHashtable, as they are * provide a similar interface, but are no genuine sub-classes of * nsBaseHashtable. * * @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 datatype returned from the by-value getter member * functions (named *Get*), for example uint32_t or nsISupports* * @param Converter that is used to map from DataType to UserDataType. A * default converter is provided that assumes implicit conversion is an * option. */ template class nsBaseHashtable : protected nsTHashtable> { using Base = nsTHashtable>; typedef mozilla::fallible_t fallible_t; public: typedef typename KeyClass::KeyType KeyType; typedef nsBaseHashtableET EntryType; using nsTHashtable::Contains; using nsTHashtable::GetGeneration; using nsTHashtable::SizeOfExcludingThis; using nsTHashtable::SizeOfIncludingThis; nsBaseHashtable() = default; explicit nsBaseHashtable(uint32_t aInitLength) : nsTHashtable(aInitLength) {} /** * Return the number of entries in the table. * @return number of entries */ [[nodiscard]] uint32_t Count() const { return nsTHashtable::Count(); } /** * Return whether the table is empty. * @return whether empty */ [[nodiscard]] bool IsEmpty() const { return nsTHashtable::IsEmpty(); } /** * Get the value, returning a flag indicating the presence of the entry in * the table. * * @param aKey the key to retrieve * @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. * * @attention As opposed to Remove, this does not assign a value to *aData if * no entry is present! (And also as opposed to the member function Get with * the same signature that nsClassHashtable defines and hides this one.) */ [[nodiscard]] 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. * * This overload can only be used if UserDataType is default-constructible. * Use the double-argument Get or MaybeGet with non-default-constructible * UserDataType. * * @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. */ [[nodiscard]] UserDataType Get(KeyType aKey) const { EntryType* ent = this->GetEntry(aKey); if (!ent) { return UserDataType{}; } return Converter::Unwrap(ent->mData); } /** * Get the value, returning Nothing if the entry is not present in the table. * * @param aKey the key to retrieve * @return The found value wrapped in a Maybe, or Nothing if no entry was * found with the given key. */ [[nodiscard]] mozilla::Maybe MaybeGet(KeyType aKey) const { EntryType* ent = this->GetEntry(aKey); if (!ent) { return mozilla::Nothing(); } return mozilla::Some(Converter::Unwrap(ent->mData)); } using SmartPtrTraits = mozilla::detail::SmartPtrTraits; /** * Looks up aKey in the hash table. If it doesn't exist a new object of * SmartPtrTraits::PointeeType will be created (using the arguments provided) * and then returned. * * \note This can only be instantiated if DataType is a smart pointer. */ template auto GetOrInsertNew(KeyType aKey, Args&&... aConstructionArgs) { static_assert( SmartPtrTraits::IsSmartPointer, "GetOrInsertNew can only be used with smart pointer data types"); return mozilla::detail::PtrGetWeak(LookupOrInsertWith(std::move(aKey), [&] { return SmartPtrTraits::template NewObject< typename SmartPtrTraits::PointeeType>( std::forward(aConstructionArgs)...); })); } /** * Add aKey to the table if not already present, and return a reference to its * value. If aKey is not already in the table then the a default-constructed * or the provided value aData is used. * * If the arguments are non-trivial to provide, consider using * LookupOrInsertWith instead. */ template DataType& LookupOrInsert(const KeyType& aKey, Args&&... aArgs) { return WithEntryHandle(aKey, [&](auto entryHandle) -> DataType& { return entryHandle.OrInsert(std::forward(aArgs)...); }); } /** * Add aKey to the table if not already present, and return a reference to its * value. If aKey is not already in the table then the value is * constructed using the given factory. */ template DataType& LookupOrInsertWith(const KeyType& aKey, F&& aFunc) { return WithEntryHandle(aKey, [&aFunc](auto entryHandle) -> DataType& { return entryHandle.OrInsertWith(std::forward(aFunc)); }); } /** * Add aKey to the table if not already present, and return a reference to its * value. If aKey is not already in the table then the value is * constructed using the given factory. */ template [[nodiscard]] auto TryLookupOrInsertWith(const KeyType& aKey, F&& aFunc) { return WithEntryHandle( aKey, [&aFunc](auto entryHandle) -> mozilla::Result, typename std::invoke_result_t::err_type> { if (entryHandle) { return std::ref(entryHandle.Data()); } // XXX Use MOZ_TRY after generalizing QM_TRY to mfbt. auto res = std::forward(aFunc)(); if (res.isErr()) { return res.propagateErr(); } return std::ref(entryHandle.Insert(res.unwrap())); }); } /** * If it does not yet, inserts a new entry with the handle's key and the * value passed to this function. Otherwise, it updates the entry by the * value passed to this function. * * \tparam U DataType must be implicitly convertible (and assignable) from U * \post HasEntry() * \param aKey the key to put * \param aData the new data */ template DataType& InsertOrUpdate(KeyType aKey, U&& aData) { return WithEntryHandle(aKey, [&aData](auto entryHandle) -> DataType& { return entryHandle.InsertOrUpdate(std::forward(aData)); }); } template [[nodiscard]] bool InsertOrUpdate(KeyType aKey, U&& aData, const fallible_t& aFallible) { return WithEntryHandle(aKey, aFallible, [&aData](auto maybeEntryHandle) { if (!maybeEntryHandle) { return false; } maybeEntryHandle->InsertOrUpdate(std::forward(aData)); return true; }); } /** * Remove the entry associated with aKey (if any), _moving_ its current value * into *aData. Return true if found. * * This overload can only be used if DataType is default-constructible. Use * the single-argument Remove or Extract with non-default-constructible * DataType. * * @param aKey the key to remove from the hashtable * @param aData where to move the value. 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) */ // XXX This should also better be marked nodiscard, but due to // nsClassHashtable not guaranteeing non-nullness of entries, it is usually // only checked if aData is nullptr in such cases. // [[nodiscard]] bool Remove(KeyType aKey, DataType* aData) { 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; } /** * Remove the entry associated with aKey (if any). Return true if found. * * @param aKey the key to remove from the hashtable * @return true if an entry for aKey was found (and removed) */ bool Remove(KeyType aKey) { if (auto* ent = this->GetEntry(aKey)) { this->RemoveEntry(ent); return true; } return false; } /** * Retrieve the value for a key and remove the corresponding entry at * the same time. * * @param aKey the key to retrieve and remove * @return the found value, or Nothing if no entry was found with the * given key. */ [[nodiscard]] mozilla::Maybe Extract(KeyType aKey) { mozilla::Maybe value; if (EntryType* ent = this->GetEntry(aKey)) { value.emplace(std::move(ent->mData)); this->RemoveEntry(ent); } return value; } template struct LookupResult { private: EntryType* mEntry; HashtableRef mTable; #ifdef DEBUG uint32_t mTableGeneration; #endif public: LookupResult(EntryType* aEntry, HashtableRef 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; } [[nodiscard]] const DataType& Data() const { MOZ_ASSERT(!!*this, "must have an entry to access its value"); return mEntry->mData; } [[nodiscard]] DataType* DataPtrOrNull() { return static_cast(*this) ? &mEntry->mData : nullptr; } [[nodiscard]] const DataType* DataPtrOrNull() const { return static_cast(*this) ? &mEntry->mData : nullptr; } [[nodiscard]] DataType* operator->() { return &Data(); } [[nodiscard]] const DataType* operator->() const { return &Data(); } [[nodiscard]] DataType& operator*() { return Data(); } [[nodiscard]] const DataType& operator*() const { return Data(); } }; /** * Removes all entries matching a predicate. * * The predicate must be compatible with signature bool (const Iterator &). */ template void RemoveIf(Pred&& aPred) { for (auto iter = Iter(); !iter.Done(); iter.Next()) { if (aPred(const_cast&>(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 * WithEntryHandle instead, see below. */ [[nodiscard]] auto Lookup(KeyType aKey) { return LookupResult(this->GetEntry(aKey), *this); } [[nodiscard]] auto Lookup(KeyType aKey) const { return LookupResult(this->GetEntry(aKey), *this); } /** * Used by WithEntryHandle as the argument type to its functor. It is * associated with the Key passed to WithEntryHandle and manages only the * potential entry with that key. Note that in case no modifying operations * are called on the handle, the state of the hashtable remains unchanged, * i.e. WithEntryHandle does not modify the hashtable itself. * * Provides query functions (Key, HasEntry/operator bool, Data) and * modifying operations for inserting new entries (Insert), updating existing * entries (Update) and removing existing entries (Remove). They have * debug-only assertion that fail when the state of the entry doesn't match * the expectation. There are variants prefixed with "Or" (OrInsert, OrUpdate, * OrRemove) that are a no-op in case the entry does already exist resp. does * not exist. There are also variants OrInsertWith and OrUpdateWith that don't * accept a value, but a functor, which is only called if the operation takes * place, which should be used if the provision of the value is not trivial * (e.g. allocates a heap object). Finally, there's InsertOrUpdate that * handles both existing and non-existing entries. * * Note that all functions of EntryHandle only deal with DataType, not with * UserDataType. */ class EntryHandle : protected nsTHashtable::EntryHandle { public: using Base = typename nsTHashtable::EntryHandle; EntryHandle(EntryHandle&& aOther) = default; ~EntryHandle() = default; EntryHandle(const EntryHandle&) = delete; EntryHandle& operator=(const EntryHandle&) = delete; EntryHandle& operator=(const EntryHandle&&) = delete; using Base::Key; using Base::HasEntry; using Base::operator bool; using Base::Entry; /** * Inserts a new entry with the handle's key and the value passed to this * function. * * \tparam Args DataType must be constructible from Args * \pre !HasEntry() * \post HasEntry() */ template DataType& Insert(Args&&... aArgs) { Base::InsertInternal(std::forward(aArgs)...); return Data(); } /** * If it doesn't yet exist, inserts a new entry with the handle's key and * the value passed to this function. The value is not consumed if no insert * takes place. * * \tparam Args DataType must be constructible from Args * \post HasEntry() */ template DataType& OrInsert(Args&&... aArgs) { if (!HasEntry()) { return Insert(std::forward(aArgs)...); } return Data(); } /** * If it doesn't yet exist, inserts a new entry with the handle's key and * the result of the functor passed to this function. The functor is not * called if no insert takes place. * * \tparam F must return a value that is implicitly convertible to DataType * \post HasEntry() */ template DataType& OrInsertWith(F&& aFunc) { if (!HasEntry()) { return Insert(std::forward(aFunc)()); } return Data(); } /** * Updates the entry with the handle's key by the value passed to this * function. * * \tparam U DataType must be assignable from U * \pre HasEntry() */ template DataType& Update(U&& aData) { MOZ_RELEASE_ASSERT(HasEntry()); Data() = std::forward(aData); return Data(); } /** * If an entry with the handle's key already exists, updates its value by * the value passed to this function. The value is not consumed if no update * takes place. * * \tparam U DataType must be assignable from U */ template void OrUpdate(U&& aData) { if (HasEntry()) { Update(std::forward(aData)); } } /** * If an entry with the handle's key already exists, updates its value by * the the result of the functor passed to this function. The functor is not * called if no update takes place. * * \tparam F must return a value that DataType is assignable from */ template void OrUpdateWith(F&& aFunc) { if (HasEntry()) { Update(std::forward(aFunc)()); } } /** * If it does not yet, inserts a new entry with the handle's key and the * value passed to this function. Otherwise, it updates the entry by the * value passed to this function. * * \tparam U DataType must be implicitly convertible (and assignable) from U * \post HasEntry() */ template DataType& InsertOrUpdate(U&& aData) { if (!HasEntry()) { Insert(std::forward(aData)); } else { Update(std::forward(aData)); } return Data(); } using Base::Remove; using Base::OrRemove; /** * Returns a reference to the value of the entry. * * \pre HasEntry() */ [[nodiscard]] DataType& Data() { return Entry()->mData; } [[nodiscard]] DataType* DataPtrOrNull() { return static_cast(*this) ? &Data() : nullptr; } [[nodiscard]] DataType* operator->() { return &Data(); } [[nodiscard]] DataType& operator*() { return Data(); } private: friend class nsBaseHashtable; explicit EntryHandle(Base&& aBase) : Base(std::move(aBase)) {} }; /** * Performs a scoped operation on the entry for aKey, which may or may not * exist when the function is called. It calls aFunc with an EntryHandle. The * result of aFunc is returned as the result of this function. Its return type * may be void. See the documentation of EntryHandle for the query and * modifying operations it offers. * * A simple use of this function is, e.g., * * hashtable.WithEntryHandle(key, [](auto&& entry) { entry.OrInsert(42); }); * * \attention It is not safe to perform modifying operations on the hashtable * other than through the EntryHandle within aFunc, and trying to do so will * trigger debug assertions, and result in undefined behaviour otherwise. */ template [[nodiscard]] auto WithEntryHandle(KeyType aKey, F&& aFunc) -> std::invoke_result_t { return Base::WithEntryHandle( aKey, [&aFunc](auto entryHandle) -> decltype(auto) { return std::forward(aFunc)(EntryHandle{std::move(entryHandle)}); }); } /** * Fallible variant of WithEntryHandle, with the following differences: * - The functor aFunc must accept a Maybe (instead of an * EntryHandle). * - In case allocation of the slot for the entry fails, Nothing is passed to * the functor. * * For more details, see the explanation on the non-fallible overload above. */ template [[nodiscard]] auto WithEntryHandle(KeyType aKey, const fallible_t& aFallible, F&& aFunc) -> std::invoke_result_t&&> { return Base::WithEntryHandle( aKey, aFallible, [&aFunc](auto maybeEntryHandle) { return std::forward(aFunc)( maybeEntryHandle ? mozilla::Some(EntryHandle{maybeEntryHandle.extract()}) : mozilla::Nothing()); }); } public: class ConstIterator { public: explicit ConstIterator(nsBaseHashtable* aTable) : mBaseIterator(&aTable->mTable) {} ~ConstIterator() = default; KeyType Key() const { return static_cast(mBaseIterator.Get())->GetKey(); } UserDataType UserData() const { return Converter::Unwrap( static_cast(mBaseIterator.Get())->mData); } const DataType& Data() const { return static_cast(mBaseIterator.Get())->mData; } bool Done() const { return mBaseIterator.Done(); } void Next() { mBaseIterator.Next(); } ConstIterator() = delete; ConstIterator(const ConstIterator&) = delete; ConstIterator(ConstIterator&& aOther) = delete; ConstIterator& operator=(const ConstIterator&) = delete; ConstIterator& operator=(ConstIterator&&) = delete; protected: PLDHashTable::Iterator mBaseIterator; }; // 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 final : public ConstIterator { public: using ConstIterator::ConstIterator; using ConstIterator::Data; DataType& Data() { return static_cast(this->mBaseIterator.Get())->mData; } void Remove() { this->mBaseIterator.Remove(); } }; Iterator Iter() { return Iterator(this); } ConstIterator ConstIter() const { return ConstIterator(const_cast(this)); } using nsTHashtable::Remove; /** * Remove the entry associated with aIter. * * @param aIter the iterator pointing to the entry * @pre !aIter.Done() */ void Remove(ConstIterator& aIter) { aIter.mBaseIterator.Remove(); } using typename nsTHashtable::iterator; using typename nsTHashtable::const_iterator; using nsTHashtable::begin; using nsTHashtable::end; using nsTHashtable::cbegin; using nsTHashtable::cend; using nsTHashtable::Keys; /** * Return a range of the values (of DataType). Note this range iterates over * the values in place, so modifications to the nsTHashtable invalidate the * range while it's iterated, except when calling Remove() with a value * iterator derived from that range. */ auto Values() const { return mozilla::detail::nsBaseHashtableValueRange{this->mTable}; } /** * Remove an entry from a value range, specified via a value iterator, e.g. * * for (auto it = hash.Values().begin(), end = hash.Values().end(); * it != end; * ++it) { * if (*it > 42) { hash.Remove(it); } * } * * You might also consider using RemoveIf though. */ void Remove(mozilla::detail::nsBaseHashtableValueIterator& aIter) { aIter.mIterator.Remove(); } /** * reset the hashtable, removing all entries */ void Clear() { nsTHashtable::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::SwapElements(aOther); } using nsTHashtable::MarkImmutable; /** * Makes a clone of this hashtable by copying all entries. This requires * KeyType and DataType to be copy-constructible. */ nsBaseHashtable Clone() const { return CloneAs(); } protected: template T CloneAs() const { static_assert(std::is_base_of_v); // XXX This can probably be optimized, see Bug 1694368. T result(Count()); for (const auto& srcEntry : *this) { result.WithEntryHandle(srcEntry.GetKey(), [&](auto&& dstEntry) { dstEntry.Insert(srcEntry.GetData()); }); } return result; } }; // // nsBaseHashtableET definitions // template template nsBaseHashtableET::nsBaseHashtableET(KeyTypePointer aKey, Args&&... aArgs) : KeyClass(aKey), mData(std::forward(aArgs)...) {} #endif // nsBaseHashtable_h__