forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			1029 lines
		
	
	
	
		
			32 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1029 lines
		
	
	
	
		
			32 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- 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 <functional>
 | |
| #include <utility>
 | |
| 
 | |
| #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 <typename SmartPtr>
 | |
| struct SmartPtrTraits {
 | |
|   static constexpr bool IsSmartPointer = false;
 | |
|   static constexpr bool IsRefCounted = false;
 | |
| };
 | |
| 
 | |
| template <typename Pointee>
 | |
| struct SmartPtrTraits<UniquePtr<Pointee>> {
 | |
|   static constexpr bool IsSmartPointer = true;
 | |
|   static constexpr bool IsRefCounted = false;
 | |
|   using SmartPointerType = UniquePtr<Pointee>;
 | |
|   using PointeeType = Pointee;
 | |
|   using RawPointerType = Pointee*;
 | |
|   template <typename U>
 | |
|   using OtherSmartPtrType = UniquePtr<U>;
 | |
| 
 | |
|   template <typename U, typename... Args>
 | |
|   static SmartPointerType NewObject(Args&&... aConstructionArgs) {
 | |
|     return mozilla::MakeUnique<U>(std::forward<Args>(aConstructionArgs)...);
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <typename Pointee>
 | |
| struct SmartPtrTraits<RefPtr<Pointee>> {
 | |
|   static constexpr bool IsSmartPointer = true;
 | |
|   static constexpr bool IsRefCounted = true;
 | |
|   using SmartPointerType = RefPtr<Pointee>;
 | |
|   using PointeeType = Pointee;
 | |
|   using RawPointerType = Pointee*;
 | |
|   template <typename U>
 | |
|   using OtherSmartPtrType = RefPtr<U>;
 | |
| 
 | |
|   template <typename U, typename... Args>
 | |
|   static SmartPointerType NewObject(Args&&... aConstructionArgs) {
 | |
|     return MakeRefPtr<U>(std::forward<Args>(aConstructionArgs)...);
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <typename Pointee>
 | |
| struct SmartPtrTraits<SafeRefPtr<Pointee>> {
 | |
|   static constexpr bool IsSmartPointer = true;
 | |
|   static constexpr bool IsRefCounted = true;
 | |
|   using SmartPointerType = SafeRefPtr<Pointee>;
 | |
|   using PointeeType = Pointee;
 | |
|   using RawPointerType = Pointee*;
 | |
|   template <typename U>
 | |
|   using OtherSmartPtrType = SafeRefPtr<U>;
 | |
| 
 | |
|   template <typename U, typename... Args>
 | |
|   static SmartPointerType NewObject(Args&&... aConstructionArgs) {
 | |
|     return MakeSafeRefPtr<U>(std::forward<Args>(aConstructionArgs)...);
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <typename Pointee>
 | |
| struct SmartPtrTraits<nsCOMPtr<Pointee>> {
 | |
|   static constexpr bool IsSmartPointer = true;
 | |
|   static constexpr bool IsRefCounted = true;
 | |
|   using SmartPointerType = nsCOMPtr<Pointee>;
 | |
|   using PointeeType = Pointee;
 | |
|   using RawPointerType = Pointee*;
 | |
|   template <typename U>
 | |
|   using OtherSmartPtrType = nsCOMPtr<U>;
 | |
| 
 | |
|   template <typename U, typename... Args>
 | |
|   static SmartPointerType NewObject(Args&&... aConstructionArgs) {
 | |
|     return MakeRefPtr<U>(std::forward<Args>(aConstructionArgs)...);
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <class T>
 | |
| T* PtrGetWeak(T* aPtr) {
 | |
|   return aPtr;
 | |
| }
 | |
| 
 | |
| template <class T>
 | |
| T* PtrGetWeak(const RefPtr<T>& aPtr) {
 | |
|   return aPtr.get();
 | |
| }
 | |
| 
 | |
| template <class T>
 | |
| T* PtrGetWeak(const SafeRefPtr<T>& aPtr) {
 | |
|   return aPtr.unsafeGetRawPtr();
 | |
| }
 | |
| 
 | |
| template <class T>
 | |
| T* PtrGetWeak(const nsCOMPtr<T>& aPtr) {
 | |
|   return aPtr.get();
 | |
| }
 | |
| 
 | |
| template <class T>
 | |
| T* PtrGetWeak(const UniquePtr<T>& aPtr) {
 | |
|   return aPtr.get();
 | |
| }
 | |
| 
 | |
| template <typename EntryType>
 | |
| class nsBaseHashtableValueIterator : public ::detail::nsTHashtableIteratorBase {
 | |
|   // friend class nsTHashtable<EntryType>;
 | |
| 
 | |
|  public:
 | |
|   using iterator_category = std::forward_iterator_tag;
 | |
|   using value_type = const std::decay_t<typename EntryType::DataType>;
 | |
|   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<const EntryType*>(mIterator.Get())->GetData();
 | |
|   }
 | |
|   decltype(auto) operator*() const {
 | |
|     return static_cast<const EntryType*>(mIterator.Get())->GetData();
 | |
|   }
 | |
| 
 | |
|   iterator_type& operator++() {
 | |
|     mIterator.Next();
 | |
|     return *this;
 | |
|   }
 | |
|   iterator_type operator++(int) {
 | |
|     iterator_type it = *this;
 | |
|     ++*this;
 | |
|     return it;
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <typename EntryType>
 | |
| class nsBaseHashtableValueRange {
 | |
|  public:
 | |
|   using IteratorType = nsBaseHashtableValueIterator<EntryType>;
 | |
|   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 <typename EntryType>
 | |
| auto RangeSize(const detail::nsBaseHashtableValueRange<EntryType>& aRange) {
 | |
|   return aRange.Count();
 | |
| }
 | |
| 
 | |
| }  // namespace mozilla::detail
 | |
| 
 | |
| /**
 | |
|  * 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::forward<U>(src);
 | |
|   }
 | |
| 
 | |
|   template <typename U>
 | |
|   static UserDataType Unwrap(U&& src) {
 | |
|     return std::forward<U>(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 TDataType>
 | |
| class nsBaseHashtableET : public KeyClass {
 | |
|  public:
 | |
|   using DataType = TDataType;
 | |
| 
 | |
|   const DataType& GetData() const { return mData; }
 | |
|   DataType* GetModifiableData() { return &mData; }
 | |
|   template <typename U>
 | |
|   void SetData(U&& aData) {
 | |
|     mData = std::forward<U>(aData);
 | |
|   }
 | |
| 
 | |
|   decltype(auto) GetWeak() const {
 | |
|     return mozilla::detail::PtrGetWeak(GetData());
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   DataType mData;
 | |
|   friend class nsTHashtable<nsBaseHashtableET<KeyClass, DataType>>;
 | |
|   template <typename KeyClassX, typename DataTypeX, typename UserDataTypeX,
 | |
|             typename ConverterX>
 | |
|   friend class nsBaseHashtable;
 | |
|   friend class ::detail::nsTHashtableKeyIterator<
 | |
|       nsBaseHashtableET<KeyClass, DataType>>;
 | |
| 
 | |
|   typedef typename KeyClass::KeyType KeyType;
 | |
|   typedef typename KeyClass::KeyTypePointer KeyTypePointer;
 | |
| 
 | |
|   template <typename... Args>
 | |
|   explicit nsBaseHashtableET(KeyTypePointer aKey, Args&&... aArgs);
 | |
|   nsBaseHashtableET(nsBaseHashtableET<KeyClass, DataType>&& 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 KeyClass, class DataType, class UserDataType, class Converter>
 | |
| class nsBaseHashtable
 | |
|     : protected nsTHashtable<nsBaseHashtableET<KeyClass, DataType>> {
 | |
|   using Base = 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
 | |
|    */
 | |
|   [[nodiscard]] uint32_t Count() const {
 | |
|     return nsTHashtable<EntryType>::Count();
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Return whether the table is empty.
 | |
|    * @return    whether empty
 | |
|    */
 | |
|   [[nodiscard]] bool IsEmpty() const {
 | |
|     return nsTHashtable<EntryType>::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<UserDataType> 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<DataType>;
 | |
| 
 | |
|   /**
 | |
|    * 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 <typename... Args>
 | |
|   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<Args>(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 <typename... Args>
 | |
|   DataType& LookupOrInsert(const KeyType& aKey, Args&&... aArgs) {
 | |
|     return WithEntryHandle(aKey, [&](auto entryHandle) -> DataType& {
 | |
|       return entryHandle.OrInsert(std::forward<Args>(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 <typename F>
 | |
|   DataType& LookupOrInsertWith(const KeyType& aKey, F&& aFunc) {
 | |
|     return WithEntryHandle(aKey, [&aFunc](auto entryHandle) -> DataType& {
 | |
|       return entryHandle.OrInsertWith(std::forward<F>(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 <typename F>
 | |
|   [[nodiscard]] auto TryLookupOrInsertWith(const KeyType& aKey, F&& aFunc) {
 | |
|     return WithEntryHandle(
 | |
|         aKey,
 | |
|         [&aFunc](auto entryHandle)
 | |
|             -> mozilla::Result<std::reference_wrapper<DataType>,
 | |
|                                typename std::invoke_result_t<F>::err_type> {
 | |
|           if (entryHandle) {
 | |
|             return std::ref(entryHandle.Data());
 | |
|           }
 | |
| 
 | |
|           // XXX Use MOZ_TRY after generalizing QM_TRY to mfbt.
 | |
|           auto res = std::forward<F>(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 <typename U>
 | |
|   DataType& InsertOrUpdate(KeyType aKey, U&& aData) {
 | |
|     return WithEntryHandle(aKey, [&aData](auto entryHandle) -> DataType& {
 | |
|       return entryHandle.InsertOrUpdate(std::forward<U>(aData));
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   template <typename U>
 | |
|   [[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<U>(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<DataType> Extract(KeyType aKey) {
 | |
|     mozilla::Maybe<DataType> value;
 | |
|     if (EntryType* ent = this->GetEntry(aKey)) {
 | |
|       value.emplace(std::move(ent->mData));
 | |
|       this->RemoveEntry(ent);
 | |
|     }
 | |
|     return value;
 | |
|   }
 | |
| 
 | |
|   template <typename HashtableRef>
 | |
|   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<bool>(*this) ? &mEntry->mData : nullptr;
 | |
|     }
 | |
| 
 | |
|     [[nodiscard]] const DataType* DataPtrOrNull() const {
 | |
|       return static_cast<bool>(*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 <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
 | |
|    * WithEntryHandle instead, see below.
 | |
|    */
 | |
|   [[nodiscard]] auto Lookup(KeyType aKey) {
 | |
|     return LookupResult<nsBaseHashtable&>(this->GetEntry(aKey), *this);
 | |
|   }
 | |
| 
 | |
|   [[nodiscard]] auto Lookup(KeyType aKey) const {
 | |
|     return LookupResult<const nsBaseHashtable&>(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<EntryType>::EntryHandle {
 | |
|    public:
 | |
|     using Base = typename nsTHashtable<EntryType>::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 <typename... Args>
 | |
|     DataType& Insert(Args&&... aArgs) {
 | |
|       Base::InsertInternal(std::forward<Args>(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 <typename... Args>
 | |
|     DataType& OrInsert(Args&&... aArgs) {
 | |
|       if (!HasEntry()) {
 | |
|         return Insert(std::forward<Args>(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 <typename F>
 | |
|     DataType& OrInsertWith(F&& aFunc) {
 | |
|       if (!HasEntry()) {
 | |
|         return Insert(std::forward<F>(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 <typename U>
 | |
|     DataType& Update(U&& aData) {
 | |
|       MOZ_RELEASE_ASSERT(HasEntry());
 | |
|       Data() = std::forward<U>(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 <typename U>
 | |
|     void OrUpdate(U&& aData) {
 | |
|       if (HasEntry()) {
 | |
|         Update(std::forward<U>(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 <typename F>
 | |
|     void OrUpdateWith(F&& aFunc) {
 | |
|       if (HasEntry()) {
 | |
|         Update(std::forward<F>(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 <typename U>
 | |
|     DataType& InsertOrUpdate(U&& aData) {
 | |
|       if (!HasEntry()) {
 | |
|         Insert(std::forward<U>(aData));
 | |
|       } else {
 | |
|         Update(std::forward<U>(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<bool>(*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 <class F>
 | |
|   [[nodiscard]] auto WithEntryHandle(KeyType aKey, F&& aFunc)
 | |
|       -> std::invoke_result_t<F, EntryHandle&&> {
 | |
|     return Base::WithEntryHandle(
 | |
|         aKey, [&aFunc](auto entryHandle) -> decltype(auto) {
 | |
|           return std::forward<F>(aFunc)(EntryHandle{std::move(entryHandle)});
 | |
|         });
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Fallible variant of WithEntryHandle, with the following differences:
 | |
|    * - The functor aFunc must accept a Maybe<EntryHandle> (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 <class F>
 | |
|   [[nodiscard]] auto WithEntryHandle(KeyType aKey, const fallible_t& aFallible,
 | |
|                                      F&& aFunc)
 | |
|       -> std::invoke_result_t<F, mozilla::Maybe<EntryHandle>&&> {
 | |
|     return Base::WithEntryHandle(
 | |
|         aKey, aFallible, [&aFunc](auto maybeEntryHandle) {
 | |
|           return std::forward<F>(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<EntryType*>(mBaseIterator.Get())->GetKey();
 | |
|     }
 | |
|     UserDataType UserData() const {
 | |
|       return Converter::Unwrap(
 | |
|           static_cast<EntryType*>(mBaseIterator.Get())->mData);
 | |
|     }
 | |
|     const DataType& Data() const {
 | |
|       return static_cast<EntryType*>(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<EntryType*>(this->mBaseIterator.Get())->mData;
 | |
|     }
 | |
| 
 | |
|     void Remove() { this->mBaseIterator.Remove(); }
 | |
|   };
 | |
| 
 | |
|   Iterator Iter() { return Iterator(this); }
 | |
| 
 | |
|   ConstIterator ConstIter() const {
 | |
|     return ConstIterator(const_cast<nsBaseHashtable*>(this));
 | |
|   }
 | |
| 
 | |
|   using nsTHashtable<EntryType>::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<EntryType>::iterator;
 | |
|   using typename nsTHashtable<EntryType>::const_iterator;
 | |
| 
 | |
|   using nsTHashtable<EntryType>::begin;
 | |
|   using nsTHashtable<EntryType>::end;
 | |
|   using nsTHashtable<EntryType>::cbegin;
 | |
|   using nsTHashtable<EntryType>::cend;
 | |
| 
 | |
|   using nsTHashtable<EntryType>::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<EntryType>{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<EntryType>& aIter) {
 | |
|     aIter.mIterator.Remove();
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * 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;
 | |
| 
 | |
|   /**
 | |
|    * Makes a clone of this hashtable by copying all entries. This requires
 | |
|    * KeyType and DataType to be copy-constructible.
 | |
|    */
 | |
|   nsBaseHashtable Clone() const { return CloneAs<nsBaseHashtable>(); }
 | |
| 
 | |
|  protected:
 | |
|   template <typename T>
 | |
|   T CloneAs() const {
 | |
|     static_assert(std::is_base_of_v<nsBaseHashtable, T>);
 | |
|     // 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 <class KeyClass, class DataType>
 | |
| template <typename... Args>
 | |
| nsBaseHashtableET<KeyClass, DataType>::nsBaseHashtableET(KeyTypePointer aKey,
 | |
|                                                          Args&&... aArgs)
 | |
|     : KeyClass(aKey), mData(std::forward<Args>(aArgs)...) {}
 | |
| 
 | |
| #endif  // nsBaseHashtable_h__
 | 
