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__
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"
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
*;
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
*;
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
*;
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
*;
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
)...);
97 T
* PtrGetWeak(T
* aPtr
) {
102 T
* PtrGetWeak(const RefPtr
<T
>& aPtr
) {
107 T
* PtrGetWeak(const SafeRefPtr
<T
>& aPtr
) {
108 return aPtr
.unsafeGetRawPtr();
112 T
* PtrGetWeak(const nsCOMPtr
<T
>& aPtr
) {
117 T
* PtrGetWeak(const UniquePtr
<T
>& aPtr
) {
121 template <typename EntryType
>
122 class nsBaseHashtableValueIterator
: public ::detail::nsTHashtableIteratorBase
{
123 // friend class nsTHashtable<EntryType>;
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++() {
148 iterator_type
operator++(int) {
149 iterator_type it
= *this;
155 template <typename EntryType
>
156 class nsBaseHashtableValueRange
{
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
}; }
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(); }
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
188 template <class DataType
, class UserDataType
>
189 class nsDefaultConverter
{
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
{
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());
238 friend class nsTHashtable
<nsBaseHashtableET
<KeyClass
, DataType
>>;
239 template <typename KeyClassX
, typename DataTypeX
, typename UserDataTypeX
,
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
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
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
;
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
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
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
);
334 *aData
= Converter::Unwrap(ent
->mData
);
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
348 * @param aKey the key to retrieve
349 * @return The found value, or UserDataType{} if no entry was found with the
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
);
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
);
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)
386 * \note This can only be instantiated if DataType is a smart pointer.
388 template <typename
... Args
>
389 auto GetOrInsertNew(KeyType aKey
, Args
&&... aConstructionArgs
) {
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
)...);
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(
436 [&aFunc
](auto entryHandle
)
437 -> mozilla::Result
<std::reference_wrapper
<DataType
>,
438 typename
std::invoke_result_t
<F
>::err_type
> {
440 return std::ref(entryHandle
.Data());
443 // XXX Use MOZ_TRY after generalizing QM_TRY to mfbt.
444 auto res
= std::forward
<F
>(aFunc
)();
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
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
) {
476 maybeEntryHandle
->InsertOrUpdate(std::forward
<U
>(aData
));
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
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.
499 bool Remove(KeyType aKey
, DataType
* aData
) {
500 if (auto* ent
= this->GetEntry(aKey
)) {
502 *aData
= std::move(ent
->mData
);
504 this->RemoveEntry(ent
);
508 *aData
= std::move(DataType());
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
);
529 * Retrieve the value for a key and remove the corresponding entry at
532 * @param aKey the key to retrieve and remove
533 * @return the found value, or Nothing if no entry was found with the
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
);
545 template <typename HashtableRef
>
546 struct LookupResult
{
551 uint32_t mTableGeneration
;
555 LookupResult(EntryType
* aEntry
, HashtableRef aTable
)
560 mTableGeneration(aTable
.GetGeneration())
565 // Is there something stored in the table?
566 explicit operator bool() const {
567 MOZ_ASSERT(mTableGeneration
== mTable
.GetGeneration());
575 mTable
.RemoveEntry(mEntry
);
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
))) {
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) {
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
666 class EntryHandle
: protected nsTHashtable
<EntryType
>::EntryHandle
{
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;
679 using Base::HasEntry
;
681 using Base::operator bool;
686 * Inserts a new entry with the handle's key and the value passed to this
689 * \tparam Args DataType must be constructible from Args
693 template <typename
... Args
>
694 DataType
& Insert(Args
&&... aArgs
) {
695 Base::InsertInternal(std::forward
<Args
>(aArgs
)...);
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
704 * \tparam Args DataType must be constructible from Args
707 template <typename
... Args
>
708 DataType
& OrInsert(Args
&&... aArgs
) {
710 return Insert(std::forward
<Args
>(aArgs
)...);
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
723 template <typename F
>
724 DataType
& OrInsertWith(F
&& aFunc
) {
726 return Insert(std::forward
<F
>(aFunc
)());
732 * Updates the entry with the handle's key by the value passed to this
735 * \tparam U DataType must be assignable from U
738 template <typename U
>
739 DataType
& Update(U
&& aData
) {
740 MOZ_RELEASE_ASSERT(HasEntry());
741 Data() = std::forward
<U
>(aData
);
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
750 * \tparam U DataType must be assignable from U
752 template <typename U
>
753 void OrUpdate(U
&& aData
) {
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
) {
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
781 template <typename U
>
782 DataType
& InsertOrUpdate(U
&& aData
) {
784 Insert(std::forward
<U
>(aData
));
786 Update(std::forward
<U
>(aData
));
793 using Base::OrRemove
;
796 * Returns a reference to the value of the entry.
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(); }
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.
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
844 * - In case allocation of the slot for the entry fails, Nothing is passed to
847 * For more details, see the explanation on the non-fallible overload above.
850 [[nodiscard
]] auto WithEntryHandle(KeyType aKey
, const fallible_t
& aFallible
,
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
)(
857 ? mozilla::Some(EntryHandle
{maybeEntryHandle
.extract()})
858 : mozilla::Nothing());
863 class ConstIterator
{
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;
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();
899 // const DataType& data = iter.Data();
900 // // ... do stuff with |key| and/or |data| ...
901 // // ... possibly call iter.Remove() once ...
904 class Iterator final
: public ConstIterator
{
906 using ConstIterator::ConstIterator
;
908 using ConstIterator::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
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
>(); }
1005 template <typename T
>
1007 static_assert(std::is_base_of_v
<nsBaseHashtable
, T
>);
1008 // XXX This can probably be optimized, see Bug 1694368.
1010 for (const auto& srcEntry
: *this) {
1011 result
.WithEntryHandle(srcEntry
.GetKey(), [&](auto&& dstEntry
) {
1012 dstEntry
.Insert(srcEntry
.GetData());
1020 // nsBaseHashtableET definitions
1023 template <class KeyClass
, class DataType
>
1024 template <typename
... Args
>
1025 nsBaseHashtableET
<KeyClass
, DataType
>::nsBaseHashtableET(KeyTypePointer aKey
,
1027 : KeyClass(aKey
), mData(std::forward
<Args
>(aArgs
)...) {}
1029 #endif // nsBaseHashtable_h__