Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:49:20

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