Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-08 09:23:35

0001 // Copyright (C) 2020 The Qt Company Ltd.
0002 // Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
0003 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
0004 // Qt-Security score:significant reason:default
0005 
0006 #ifndef QHASH_H
0007 #define QHASH_H
0008 
0009 #include <QtCore/qalgorithms.h>
0010 #include <QtCore/qcontainertools_impl.h>
0011 #include <QtCore/qhashfunctions.h>
0012 #include <QtCore/qiterator.h>
0013 #include <QtCore/qlist.h>
0014 #include <QtCore/qrefcount.h>
0015 #include <QtCore/qttypetraits.h>
0016 
0017 #include <initializer_list>
0018 #include <functional> // for std::hash
0019 #include <QtCore/q20type_traits.h>
0020 
0021 class tst_QHash; // for befriending
0022 
0023 QT_BEGIN_NAMESPACE
0024 
0025 struct QHashDummyValue
0026 {
0027     explicit QHashDummyValue() = default;
0028     friend constexpr bool operator==(QHashDummyValue, QHashDummyValue) noexcept { return true; }
0029 #ifndef __cpp_impl_three_way_comparison
0030     friend constexpr bool operator!=(QHashDummyValue, QHashDummyValue) noexcept { return false; }
0031 #endif
0032     friend constexpr size_t qHash(QHashDummyValue) noexcept = delete;
0033     friend constexpr size_t qHash(QHashDummyValue, size_t) noexcept = delete;
0034 };
0035 
0036 namespace QHashPrivate {
0037 
0038 template <typename T, typename = void>
0039 constexpr inline bool HasQHashOverload = false;
0040 
0041 template <typename T>
0042 constexpr inline bool HasQHashOverload<T, std::enable_if_t<
0043     std::is_convertible_v<decltype(qHash(std::declval<const T &>(), std::declval<size_t>())), size_t>
0044 >> = true;
0045 
0046 template <typename T, typename = void>
0047 constexpr inline bool HasStdHashSpecializationWithSeed = false;
0048 
0049 template <typename T>
0050 constexpr inline bool HasStdHashSpecializationWithSeed<T, std::enable_if_t<
0051     std::is_convertible_v<decltype(std::hash<T>()(std::declval<const T &>(), std::declval<size_t>())), size_t>
0052 >> = true;
0053 
0054 template <typename T, typename = void>
0055 constexpr inline bool HasStdHashSpecializationWithoutSeed = false;
0056 
0057 template <typename T>
0058 constexpr inline bool HasStdHashSpecializationWithoutSeed<T, std::enable_if_t<
0059     std::is_convertible_v<decltype(std::hash<T>()(std::declval<const T &>())), size_t>
0060 >> = true;
0061 
0062 template <typename T>
0063 size_t calculateHash(const T &t, size_t seed = 0)
0064 {
0065     if constexpr (HasQHashOverload<T>) {
0066         return qHash(t, seed);
0067     } else if constexpr (HasStdHashSpecializationWithSeed<T>) {
0068         return std::hash<T>()(t, seed);
0069     } else if constexpr (HasStdHashSpecializationWithoutSeed<T>) {
0070         Q_UNUSED(seed);
0071         return std::hash<T>()(t);
0072     } else {
0073         static_assert(QtPrivate::type_dependent_false<T>(), "The key type must have a qHash overload or a std::hash specialization");
0074         return 0;
0075     }
0076 }
0077 
0078 template <typename Key, typename T>
0079 struct Node
0080 {
0081     using KeyType = Key;
0082     using ValueType = T;
0083 
0084     Key key;
0085     T value;
0086     template<typename ...Args>
0087     static void createInPlace(Node *n, Key &&k, Args &&... args)
0088     { new (n) Node{ std::move(k), T(std::forward<Args>(args)...) }; }
0089     template<typename ...Args>
0090     static void createInPlace(Node *n, const Key &k, Args &&... args)
0091     { new (n) Node{ Key(k), T(std::forward<Args>(args)...) }; }
0092     template<typename ...Args>
0093     void emplaceValue(Args &&... args)
0094     {
0095         value = T(std::forward<Args>(args)...);
0096     }
0097     T &&takeValue() noexcept
0098     {
0099         return std::move(value);
0100     }
0101     bool valuesEqual(const Node *other) const { return value == other->value; }
0102 };
0103 
0104 template <typename Key>
0105 struct Node<Key, QHashDummyValue> {
0106     using KeyType = Key;
0107     using ValueType = QHashDummyValue;
0108 
0109     Key key;
0110     template<typename ...Args>
0111     static void createInPlace(Node *n, Key &&k, Args &&...)
0112     { new (n) Node{ std::move(k) }; }
0113     template<typename ...Args>
0114     static void createInPlace(Node *n, const Key &k, Args &&...)
0115     { new (n) Node{ k }; }
0116     template<typename ...Args>
0117     void emplaceValue(Args &&...)
0118     {
0119     }
0120     ValueType takeValue() noexcept { return QHashDummyValue(); }
0121     bool valuesEqual(const Node *) const { return true; }
0122 };
0123 
0124 template <typename T>
0125 struct MultiNodeChain
0126 {
0127     T value;
0128     MultiNodeChain *next = nullptr;
0129     ~MultiNodeChain()
0130     {
0131     }
0132     qsizetype free() noexcept(std::is_nothrow_destructible_v<T>)
0133     {
0134         qsizetype nEntries = 0;
0135         MultiNodeChain *e = this;
0136         while (e) {
0137             MultiNodeChain *n = e->next;
0138             ++nEntries;
0139             delete e;
0140             e = n;
0141         }
0142         return  nEntries;
0143     }
0144     bool contains(const T &val) const noexcept
0145     {
0146         const MultiNodeChain *e = this;
0147         while (e) {
0148             if (e->value == val)
0149                 return true;
0150             e = e->next;
0151         }
0152         return false;
0153     }
0154 };
0155 
0156 template <typename Key, typename T>
0157 struct MultiNode
0158 {
0159     using KeyType = Key;
0160     using ValueType = T;
0161     using Chain = MultiNodeChain<T>;
0162 
0163     Key key;
0164     Chain *value;
0165 
0166     template<typename ...Args>
0167     static void createInPlace(MultiNode *n, Key &&k, Args &&... args)
0168     { new (n) MultiNode(std::move(k), new Chain{ T(std::forward<Args>(args)...), nullptr }); }
0169     template<typename ...Args>
0170     static void createInPlace(MultiNode *n, const Key &k, Args &&... args)
0171     { new (n) MultiNode(k, new Chain{ T(std::forward<Args>(args)...), nullptr }); }
0172 
0173     MultiNode(const Key &k, Chain *c)
0174         : key(k),
0175           value(c)
0176     {}
0177     MultiNode(Key &&k, Chain *c) noexcept(std::is_nothrow_move_assignable_v<Key>)
0178         : key(std::move(k)),
0179           value(c)
0180     {}
0181 
0182     MultiNode(MultiNode &&other)
0183         : key(std::move(other.key)),
0184           value(std::exchange(other.value, nullptr))
0185     {
0186     }
0187 
0188     MultiNode(const MultiNode &other)
0189         : key(other.key)
0190     {
0191         Chain *c = other.value;
0192         Chain **e = &value;
0193         while (c) {
0194             Chain *chain = new Chain{ c->value, nullptr };
0195             *e = chain;
0196             e = &chain->next;
0197             c = c->next;
0198         }
0199     }
0200     ~MultiNode()
0201     {
0202         if (value)
0203             value->free();
0204     }
0205     static qsizetype freeChain(MultiNode *n) noexcept(std::is_nothrow_destructible_v<T>)
0206     {
0207         qsizetype size = n->value->free();
0208         n->value = nullptr;
0209         return size;
0210     }
0211     template<typename ...Args>
0212     void insertMulti(Args &&... args)
0213     {
0214         Chain *e = new Chain{ T(std::forward<Args>(args)...), nullptr };
0215         e->next = std::exchange(value, e);
0216     }
0217     template<typename ...Args>
0218     void emplaceValue(Args &&... args)
0219     {
0220         value->value = T(std::forward<Args>(args)...);
0221     }
0222 };
0223 
0224 template<typename  Node>
0225 inline constexpr bool isRelocatable_v =
0226         QTypeInfo<typename Node::KeyType>::isRelocatable &&
0227         QTypeInfo<typename Node::ValueType>::isRelocatable;
0228 
0229 struct SpanConstants {
0230     static constexpr size_t SpanShift = 7;
0231     static constexpr size_t NEntries = (1 << SpanShift);
0232     static constexpr size_t LocalBucketMask = (NEntries - 1);
0233     static constexpr size_t UnusedEntry = 0xff;
0234 
0235     static_assert ((NEntries & LocalBucketMask) == 0, "NEntries must be a power of two.");
0236 };
0237 
0238 // Regular hash tables consist of a list of buckets that can store Nodes. But simply allocating one large array of buckets
0239 // would waste a lot of memory. To avoid this, we split the vector of buckets up into a vector of Spans. Each Span represents
0240 // NEntries buckets. To quickly find the correct Span that holds a bucket, NEntries must be a power of two.
0241 //
0242 // Inside each Span, there is an offset array that represents the actual buckets. offsets contains either an index into the
0243 // actual storage space for the Nodes (the 'entries' member) or 0xff (UnusedEntry) to flag that the bucket is empty.
0244 // As we have only 128 entries per Span, the offset array can be represented using an unsigned char. This trick makes the hash
0245 // table have a very small memory overhead compared to many other implementations.
0246 template<typename Node>
0247 struct Span {
0248     // Entry is a slot available for storing a Node. The Span holds a pointer to
0249     // an array of Entries. Upon construction of the array, those entries are
0250     // unused, and nextFree() is being used to set up a singly linked list
0251     // of free entries.
0252     // When a node gets inserted, the first free entry is being picked, removed
0253     // from the singly linked list and the Node gets constructed in place.
0254     struct Entry {
0255         struct { alignas(Node) unsigned char data[sizeof(Node)]; } storage;
0256 
0257         unsigned char &nextFree() { return *reinterpret_cast<unsigned char *>(&storage); }
0258         Node &node() { return *reinterpret_cast<Node *>(&storage); }
0259     };
0260 
0261     unsigned char offsets[SpanConstants::NEntries];
0262     Entry *entries = nullptr;
0263     unsigned char allocated = 0;
0264     unsigned char nextFree = 0;
0265     Span() noexcept
0266     {
0267         memset(offsets, SpanConstants::UnusedEntry, sizeof(offsets));
0268     }
0269     ~Span()
0270     {
0271         freeData();
0272     }
0273     void freeData() noexcept(std::is_nothrow_destructible<Node>::value)
0274     {
0275         if (entries) {
0276             if constexpr (!std::is_trivially_destructible<Node>::value) {
0277                 for (auto o : offsets) {
0278                     if (o != SpanConstants::UnusedEntry)
0279                         entries[o].node().~Node();
0280                 }
0281             }
0282             delete[] entries;
0283             entries = nullptr;
0284         }
0285     }
0286     Node *insert(size_t i)
0287     {
0288         Q_ASSERT(i < SpanConstants::NEntries);
0289         Q_ASSERT(offsets[i] == SpanConstants::UnusedEntry);
0290         if (nextFree == allocated)
0291             addStorage();
0292         unsigned char entry = nextFree;
0293         Q_ASSERT(entry < allocated);
0294         nextFree = entries[entry].nextFree();
0295         offsets[i] = entry;
0296         return &entries[entry].node();
0297     }
0298     void erase(size_t bucket) noexcept(std::is_nothrow_destructible<Node>::value)
0299     {
0300         Q_ASSERT(bucket < SpanConstants::NEntries);
0301         Q_ASSERT(offsets[bucket] != SpanConstants::UnusedEntry);
0302 
0303         unsigned char entry = offsets[bucket];
0304         offsets[bucket] = SpanConstants::UnusedEntry;
0305 
0306         entries[entry].node().~Node();
0307         entries[entry].nextFree() = nextFree;
0308         nextFree = entry;
0309     }
0310     size_t offset(size_t i) const noexcept
0311     {
0312         return offsets[i];
0313     }
0314     bool hasNode(size_t i) const noexcept
0315     {
0316         return (offsets[i] != SpanConstants::UnusedEntry);
0317     }
0318     Node &at(size_t i) noexcept
0319     {
0320         Q_ASSERT(i < SpanConstants::NEntries);
0321         Q_ASSERT(offsets[i] != SpanConstants::UnusedEntry);
0322 
0323         return entries[offsets[i]].node();
0324     }
0325     const Node &at(size_t i) const noexcept
0326     {
0327         Q_ASSERT(i < SpanConstants::NEntries);
0328         Q_ASSERT(offsets[i] != SpanConstants::UnusedEntry);
0329 
0330         return entries[offsets[i]].node();
0331     }
0332     Node &atOffset(size_t o) noexcept
0333     {
0334         Q_ASSERT(o < allocated);
0335 
0336         return entries[o].node();
0337     }
0338     const Node &atOffset(size_t o) const noexcept
0339     {
0340         Q_ASSERT(o < allocated);
0341 
0342         return entries[o].node();
0343     }
0344     void moveLocal(size_t from, size_t to) noexcept
0345     {
0346         Q_ASSERT(offsets[from] != SpanConstants::UnusedEntry);
0347         Q_ASSERT(offsets[to] == SpanConstants::UnusedEntry);
0348         offsets[to] = offsets[from];
0349         offsets[from] = SpanConstants::UnusedEntry;
0350     }
0351     void moveFromSpan(Span &fromSpan, size_t fromIndex, size_t to) noexcept(std::is_nothrow_move_constructible_v<Node>)
0352     {
0353         Q_ASSERT(to < SpanConstants::NEntries);
0354         Q_ASSERT(offsets[to] == SpanConstants::UnusedEntry);
0355         Q_ASSERT(fromIndex < SpanConstants::NEntries);
0356         Q_ASSERT(fromSpan.offsets[fromIndex] != SpanConstants::UnusedEntry);
0357         if (nextFree == allocated)
0358             addStorage();
0359         Q_ASSERT(nextFree < allocated);
0360         offsets[to] = nextFree;
0361         Entry &toEntry = entries[nextFree];
0362         nextFree = toEntry.nextFree();
0363 
0364         size_t fromOffset = fromSpan.offsets[fromIndex];
0365         fromSpan.offsets[fromIndex] = SpanConstants::UnusedEntry;
0366         Entry &fromEntry = fromSpan.entries[fromOffset];
0367 
0368         if constexpr (isRelocatable_v<Node>) {
0369             memcpy(&toEntry, &fromEntry, sizeof(Entry));
0370         } else {
0371             new (&toEntry.node()) Node(std::move(fromEntry.node()));
0372             fromEntry.node().~Node();
0373         }
0374         fromEntry.nextFree() = fromSpan.nextFree;
0375         fromSpan.nextFree = static_cast<unsigned char>(fromOffset);
0376     }
0377 
0378     void addStorage()
0379     {
0380         Q_ASSERT(allocated < SpanConstants::NEntries);
0381         Q_ASSERT(nextFree == allocated);
0382         // the hash table should always be between 25 and 50% full
0383         // this implies that we on average have between 32 and 64 entries
0384         // in here. More exactly, we have a binominal distribution of the amount of
0385         // occupied entries.
0386         // For a 25% filled table, the average is 32 entries, with a 95% chance that we have between
0387         // 23 and 41 entries.
0388         // For a 50% filled table, the average is 64 entries, with a 95% chance that we have between
0389         // 53 and 75 entries.
0390         // Since we only resize the table once it's 50% filled and we want to avoid copies of
0391         // data where possible, we initially allocate 48 entries, then resize to 80 entries, after that
0392         // resize by increments of 16. That way, we usually only get one resize of the table
0393         // while filling it.
0394         size_t alloc;
0395         static_assert(SpanConstants::NEntries % 8 == 0);
0396         if (!allocated)
0397             alloc = SpanConstants::NEntries / 8 * 3;
0398         else if (allocated == SpanConstants::NEntries / 8 * 3)
0399             alloc = SpanConstants::NEntries / 8 * 5;
0400         else
0401             alloc = allocated + SpanConstants::NEntries/8;
0402         Entry *newEntries = new Entry[alloc];
0403         // we only add storage if the previous storage was fully filled, so
0404         // simply copy the old data over
0405         if constexpr (isRelocatable_v<Node>) {
0406             if (allocated)
0407                 memcpy(newEntries, entries, allocated * sizeof(Entry));
0408         } else {
0409             for (size_t i = 0; i < allocated; ++i) {
0410                 new (&newEntries[i].node()) Node(std::move(entries[i].node()));
0411                 entries[i].node().~Node();
0412             }
0413         }
0414         for (size_t i = allocated; i < alloc; ++i) {
0415             newEntries[i].nextFree() = uchar(i + 1);
0416         }
0417         delete[] entries;
0418         entries = newEntries;
0419         allocated = uchar(alloc);
0420     }
0421 };
0422 
0423 // QHash uses a power of two growth policy.
0424 namespace GrowthPolicy {
0425 inline constexpr size_t bucketsForCapacity(size_t requestedCapacity) noexcept
0426 {
0427     constexpr int SizeDigits = std::numeric_limits<size_t>::digits;
0428 
0429     // We want to use at minimum a full span (128 entries), so we hardcode it for any requested
0430     // capacity <= 64. Any capacity above that gets rounded to a later power of two.
0431     if (requestedCapacity <= 64)
0432         return SpanConstants::NEntries;
0433 
0434     // Same as
0435     //    qNextPowerOfTwo(2 * requestedCapacity);
0436     //
0437     // but ensuring neither our multiplication nor the function overflow.
0438     // Additionally, the maximum memory allocation is 2^31-1 or 2^63-1 bytes
0439     // (limited by qsizetype and ptrdiff_t).
0440     int count = qCountLeadingZeroBits(requestedCapacity);
0441     if (count < 2)
0442         return (std::numeric_limits<size_t>::max)();    // will cause std::bad_alloc
0443     return size_t(1) << (SizeDigits - count + 1);
0444 }
0445 inline constexpr size_t bucketForHash(size_t nBuckets, size_t hash) noexcept
0446 {
0447     return hash & (nBuckets - 1);
0448 }
0449 } // namespace GrowthPolicy
0450 
0451 template <typename Node>
0452 struct iterator;
0453 
0454 template <typename Node>
0455 struct Data
0456 {
0457     using Key = typename Node::KeyType;
0458     using T = typename Node::ValueType;
0459     using Span = QHashPrivate::Span<Node>;
0460     using iterator = QHashPrivate::iterator<Node>;
0461 
0462     QtPrivate::RefCount ref = {{1}};
0463     size_t size = 0;
0464     size_t numBuckets = 0;
0465     size_t seed = 0;
0466     Span *spans = nullptr;
0467 
0468     static constexpr size_t maxNumBuckets() noexcept
0469     {
0470         return (std::numeric_limits<ptrdiff_t>::max)() / sizeof(Span);
0471     }
0472 
0473     struct Bucket {
0474         Span *span;
0475         size_t index;
0476 
0477         Bucket(Span *s, size_t i) noexcept
0478             : span(s), index(i)
0479         {}
0480         Bucket(const Data *d, size_t bucket) noexcept
0481             : span(d->spans + (bucket >> SpanConstants::SpanShift)),
0482             index(bucket & SpanConstants::LocalBucketMask)
0483         {}
0484         Bucket(iterator it) noexcept
0485             : Bucket(it.d, it.bucket)
0486         {}
0487 
0488         size_t toBucketIndex(const Data *d) const noexcept
0489         {
0490             return ((span - d->spans) << SpanConstants::SpanShift) | index;
0491         }
0492         iterator toIterator(const Data *d) const noexcept { return iterator{d, toBucketIndex(d)}; }
0493         void advanceWrapped(const Data *d) noexcept
0494         {
0495             advance_impl(d, d->spans);
0496         }
0497         void advance(const Data *d) noexcept
0498         {
0499             advance_impl(d, nullptr);
0500         }
0501         bool isUnused() const noexcept
0502         {
0503             return !span->hasNode(index);
0504         }
0505         size_t offset() const noexcept
0506         {
0507             return span->offset(index);
0508         }
0509         Node &nodeAtOffset(size_t offset)
0510         {
0511             return span->atOffset(offset);
0512         }
0513         Node *node()
0514         {
0515             return &span->at(index);
0516         }
0517         Node *insert() const
0518         {
0519             return span->insert(index);
0520         }
0521 
0522     private:
0523         friend bool operator==(Bucket lhs, Bucket rhs) noexcept
0524         {
0525             return lhs.span == rhs.span && lhs.index == rhs.index;
0526         }
0527         friend bool operator!=(Bucket lhs, Bucket rhs) noexcept { return !(lhs == rhs); }
0528 
0529         void advance_impl(const Data *d, Span *whenAtEnd) noexcept
0530         {
0531             Q_ASSERT(span);
0532             ++index;
0533             if (Q_UNLIKELY(index == SpanConstants::NEntries)) {
0534                 index = 0;
0535                 ++span;
0536                 if (span - d->spans == ptrdiff_t(d->numBuckets >> SpanConstants::SpanShift))
0537                     span = whenAtEnd;
0538             }
0539         }
0540     };
0541 
0542     static auto allocateSpans(size_t numBuckets)
0543     {
0544         struct R {
0545             Span *spans;
0546             size_t nSpans;
0547         };
0548 
0549         constexpr qptrdiff MaxSpanCount = (std::numeric_limits<qptrdiff>::max)() / sizeof(Span);
0550         constexpr size_t MaxBucketCount = MaxSpanCount << SpanConstants::SpanShift;
0551 
0552         if (numBuckets > MaxBucketCount) {
0553             Q_CHECK_PTR(false);
0554             Q_UNREACHABLE();    // no exceptions and no assertions -> no error reporting
0555         }
0556 
0557         size_t nSpans = numBuckets >> SpanConstants::SpanShift;
0558         return R{ new Span[nSpans], nSpans };
0559     }
0560 
0561     Data(size_t reserve = 0)
0562     {
0563         numBuckets = GrowthPolicy::bucketsForCapacity(reserve);
0564         spans = allocateSpans(numBuckets).spans;
0565         seed = QHashSeed::globalSeed();
0566     }
0567 
0568     // The Resized parameter is a template param to make sure the compiler will get rid of the
0569     // branch, for performance.
0570     template <bool Resized>
0571     Q_ALWAYS_INLINE
0572     void reallocationHelper(const Data &other, size_t nSpans)
0573     {
0574         for (size_t s = 0; s < nSpans; ++s) {
0575             const Span &span = other.spans[s];
0576             for (size_t index = 0; index < SpanConstants::NEntries; ++index) {
0577                 if (!span.hasNode(index))
0578                     continue;
0579                 const Node &n = span.at(index);
0580                 auto it = Resized ? findBucket(n.key) : Bucket { spans + s, index };
0581                 Q_ASSERT(it.isUnused());
0582                 Node *newNode = it.insert();
0583                 new (newNode) Node(n);
0584             }
0585         }
0586     }
0587 
0588     Data(const Data &other) : size(other.size), numBuckets(other.numBuckets), seed(other.seed)
0589     {
0590         auto r = allocateSpans(numBuckets);
0591         spans = r.spans;
0592         reallocationHelper<false>(other, r.nSpans);
0593     }
0594     Data(const Data &other, size_t reserved) : size(other.size), seed(other.seed)
0595     {
0596         numBuckets = GrowthPolicy::bucketsForCapacity(qMax(size, reserved));
0597         spans = allocateSpans(numBuckets).spans;
0598         size_t otherNSpans = other.numBuckets >> SpanConstants::SpanShift;
0599         reallocationHelper<true>(other, otherNSpans);
0600     }
0601 
0602     static Data *detached(Data *d)
0603     {
0604         if (!d)
0605             return new Data;
0606         Data *dd = new Data(*d);
0607         if (!d->ref.deref())
0608             delete d;
0609         return dd;
0610     }
0611     static Data *detached(Data *d, size_t size)
0612     {
0613         if (!d)
0614             return new Data(size);
0615         Data *dd = new Data(*d, size);
0616         if (!d->ref.deref())
0617             delete d;
0618         return dd;
0619     }
0620 
0621     void clear()
0622     {
0623         delete[] spans;
0624         spans = nullptr;
0625         size = 0;
0626         numBuckets = 0;
0627     }
0628 
0629     iterator detachedIterator(iterator other) const noexcept
0630     {
0631         return iterator{this, other.bucket};
0632     }
0633 
0634     iterator begin() const noexcept
0635     {
0636         iterator it{ this, 0 };
0637         if (it.isUnused())
0638             ++it;
0639         return it;
0640     }
0641 
0642     constexpr iterator end() const noexcept
0643     {
0644         return iterator();
0645     }
0646 
0647     void rehash(size_t sizeHint = 0)
0648     {
0649         if (sizeHint == 0)
0650             sizeHint = size;
0651         size_t newBucketCount = GrowthPolicy::bucketsForCapacity(sizeHint);
0652 
0653         Span *oldSpans = spans;
0654         size_t oldBucketCount = numBuckets;
0655         spans = allocateSpans(newBucketCount).spans;
0656         numBuckets = newBucketCount;
0657         size_t oldNSpans = oldBucketCount >> SpanConstants::SpanShift;
0658 
0659         for (size_t s = 0; s < oldNSpans; ++s) {
0660             Span &span = oldSpans[s];
0661             for (size_t index = 0; index < SpanConstants::NEntries; ++index) {
0662                 if (!span.hasNode(index))
0663                     continue;
0664                 Node &n = span.at(index);
0665                 auto it = findBucket(n.key);
0666                 Q_ASSERT(it.isUnused());
0667                 Node *newNode = it.insert();
0668                 new (newNode) Node(std::move(n));
0669             }
0670             span.freeData();
0671         }
0672         delete[] oldSpans;
0673     }
0674 
0675     size_t nextBucket(size_t bucket) const noexcept
0676     {
0677         ++bucket;
0678         if (bucket == numBuckets)
0679             bucket = 0;
0680         return bucket;
0681     }
0682 
0683     float loadFactor() const noexcept
0684     {
0685         return float(size)/numBuckets;
0686     }
0687     bool shouldGrow() const noexcept
0688     {
0689         return size >= (numBuckets >> 1);
0690     }
0691 
0692     template <typename K> Bucket findBucket(const K &key) const noexcept
0693     {
0694         size_t hash = QHashPrivate::calculateHash(key, seed);
0695         return findBucketWithHash(key, hash);
0696     }
0697 
0698     template <typename K> Bucket findBucketWithHash(const K &key, size_t hash) const noexcept
0699     {
0700         static_assert(std::is_same_v<std::remove_cv_t<Key>, K> ||
0701                 QHashHeterogeneousSearch<std::remove_cv_t<Key>, K>::value);
0702         Q_ASSERT(numBuckets > 0);
0703         Bucket bucket(this, GrowthPolicy::bucketForHash(numBuckets, hash));
0704         // loop over the buckets until we find the entry we search for
0705         // or an empty slot, in which case we know the entry doesn't exist
0706         while (true) {
0707             size_t offset = bucket.offset();
0708             if (offset == SpanConstants::UnusedEntry) {
0709                 return bucket;
0710             } else {
0711                 Node &n = bucket.nodeAtOffset(offset);
0712                 if (qHashEquals(n.key, key))
0713                     return bucket;
0714             }
0715             bucket.advanceWrapped(this);
0716         }
0717     }
0718 
0719     template <typename K> Node *findNode(const K &key) const noexcept
0720     {
0721         auto bucket = findBucket(key);
0722         if (bucket.isUnused())
0723             return nullptr;
0724         return bucket.node();
0725     }
0726 
0727     struct InsertionResult
0728     {
0729         iterator it;
0730         bool initialized;
0731     };
0732 
0733     template <typename K> InsertionResult findOrInsert(const K &key) noexcept
0734     {
0735         Bucket it(static_cast<Span *>(nullptr), 0);
0736         size_t hash = QHashPrivate::calculateHash(key, seed);
0737         if (numBuckets > 0) {
0738             it = findBucketWithHash(key, hash);
0739             if (!it.isUnused())
0740                 return { it.toIterator(this), true };
0741         }
0742         if (shouldGrow()) {
0743             rehash(size + 1);
0744             it = findBucketWithHash(key, hash); // need to get a new iterator after rehashing
0745         }
0746         Q_ASSERT(it.span != nullptr);
0747         Q_ASSERT(it.isUnused());
0748         it.insert();
0749         ++size;
0750         return { it.toIterator(this), false };
0751     }
0752 
0753     void erase(Bucket bucket) noexcept(std::is_nothrow_destructible<Node>::value)
0754     {
0755         Q_ASSERT(bucket.span->hasNode(bucket.index));
0756         bucket.span->erase(bucket.index);
0757         --size;
0758 
0759         // re-insert the following entries to avoid holes
0760         Bucket next = bucket;
0761         while (true) {
0762             next.advanceWrapped(this);
0763             size_t offset = next.offset();
0764             if (offset == SpanConstants::UnusedEntry)
0765                 return;
0766             size_t hash = QHashPrivate::calculateHash(next.nodeAtOffset(offset).key, seed);
0767             Bucket newBucket(this, GrowthPolicy::bucketForHash(numBuckets, hash));
0768             while (true) {
0769                 if (newBucket == next) {
0770                     // nothing to do, item is at the right plae
0771                     break;
0772                 } else if (newBucket == bucket) {
0773                     // move into the hole we created earlier
0774                     if (next.span == bucket.span) {
0775                         bucket.span->moveLocal(next.index, bucket.index);
0776                     } else {
0777                         // move between spans, more expensive
0778                         bucket.span->moveFromSpan(*next.span, next.index, bucket.index);
0779                     }
0780                     bucket = next;
0781                     break;
0782                 }
0783                 newBucket.advanceWrapped(this);
0784             }
0785         }
0786     }
0787 
0788     ~Data()
0789     {
0790         delete [] spans;
0791     }
0792 };
0793 
0794 template <typename Node>
0795 struct iterator {
0796     using Span = QHashPrivate::Span<Node>;
0797 
0798     const Data<Node> *d = nullptr;
0799     size_t bucket = 0;
0800 
0801     size_t span() const noexcept { return bucket >> SpanConstants::SpanShift; }
0802     size_t index() const noexcept { return bucket & SpanConstants::LocalBucketMask; }
0803     inline bool isUnused() const noexcept { return !d->spans[span()].hasNode(index()); }
0804 
0805     inline Node *node() const noexcept
0806     {
0807         Q_ASSERT(!isUnused());
0808         return &d->spans[span()].at(index());
0809     }
0810     bool atEnd() const noexcept { return !d; }
0811 
0812     iterator operator++() noexcept
0813     {
0814         while (true) {
0815             ++bucket;
0816             if (bucket == d->numBuckets) {
0817                 d = nullptr;
0818                 bucket = 0;
0819                 break;
0820             }
0821             if (!isUnused())
0822                 break;
0823         }
0824         return *this;
0825     }
0826     bool operator==(iterator other) const noexcept
0827     { return d == other.d && bucket == other.bucket; }
0828     bool operator!=(iterator other) const noexcept
0829     { return !(*this == other); }
0830 };
0831 
0832 template <typename HashKey, typename KeyArgument>
0833 using HeterogenousConstructProxy = std::conditional_t<
0834         std::is_same_v<HashKey, q20::remove_cvref_t<KeyArgument>>,
0835         KeyArgument, // HashKey == KeyArg w/ potential modifiers, so we keep modifiers
0836         HashKey
0837     >;
0838 
0839 } // namespace QHashPrivate
0840 
0841 template <typename Key, typename T>
0842 class QHash
0843 {
0844     using Node = QHashPrivate::Node<Key, T>;
0845     using Data = QHashPrivate::Data<Node>;
0846     friend class QSet<Key>;
0847     friend class QMultiHash<Key, T>;
0848     friend tst_QHash;
0849 
0850     Data *d = nullptr;
0851 
0852 public:
0853     using key_type = Key;
0854     using mapped_type = T;
0855     using value_type = T;
0856     using size_type = qsizetype;
0857     using difference_type = qsizetype;
0858     using reference = T &;
0859     using const_reference = const T &;
0860 
0861     inline QHash() noexcept = default;
0862     inline QHash(std::initializer_list<std::pair<Key,T> > list)
0863         : d(new Data(list.size()))
0864     {
0865         for (typename std::initializer_list<std::pair<Key,T> >::const_iterator it = list.begin(); it != list.end(); ++it)
0866             insert(it->first, it->second);
0867     }
0868     QHash(const QHash &other) noexcept
0869         : d(other.d)
0870     {
0871         if (d)
0872             d->ref.ref();
0873     }
0874     ~QHash()
0875     {
0876         static_assert(std::is_nothrow_destructible_v<Key>, "Types with throwing destructors are not supported in Qt containers.");
0877         static_assert(std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers.");
0878 
0879         if (d && !d->ref.deref())
0880             delete d;
0881     }
0882 
0883     QHash &operator=(const QHash &other) noexcept(std::is_nothrow_destructible<Node>::value)
0884     {
0885         if (d != other.d) {
0886             Data *o = other.d;
0887             if (o)
0888                 o->ref.ref();
0889             if (d && !d->ref.deref())
0890                 delete d;
0891             d = o;
0892         }
0893         return *this;
0894     }
0895 
0896     QHash(QHash &&other) noexcept
0897         : d(std::exchange(other.d, nullptr))
0898     {
0899     }
0900     QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QHash)
0901 #ifdef Q_QDOC
0902     template <typename InputIterator>
0903     QHash(InputIterator f, InputIterator l);
0904 #else
0905     template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasKeyAndValue<InputIterator> = true>
0906     QHash(InputIterator f, InputIterator l)
0907         : QHash()
0908     {
0909         QtPrivate::reserveIfForwardIterator(this, f, l);
0910         for (; f != l; ++f)
0911             insert(f.key(), f.value());
0912     }
0913 
0914     template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasFirstAndSecond<InputIterator> = true>
0915     QHash(InputIterator f, InputIterator l)
0916         : QHash()
0917     {
0918         QtPrivate::reserveIfForwardIterator(this, f, l);
0919         for (; f != l; ++f) {
0920             auto &&e = *f;
0921             using V = decltype(e);
0922             insert(std::forward<V>(e).first, std::forward<V>(e).second);
0923         }
0924     }
0925 #endif
0926     void swap(QHash &other) noexcept { qt_ptr_swap(d, other.d); }
0927 
0928     class const_iterator;
0929 
0930 #ifndef Q_QDOC
0931 private:
0932     static bool compareIterators(const const_iterator &lhs, const const_iterator &rhs)
0933     {
0934         return lhs.i.node()->valuesEqual(rhs.i.node());
0935     }
0936 
0937     template <typename AKey = Key, typename AT = T,
0938               QTypeTraits::compare_eq_result_container<QHash, AKey, AT> = true>
0939     friend bool comparesEqual(const QHash &lhs, const QHash &rhs) noexcept
0940     {
0941         if (lhs.d == rhs.d)
0942             return true;
0943         if (lhs.size() != rhs.size())
0944             return false;
0945 
0946         for (const_iterator it = rhs.begin(); it != rhs.end(); ++it) {
0947             const_iterator i = lhs.find(it.key());
0948             if (i == lhs.end() || !compareIterators(i, it))
0949                 return false;
0950         }
0951         // all values must be the same as size is the same
0952         return true;
0953     }
0954     QT_DECLARE_EQUALITY_OPERATORS_HELPER(QHash, QHash, /* non-constexpr */, noexcept,
0955                      template <typename AKey = Key, typename AT = T,
0956                                QTypeTraits::compare_eq_result_container<QHash, AKey, AT> = true>)
0957 public:
0958 #else
0959     friend bool operator==(const QHash &lhs, const QHash &rhs) noexcept;
0960     friend bool operator!=(const QHash &lhs, const QHash &rhs) noexcept;
0961 #endif // Q_QDOC
0962 
0963     inline qsizetype size() const noexcept { return d ? qsizetype(d->size) : 0; }
0964 
0965     [[nodiscard]]
0966     inline bool isEmpty() const noexcept { return !d || d->size == 0; }
0967 
0968     inline qsizetype capacity() const noexcept { return d ? qsizetype(d->numBuckets >> 1) : 0; }
0969     void reserve(qsizetype size)
0970     {
0971         // reserve(0) is used in squeeze()
0972         if (size && (this->capacity() >= size))
0973             return;
0974         if (isDetached())
0975             d->rehash(size);
0976         else
0977             d = Data::detached(d, size_t(size));
0978     }
0979     inline void squeeze()
0980     {
0981         if (capacity())
0982             reserve(0);
0983     }
0984 
0985     inline void detach() { if (!d || d->ref.isShared()) d = Data::detached(d); }
0986     inline bool isDetached() const noexcept { return d && !d->ref.isShared(); }
0987     bool isSharedWith(const QHash &other) const noexcept { return d == other.d; }
0988 
0989     void clear() noexcept(std::is_nothrow_destructible<Node>::value)
0990     {
0991         if (d && !d->ref.deref())
0992             delete d;
0993         d = nullptr;
0994     }
0995 
0996     bool remove(const Key &key)
0997     {
0998         return removeImpl(key);
0999     }
1000 private:
1001     template <typename K> bool removeImpl(const K &key)
1002     {
1003         if (isEmpty()) // prevents detaching shared null
1004             return false;
1005         auto it = d->findBucket(key);
1006         if (it.isUnused())
1007             return false;
1008 
1009         size_t bucket = it.toBucketIndex(d);
1010         detach();
1011         it = typename Data::Bucket(d, bucket); // reattach in case of detach
1012 
1013         d->erase(it);
1014         return true;
1015     }
1016 
1017 public:
1018     template <typename Predicate>
1019     qsizetype removeIf(Predicate pred)
1020     {
1021         return QtPrivate::associative_erase_if(*this, pred);
1022     }
1023 
1024     T take(const Key &key)
1025     {
1026         return takeImpl(key);
1027     }
1028 private:
1029     template <typename K> T takeImpl(const K &key)
1030     {
1031         if (isEmpty()) // prevents detaching shared null
1032             return T();
1033         auto it = d->findBucket(key);
1034         size_t bucket = it.toBucketIndex(d);
1035         detach();
1036         it = typename Data::Bucket(d, bucket); // reattach in case of detach
1037 
1038         if (it.isUnused())
1039             return T();
1040         return [&] {
1041             T value = it.node()->takeValue();
1042             d->erase(it);
1043             return value;
1044         }();
1045     }
1046 
1047 public:
1048     bool contains(const Key &key) const noexcept
1049     {
1050         if (!d)
1051             return false;
1052         return d->findNode(key) != nullptr;
1053     }
1054     qsizetype count(const Key &key) const noexcept
1055     {
1056         return contains(key) ? 1 : 0;
1057     }
1058 
1059 private:
1060     const Key *keyImpl(const T &value) const noexcept
1061     {
1062         if (d) {
1063             const_iterator i = begin();
1064             while (i != end()) {
1065                 if (i.value() == value)
1066                     return &i.key();
1067                 ++i;
1068             }
1069         }
1070 
1071         return nullptr;
1072     }
1073 
1074 public:
1075     Key key(const T &value) const noexcept
1076     {
1077         if (auto *k = keyImpl(value))
1078             return *k;
1079         else
1080             return Key();
1081     }
1082     Key key(const T &value, const Key &defaultKey) const noexcept
1083     {
1084         if (auto *k = keyImpl(value))
1085             return *k;
1086         else
1087             return defaultKey;
1088     }
1089 
1090 private:
1091     template <typename K>
1092     T *valueImpl(const K &key) const noexcept
1093     {
1094         if (d) {
1095             Node *n = d->findNode(key);
1096             if (n)
1097                 return &n->value;
1098         }
1099         return nullptr;
1100     }
1101 public:
1102     T value(const Key &key) const noexcept
1103     {
1104         if (T *v = valueImpl(key))
1105             return *v;
1106         else
1107             return T();
1108     }
1109 
1110     T value(const Key &key, const T &defaultValue) const noexcept
1111     {
1112         if (T *v = valueImpl(key))
1113             return *v;
1114         else
1115             return defaultValue;
1116     }
1117 
1118     T &operator[](const Key &key)
1119     {
1120         return *tryEmplace(key).iterator;
1121     }
1122 
1123     const T operator[](const Key &key) const noexcept
1124     {
1125         return value(key);
1126     }
1127 
1128     QList<Key> keys() const { return QList<Key>(keyBegin(), keyEnd()); }
1129     QList<Key> keys(const T &value) const
1130     {
1131         QList<Key> res;
1132         const_iterator i = begin();
1133         while (i != end()) {
1134             if (i.value() == value)
1135                 res.append(i.key());
1136             ++i;
1137         }
1138         return res;
1139     }
1140     QList<T> values() const { return QList<T>(begin(), end()); }
1141 
1142     class iterator
1143     {
1144         using piter = typename QHashPrivate::iterator<Node>;
1145         friend class const_iterator;
1146         friend class QHash<Key, T>;
1147         friend class QSet<Key>;
1148         piter i;
1149         explicit inline iterator(piter it) noexcept : i(it) { }
1150 
1151     public:
1152         typedef std::forward_iterator_tag iterator_category;
1153         typedef qptrdiff difference_type;
1154         typedef T value_type;
1155         typedef T *pointer;
1156         typedef T &reference;
1157 
1158         constexpr iterator() noexcept = default;
1159 
1160         inline const Key &key() const noexcept { return i.node()->key; }
1161         inline T &value() const noexcept { return i.node()->value; }
1162         inline T &operator*() const noexcept { return i.node()->value; }
1163         inline T *operator->() const noexcept { return &i.node()->value; }
1164         inline bool operator==(const iterator &o) const noexcept { return i == o.i; }
1165         inline bool operator!=(const iterator &o) const noexcept { return i != o.i; }
1166 
1167         inline iterator &operator++() noexcept
1168         {
1169             ++i;
1170             return *this;
1171         }
1172         inline iterator operator++(int) noexcept
1173         {
1174             iterator r = *this;
1175             ++i;
1176             return r;
1177         }
1178 
1179         inline bool operator==(const const_iterator &o) const noexcept { return i == o.i; }
1180         inline bool operator!=(const const_iterator &o) const noexcept { return i != o.i; }
1181     };
1182     friend class iterator;
1183 
1184     class const_iterator
1185     {
1186         using piter = typename QHashPrivate::iterator<Node>;
1187         friend class iterator;
1188         friend class QHash<Key, T>;
1189         friend class QSet<Key>;
1190         piter i;
1191         explicit inline const_iterator(piter it) : i(it) { }
1192 
1193     public:
1194         typedef std::forward_iterator_tag iterator_category;
1195         typedef qptrdiff difference_type;
1196         typedef T value_type;
1197         typedef const T *pointer;
1198         typedef const T &reference;
1199 
1200         constexpr const_iterator() noexcept = default;
1201         inline const_iterator(const iterator &o) noexcept : i(o.i) { }
1202 
1203         inline const Key &key() const noexcept { return i.node()->key; }
1204         inline const T &value() const noexcept { return i.node()->value; }
1205         inline const T &operator*() const noexcept { return i.node()->value; }
1206         inline const T *operator->() const noexcept { return &i.node()->value; }
1207         inline bool operator==(const const_iterator &o) const noexcept { return i == o.i; }
1208         inline bool operator!=(const const_iterator &o) const noexcept { return i != o.i; }
1209 
1210         inline const_iterator &operator++() noexcept
1211         {
1212             ++i;
1213             return *this;
1214         }
1215         inline const_iterator operator++(int) noexcept
1216         {
1217             const_iterator r = *this;
1218             ++i;
1219             return r;
1220         }
1221     };
1222     friend class const_iterator;
1223 
1224     class key_iterator
1225     {
1226         const_iterator i;
1227 
1228     public:
1229         typedef typename const_iterator::iterator_category iterator_category;
1230         typedef qptrdiff difference_type;
1231         typedef Key value_type;
1232         typedef const Key *pointer;
1233         typedef const Key &reference;
1234 
1235         key_iterator() noexcept = default;
1236         explicit key_iterator(const_iterator o) noexcept : i(o) { }
1237 
1238         const Key &operator*() const noexcept { return i.key(); }
1239         const Key *operator->() const noexcept { return &i.key(); }
1240         bool operator==(key_iterator o) const noexcept { return i == o.i; }
1241         bool operator!=(key_iterator o) const noexcept { return i != o.i; }
1242 
1243         inline key_iterator &operator++() noexcept { ++i; return *this; }
1244         inline key_iterator operator++(int) noexcept { return key_iterator(i++);}
1245         const_iterator base() const noexcept { return i; }
1246     };
1247 
1248     typedef QKeyValueIterator<const Key&, const T&, const_iterator> const_key_value_iterator;
1249     typedef QKeyValueIterator<const Key&, T&, iterator> key_value_iterator;
1250 
1251     // STL style
1252     inline iterator begin() { if (!d) return iterator(); detach(); return iterator(d->begin()); }
1253     inline const_iterator begin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
1254     inline const_iterator cbegin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
1255     inline const_iterator constBegin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
1256     inline iterator end() noexcept { return iterator(); }
1257     inline const_iterator end() const noexcept { return const_iterator(); }
1258     inline const_iterator cend() const noexcept { return const_iterator(); }
1259     inline const_iterator constEnd() const noexcept { return const_iterator(); }
1260     inline key_iterator keyBegin() const noexcept { return key_iterator(begin()); }
1261     inline key_iterator keyEnd() const noexcept { return key_iterator(end()); }
1262     inline key_value_iterator keyValueBegin() { return key_value_iterator(begin()); }
1263     inline key_value_iterator keyValueEnd() { return key_value_iterator(end()); }
1264     inline const_key_value_iterator keyValueBegin() const noexcept { return const_key_value_iterator(begin()); }
1265     inline const_key_value_iterator constKeyValueBegin() const noexcept { return const_key_value_iterator(begin()); }
1266     inline const_key_value_iterator keyValueEnd() const noexcept { return const_key_value_iterator(end()); }
1267     inline const_key_value_iterator constKeyValueEnd() const noexcept { return const_key_value_iterator(end()); }
1268     auto asKeyValueRange() & { return QtPrivate::QKeyValueRange<QHash &>(*this); }
1269     auto asKeyValueRange() const & { return QtPrivate::QKeyValueRange<const QHash &>(*this); }
1270     auto asKeyValueRange() && { return QtPrivate::QKeyValueRange<QHash>(std::move(*this)); }
1271     auto asKeyValueRange() const && { return QtPrivate::QKeyValueRange<QHash>(std::move(*this)); }
1272 
1273     struct TryEmplaceResult
1274     {
1275         QHash::iterator iterator;
1276         bool inserted;
1277 
1278         TryEmplaceResult() = default;
1279         // Generated SMFs are fine!
1280         TryEmplaceResult(QHash::iterator it, bool b)
1281             : iterator(it), inserted(b)
1282         {
1283         }
1284 
1285         // Implicit conversion _from_ the return-type of try_emplace:
1286         Q_IMPLICIT TryEmplaceResult(const std::pair<key_value_iterator, bool> &p)
1287             : iterator(p.first.base()), inserted(p.second)
1288         {
1289         }
1290         // Implicit conversion _to_ the return-type of try_emplace:
1291         Q_IMPLICIT operator std::pair<key_value_iterator, bool>()
1292         {
1293             return { key_value_iterator(iterator), inserted };
1294         }
1295     };
1296 
1297     iterator erase(const_iterator it)
1298     {
1299         Q_ASSERT(it != constEnd());
1300         detach();
1301         // ensure a valid iterator across the detach:
1302         iterator i = iterator{d->detachedIterator(it.i)};
1303         typename Data::Bucket bucket(i.i);
1304 
1305         d->erase(bucket);
1306         if (bucket.toBucketIndex(d) == d->numBuckets - 1 || bucket.isUnused())
1307             ++i;
1308         return i;
1309     }
1310 
1311     std::pair<iterator, iterator> equal_range(const Key &key)
1312     {
1313         return equal_range_impl(*this, key);
1314     }
1315     std::pair<const_iterator, const_iterator> equal_range(const Key &key) const noexcept
1316     {
1317         return equal_range_impl(*this, key);
1318     }
1319 private:
1320     template <typename Hash, typename K> static auto equal_range_impl(Hash &self, const K &key)
1321     {
1322         auto first = self.find(key);
1323         auto second = first;
1324         if (second != decltype(first){})
1325             ++second;
1326         return std::make_pair(first, second);
1327     }
1328 
1329     template <typename K> iterator findImpl(const K &key)
1330     {
1331         if (isEmpty()) // prevents detaching shared null
1332             return end();
1333         auto it = d->findBucket(key);
1334         size_t bucket = it.toBucketIndex(d);
1335         detach();
1336         it = typename Data::Bucket(d, bucket); // reattach in case of detach
1337         if (it.isUnused())
1338             return end();
1339         return iterator(it.toIterator(d));
1340     }
1341     template <typename K> const_iterator constFindImpl(const K &key) const noexcept
1342     {
1343         if (isEmpty())
1344             return end();
1345         auto it = d->findBucket(key);
1346         if (it.isUnused())
1347             return end();
1348         return const_iterator({d, it.toBucketIndex(d)});
1349     }
1350 
1351 public:
1352     typedef iterator Iterator;
1353     typedef const_iterator ConstIterator;
1354     inline qsizetype count() const noexcept { return d ? qsizetype(d->size) : 0; }
1355     iterator find(const Key &key)
1356     {
1357         return findImpl(key);
1358     }
1359     const_iterator find(const Key &key) const noexcept
1360     {
1361         return constFindImpl(key);
1362     }
1363     const_iterator constFind(const Key &key) const noexcept
1364     {
1365         return find(key);
1366     }
1367 
1368     iterator insert(const Key &key, const T &value)
1369     {
1370         return emplace(key, value);
1371     }
1372 
1373     iterator insert(const Key &key, T &&value)
1374     {
1375         return emplace(key, std::move(value));
1376     }
1377 
1378     iterator insert(Key &&key, const T &value)
1379     {
1380         return emplace(std::move(key), value);
1381     }
1382 
1383     iterator insert(Key &&key, T &&value)
1384     {
1385         return emplace(std::move(key), std::move(value));
1386     }
1387 
1388     void insert(const QHash &hash)
1389     {
1390         if (d == hash.d || !hash.d)
1391             return;
1392         if (!d) {
1393             *this = hash;
1394             return;
1395         }
1396 
1397         detach();
1398 
1399         for (auto it = hash.begin(); it != hash.end(); ++it)
1400             emplace(it.key(), it.value());
1401     }
1402 
1403     template <typename ...Args>
1404     iterator emplace(const Key &key, Args &&... args)
1405     {
1406         Key copy = key; // Needs to be explicit for MSVC 2019
1407         return emplace(std::move(copy), std::forward<Args>(args)...);
1408     }
1409 
1410     template <typename ...Args>
1411     iterator emplace(Key &&key, Args &&... args)
1412     {
1413         if (isDetached()) {
1414             if (d->shouldGrow()) // Construct the value now so that no dangling references are used
1415                 return emplace_helper(std::move(key), T(std::forward<Args>(args)...));
1416             return emplace_helper(std::move(key), std::forward<Args>(args)...);
1417         }
1418         // else: we must detach
1419         const auto copy = *this; // keep 'args' alive across the detach/growth
1420         detach();
1421         return emplace_helper(std::move(key), std::forward<Args>(args)...);
1422     }
1423 
1424     template <typename... Args>
1425     TryEmplaceResult tryEmplace(const Key &key, Args &&...args)
1426     {
1427         return tryEmplace_impl(key, std::forward<Args>(args)...);
1428     }
1429     template <typename... Args>
1430     TryEmplaceResult tryEmplace(Key &&key, Args &&...args)
1431     {
1432         return tryEmplace_impl(std::move(key), std::forward<Args>(args)...);
1433     }
1434 
1435     TryEmplaceResult tryInsert(const Key &key, const T &value)
1436     {
1437         return tryEmplace_impl(key, value);
1438     }
1439 
1440     template <typename... Args>
1441     std::pair<key_value_iterator, bool> try_emplace(const Key &key, Args &&...args)
1442     {
1443         return tryEmplace_impl(key, std::forward<Args>(args)...);
1444     }
1445     template <typename... Args>
1446     std::pair<key_value_iterator, bool> try_emplace(Key &&key, Args &&...args)
1447     {
1448         return tryEmplace_impl(std::move(key), std::forward<Args>(args)...);
1449     }
1450     template <typename... Args>
1451     key_value_iterator try_emplace(const_iterator /*hint*/, const Key &key, Args &&...args)
1452     {
1453         return key_value_iterator(tryEmplace_impl(key, std::forward<Args>(args)...).iterator);
1454     }
1455     template <typename... Args>
1456     key_value_iterator try_emplace(const_iterator /*hint*/, Key &&key, Args &&...args)
1457     {
1458         return key_value_iterator(tryEmplace_impl(std::move(key), std::forward<Args>(args)...).iterator);
1459     }
1460 
1461 private:
1462     template <typename K, typename... Args>
1463     TryEmplaceResult tryEmplace_impl(K &&key, Args &&...args)
1464     {
1465         if (!d)
1466             detach();
1467         QHash detachGuard;
1468 
1469         size_t hash = QHashPrivate::calculateHash(key, d->seed);
1470         typename Data::Bucket bucket = d->findBucketWithHash(key, hash);
1471         const bool shouldInsert = bucket.isUnused();
1472 
1473         // Even if we don't insert we may have to detach because we are
1474         // returning a non-const iterator:
1475         if (!isDetached() || (shouldInsert && d->shouldGrow())) {
1476             detachGuard = *this;
1477             const bool resized = shouldInsert && d->shouldGrow();
1478             const size_t bucketIndex = bucket.toBucketIndex(d);
1479 
1480             // Must detach from detachGuard
1481             d = resized ? Data::detached(d, d->size + 1) : Data::detached(d);
1482             bucket = resized ? d->findBucketWithHash(key, hash) : typename Data::Bucket(d, bucketIndex);
1483         }
1484         if (shouldInsert) {
1485             Node *n = bucket.insert();
1486             using ConstructProxy = typename QHashPrivate::HeterogenousConstructProxy<Key, K>;
1487             Node::createInPlace(n, ConstructProxy(std::forward<K>(key)),
1488                                 std::forward<Args>(args)...);
1489             ++d->size;
1490         }
1491         return {iterator(bucket.toIterator(d)), shouldInsert};
1492     }
1493 public:
1494     template <typename Value>
1495     TryEmplaceResult insertOrAssign(const Key &key, Value &&value)
1496     {
1497         return insertOrAssign_impl(key, std::forward<Value>(value));
1498     }
1499     template <typename Value>
1500     TryEmplaceResult insertOrAssign(Key &&key, Value &&value)
1501     {
1502         return insertOrAssign_impl(std::move(key), std::forward<Value>(value));
1503     }
1504     template <typename Value>
1505     std::pair<key_value_iterator, bool> insert_or_assign(const Key &key, Value &&value)
1506     {
1507         return insertOrAssign_impl(key, std::forward<Value>(value));
1508     }
1509     template <typename Value>
1510     std::pair<key_value_iterator, bool> insert_or_assign(Key &&key, Value &&value)
1511     {
1512         return insertOrAssign_impl(std::move(key), std::forward<Value>(value));
1513     }
1514     template <typename Value>
1515     key_value_iterator insert_or_assign(const_iterator /*hint*/, const Key &key, Value &&value)
1516     {
1517         return key_value_iterator(insertOrAssign_impl(key, std::forward<Value>(value)).iterator);
1518     }
1519     template <typename Value>
1520     key_value_iterator insert_or_assign(const_iterator /*hint*/, Key &&key, Value &&value)
1521     {
1522         return key_value_iterator(insertOrAssign_impl(std::move(key), std::forward<Value>(value)).iterator);
1523     }
1524 
1525 private:
1526     template <typename K, typename Value>
1527     TryEmplaceResult insertOrAssign_impl(K &&key, Value &&value)
1528     {
1529         auto r = tryEmplace(std::forward<K>(key), std::forward<Value>(value));
1530         if (!r.inserted)
1531             *r.iterator = std::forward<Value>(value); // `value` is untouched if we get here
1532         return r;
1533     }
1534 
1535 public:
1536 
1537     float load_factor() const noexcept { return d ? d->loadFactor() : 0; }
1538     static float max_load_factor() noexcept { return 0.5; }
1539     size_t bucket_count() const noexcept { return d ? d->numBuckets : 0; }
1540     static size_t max_bucket_count() noexcept { return Data::maxNumBuckets(); }
1541 
1542     [[nodiscard]]
1543     inline bool empty() const noexcept { return isEmpty(); }
1544 
1545 private:
1546     template <typename ...Args>
1547     iterator emplace_helper(Key &&key, Args &&... args)
1548     {
1549         auto result = d->findOrInsert(key);
1550         if (!result.initialized)
1551             Node::createInPlace(result.it.node(), std::move(key), std::forward<Args>(args)...);
1552         else
1553             result.it.node()->emplaceValue(std::forward<Args>(args)...);
1554         return iterator(result.it);
1555     }
1556 
1557     template <typename K>
1558     using if_heterogeneously_searchable = QHashPrivate::if_heterogeneously_searchable_with<Key, K>;
1559 
1560     template <typename K>
1561     using if_key_constructible_from = std::enable_if_t<std::is_constructible_v<Key, K>, bool>;
1562 
1563 public:
1564     template <typename K, if_heterogeneously_searchable<K> = true>
1565     bool remove(const K &key)
1566     {
1567         return removeImpl(key);
1568     }
1569     template <typename K, if_heterogeneously_searchable<K> = true>
1570     T take(const K &key)
1571     {
1572         return takeImpl(key);
1573     }
1574     template <typename K, if_heterogeneously_searchable<K> = true>
1575     bool contains(const K &key) const
1576     {
1577         return d ? d->findNode(key) != nullptr : false;
1578     }
1579     template <typename K, if_heterogeneously_searchable<K> = true>
1580     qsizetype count(const K &key) const
1581     {
1582         return contains(key) ? 1 : 0;
1583     }
1584     template <typename K, if_heterogeneously_searchable<K> = true>
1585     T value(const K &key) const noexcept
1586     {
1587         if (auto *v = valueImpl(key))
1588             return *v;
1589         else
1590             return T();
1591     }
1592     template <typename K, if_heterogeneously_searchable<K> = true>
1593     T value(const K &key, const T &defaultValue) const noexcept
1594     {
1595         if (auto *v = valueImpl(key))
1596             return *v;
1597         else
1598             return defaultValue;
1599     }
1600     template <typename K, if_heterogeneously_searchable<K> = true, if_key_constructible_from<K> = true>
1601     T &operator[](const K &key)
1602     {
1603         return *tryEmplace(key).iterator;
1604     }
1605     template <typename K, if_heterogeneously_searchable<K> = true>
1606     const T operator[](const K &key) const noexcept
1607     {
1608         return value(key);
1609     }
1610     template <typename K, if_heterogeneously_searchable<K> = true>
1611     std::pair<iterator, iterator>
1612     equal_range(const K &key)
1613     {
1614         return equal_range_impl(*this, key);
1615     }
1616     template <typename K, if_heterogeneously_searchable<K> = true>
1617     std::pair<const_iterator, const_iterator>
1618     equal_range(const K &key) const noexcept
1619     {
1620         return equal_range_impl(*this, key);
1621     }
1622     template <typename K, if_heterogeneously_searchable<K> = true>
1623     iterator find(const K &key)
1624     {
1625         return findImpl(key);
1626     }
1627     template <typename K, if_heterogeneously_searchable<K> = true>
1628     const_iterator find(const K &key) const noexcept
1629     {
1630         return constFindImpl(key);
1631     }
1632     template <typename K, if_heterogeneously_searchable<K> = true>
1633     const_iterator constFind(const K &key) const noexcept
1634     {
1635         return find(key);
1636     }
1637     template <typename K, typename... Args, if_heterogeneously_searchable<K> = true, if_key_constructible_from<K> = true>
1638     TryEmplaceResult tryEmplace(K &&key, Args &&...args)
1639     {
1640         return tryEmplace_impl(std::forward<K>(key), std::forward<Args>(args)...);
1641     }
1642     template <typename K, if_heterogeneously_searchable<K> = true, if_key_constructible_from<K> = true>
1643     TryEmplaceResult tryInsert(K &&key, const T &value)
1644     {
1645         return tryEmplace_impl(std::forward<K>(key), value);
1646     }
1647     template <typename K, typename... Args, if_heterogeneously_searchable<K> = true, if_key_constructible_from<K> = true>
1648     std::pair<key_value_iterator, bool> try_emplace(K &&key, Args &&...args)
1649     {
1650         return tryEmplace_impl(std::forward<K>(key), std::forward<Args>(args)...);
1651     }
1652     template <typename K, typename... Args, if_heterogeneously_searchable<K> = true, if_key_constructible_from<K> = true>
1653     key_value_iterator try_emplace(const_iterator /*hint*/, K &&key, Args &&...args)
1654     {
1655         return key_value_iterator(tryEmplace_impl(std::forward<K>(key), std::forward<Args>(args)...).iterator);
1656     }
1657     template <typename K, typename Value, if_heterogeneously_searchable<K> = true, if_key_constructible_from<K> = true>
1658     TryEmplaceResult insertOrAssign(K &&key, Value &&value)
1659     {
1660         return insertOrAssign_impl(std::forward<K>(key), std::forward<Value>(value));
1661     }
1662     template <typename K, typename Value, if_heterogeneously_searchable<K> = true, if_key_constructible_from<K> = true>
1663     std::pair<key_value_iterator, bool> insert_or_assign(K &&key, Value &&value)
1664     {
1665         return insertOrAssign_impl(std::forward<K>(key), std::forward<Value>(value));
1666     }
1667     template <typename K, typename Value, if_heterogeneously_searchable<K> = true, if_key_constructible_from<K> = true>
1668     key_value_iterator insert_or_assign(const_iterator /*hint*/, K &&key, Value &&value)
1669     {
1670         return key_value_iterator(insertOrAssign_impl(std::forward<K>(key), std::forward<Value>(value)).iterator);
1671     }
1672 };
1673 
1674 
1675 template <typename Key, typename T>
1676 class QMultiHash
1677 {
1678     using Node = QHashPrivate::MultiNode<Key, T>;
1679     using Data = QHashPrivate::Data<Node>;
1680     using Chain = QHashPrivate::MultiNodeChain<T>;
1681 
1682     Data *d = nullptr;
1683     qsizetype m_size = 0;
1684 
1685 public:
1686     using key_type = Key;
1687     using mapped_type = T;
1688     using value_type = T;
1689     using size_type = qsizetype;
1690     using difference_type = qsizetype;
1691     using reference = T &;
1692     using const_reference = const T &;
1693 
1694     QMultiHash() noexcept = default;
1695     inline QMultiHash(std::initializer_list<std::pair<Key,T> > list)
1696         : d(new Data(list.size()))
1697     {
1698         for (typename std::initializer_list<std::pair<Key,T> >::const_iterator it = list.begin(); it != list.end(); ++it)
1699             insert(it->first, it->second);
1700     }
1701 #ifdef Q_QDOC
1702     template <typename InputIterator>
1703     QMultiHash(InputIterator f, InputIterator l);
1704 #else
1705     template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasKeyAndValue<InputIterator> = true>
1706     QMultiHash(InputIterator f, InputIterator l)
1707     {
1708         QtPrivate::reserveIfForwardIterator(this, f, l);
1709         for (; f != l; ++f)
1710             insert(f.key(), f.value());
1711     }
1712 
1713     template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasFirstAndSecond<InputIterator> = true>
1714     QMultiHash(InputIterator f, InputIterator l)
1715     {
1716         QtPrivate::reserveIfForwardIterator(this, f, l);
1717         for (; f != l; ++f) {
1718             auto &&e = *f;
1719             using V = decltype(e);
1720             insert(std::forward<V>(e).first, std::forward<V>(e).second);
1721         }
1722     }
1723 #endif
1724     QMultiHash(const QMultiHash &other) noexcept
1725         : d(other.d), m_size(other.m_size)
1726     {
1727         if (d)
1728             d->ref.ref();
1729     }
1730     ~QMultiHash()
1731     {
1732         static_assert(std::is_nothrow_destructible_v<Key>, "Types with throwing destructors are not supported in Qt containers.");
1733         static_assert(std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers.");
1734 
1735         if (d && !d->ref.deref())
1736             delete d;
1737     }
1738 
1739     QMultiHash &operator=(const QMultiHash &other) noexcept(std::is_nothrow_destructible<Node>::value)
1740     {
1741         if (d != other.d) {
1742             Data *o = other.d;
1743             if (o)
1744                 o->ref.ref();
1745             if (d && !d->ref.deref())
1746                 delete d;
1747             d = o;
1748             m_size = other.m_size;
1749         }
1750         return *this;
1751     }
1752     QMultiHash(QMultiHash &&other) noexcept
1753         : d(std::exchange(other.d, nullptr)),
1754           m_size(std::exchange(other.m_size, 0))
1755     {
1756     }
1757     QMultiHash &operator=(QMultiHash &&other) noexcept(std::is_nothrow_destructible<Node>::value)
1758     {
1759         QMultiHash moved(std::move(other));
1760         swap(moved);
1761         return *this;
1762     }
1763 
1764     explicit QMultiHash(const QHash<Key, T> &other)
1765         : QMultiHash(other.begin(), other.end())
1766     {}
1767 
1768     explicit QMultiHash(QHash<Key, T> &&other)
1769     {
1770         unite(std::move(other));
1771     }
1772 
1773     void swap(QMultiHash &other) noexcept
1774     {
1775         qt_ptr_swap(d, other.d);
1776         std::swap(m_size, other.m_size);
1777     }
1778 
1779 #ifndef Q_QDOC
1780 private:
1781     template <typename AKey = Key, typename AT = T,
1782               QTypeTraits::compare_eq_result_container<QMultiHash, AKey, AT> = true>
1783     friend bool comparesEqual(const QMultiHash &lhs, const QMultiHash &rhs) noexcept
1784     {
1785         if (lhs.d == rhs.d)
1786             return true;
1787         if (lhs.m_size != rhs.m_size)
1788             return false;
1789         if (lhs.m_size == 0)
1790             return true;
1791         // equal size, and both non-zero size => d pointers allocated for both
1792         Q_ASSERT(lhs.d);
1793         Q_ASSERT(rhs.d);
1794         if (lhs.d->size != rhs.d->size)
1795             return false;
1796         for (auto it = rhs.d->begin(); it != rhs.d->end(); ++it) {
1797             auto *n = lhs.d->findNode(it.node()->key);
1798             if (!n)
1799                 return false;
1800             Chain *e = it.node()->value;
1801             while (e) {
1802                 Chain *oe = n->value;
1803                 while (oe) {
1804                     if (oe->value == e->value)
1805                         break;
1806                     oe = oe->next;
1807                 }
1808                 if (!oe)
1809                     return false;
1810                 e = e->next;
1811             }
1812         }
1813         // all values must be the same as size is the same
1814         return true;
1815     }
1816     QT_DECLARE_EQUALITY_OPERATORS_HELPER(QMultiHash, QMultiHash, /* non-constexpr */, noexcept,
1817                  template <typename AKey = Key, typename AT = T,
1818                            QTypeTraits::compare_eq_result_container<QMultiHash, AKey, AT> = true>)
1819 public:
1820 #else
1821     friend bool operator==(const QMultiHash &lhs, const QMultiHash &rhs) noexcept;
1822     friend bool operator!=(const QMultiHash &lhs, const QMultiHash &rhs) noexcept;
1823 #endif // Q_QDOC
1824 
1825     inline qsizetype size() const noexcept { return m_size; }
1826 
1827     [[nodiscard]]
1828     inline bool isEmpty() const noexcept { return !m_size; }
1829 
1830     inline qsizetype capacity() const noexcept { return d ? qsizetype(d->numBuckets >> 1) : 0; }
1831     void reserve(qsizetype size)
1832     {
1833         // reserve(0) is used in squeeze()
1834         if (size && (this->capacity() >= size))
1835             return;
1836         if (isDetached())
1837             d->rehash(size);
1838         else
1839             d = Data::detached(d, size_t(size));
1840     }
1841     inline void squeeze() { reserve(0); }
1842 
1843     inline void detach() { if (!d || d->ref.isShared()) d = Data::detached(d); }
1844     inline bool isDetached() const noexcept { return d && !d->ref.isShared(); }
1845     bool isSharedWith(const QMultiHash &other) const noexcept { return d == other.d; }
1846 
1847     void clear() noexcept(std::is_nothrow_destructible<Node>::value)
1848     {
1849         if (d && !d->ref.deref())
1850             delete d;
1851         d = nullptr;
1852         m_size = 0;
1853     }
1854 
1855     qsizetype remove(const Key &key)
1856     {
1857         return removeImpl(key);
1858     }
1859 private:
1860     template <typename K> qsizetype removeImpl(const K &key)
1861     {
1862         if (isEmpty()) // prevents detaching shared null
1863             return 0;
1864         auto it = d->findBucket(key);
1865         size_t bucket = it.toBucketIndex(d);
1866         detach();
1867         it = typename Data::Bucket(d, bucket); // reattach in case of detach
1868 
1869         if (it.isUnused())
1870             return 0;
1871         qsizetype n = Node::freeChain(it.node());
1872         m_size -= n;
1873         Q_ASSERT(m_size >= 0);
1874         d->erase(it);
1875         return n;
1876     }
1877 
1878 public:
1879     template <typename Predicate>
1880     qsizetype removeIf(Predicate pred)
1881     {
1882         return QtPrivate::associative_erase_if(*this, pred);
1883     }
1884 
1885     T take(const Key &key)
1886     {
1887         return takeImpl(key);
1888     }
1889 private:
1890     template <typename K> T takeImpl(const K &key)
1891     {
1892         if (isEmpty()) // prevents detaching shared null
1893             return T();
1894         auto it = d->findBucket(key);
1895         size_t bucket = it.toBucketIndex(d);
1896         detach();
1897         it = typename Data::Bucket(d, bucket); // reattach in case of detach
1898 
1899         if (it.isUnused())
1900             return T();
1901         Chain *e = it.node()->value;
1902         Q_ASSERT(e);
1903         T t = std::move(e->value);
1904         if (e->next) {
1905             it.node()->value = e->next;
1906             delete e;
1907         } else {
1908             // erase() deletes the values.
1909             d->erase(it);
1910         }
1911         --m_size;
1912         Q_ASSERT(m_size >= 0);
1913         return t;
1914     }
1915 
1916 public:
1917     bool contains(const Key &key) const noexcept
1918     {
1919         if (!d)
1920             return false;
1921         return d->findNode(key) != nullptr;
1922     }
1923 
1924 private:
1925     const Key *keyImpl(const T &value) const noexcept
1926     {
1927         if (d) {
1928             auto i = d->begin();
1929             while (i != d->end()) {
1930                 Chain *e = i.node()->value;
1931                 if (e->contains(value))
1932                     return &i.node()->key;
1933                 ++i;
1934             }
1935         }
1936 
1937         return nullptr;
1938     }
1939 public:
1940     Key key(const T &value) const noexcept
1941     {
1942         if (auto *k = keyImpl(value))
1943             return *k;
1944         else
1945             return Key();
1946     }
1947     Key key(const T &value, const Key &defaultKey) const noexcept
1948     {
1949         if (auto *k = keyImpl(value))
1950             return *k;
1951         else
1952             return defaultKey;
1953     }
1954 
1955 private:
1956     template <typename K>
1957     T *valueImpl(const K &key) const noexcept
1958     {
1959         if (d) {
1960             Node *n = d->findNode(key);
1961             if (n) {
1962                 Q_ASSERT(n->value);
1963                 return &n->value->value;
1964             }
1965         }
1966         return nullptr;
1967     }
1968 public:
1969     T value(const Key &key) const noexcept
1970     {
1971         if (auto *v = valueImpl(key))
1972             return *v;
1973         else
1974             return T();
1975     }
1976     T value(const Key &key, const T &defaultValue) const noexcept
1977     {
1978         if (auto *v = valueImpl(key))
1979             return *v;
1980         else
1981             return defaultValue;
1982     }
1983 
1984     T &operator[](const Key &key)
1985     {
1986         return operatorIndexImpl(key);
1987     }
1988 private:
1989     template <typename K> T &operatorIndexImpl(const K &key)
1990     {
1991         const auto copy = isDetached() ? QMultiHash() : *this; // keep 'key' alive across the detach
1992         detach();
1993         auto result = d->findOrInsert(key);
1994         Q_ASSERT(!result.it.atEnd());
1995         if (!result.initialized) {
1996             Node::createInPlace(result.it.node(), Key(key), T());
1997             ++m_size;
1998         }
1999         return result.it.node()->value->value;
2000     }
2001 
2002 public:
2003     const T operator[](const Key &key) const noexcept
2004     {
2005         return value(key);
2006     }
2007 
2008     QList<Key> uniqueKeys() const
2009     {
2010         QList<Key> res;
2011         if (d) {
2012             auto i = d->begin();
2013             while (i != d->end()) {
2014                 res.append(i.node()->key);
2015                 ++i;
2016             }
2017         }
2018         return res;
2019     }
2020 
2021     QList<Key> keys() const { return QList<Key>(keyBegin(), keyEnd()); }
2022     QList<Key> keys(const T &value) const
2023     {
2024         QList<Key> res;
2025         const_iterator i = begin();
2026         while (i != end()) {
2027             if (i.value() == value)
2028                 res.append(i.key());
2029             ++i;
2030         }
2031         return res;
2032     }
2033 
2034     QList<T> values() const { return QList<T>(begin(), end()); }
2035     QList<T> values(const Key &key) const
2036     {
2037         return valuesImpl(key);
2038     }
2039 private:
2040     template <typename K> QList<T> valuesImpl(const K &key) const
2041     {
2042         QList<T> values;
2043         if (d) {
2044             Node *n = d->findNode(key);
2045             if (n) {
2046                 Chain *e = n->value;
2047                 while (e) {
2048                     values.append(e->value);
2049                     e = e->next;
2050                 }
2051             }
2052         }
2053         return values;
2054     }
2055 
2056 public:
2057     class const_iterator;
2058 
2059     class iterator
2060     {
2061         using piter = typename QHashPrivate::iterator<Node>;
2062         friend class const_iterator;
2063         friend class QMultiHash<Key, T>;
2064         piter i;
2065         Chain **e = nullptr;
2066         explicit inline iterator(piter it, Chain **entry = nullptr) noexcept : i(it), e(entry)
2067         {
2068             if (!it.atEnd() && !e) {
2069                 e = &it.node()->value;
2070                 Q_ASSERT(e && *e);
2071             }
2072         }
2073 
2074     public:
2075         typedef std::forward_iterator_tag iterator_category;
2076         typedef qptrdiff difference_type;
2077         typedef T value_type;
2078         typedef T *pointer;
2079         typedef T &reference;
2080 
2081         constexpr iterator() noexcept = default;
2082 
2083         inline const Key &key() const noexcept { return i.node()->key; }
2084         inline T &value() const noexcept { return (*e)->value; }
2085         inline T &operator*() const noexcept { return (*e)->value; }
2086         inline T *operator->() const noexcept { return &(*e)->value; }
2087         inline bool operator==(const iterator &o) const noexcept { return e == o.e; }
2088         inline bool operator!=(const iterator &o) const noexcept { return e != o.e; }
2089 
2090         inline iterator &operator++() noexcept {
2091             Q_ASSERT(e && *e);
2092             e = &(*e)->next;
2093             Q_ASSERT(e);
2094             if (!*e) {
2095                 ++i;
2096                 e = i.atEnd() ? nullptr : &i.node()->value;
2097             }
2098             return *this;
2099         }
2100         inline iterator operator++(int) noexcept {
2101             iterator r = *this;
2102             ++(*this);
2103             return r;
2104         }
2105 
2106         inline bool operator==(const const_iterator &o) const noexcept { return e == o.e; }
2107         inline bool operator!=(const const_iterator &o) const noexcept { return e != o.e; }
2108     };
2109     friend class iterator;
2110 
2111     class const_iterator
2112     {
2113         using piter = typename QHashPrivate::iterator<Node>;
2114         friend class iterator;
2115         friend class QMultiHash<Key, T>;
2116         piter i;
2117         Chain **e = nullptr;
2118         explicit inline const_iterator(piter it, Chain **entry = nullptr) noexcept : i(it), e(entry)
2119         {
2120             if (!it.atEnd() && !e) {
2121                 e = &it.node()->value;
2122                 Q_ASSERT(e && *e);
2123             }
2124         }
2125 
2126     public:
2127         typedef std::forward_iterator_tag iterator_category;
2128         typedef qptrdiff difference_type;
2129         typedef T value_type;
2130         typedef const T *pointer;
2131         typedef const T &reference;
2132 
2133         constexpr const_iterator() noexcept = default;
2134         inline const_iterator(const iterator &o) noexcept : i(o.i), e(o.e) { }
2135 
2136         inline const Key &key() const noexcept { return i.node()->key; }
2137         inline T &value() const noexcept { return (*e)->value; }
2138         inline T &operator*() const noexcept { return (*e)->value; }
2139         inline T *operator->() const noexcept { return &(*e)->value; }
2140         inline bool operator==(const const_iterator &o) const noexcept { return e == o.e; }
2141         inline bool operator!=(const const_iterator &o) const noexcept { return e != o.e; }
2142 
2143         inline const_iterator &operator++() noexcept {
2144             Q_ASSERT(e && *e);
2145             e = &(*e)->next;
2146             Q_ASSERT(e);
2147             if (!*e) {
2148                 ++i;
2149                 e = i.atEnd() ? nullptr : &i.node()->value;
2150             }
2151             return *this;
2152         }
2153         inline const_iterator operator++(int) noexcept
2154         {
2155             const_iterator r = *this;
2156             ++(*this);
2157             return r;
2158         }
2159     };
2160     friend class const_iterator;
2161 
2162     class key_iterator
2163     {
2164         const_iterator i;
2165 
2166     public:
2167         typedef typename const_iterator::iterator_category iterator_category;
2168         typedef qptrdiff difference_type;
2169         typedef Key value_type;
2170         typedef const Key *pointer;
2171         typedef const Key &reference;
2172 
2173         key_iterator() noexcept = default;
2174         explicit key_iterator(const_iterator o) noexcept : i(o) { }
2175 
2176         const Key &operator*() const noexcept { return i.key(); }
2177         const Key *operator->() const noexcept { return &i.key(); }
2178         bool operator==(key_iterator o) const noexcept { return i == o.i; }
2179         bool operator!=(key_iterator o) const noexcept { return i != o.i; }
2180 
2181         inline key_iterator &operator++() noexcept { ++i; return *this; }
2182         inline key_iterator operator++(int) noexcept { return key_iterator(i++);}
2183         const_iterator base() const noexcept { return i; }
2184     };
2185 
2186     typedef QKeyValueIterator<const Key&, const T&, const_iterator> const_key_value_iterator;
2187     typedef QKeyValueIterator<const Key&, T&, iterator> key_value_iterator;
2188 
2189     // STL style
2190     inline iterator begin() { if (!d) return iterator(); detach(); return iterator(d->begin()); }
2191     inline const_iterator begin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
2192     inline const_iterator cbegin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
2193     inline const_iterator constBegin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
2194     inline iterator end() noexcept { return iterator(); }
2195     inline const_iterator end() const noexcept { return const_iterator(); }
2196     inline const_iterator cend() const noexcept { return const_iterator(); }
2197     inline const_iterator constEnd() const noexcept { return const_iterator(); }
2198     inline key_iterator keyBegin() const noexcept { return key_iterator(begin()); }
2199     inline key_iterator keyEnd() const noexcept { return key_iterator(end()); }
2200     inline key_value_iterator keyValueBegin() noexcept { return key_value_iterator(begin()); }
2201     inline key_value_iterator keyValueEnd() noexcept { return key_value_iterator(end()); }
2202     inline const_key_value_iterator keyValueBegin() const noexcept { return const_key_value_iterator(begin()); }
2203     inline const_key_value_iterator constKeyValueBegin() const noexcept { return const_key_value_iterator(begin()); }
2204     inline const_key_value_iterator keyValueEnd() const noexcept { return const_key_value_iterator(end()); }
2205     inline const_key_value_iterator constKeyValueEnd() const noexcept { return const_key_value_iterator(end()); }
2206     auto asKeyValueRange() & { return QtPrivate::QKeyValueRange<QMultiHash &>(*this); }
2207     auto asKeyValueRange() const & { return QtPrivate::QKeyValueRange<const QMultiHash &>(*this); }
2208     auto asKeyValueRange() && { return QtPrivate::QKeyValueRange<QMultiHash>(std::move(*this)); }
2209     auto asKeyValueRange() const && { return QtPrivate::QKeyValueRange<QMultiHash>(std::move(*this)); }
2210 
2211     iterator detach(const_iterator it)
2212     {
2213         auto i = it.i;
2214         Chain **e = it.e;
2215         if (d->ref.isShared()) {
2216             // need to store iterator position before detaching
2217             qsizetype n = 0;
2218             Chain *entry = i.node()->value;
2219             while (entry != *it.e) {
2220                 ++n;
2221                 entry = entry->next;
2222             }
2223             Q_ASSERT(entry);
2224             detach_helper();
2225 
2226             i = d->detachedIterator(i);
2227             e = &i.node()->value;
2228             while (n) {
2229                 e = &(*e)->next;
2230                 --n;
2231             }
2232             Q_ASSERT(e && *e);
2233         }
2234         return iterator(i, e);
2235     }
2236 
2237     iterator erase(const_iterator it)
2238     {
2239         Q_ASSERT(d);
2240         iterator iter = detach(it);
2241         iterator i = iter;
2242         Chain *e = *i.e;
2243         Chain *next = e->next;
2244         *i.e = next;
2245         delete e;
2246         if (!next) {
2247             if (i.e == &i.i.node()->value) {
2248                 // last remaining entry, erase
2249                 typename Data::Bucket bucket(i.i);
2250                 d->erase(bucket);
2251                 if (bucket.toBucketIndex(d) == d->numBuckets - 1 || bucket.isUnused())
2252                     i = iterator(++iter.i);
2253                 else // 'i' currently has a nullptr chain. So, we must recreate it
2254                     i = iterator(bucket.toIterator(d));
2255             } else {
2256                 i = iterator(++iter.i);
2257             }
2258         }
2259         --m_size;
2260         Q_ASSERT(m_size >= 0);
2261         return i;
2262     }
2263 
2264     // more Qt
2265     typedef iterator Iterator;
2266     typedef const_iterator ConstIterator;
2267     inline qsizetype count() const noexcept { return size(); }
2268 
2269 private:
2270     template <typename K> iterator findImpl(const K &key)
2271     {
2272         if (isEmpty())
2273             return end();
2274         auto it = d->findBucket(key);
2275         size_t bucket = it.toBucketIndex(d);
2276         detach();
2277         it = typename Data::Bucket(d, bucket); // reattach in case of detach
2278 
2279         if (it.isUnused())
2280             return end();
2281         return iterator(it.toIterator(d));
2282     }
2283     template <typename K> const_iterator constFindImpl(const K &key) const noexcept
2284     {
2285         if (isEmpty())
2286             return end();
2287         auto it = d->findBucket(key);
2288         if (it.isUnused())
2289             return constEnd();
2290         return const_iterator(it.toIterator(d));
2291     }
2292 public:
2293     iterator find(const Key &key)
2294     {
2295         return findImpl(key);
2296     }
2297     const_iterator constFind(const Key &key) const noexcept
2298     {
2299         return constFindImpl(key);
2300     }
2301     const_iterator find(const Key &key) const noexcept
2302     {
2303         return constFindImpl(key);
2304     }
2305 
2306     iterator insert(const Key &key, const T &value)
2307     {
2308         return emplace(key, value);
2309     }
2310 
2311     iterator insert(const Key &key, T &&value)
2312     {
2313         return emplace(key, std::move(value));
2314     }
2315 
2316     iterator insert(Key &&key, const T &value)
2317     {
2318         return emplace(std::move(key), value);
2319     }
2320 
2321     iterator insert(Key &&key, T &&value)
2322     {
2323         return emplace(std::move(key), std::move(value));
2324     }
2325 
2326     template <typename ...Args>
2327     iterator emplace(const Key &key, Args &&... args)
2328     {
2329         return emplace(Key(key), std::forward<Args>(args)...);
2330     }
2331 
2332     template <typename ...Args>
2333     iterator emplace(Key &&key, Args &&... args)
2334     {
2335         if (isDetached()) {
2336             if (d->shouldGrow()) // Construct the value now so that no dangling references are used
2337                 return emplace_helper(std::move(key), T(std::forward<Args>(args)...));
2338             return emplace_helper(std::move(key), std::forward<Args>(args)...);
2339         }
2340         // else: we must detach
2341         const auto copy = *this; // keep 'args' alive across the detach/growth
2342         detach();
2343         return emplace_helper(std::move(key), std::forward<Args>(args)...);
2344     }
2345 
2346 
2347     float load_factor() const noexcept { return d ? d->loadFactor() : 0; }
2348     static float max_load_factor() noexcept { return 0.5; }
2349     size_t bucket_count() const noexcept { return d ? d->numBuckets : 0; }
2350     static size_t max_bucket_count() noexcept { return Data::maxNumBuckets(); }
2351 
2352     [[nodiscard]]
2353     inline bool empty() const noexcept { return isEmpty(); }
2354 
2355     inline iterator replace(const Key &key, const T &value)
2356     {
2357         return emplaceReplace(key, value);
2358     }
2359 
2360     template <typename ...Args>
2361     iterator emplaceReplace(const Key &key, Args &&... args)
2362     {
2363         return emplaceReplace(Key(key), std::forward<Args>(args)...);
2364     }
2365 
2366     template <typename ...Args>
2367     iterator emplaceReplace(Key &&key, Args &&... args)
2368     {
2369         if (isDetached()) {
2370             if (d->shouldGrow()) // Construct the value now so that no dangling references are used
2371                 return emplaceReplace_helper(std::move(key), T(std::forward<Args>(args)...));
2372             return emplaceReplace_helper(std::move(key), std::forward<Args>(args)...);
2373         }
2374         // else: we must detach
2375         const auto copy = *this; // keep 'args' alive across the detach/growth
2376         detach();
2377         return emplaceReplace_helper(std::move(key), std::forward<Args>(args)...);
2378     }
2379 
2380     inline QMultiHash &operator+=(const QMultiHash &other)
2381     { this->unite(other); return *this; }
2382     inline QMultiHash operator+(const QMultiHash &other) const
2383     { QMultiHash result = *this; result += other; return result; }
2384 
2385     bool contains(const Key &key, const T &value) const noexcept
2386     {
2387         return containsImpl(key, value);
2388     }
2389 private:
2390     template <typename K> bool containsImpl(const K &key, const T &value) const noexcept
2391     {
2392         if (isEmpty())
2393             return false;
2394         auto n = d->findNode(key);
2395         if (n == nullptr)
2396             return false;
2397         return n->value->contains(value);
2398     }
2399 
2400 public:
2401     qsizetype remove(const Key &key, const T &value)
2402     {
2403         return removeImpl(key, value);
2404     }
2405 private:
2406     template <typename K> qsizetype removeImpl(const K &key, const T &value)
2407     {
2408         if (isEmpty()) // prevents detaching shared null
2409             return 0;
2410         auto it = d->findBucket(key);
2411         size_t bucket = it.toBucketIndex(d);
2412         detach();
2413         it = typename Data::Bucket(d, bucket); // reattach in case of detach
2414 
2415         if (it.isUnused())
2416             return 0;
2417         qsizetype n = 0;
2418         Chain **e = &it.node()->value;
2419         while (*e) {
2420             Chain *entry = *e;
2421             if (entry->value == value) {
2422                 *e = entry->next;
2423                 delete entry;
2424                 ++n;
2425             } else {
2426                 e = &entry->next;
2427             }
2428         }
2429         if (!it.node()->value)
2430             d->erase(it);
2431         m_size -= n;
2432         Q_ASSERT(m_size >= 0);
2433         return n;
2434     }
2435 
2436 public:
2437     qsizetype count(const Key &key) const noexcept
2438     {
2439         return countImpl(key);
2440     }
2441 private:
2442     template <typename K> qsizetype countImpl(const K &key) const noexcept
2443     {
2444         if (!d)
2445             return 0;
2446         auto it = d->findBucket(key);
2447         if (it.isUnused())
2448             return 0;
2449         qsizetype n = 0;
2450         Chain *e = it.node()->value;
2451         while (e) {
2452             ++n;
2453             e = e->next;
2454         }
2455 
2456         return n;
2457     }
2458 
2459 public:
2460     qsizetype count(const Key &key, const T &value) const noexcept
2461     {
2462         return countImpl(key, value);
2463     }
2464 private:
2465     template <typename K> qsizetype countImpl(const K &key, const T &value) const noexcept
2466     {
2467         if (!d)
2468             return 0;
2469         auto it = d->findBucket(key);
2470         if (it.isUnused())
2471             return 0;
2472         qsizetype n = 0;
2473         Chain *e = it.node()->value;
2474         while (e) {
2475             if (e->value == value)
2476                 ++n;
2477             e = e->next;
2478         }
2479 
2480         return n;
2481     }
2482 
2483     template <typename K> iterator findImpl(const K &key, const T &value)
2484     {
2485         if (isEmpty())
2486             return end();
2487         const auto copy = isDetached() ? QMultiHash() : *this; // keep 'key'/'value' alive across the detach
2488         detach();
2489         auto it = constFind(key, value);
2490         return iterator(it.i, it.e);
2491     }
2492     template <typename K> const_iterator constFindImpl(const K &key, const T &value) const noexcept
2493     {
2494         const_iterator i(constFind(key));
2495         const_iterator end(constEnd());
2496         while (i != end && i.key() == key) {
2497             if (i.value() == value)
2498                 return i;
2499             ++i;
2500         }
2501         return end;
2502     }
2503 
2504 public:
2505     iterator find(const Key &key, const T &value)
2506     {
2507         return findImpl(key, value);
2508     }
2509 
2510     const_iterator constFind(const Key &key, const T &value) const noexcept
2511     {
2512         return constFindImpl(key, value);
2513     }
2514     const_iterator find(const Key &key, const T &value) const noexcept
2515     {
2516         return constFind(key, value);
2517     }
2518 
2519     QMultiHash &unite(const QMultiHash &other)
2520     {
2521         if (isEmpty()) {
2522             *this = other;
2523         } else if (other.isEmpty()) {
2524             ;
2525         } else {
2526             QMultiHash copy(other);
2527             detach();
2528             for (auto cit = copy.cbegin(); cit != copy.cend(); ++cit)
2529                 insert(cit.key(), *cit);
2530         }
2531         return *this;
2532     }
2533 
2534     QMultiHash &unite(const QHash<Key, T> &other)
2535     {
2536         for (auto cit = other.cbegin(); cit != other.cend(); ++cit)
2537             insert(cit.key(), *cit);
2538         return *this;
2539     }
2540 
2541     QMultiHash &unite(QHash<Key, T> &&other)
2542     {
2543         if (!other.isDetached()) {
2544             unite(other);
2545             return *this;
2546         }
2547         auto it = other.d->begin();
2548         for (const auto end = other.d->end(); it != end; ++it)
2549             emplace(std::move(it.node()->key), it.node()->takeValue());
2550         other.clear();
2551         return *this;
2552     }
2553 
2554     std::pair<iterator, iterator> equal_range(const Key &key)
2555     {
2556         return equal_range_impl(key);
2557     }
2558 private:
2559     template <typename K> std::pair<iterator, iterator> equal_range_impl(const K &key)
2560     {
2561         const auto copy = isDetached() ? QMultiHash() : *this; // keep 'key' alive across the detach
2562         detach();
2563         auto pair = std::as_const(*this).equal_range(key);
2564         return {iterator(pair.first.i), iterator(pair.second.i)};
2565     }
2566 
2567 public:
2568     std::pair<const_iterator, const_iterator> equal_range(const Key &key) const noexcept
2569     {
2570         return equal_range_impl(key);
2571     }
2572 private:
2573     template <typename K> std::pair<const_iterator, const_iterator> equal_range_impl(const K &key) const noexcept
2574     {
2575         if (!d)
2576             return {end(), end()};
2577 
2578         auto bucket = d->findBucket(key);
2579         if (bucket.isUnused())
2580             return {end(), end()};
2581         auto it = bucket.toIterator(d);
2582         auto end = it;
2583         ++end;
2584         return {const_iterator(it), const_iterator(end)};
2585     }
2586 
2587     void detach_helper()
2588     {
2589         if (!d) {
2590             d = new Data;
2591             return;
2592         }
2593         Data *dd = new Data(*d);
2594         if (!d->ref.deref())
2595             delete d;
2596         d = dd;
2597     }
2598 
2599     template<typename... Args>
2600     iterator emplace_helper(Key &&key, Args &&...args)
2601     {
2602         auto result = d->findOrInsert(key);
2603         if (!result.initialized)
2604             Node::createInPlace(result.it.node(), std::move(key), std::forward<Args>(args)...);
2605         else
2606             result.it.node()->insertMulti(std::forward<Args>(args)...);
2607         ++m_size;
2608         return iterator(result.it);
2609     }
2610 
2611     template<typename... Args>
2612     iterator emplaceReplace_helper(Key &&key, Args &&...args)
2613     {
2614         auto result = d->findOrInsert(key);
2615         if (!result.initialized) {
2616             Node::createInPlace(result.it.node(), std::move(key), std::forward<Args>(args)...);
2617             ++m_size;
2618         } else {
2619             result.it.node()->emplaceValue(std::forward<Args>(args)...);
2620         }
2621         return iterator(result.it);
2622     }
2623 
2624     template <typename K>
2625     using if_heterogeneously_searchable = QHashPrivate::if_heterogeneously_searchable_with<Key, K>;
2626 
2627     template <typename K>
2628     using if_key_constructible_from = std::enable_if_t<std::is_constructible_v<Key, K>, bool>;
2629 
2630 public:
2631     template <typename K, if_heterogeneously_searchable<K> = true>
2632     qsizetype remove(const K &key)
2633     {
2634         return removeImpl(key);
2635     }
2636     template <typename K, if_heterogeneously_searchable<K> = true>
2637     T take(const K &key)
2638     {
2639         return takeImpl(key);
2640     }
2641     template <typename K, if_heterogeneously_searchable<K> = true>
2642     bool contains(const K &key) const noexcept
2643     {
2644         if (!d)
2645             return false;
2646         return d->findNode(key) != nullptr;
2647     }
2648     template <typename K, if_heterogeneously_searchable<K> = true>
2649     T value(const K &key) const noexcept
2650     {
2651         if (auto *v = valueImpl(key))
2652             return *v;
2653         else
2654             return T();
2655     }
2656     template <typename K, if_heterogeneously_searchable<K> = true>
2657     T value(const K &key, const T &defaultValue) const noexcept
2658     {
2659         if (auto *v = valueImpl(key))
2660             return *v;
2661         else
2662             return defaultValue;
2663     }
2664     template <typename K, if_heterogeneously_searchable<K> = true, if_key_constructible_from<K> = true>
2665     T &operator[](const K &key)
2666     {
2667         return operatorIndexImpl(key);
2668     }
2669     template <typename K, if_heterogeneously_searchable<K> = true>
2670     const T operator[](const K &key) const noexcept
2671     {
2672         return value(key);
2673     }
2674     template <typename K, if_heterogeneously_searchable<K> = true>
2675     QList<T> values(const K &key)
2676     {
2677         return valuesImpl(key);
2678     }
2679     template <typename K, if_heterogeneously_searchable<K> = true>
2680     iterator find(const K &key)
2681     {
2682         return findImpl(key);
2683     }
2684     template <typename K, if_heterogeneously_searchable<K> = true>
2685     const_iterator constFind(const K &key) const noexcept
2686     {
2687         return constFindImpl(key);
2688     }
2689     template <typename K, if_heterogeneously_searchable<K> = true>
2690     const_iterator find(const K &key) const noexcept
2691     {
2692         return constFindImpl(key);
2693     }
2694     template <typename K, if_heterogeneously_searchable<K> = true>
2695     bool contains(const K &key, const T &value) const noexcept
2696     {
2697         return containsImpl(key, value);
2698     }
2699     template <typename K, if_heterogeneously_searchable<K> = true>
2700     qsizetype remove(const K &key, const T &value)
2701     {
2702         return removeImpl(key, value);
2703     }
2704     template <typename K, if_heterogeneously_searchable<K> = true>
2705     qsizetype count(const K &key) const noexcept
2706     {
2707         return countImpl(key);
2708     }
2709     template <typename K, if_heterogeneously_searchable<K> = true>
2710     qsizetype count(const K &key, const T &value) const noexcept
2711     {
2712         return countImpl(key, value);
2713     }
2714     template <typename K, if_heterogeneously_searchable<K> = true>
2715     iterator find(const K &key, const T &value)
2716     {
2717         return findImpl(key, value);
2718     }
2719     template <typename K, if_heterogeneously_searchable<K> = true>
2720     const_iterator constFind(const K &key, const T &value) const noexcept
2721     {
2722         return constFindImpl(key, value);
2723     }
2724     template <typename K, if_heterogeneously_searchable<K> = true>
2725     const_iterator find(const K &key, const T &value) const noexcept
2726     {
2727         return constFind(key, value);
2728     }
2729     template <typename K, if_heterogeneously_searchable<K> = true>
2730     std::pair<iterator, iterator>
2731     equal_range(const K &key)
2732     {
2733         return equal_range_impl(key);
2734     }
2735     template <typename K, if_heterogeneously_searchable<K> = true>
2736     std::pair<const_iterator, const_iterator>
2737     equal_range(const K &key) const noexcept
2738     {
2739         return equal_range_impl(key);
2740     }
2741 };
2742 
2743 Q_DECLARE_ASSOCIATIVE_FORWARD_ITERATOR(Hash)
2744 Q_DECLARE_MUTABLE_ASSOCIATIVE_FORWARD_ITERATOR(Hash)
2745 Q_DECLARE_ASSOCIATIVE_FORWARD_ITERATOR(MultiHash)
2746 Q_DECLARE_MUTABLE_ASSOCIATIVE_FORWARD_ITERATOR(MultiHash)
2747 
2748 template <class Key, class T>
2749 size_t qHash(const QHash<Key, T> &key, size_t seed = 0)
2750     noexcept(noexcept(qHash(std::declval<Key&>())) && noexcept(qHash(std::declval<T&>())))
2751 {
2752     const QtPrivate::QHashCombine combine(seed);
2753     size_t hash = 0;
2754     for (auto it = key.begin(), end = key.end(); it != end; ++it) {
2755         size_t h = combine(seed, it.key());
2756         // use + to keep the result independent of the ordering of the keys
2757         hash += combine(h, it.value());
2758     }
2759     return hash;
2760 }
2761 
2762 template <class Key, class T>
2763 inline size_t qHash(const QMultiHash<Key, T> &key, size_t seed = 0)
2764     noexcept(noexcept(qHash(std::declval<Key&>())) && noexcept(qHash(std::declval<T&>())))
2765 {
2766     const QtPrivate::QHashCombine combine(seed);
2767     size_t hash = 0;
2768     for (auto it = key.begin(), end = key.end(); it != end; ++it) {
2769         size_t h = combine(seed, it.key());
2770         // use + to keep the result independent of the ordering of the keys
2771         hash += combine(h, it.value());
2772     }
2773     return hash;
2774 }
2775 
2776 template <typename Key, typename T, typename Predicate>
2777 qsizetype erase_if(QHash<Key, T> &hash, Predicate pred)
2778 {
2779     return QtPrivate::associative_erase_if(hash, pred);
2780 }
2781 
2782 template <typename Key, typename T, typename Predicate>
2783 qsizetype erase_if(QMultiHash<Key, T> &hash, Predicate pred)
2784 {
2785     return QtPrivate::associative_erase_if(hash, pred);
2786 }
2787 
2788 QT_END_NAMESPACE
2789 
2790 #endif // QHASH_H