Bug 1909613 - Enable <details name=''> everywhere, r=emilio
[gecko.git] / xpcom / ds / nsBaseHashtable.h
blob7b57ef46660f1cc017dfeaa79169eeb2d37ff1be
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef nsBaseHashtable_h__
8 #define nsBaseHashtable_h__
10 #include <functional>
11 #include <utility>
13 #include "mozilla/dom/SafeRefPtr.h"
14 #include "mozilla/Maybe.h"
15 #include "mozilla/MemoryReporting.h"
16 #include "mozilla/RefPtr.h"
17 #include "mozilla/Result.h"
18 #include "mozilla/UniquePtr.h"
19 #include "nsCOMPtr.h"
20 #include "nsDebug.h"
21 #include "nsHashtablesFwd.h"
22 #include "nsTHashtable.h"
24 namespace mozilla::detail {
26 template <typename SmartPtr>
27 struct SmartPtrTraits {
28 static constexpr bool IsSmartPointer = false;
29 static constexpr bool IsRefCounted = false;
32 template <typename Pointee>
33 struct SmartPtrTraits<UniquePtr<Pointee>> {
34 static constexpr bool IsSmartPointer = true;
35 static constexpr bool IsRefCounted = false;
36 using SmartPointerType = UniquePtr<Pointee>;
37 using PointeeType = Pointee;
38 using RawPointerType = Pointee*;
39 template <typename U>
40 using OtherSmartPtrType = UniquePtr<U>;
42 template <typename U, typename... Args>
43 static SmartPointerType NewObject(Args&&... aConstructionArgs) {
44 return mozilla::MakeUnique<U>(std::forward<Args>(aConstructionArgs)...);
48 template <typename Pointee>
49 struct SmartPtrTraits<RefPtr<Pointee>> {
50 static constexpr bool IsSmartPointer = true;
51 static constexpr bool IsRefCounted = true;
52 using SmartPointerType = RefPtr<Pointee>;
53 using PointeeType = Pointee;
54 using RawPointerType = Pointee*;
55 template <typename U>
56 using OtherSmartPtrType = RefPtr<U>;
58 template <typename U, typename... Args>
59 static SmartPointerType NewObject(Args&&... aConstructionArgs) {
60 return MakeRefPtr<U>(std::forward<Args>(aConstructionArgs)...);
64 template <typename Pointee>
65 struct SmartPtrTraits<SafeRefPtr<Pointee>> {
66 static constexpr bool IsSmartPointer = true;
67 static constexpr bool IsRefCounted = true;
68 using SmartPointerType = SafeRefPtr<Pointee>;
69 using PointeeType = Pointee;
70 using RawPointerType = Pointee*;
71 template <typename U>
72 using OtherSmartPtrType = SafeRefPtr<U>;
74 template <typename U, typename... Args>
75 static SmartPointerType NewObject(Args&&... aConstructionArgs) {
76 return MakeSafeRefPtr<U>(std::forward<Args>(aConstructionArgs)...);
80 template <typename Pointee>
81 struct SmartPtrTraits<nsCOMPtr<Pointee>> {
82 static constexpr bool IsSmartPointer = true;
83 static constexpr bool IsRefCounted = true;
84 using SmartPointerType = nsCOMPtr<Pointee>;
85 using PointeeType = Pointee;
86 using RawPointerType = Pointee*;
87 template <typename U>
88 using OtherSmartPtrType = nsCOMPtr<U>;
90 template <typename U, typename... Args>
91 static SmartPointerType NewObject(Args&&... aConstructionArgs) {
92 return MakeRefPtr<U>(std::forward<Args>(aConstructionArgs)...);
96 template <class T>
97 T* PtrGetWeak(T* aPtr) {
98 return aPtr;
101 template <class T>
102 T* PtrGetWeak(const RefPtr<T>& aPtr) {
103 return aPtr.get();
106 template <class T>
107 T* PtrGetWeak(const SafeRefPtr<T>& aPtr) {
108 return aPtr.unsafeGetRawPtr();
111 template <class T>
112 T* PtrGetWeak(const nsCOMPtr<T>& aPtr) {
113 return aPtr.get();
116 template <class T>
117 T* PtrGetWeak(const UniquePtr<T>& aPtr) {
118 return aPtr.get();
121 template <typename EntryType>
122 class nsBaseHashtableValueIterator : public ::detail::nsTHashtableIteratorBase {
123 // friend class nsTHashtable<EntryType>;
125 public:
126 using iterator_category = std::forward_iterator_tag;
127 using value_type = const std::decay_t<typename EntryType::DataType>;
128 using difference_type = int32_t;
129 using pointer = value_type*;
130 using reference = value_type&;
132 using iterator_type = nsBaseHashtableValueIterator;
133 using const_iterator_type = nsBaseHashtableValueIterator;
135 using nsTHashtableIteratorBase::nsTHashtableIteratorBase;
137 value_type* operator->() const {
138 return &static_cast<const EntryType*>(mIterator.Get())->GetData();
140 decltype(auto) operator*() const {
141 return static_cast<const EntryType*>(mIterator.Get())->GetData();
144 iterator_type& operator++() {
145 mIterator.Next();
146 return *this;
148 iterator_type operator++(int) {
149 iterator_type it = *this;
150 ++*this;
151 return it;
155 template <typename EntryType>
156 class nsBaseHashtableValueRange {
157 public:
158 using IteratorType = nsBaseHashtableValueIterator<EntryType>;
159 using iterator = IteratorType;
161 explicit nsBaseHashtableValueRange(const PLDHashTable& aHashtable)
162 : mHashtable{aHashtable} {}
164 auto begin() const { return IteratorType{mHashtable}; }
165 auto end() const {
166 return IteratorType{mHashtable, typename IteratorType::EndIteratorTag{}};
168 auto cbegin() const { return begin(); }
169 auto cend() const { return end(); }
171 uint32_t Count() const { return mHashtable.EntryCount(); }
173 private:
174 const PLDHashTable& mHashtable;
177 template <typename EntryType>
178 auto RangeSize(const detail::nsBaseHashtableValueRange<EntryType>& aRange) {
179 return aRange.Count();
182 } // namespace mozilla::detail
185 * Data type conversion helper that is used to wrap and unwrap the specified
186 * DataType.
188 template <class DataType, class UserDataType>
189 class nsDefaultConverter {
190 public:
192 * Maps the storage DataType to the exposed UserDataType.
194 static UserDataType Unwrap(DataType& src) { return UserDataType(src); }
197 * Const ref variant used for example with nsCOMPtr wrappers.
199 static DataType Wrap(const UserDataType& src) { return DataType(src); }
202 * Generic conversion, this is useful for things like already_AddRefed.
204 template <typename U>
205 static DataType Wrap(U&& src) {
206 return std::forward<U>(src);
209 template <typename U>
210 static UserDataType Unwrap(U&& src) {
211 return std::forward<U>(src);
216 * the private nsTHashtable::EntryType class used by nsBaseHashtable
217 * @see nsTHashtable for the specification of this class
218 * @see nsBaseHashtable for template parameters
220 template <class KeyClass, class TDataType>
221 class nsBaseHashtableET : public KeyClass {
222 public:
223 using DataType = TDataType;
225 const DataType& GetData() const { return mData; }
226 DataType* GetModifiableData() { return &mData; }
227 template <typename U>
228 void SetData(U&& aData) {
229 mData = std::forward<U>(aData);
232 decltype(auto) GetWeak() const {
233 return mozilla::detail::PtrGetWeak(GetData());
236 private:
237 DataType mData;
238 friend class nsTHashtable<nsBaseHashtableET<KeyClass, DataType>>;
239 template <typename KeyClassX, typename DataTypeX, typename UserDataTypeX,
240 typename ConverterX>
241 friend class nsBaseHashtable;
242 friend class ::detail::nsTHashtableKeyIterator<
243 nsBaseHashtableET<KeyClass, DataType>>;
245 typedef typename KeyClass::KeyType KeyType;
246 typedef typename KeyClass::KeyTypePointer KeyTypePointer;
248 template <typename... Args>
249 explicit nsBaseHashtableET(KeyTypePointer aKey, Args&&... aArgs);
250 nsBaseHashtableET(nsBaseHashtableET<KeyClass, DataType>&& aToMove) = default;
251 ~nsBaseHashtableET() = default;
255 * Templated hashtable. Usually, this isn't instantiated directly but through
256 * its sub-class templates nsInterfaceHashtable, nsClassHashtable,
257 * nsRefPtrHashtable and nsTHashMap.
259 * Originally, UserDataType used to be the only type exposed to the user in the
260 * public member function signatures (hence its name), but this has proven to
261 * inadequate over time. Now, UserDataType is only exposed in by-value
262 * getter member functions that are called *Get*. Member functions that provide
263 * access to the DataType are called Lookup rather than Get. Note that this rule
264 * does not apply to nsRefPtrHashtable and nsInterfaceHashtable, as they are
265 * provide a similar interface, but are no genuine sub-classes of
266 * nsBaseHashtable.
268 * @param KeyClass a wrapper-class for the hashtable key, see nsHashKeys.h
269 * for a complete specification.
270 * @param DataType the datatype stored in the hashtable,
271 * for example, uint32_t or nsCOMPtr.
272 * @param UserDataType the datatype returned from the by-value getter member
273 * functions (named *Get*), for example uint32_t or nsISupports*
274 * @param Converter that is used to map from DataType to UserDataType. A
275 * default converter is provided that assumes implicit conversion is an
276 * option.
278 template <class KeyClass, class DataType, class UserDataType, class Converter>
279 class nsBaseHashtable
280 : protected nsTHashtable<nsBaseHashtableET<KeyClass, DataType>> {
281 using Base = nsTHashtable<nsBaseHashtableET<KeyClass, DataType>>;
282 typedef mozilla::fallible_t fallible_t;
284 public:
285 typedef typename KeyClass::KeyType KeyType;
286 typedef nsBaseHashtableET<KeyClass, DataType> EntryType;
288 using nsTHashtable<EntryType>::Contains;
289 using nsTHashtable<EntryType>::GetGeneration;
290 using nsTHashtable<EntryType>::SizeOfExcludingThis;
291 using nsTHashtable<EntryType>::SizeOfIncludingThis;
293 nsBaseHashtable() = default;
294 explicit nsBaseHashtable(uint32_t aInitLength)
295 : nsTHashtable<EntryType>(aInitLength) {}
298 * Return the number of entries in the table.
299 * @return number of entries
301 [[nodiscard]] uint32_t Count() const {
302 return nsTHashtable<EntryType>::Count();
306 * Return whether the table is empty.
307 * @return whether empty
309 [[nodiscard]] bool IsEmpty() const {
310 return nsTHashtable<EntryType>::IsEmpty();
314 * Get the value, returning a flag indicating the presence of the entry in
315 * the table.
317 * @param aKey the key to retrieve
318 * @param aData data associated with this key will be placed at this pointer.
319 * If you only need to check if the key exists, aData may be null.
320 * @return true if the key exists. If key does not exist, aData is not
321 * modified.
323 * @attention As opposed to Remove, this does not assign a value to *aData if
324 * no entry is present! (And also as opposed to the member function Get with
325 * the same signature that nsClassHashtable defines and hides this one.)
327 [[nodiscard]] bool Get(KeyType aKey, UserDataType* aData) const {
328 EntryType* ent = this->GetEntry(aKey);
329 if (!ent) {
330 return false;
333 if (aData) {
334 *aData = Converter::Unwrap(ent->mData);
337 return true;
341 * Get the value, returning a zero-initialized POD or a default-initialized
342 * object if the entry is not present in the table.
344 * This overload can only be used if UserDataType is default-constructible.
345 * Use the double-argument Get or MaybeGet with non-default-constructible
346 * UserDataType.
348 * @param aKey the key to retrieve
349 * @return The found value, or UserDataType{} if no entry was found with the
350 * given key.
351 * @note If zero/default-initialized values are stored in the table, it is
352 * not possible to distinguish between such a value and a missing entry.
354 [[nodiscard]] UserDataType Get(KeyType aKey) const {
355 EntryType* ent = this->GetEntry(aKey);
356 if (!ent) {
357 return UserDataType{};
360 return Converter::Unwrap(ent->mData);
364 * Get the value, returning Nothing if the entry is not present in the table.
366 * @param aKey the key to retrieve
367 * @return The found value wrapped in a Maybe, or Nothing if no entry was
368 * found with the given key.
370 [[nodiscard]] mozilla::Maybe<UserDataType> MaybeGet(KeyType aKey) const {
371 EntryType* ent = this->GetEntry(aKey);
372 if (!ent) {
373 return mozilla::Nothing();
376 return mozilla::Some(Converter::Unwrap(ent->mData));
379 using SmartPtrTraits = mozilla::detail::SmartPtrTraits<DataType>;
382 * Looks up aKey in the hash table. If it doesn't exist a new object of
383 * SmartPtrTraits::PointeeType will be created (using the arguments provided)
384 * and then returned.
386 * \note This can only be instantiated if DataType is a smart pointer.
388 template <typename... Args>
389 auto GetOrInsertNew(KeyType aKey, Args&&... aConstructionArgs) {
390 static_assert(
391 SmartPtrTraits::IsSmartPointer,
392 "GetOrInsertNew can only be used with smart pointer data types");
393 return mozilla::detail::PtrGetWeak(LookupOrInsertWith(std::move(aKey), [&] {
394 return SmartPtrTraits::template NewObject<
395 typename SmartPtrTraits::PointeeType>(
396 std::forward<Args>(aConstructionArgs)...);
397 }));
401 * Add aKey to the table if not already present, and return a reference to its
402 * value. If aKey is not already in the table then the a default-constructed
403 * or the provided value aData is used.
405 * If the arguments are non-trivial to provide, consider using
406 * LookupOrInsertWith instead.
408 template <typename... Args>
409 DataType& LookupOrInsert(const KeyType& aKey, Args&&... aArgs) {
410 return WithEntryHandle(aKey, [&](auto entryHandle) -> DataType& {
411 return entryHandle.OrInsert(std::forward<Args>(aArgs)...);
416 * Add aKey to the table if not already present, and return a reference to its
417 * value. If aKey is not already in the table then the value is
418 * constructed using the given factory.
420 template <typename F>
421 DataType& LookupOrInsertWith(const KeyType& aKey, F&& aFunc) {
422 return WithEntryHandle(aKey, [&aFunc](auto entryHandle) -> DataType& {
423 return entryHandle.OrInsertWith(std::forward<F>(aFunc));
428 * Add aKey to the table if not already present, and return a reference to its
429 * value. If aKey is not already in the table then the value is
430 * constructed using the given factory.
432 template <typename F>
433 [[nodiscard]] auto TryLookupOrInsertWith(const KeyType& aKey, F&& aFunc) {
434 return WithEntryHandle(
435 aKey,
436 [&aFunc](auto entryHandle)
437 -> mozilla::Result<std::reference_wrapper<DataType>,
438 typename std::invoke_result_t<F>::err_type> {
439 if (entryHandle) {
440 return std::ref(entryHandle.Data());
443 // XXX Use MOZ_TRY after generalizing QM_TRY to mfbt.
444 auto res = std::forward<F>(aFunc)();
445 if (res.isErr()) {
446 return res.propagateErr();
448 return std::ref(entryHandle.Insert(res.unwrap()));
453 * If it does not yet, inserts a new entry with the handle's key and the
454 * value passed to this function. Otherwise, it updates the entry by the
455 * value passed to this function.
457 * \tparam U DataType must be implicitly convertible (and assignable) from U
458 * \post HasEntry()
459 * \param aKey the key to put
460 * \param aData the new data
462 template <typename U>
463 DataType& InsertOrUpdate(KeyType aKey, U&& aData) {
464 return WithEntryHandle(aKey, [&aData](auto entryHandle) -> DataType& {
465 return entryHandle.InsertOrUpdate(std::forward<U>(aData));
469 template <typename U>
470 [[nodiscard]] bool InsertOrUpdate(KeyType aKey, U&& aData,
471 const fallible_t& aFallible) {
472 return WithEntryHandle(aKey, aFallible, [&aData](auto maybeEntryHandle) {
473 if (!maybeEntryHandle) {
474 return false;
476 maybeEntryHandle->InsertOrUpdate(std::forward<U>(aData));
477 return true;
482 * Remove the entry associated with aKey (if any), _moving_ its current value
483 * into *aData. Return true if found.
485 * This overload can only be used if DataType is default-constructible. Use
486 * the single-argument Remove or Extract with non-default-constructible
487 * DataType.
489 * @param aKey the key to remove from the hashtable
490 * @param aData where to move the value. If an entry is not found, *aData
491 * will be assigned a default-constructed value (i.e. reset to
492 * zero or nullptr for primitive types).
493 * @return true if an entry for aKey was found (and removed)
495 // XXX This should also better be marked nodiscard, but due to
496 // nsClassHashtable not guaranteeing non-nullness of entries, it is usually
497 // only checked if aData is nullptr in such cases.
498 // [[nodiscard]]
499 bool Remove(KeyType aKey, DataType* aData) {
500 if (auto* ent = this->GetEntry(aKey)) {
501 if (aData) {
502 *aData = std::move(ent->mData);
504 this->RemoveEntry(ent);
505 return true;
507 if (aData) {
508 *aData = std::move(DataType());
510 return false;
514 * Remove the entry associated with aKey (if any). Return true if found.
516 * @param aKey the key to remove from the hashtable
517 * @return true if an entry for aKey was found (and removed)
519 bool Remove(KeyType aKey) {
520 if (auto* ent = this->GetEntry(aKey)) {
521 this->RemoveEntry(ent);
522 return true;
525 return false;
529 * Retrieve the value for a key and remove the corresponding entry at
530 * the same time.
532 * @param aKey the key to retrieve and remove
533 * @return the found value, or Nothing if no entry was found with the
534 * given key.
536 [[nodiscard]] mozilla::Maybe<DataType> Extract(KeyType aKey) {
537 mozilla::Maybe<DataType> value;
538 if (EntryType* ent = this->GetEntry(aKey)) {
539 value.emplace(std::move(ent->mData));
540 this->RemoveEntry(ent);
542 return value;
545 template <typename HashtableRef>
546 struct LookupResult {
547 private:
548 EntryType* mEntry;
549 HashtableRef mTable;
550 #ifdef DEBUG
551 uint32_t mTableGeneration;
552 #endif
554 public:
555 LookupResult(EntryType* aEntry, HashtableRef aTable)
556 : mEntry(aEntry),
557 mTable(aTable)
558 #ifdef DEBUG
560 mTableGeneration(aTable.GetGeneration())
561 #endif
565 // Is there something stored in the table?
566 explicit operator bool() const {
567 MOZ_ASSERT(mTableGeneration == mTable.GetGeneration());
568 return mEntry;
571 void Remove() {
572 if (!*this) {
573 return;
575 mTable.RemoveEntry(mEntry);
576 mEntry = nullptr;
579 [[nodiscard]] DataType& Data() {
580 MOZ_ASSERT(!!*this, "must have an entry to access its value");
581 return mEntry->mData;
584 [[nodiscard]] const DataType& Data() const {
585 MOZ_ASSERT(!!*this, "must have an entry to access its value");
586 return mEntry->mData;
589 [[nodiscard]] DataType* DataPtrOrNull() {
590 return static_cast<bool>(*this) ? &mEntry->mData : nullptr;
593 [[nodiscard]] const DataType* DataPtrOrNull() const {
594 return static_cast<bool>(*this) ? &mEntry->mData : nullptr;
597 [[nodiscard]] DataType* operator->() { return &Data(); }
598 [[nodiscard]] const DataType* operator->() const { return &Data(); }
600 [[nodiscard]] DataType& operator*() { return Data(); }
601 [[nodiscard]] const DataType& operator*() const { return Data(); }
605 * Removes all entries matching a predicate.
607 * The predicate must be compatible with signature bool (const Iterator &).
609 template <typename Pred>
610 void RemoveIf(Pred&& aPred) {
611 for (auto iter = Iter(); !iter.Done(); iter.Next()) {
612 if (aPred(const_cast<std::add_const_t<decltype(iter)>&>(iter))) {
613 iter.Remove();
619 * Looks up aKey in the hashtable and returns an object that allows you to
620 * read/modify the value of the entry, or remove the entry (if found).
622 * A typical usage of this API looks like this:
624 * if (auto entry = hashtable.Lookup(key)) {
625 * DoSomething(entry.Data());
626 * if (entry.Data() > 42) {
627 * entry.Remove();
629 * } // else - an entry with the given key doesn't exist
631 * This is useful for cases where you want to read/write the value of an entry
632 * and (optionally) remove the entry without having to do multiple hashtable
633 * lookups. If you want to insert a new entry if one does not exist, then use
634 * WithEntryHandle instead, see below.
636 [[nodiscard]] auto Lookup(KeyType aKey) {
637 return LookupResult<nsBaseHashtable&>(this->GetEntry(aKey), *this);
640 [[nodiscard]] auto Lookup(KeyType aKey) const {
641 return LookupResult<const nsBaseHashtable&>(this->GetEntry(aKey), *this);
645 * Used by WithEntryHandle as the argument type to its functor. It is
646 * associated with the Key passed to WithEntryHandle and manages only the
647 * potential entry with that key. Note that in case no modifying operations
648 * are called on the handle, the state of the hashtable remains unchanged,
649 * i.e. WithEntryHandle does not modify the hashtable itself.
651 * Provides query functions (Key, HasEntry/operator bool, Data) and
652 * modifying operations for inserting new entries (Insert), updating existing
653 * entries (Update) and removing existing entries (Remove). They have
654 * debug-only assertion that fail when the state of the entry doesn't match
655 * the expectation. There are variants prefixed with "Or" (OrInsert, OrUpdate,
656 * OrRemove) that are a no-op in case the entry does already exist resp. does
657 * not exist. There are also variants OrInsertWith and OrUpdateWith that don't
658 * accept a value, but a functor, which is only called if the operation takes
659 * place, which should be used if the provision of the value is not trivial
660 * (e.g. allocates a heap object). Finally, there's InsertOrUpdate that
661 * handles both existing and non-existing entries.
663 * Note that all functions of EntryHandle only deal with DataType, not with
664 * UserDataType.
666 class EntryHandle : protected nsTHashtable<EntryType>::EntryHandle {
667 public:
668 using Base = typename nsTHashtable<EntryType>::EntryHandle;
670 EntryHandle(EntryHandle&& aOther) = default;
671 ~EntryHandle() = default;
673 EntryHandle(const EntryHandle&) = delete;
674 EntryHandle& operator=(const EntryHandle&) = delete;
675 EntryHandle& operator=(const EntryHandle&&) = delete;
677 using Base::Key;
679 using Base::HasEntry;
681 using Base::operator bool;
683 using Base::Entry;
686 * Inserts a new entry with the handle's key and the value passed to this
687 * function.
689 * \tparam Args DataType must be constructible from Args
690 * \pre !HasEntry()
691 * \post HasEntry()
693 template <typename... Args>
694 DataType& Insert(Args&&... aArgs) {
695 Base::InsertInternal(std::forward<Args>(aArgs)...);
696 return Data();
700 * If it doesn't yet exist, inserts a new entry with the handle's key and
701 * the value passed to this function. The value is not consumed if no insert
702 * takes place.
704 * \tparam Args DataType must be constructible from Args
705 * \post HasEntry()
707 template <typename... Args>
708 DataType& OrInsert(Args&&... aArgs) {
709 if (!HasEntry()) {
710 return Insert(std::forward<Args>(aArgs)...);
712 return Data();
716 * If it doesn't yet exist, inserts a new entry with the handle's key and
717 * the result of the functor passed to this function. The functor is not
718 * called if no insert takes place.
720 * \tparam F must return a value that is implicitly convertible to DataType
721 * \post HasEntry()
723 template <typename F>
724 DataType& OrInsertWith(F&& aFunc) {
725 if (!HasEntry()) {
726 return Insert(std::forward<F>(aFunc)());
728 return Data();
732 * Updates the entry with the handle's key by the value passed to this
733 * function.
735 * \tparam U DataType must be assignable from U
736 * \pre HasEntry()
738 template <typename U>
739 DataType& Update(U&& aData) {
740 MOZ_RELEASE_ASSERT(HasEntry());
741 Data() = std::forward<U>(aData);
742 return Data();
746 * If an entry with the handle's key already exists, updates its value by
747 * the value passed to this function. The value is not consumed if no update
748 * takes place.
750 * \tparam U DataType must be assignable from U
752 template <typename U>
753 void OrUpdate(U&& aData) {
754 if (HasEntry()) {
755 Update(std::forward<U>(aData));
760 * If an entry with the handle's key already exists, updates its value by
761 * the the result of the functor passed to this function. The functor is not
762 * called if no update takes place.
764 * \tparam F must return a value that DataType is assignable from
766 template <typename F>
767 void OrUpdateWith(F&& aFunc) {
768 if (HasEntry()) {
769 Update(std::forward<F>(aFunc)());
774 * If it does not yet, inserts a new entry with the handle's key and the
775 * value passed to this function. Otherwise, it updates the entry by the
776 * value passed to this function.
778 * \tparam U DataType must be implicitly convertible (and assignable) from U
779 * \post HasEntry()
781 template <typename U>
782 DataType& InsertOrUpdate(U&& aData) {
783 if (!HasEntry()) {
784 Insert(std::forward<U>(aData));
785 } else {
786 Update(std::forward<U>(aData));
788 return Data();
791 using Base::Remove;
793 using Base::OrRemove;
796 * Returns a reference to the value of the entry.
798 * \pre HasEntry()
800 [[nodiscard]] DataType& Data() { return Entry()->mData; }
802 [[nodiscard]] DataType* DataPtrOrNull() {
803 return static_cast<bool>(*this) ? &Data() : nullptr;
806 [[nodiscard]] DataType* operator->() { return &Data(); }
808 [[nodiscard]] DataType& operator*() { return Data(); }
810 private:
811 friend class nsBaseHashtable;
813 explicit EntryHandle(Base&& aBase) : Base(std::move(aBase)) {}
817 * Performs a scoped operation on the entry for aKey, which may or may not
818 * exist when the function is called. It calls aFunc with an EntryHandle. The
819 * result of aFunc is returned as the result of this function. Its return type
820 * may be void. See the documentation of EntryHandle for the query and
821 * modifying operations it offers.
823 * A simple use of this function is, e.g.,
825 * hashtable.WithEntryHandle(key, [](auto&& entry) { entry.OrInsert(42); });
827 * \attention It is not safe to perform modifying operations on the hashtable
828 * other than through the EntryHandle within aFunc, and trying to do so will
829 * trigger debug assertions, and result in undefined behaviour otherwise.
831 template <class F>
832 [[nodiscard]] auto WithEntryHandle(KeyType aKey, F&& aFunc)
833 -> std::invoke_result_t<F, EntryHandle&&> {
834 return Base::WithEntryHandle(
835 aKey, [&aFunc](auto entryHandle) -> decltype(auto) {
836 return std::forward<F>(aFunc)(EntryHandle{std::move(entryHandle)});
841 * Fallible variant of WithEntryHandle, with the following differences:
842 * - The functor aFunc must accept a Maybe<EntryHandle> (instead of an
843 * EntryHandle).
844 * - In case allocation of the slot for the entry fails, Nothing is passed to
845 * the functor.
847 * For more details, see the explanation on the non-fallible overload above.
849 template <class F>
850 [[nodiscard]] auto WithEntryHandle(KeyType aKey, const fallible_t& aFallible,
851 F&& aFunc)
852 -> std::invoke_result_t<F, mozilla::Maybe<EntryHandle>&&> {
853 return Base::WithEntryHandle(
854 aKey, aFallible, [&aFunc](auto maybeEntryHandle) {
855 return std::forward<F>(aFunc)(
856 maybeEntryHandle
857 ? mozilla::Some(EntryHandle{maybeEntryHandle.extract()})
858 : mozilla::Nothing());
862 public:
863 class ConstIterator {
864 public:
865 explicit ConstIterator(nsBaseHashtable* aTable)
866 : mBaseIterator(&aTable->mTable) {}
867 ~ConstIterator() = default;
869 KeyType Key() const {
870 return static_cast<EntryType*>(mBaseIterator.Get())->GetKey();
872 UserDataType UserData() const {
873 return Converter::Unwrap(
874 static_cast<EntryType*>(mBaseIterator.Get())->mData);
876 const DataType& Data() const {
877 return static_cast<EntryType*>(mBaseIterator.Get())->mData;
880 bool Done() const { return mBaseIterator.Done(); }
881 void Next() { mBaseIterator.Next(); }
883 ConstIterator() = delete;
884 ConstIterator(const ConstIterator&) = delete;
885 ConstIterator(ConstIterator&& aOther) = delete;
886 ConstIterator& operator=(const ConstIterator&) = delete;
887 ConstIterator& operator=(ConstIterator&&) = delete;
889 protected:
890 PLDHashTable::Iterator mBaseIterator;
893 // This is an iterator that also allows entry removal. Example usage:
895 // for (auto iter = table.Iter(); !iter.Done(); iter.Next()) {
896 // const KeyType key = iter.Key();
897 // const UserDataType data = iter.UserData();
898 // // or
899 // const DataType& data = iter.Data();
900 // // ... do stuff with |key| and/or |data| ...
901 // // ... possibly call iter.Remove() once ...
902 // }
904 class Iterator final : public ConstIterator {
905 public:
906 using ConstIterator::ConstIterator;
908 using ConstIterator::Data;
909 DataType& Data() {
910 return static_cast<EntryType*>(this->mBaseIterator.Get())->mData;
913 void Remove() { this->mBaseIterator.Remove(); }
916 Iterator Iter() { return Iterator(this); }
918 ConstIterator ConstIter() const {
919 return ConstIterator(const_cast<nsBaseHashtable*>(this));
922 using nsTHashtable<EntryType>::Remove;
925 * Remove the entry associated with aIter.
927 * @param aIter the iterator pointing to the entry
928 * @pre !aIter.Done()
930 void Remove(ConstIterator& aIter) { aIter.mBaseIterator.Remove(); }
932 using typename nsTHashtable<EntryType>::iterator;
933 using typename nsTHashtable<EntryType>::const_iterator;
935 using nsTHashtable<EntryType>::begin;
936 using nsTHashtable<EntryType>::end;
937 using nsTHashtable<EntryType>::cbegin;
938 using nsTHashtable<EntryType>::cend;
940 using nsTHashtable<EntryType>::Keys;
943 * Return a range of the values (of DataType). Note this range iterates over
944 * the values in place, so modifications to the nsTHashtable invalidate the
945 * range while it's iterated, except when calling Remove() with a value
946 * iterator derived from that range.
948 auto Values() const {
949 return mozilla::detail::nsBaseHashtableValueRange<EntryType>{this->mTable};
953 * Remove an entry from a value range, specified via a value iterator, e.g.
955 * for (auto it = hash.Values().begin(), end = hash.Values().end();
956 * it != end; * ++it) {
957 * if (*it > 42) { hash.Remove(it); }
960 * You might also consider using RemoveIf though.
962 void Remove(mozilla::detail::nsBaseHashtableValueIterator<EntryType>& aIter) {
963 aIter.mIterator.Remove();
967 * reset the hashtable, removing all entries
969 void Clear() { nsTHashtable<EntryType>::Clear(); }
972 * Measure the size of the table's entry storage. The size of things pointed
973 * to by entries must be measured separately; hence the "Shallow" prefix.
975 * @param aMallocSizeOf the function used to measure heap-allocated blocks
976 * @return the summed size of the table's storage
978 size_t ShallowSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
979 return this->mTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
983 * Like ShallowSizeOfExcludingThis, but includes sizeof(*this).
985 size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
986 return aMallocSizeOf(this) + ShallowSizeOfExcludingThis(aMallocSizeOf);
990 * Swap the elements in this hashtable with the elements in aOther.
992 void SwapElements(nsBaseHashtable& aOther) {
993 nsTHashtable<EntryType>::SwapElements(aOther);
996 using nsTHashtable<EntryType>::MarkImmutable;
999 * Makes a clone of this hashtable by copying all entries. This requires
1000 * KeyType and DataType to be copy-constructible.
1002 nsBaseHashtable Clone() const { return CloneAs<nsBaseHashtable>(); }
1004 protected:
1005 template <typename T>
1006 T CloneAs() const {
1007 static_assert(std::is_base_of_v<nsBaseHashtable, T>);
1008 // XXX This can probably be optimized, see Bug 1694368.
1009 T result(Count());
1010 for (const auto& srcEntry : *this) {
1011 result.WithEntryHandle(srcEntry.GetKey(), [&](auto&& dstEntry) {
1012 dstEntry.Insert(srcEntry.GetData());
1015 return result;
1020 // nsBaseHashtableET definitions
1023 template <class KeyClass, class DataType>
1024 template <typename... Args>
1025 nsBaseHashtableET<KeyClass, DataType>::nsBaseHashtableET(KeyTypePointer aKey,
1026 Args&&... aArgs)
1027 : KeyClass(aKey), mData(std::forward<Args>(aArgs)...) {}
1029 #endif // nsBaseHashtable_h__