Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-17 09:09:19

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(std::is_nothrow_move_assignable_v<T>)
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() { 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(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         static_assert(std::is_same_v<std::remove_cv_t<Key>, K> ||
0689                 QHashHeterogeneousSearch<std::remove_cv_t<Key>, K>::value);
0690         Q_ASSERT(numBuckets > 0);
0691         size_t hash = QHashPrivate::calculateHash(key, seed);
0692         Bucket bucket(this, GrowthPolicy::bucketForHash(numBuckets, hash));
0693         // loop over the buckets until we find the entry we search for
0694         // or an empty slot, in which case we know the entry doesn't exist
0695         while (true) {
0696             size_t offset = bucket.offset();
0697             if (offset == SpanConstants::UnusedEntry) {
0698                 return bucket;
0699             } else {
0700                 Node &n = bucket.nodeAtOffset(offset);
0701                 if (qHashEquals(n.key, key))
0702                     return bucket;
0703             }
0704             bucket.advanceWrapped(this);
0705         }
0706     }
0707 
0708     template <typename K> Node *findNode(const K &key) const noexcept
0709     {
0710         auto bucket = findBucket(key);
0711         if (bucket.isUnused())
0712             return nullptr;
0713         return bucket.node();
0714     }
0715 
0716     struct InsertionResult
0717     {
0718         iterator it;
0719         bool initialized;
0720     };
0721 
0722     template <typename K> InsertionResult findOrInsert(const K &key) noexcept
0723     {
0724         Bucket it(static_cast<Span *>(nullptr), 0);
0725         if (numBuckets > 0) {
0726             it = findBucket(key);
0727             if (!it.isUnused())
0728                 return { it.toIterator(this), true };
0729         }
0730         if (shouldGrow()) {
0731             rehash(size + 1);
0732             it = findBucket(key); // need to get a new iterator after rehashing
0733         }
0734         Q_ASSERT(it.span != nullptr);
0735         Q_ASSERT(it.isUnused());
0736         it.insert();
0737         ++size;
0738         return { it.toIterator(this), false };
0739     }
0740 
0741     void erase(Bucket bucket) noexcept(std::is_nothrow_destructible<Node>::value)
0742     {
0743         Q_ASSERT(bucket.span->hasNode(bucket.index));
0744         bucket.span->erase(bucket.index);
0745         --size;
0746 
0747         // re-insert the following entries to avoid holes
0748         Bucket next = bucket;
0749         while (true) {
0750             next.advanceWrapped(this);
0751             size_t offset = next.offset();
0752             if (offset == SpanConstants::UnusedEntry)
0753                 return;
0754             size_t hash = QHashPrivate::calculateHash(next.nodeAtOffset(offset).key, seed);
0755             Bucket newBucket(this, GrowthPolicy::bucketForHash(numBuckets, hash));
0756             while (true) {
0757                 if (newBucket == next) {
0758                     // nothing to do, item is at the right plae
0759                     break;
0760                 } else if (newBucket == bucket) {
0761                     // move into the hole we created earlier
0762                     if (next.span == bucket.span) {
0763                         bucket.span->moveLocal(next.index, bucket.index);
0764                     } else {
0765                         // move between spans, more expensive
0766                         bucket.span->moveFromSpan(*next.span, next.index, bucket.index);
0767                     }
0768                     bucket = next;
0769                     break;
0770                 }
0771                 newBucket.advanceWrapped(this);
0772             }
0773         }
0774     }
0775 
0776     ~Data()
0777     {
0778         delete [] spans;
0779     }
0780 };
0781 
0782 template <typename Node>
0783 struct iterator {
0784     using Span = QHashPrivate::Span<Node>;
0785 
0786     const Data<Node> *d = nullptr;
0787     size_t bucket = 0;
0788 
0789     size_t span() const noexcept { return bucket >> SpanConstants::SpanShift; }
0790     size_t index() const noexcept { return bucket & SpanConstants::LocalBucketMask; }
0791     inline bool isUnused() const noexcept { return !d->spans[span()].hasNode(index()); }
0792 
0793     inline Node *node() const noexcept
0794     {
0795         Q_ASSERT(!isUnused());
0796         return &d->spans[span()].at(index());
0797     }
0798     bool atEnd() const noexcept { return !d; }
0799 
0800     iterator operator++() noexcept
0801     {
0802         while (true) {
0803             ++bucket;
0804             if (bucket == d->numBuckets) {
0805                 d = nullptr;
0806                 bucket = 0;
0807                 break;
0808             }
0809             if (!isUnused())
0810                 break;
0811         }
0812         return *this;
0813     }
0814     bool operator==(iterator other) const noexcept
0815     { return d == other.d && bucket == other.bucket; }
0816     bool operator!=(iterator other) const noexcept
0817     { return !(*this == other); }
0818 };
0819 
0820 template <typename HashKey, typename KeyArgument>
0821 using HeterogenousConstructProxy = std::conditional_t<
0822         std::is_same_v<HashKey, q20::remove_cvref_t<KeyArgument>>,
0823         KeyArgument, // HashKey == KeyArg w/ potential modifiers, so we keep modifiers
0824         HashKey
0825     >;
0826 
0827 } // namespace QHashPrivate
0828 
0829 template <typename Key, typename T>
0830 class QHash
0831 {
0832     using Node = QHashPrivate::Node<Key, T>;
0833     using Data = QHashPrivate::Data<Node>;
0834     friend class QSet<Key>;
0835     friend class QMultiHash<Key, T>;
0836     friend tst_QHash;
0837 
0838     Data *d = nullptr;
0839 
0840 public:
0841     using key_type = Key;
0842     using mapped_type = T;
0843     using value_type = T;
0844     using size_type = qsizetype;
0845     using difference_type = qsizetype;
0846     using reference = T &;
0847     using const_reference = const T &;
0848 
0849     inline QHash() noexcept = default;
0850     inline QHash(std::initializer_list<std::pair<Key,T> > list)
0851         : d(new Data(list.size()))
0852     {
0853         for (typename std::initializer_list<std::pair<Key,T> >::const_iterator it = list.begin(); it != list.end(); ++it)
0854             insert(it->first, it->second);
0855     }
0856     QHash(const QHash &other) noexcept
0857         : d(other.d)
0858     {
0859         if (d)
0860             d->ref.ref();
0861     }
0862     ~QHash()
0863     {
0864         static_assert(std::is_nothrow_destructible_v<Key>, "Types with throwing destructors are not supported in Qt containers.");
0865         static_assert(std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers.");
0866 
0867         if (d && !d->ref.deref())
0868             delete d;
0869     }
0870 
0871     QHash &operator=(const QHash &other) noexcept(std::is_nothrow_destructible<Node>::value)
0872     {
0873         if (d != other.d) {
0874             Data *o = other.d;
0875             if (o)
0876                 o->ref.ref();
0877             if (d && !d->ref.deref())
0878                 delete d;
0879             d = o;
0880         }
0881         return *this;
0882     }
0883 
0884     QHash(QHash &&other) noexcept
0885         : d(std::exchange(other.d, nullptr))
0886     {
0887     }
0888     QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QHash)
0889 #ifdef Q_QDOC
0890     template <typename InputIterator>
0891     QHash(InputIterator f, InputIterator l);
0892 #else
0893     template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasKeyAndValue<InputIterator> = true>
0894     QHash(InputIterator f, InputIterator l)
0895         : QHash()
0896     {
0897         QtPrivate::reserveIfForwardIterator(this, f, l);
0898         for (; f != l; ++f)
0899             insert(f.key(), f.value());
0900     }
0901 
0902     template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasFirstAndSecond<InputIterator> = true>
0903     QHash(InputIterator f, InputIterator l)
0904         : QHash()
0905     {
0906         QtPrivate::reserveIfForwardIterator(this, f, l);
0907         for (; f != l; ++f) {
0908             auto &&e = *f;
0909             using V = decltype(e);
0910             insert(std::forward<V>(e).first, std::forward<V>(e).second);
0911         }
0912     }
0913 #endif
0914     void swap(QHash &other) noexcept { qt_ptr_swap(d, other.d); }
0915 
0916     class const_iterator;
0917 
0918 #ifndef Q_QDOC
0919 private:
0920     static bool compareIterators(const const_iterator &lhs, const const_iterator &rhs)
0921     {
0922         return lhs.i.node()->valuesEqual(rhs.i.node());
0923     }
0924 
0925     template <typename AKey = Key, typename AT = T,
0926               QTypeTraits::compare_eq_result_container<QHash, AKey, AT> = true>
0927     friend bool comparesEqual(const QHash &lhs, const QHash &rhs) noexcept
0928     {
0929         if (lhs.d == rhs.d)
0930             return true;
0931         if (lhs.size() != rhs.size())
0932             return false;
0933 
0934         for (const_iterator it = rhs.begin(); it != rhs.end(); ++it) {
0935             const_iterator i = lhs.find(it.key());
0936             if (i == lhs.end() || !compareIterators(i, it))
0937                 return false;
0938         }
0939         // all values must be the same as size is the same
0940         return true;
0941     }
0942     QT_DECLARE_EQUALITY_OPERATORS_HELPER(QHash, QHash, /* non-constexpr */, noexcept,
0943                      template <typename AKey = Key, typename AT = T,
0944                                QTypeTraits::compare_eq_result_container<QHash, AKey, AT> = true>)
0945 public:
0946 #else
0947     friend bool operator==(const QHash &lhs, const QHash &rhs) noexcept;
0948     friend bool operator!=(const QHash &lhs, const QHash &rhs) noexcept;
0949 #endif // Q_QDOC
0950 
0951     inline qsizetype size() const noexcept { return d ? qsizetype(d->size) : 0; }
0952 
0953     [[nodiscard]]
0954     inline bool isEmpty() const noexcept { return !d || d->size == 0; }
0955 
0956     inline qsizetype capacity() const noexcept { return d ? qsizetype(d->numBuckets >> 1) : 0; }
0957     void reserve(qsizetype size)
0958     {
0959         // reserve(0) is used in squeeze()
0960         if (size && (this->capacity() >= size))
0961             return;
0962         if (isDetached())
0963             d->rehash(size);
0964         else
0965             d = Data::detached(d, size_t(size));
0966     }
0967     inline void squeeze()
0968     {
0969         if (capacity())
0970             reserve(0);
0971     }
0972 
0973     inline void detach() { if (!d || d->ref.isShared()) d = Data::detached(d); }
0974     inline bool isDetached() const noexcept { return d && !d->ref.isShared(); }
0975     bool isSharedWith(const QHash &other) const noexcept { return d == other.d; }
0976 
0977     void clear() noexcept(std::is_nothrow_destructible<Node>::value)
0978     {
0979         if (d && !d->ref.deref())
0980             delete d;
0981         d = nullptr;
0982     }
0983 
0984     bool remove(const Key &key)
0985     {
0986         return removeImpl(key);
0987     }
0988 private:
0989     template <typename K> bool removeImpl(const K &key)
0990     {
0991         if (isEmpty()) // prevents detaching shared null
0992             return false;
0993         auto it = d->findBucket(key);
0994         if (it.isUnused())
0995             return false;
0996 
0997         size_t bucket = it.toBucketIndex(d);
0998         detach();
0999         it = typename Data::Bucket(d, bucket); // reattach in case of detach
1000 
1001         d->erase(it);
1002         return true;
1003     }
1004 
1005 public:
1006     template <typename Predicate>
1007     qsizetype removeIf(Predicate pred)
1008     {
1009         return QtPrivate::associative_erase_if(*this, pred);
1010     }
1011 
1012     T take(const Key &key)
1013     {
1014         return takeImpl(key);
1015     }
1016 private:
1017     template <typename K> T takeImpl(const K &key)
1018     {
1019         if (isEmpty()) // prevents detaching shared null
1020             return T();
1021         auto it = d->findBucket(key);
1022         size_t bucket = it.toBucketIndex(d);
1023         detach();
1024         it = typename Data::Bucket(d, bucket); // reattach in case of detach
1025 
1026         if (it.isUnused())
1027             return T();
1028         T value = it.node()->takeValue();
1029         d->erase(it);
1030         return value;
1031     }
1032 
1033 public:
1034     bool contains(const Key &key) const noexcept
1035     {
1036         if (!d)
1037             return false;
1038         return d->findNode(key) != nullptr;
1039     }
1040     qsizetype count(const Key &key) const noexcept
1041     {
1042         return contains(key) ? 1 : 0;
1043     }
1044 
1045 private:
1046     const Key *keyImpl(const T &value) const noexcept
1047     {
1048         if (d) {
1049             const_iterator i = begin();
1050             while (i != end()) {
1051                 if (i.value() == value)
1052                     return &i.key();
1053                 ++i;
1054             }
1055         }
1056 
1057         return nullptr;
1058     }
1059 
1060 public:
1061     Key key(const T &value) const noexcept
1062     {
1063         if (auto *k = keyImpl(value))
1064             return *k;
1065         else
1066             return Key();
1067     }
1068     Key key(const T &value, const Key &defaultKey) const noexcept
1069     {
1070         if (auto *k = keyImpl(value))
1071             return *k;
1072         else
1073             return defaultKey;
1074     }
1075 
1076 private:
1077     template <typename K>
1078     T *valueImpl(const K &key) const noexcept
1079     {
1080         if (d) {
1081             Node *n = d->findNode(key);
1082             if (n)
1083                 return &n->value;
1084         }
1085         return nullptr;
1086     }
1087 public:
1088     T value(const Key &key) const noexcept
1089     {
1090         if (T *v = valueImpl(key))
1091             return *v;
1092         else
1093             return T();
1094     }
1095 
1096     T value(const Key &key, const T &defaultValue) const noexcept
1097     {
1098         if (T *v = valueImpl(key))
1099             return *v;
1100         else
1101             return defaultValue;
1102     }
1103 
1104     T &operator[](const Key &key)
1105     {
1106         return *tryEmplace(key).iterator;
1107     }
1108 
1109     const T operator[](const Key &key) const noexcept
1110     {
1111         return value(key);
1112     }
1113 
1114     QList<Key> keys() const { return QList<Key>(keyBegin(), keyEnd()); }
1115     QList<Key> keys(const T &value) const
1116     {
1117         QList<Key> res;
1118         const_iterator i = begin();
1119         while (i != end()) {
1120             if (i.value() == value)
1121                 res.append(i.key());
1122             ++i;
1123         }
1124         return res;
1125     }
1126     QList<T> values() const { return QList<T>(begin(), end()); }
1127 
1128     class iterator
1129     {
1130         using piter = typename QHashPrivate::iterator<Node>;
1131         friend class const_iterator;
1132         friend class QHash<Key, T>;
1133         friend class QSet<Key>;
1134         piter i;
1135         explicit inline iterator(piter it) noexcept : i(it) { }
1136 
1137     public:
1138         typedef std::forward_iterator_tag iterator_category;
1139         typedef qptrdiff difference_type;
1140         typedef T value_type;
1141         typedef T *pointer;
1142         typedef T &reference;
1143 
1144         constexpr iterator() noexcept = default;
1145 
1146         inline const Key &key() const noexcept { return i.node()->key; }
1147         inline T &value() const noexcept { return i.node()->value; }
1148         inline T &operator*() const noexcept { return i.node()->value; }
1149         inline T *operator->() const noexcept { return &i.node()->value; }
1150         inline bool operator==(const iterator &o) const noexcept { return i == o.i; }
1151         inline bool operator!=(const iterator &o) const noexcept { return i != o.i; }
1152 
1153         inline iterator &operator++() noexcept
1154         {
1155             ++i;
1156             return *this;
1157         }
1158         inline iterator operator++(int) noexcept
1159         {
1160             iterator r = *this;
1161             ++i;
1162             return r;
1163         }
1164 
1165         inline bool operator==(const const_iterator &o) const noexcept { return i == o.i; }
1166         inline bool operator!=(const const_iterator &o) const noexcept { return i != o.i; }
1167     };
1168     friend class iterator;
1169 
1170     class const_iterator
1171     {
1172         using piter = typename QHashPrivate::iterator<Node>;
1173         friend class iterator;
1174         friend class QHash<Key, T>;
1175         friend class QSet<Key>;
1176         piter i;
1177         explicit inline const_iterator(piter it) : i(it) { }
1178 
1179     public:
1180         typedef std::forward_iterator_tag iterator_category;
1181         typedef qptrdiff difference_type;
1182         typedef T value_type;
1183         typedef const T *pointer;
1184         typedef const T &reference;
1185 
1186         constexpr const_iterator() noexcept = default;
1187         inline const_iterator(const iterator &o) noexcept : i(o.i) { }
1188 
1189         inline const Key &key() const noexcept { return i.node()->key; }
1190         inline const T &value() const noexcept { return i.node()->value; }
1191         inline const T &operator*() const noexcept { return i.node()->value; }
1192         inline const T *operator->() const noexcept { return &i.node()->value; }
1193         inline bool operator==(const const_iterator &o) const noexcept { return i == o.i; }
1194         inline bool operator!=(const const_iterator &o) const noexcept { return i != o.i; }
1195 
1196         inline const_iterator &operator++() noexcept
1197         {
1198             ++i;
1199             return *this;
1200         }
1201         inline const_iterator operator++(int) noexcept
1202         {
1203             const_iterator r = *this;
1204             ++i;
1205             return r;
1206         }
1207     };
1208     friend class const_iterator;
1209 
1210     class key_iterator
1211     {
1212         const_iterator i;
1213 
1214     public:
1215         typedef typename const_iterator::iterator_category iterator_category;
1216         typedef qptrdiff difference_type;
1217         typedef Key value_type;
1218         typedef const Key *pointer;
1219         typedef const Key &reference;
1220 
1221         key_iterator() noexcept = default;
1222         explicit key_iterator(const_iterator o) noexcept : i(o) { }
1223 
1224         const Key &operator*() const noexcept { return i.key(); }
1225         const Key *operator->() const noexcept { return &i.key(); }
1226         bool operator==(key_iterator o) const noexcept { return i == o.i; }
1227         bool operator!=(key_iterator o) const noexcept { return i != o.i; }
1228 
1229         inline key_iterator &operator++() noexcept { ++i; return *this; }
1230         inline key_iterator operator++(int) noexcept { return key_iterator(i++);}
1231         const_iterator base() const noexcept { return i; }
1232     };
1233 
1234     typedef QKeyValueIterator<const Key&, const T&, const_iterator> const_key_value_iterator;
1235     typedef QKeyValueIterator<const Key&, T&, iterator> key_value_iterator;
1236 
1237     // STL style
1238     inline iterator begin() { if (!d) return iterator(); detach(); return iterator(d->begin()); }
1239     inline const_iterator begin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
1240     inline const_iterator cbegin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
1241     inline const_iterator constBegin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
1242     inline iterator end() noexcept { return iterator(); }
1243     inline const_iterator end() const noexcept { return const_iterator(); }
1244     inline const_iterator cend() const noexcept { return const_iterator(); }
1245     inline const_iterator constEnd() const noexcept { return const_iterator(); }
1246     inline key_iterator keyBegin() const noexcept { return key_iterator(begin()); }
1247     inline key_iterator keyEnd() const noexcept { return key_iterator(end()); }
1248     inline key_value_iterator keyValueBegin() { return key_value_iterator(begin()); }
1249     inline key_value_iterator keyValueEnd() { return key_value_iterator(end()); }
1250     inline const_key_value_iterator keyValueBegin() const noexcept { return const_key_value_iterator(begin()); }
1251     inline const_key_value_iterator constKeyValueBegin() const noexcept { return const_key_value_iterator(begin()); }
1252     inline const_key_value_iterator keyValueEnd() const noexcept { return const_key_value_iterator(end()); }
1253     inline const_key_value_iterator constKeyValueEnd() const noexcept { return const_key_value_iterator(end()); }
1254     auto asKeyValueRange() & { return QtPrivate::QKeyValueRange(*this); }
1255     auto asKeyValueRange() const & { return QtPrivate::QKeyValueRange(*this); }
1256     auto asKeyValueRange() && { return QtPrivate::QKeyValueRange(std::move(*this)); }
1257     auto asKeyValueRange() const && { return QtPrivate::QKeyValueRange(std::move(*this)); }
1258 
1259     struct TryEmplaceResult
1260     {
1261         QHash::iterator iterator;
1262         bool inserted;
1263 
1264         TryEmplaceResult() = default;
1265         // Generated SMFs are fine!
1266         TryEmplaceResult(QHash::iterator it, bool b)
1267             : iterator(it), inserted(b)
1268         {
1269         }
1270 
1271         // Implicit conversion _from_ the return-type of try_emplace:
1272         Q_IMPLICIT TryEmplaceResult(const std::pair<key_value_iterator, bool> &p)
1273             : iterator(p.first.base()), inserted(p.second)
1274         {
1275         }
1276         // Implicit conversion _to_ the return-type of try_emplace:
1277         Q_IMPLICIT operator std::pair<key_value_iterator, bool>()
1278         {
1279             return { key_value_iterator(iterator), inserted };
1280         }
1281     };
1282 
1283     iterator erase(const_iterator it)
1284     {
1285         Q_ASSERT(it != constEnd());
1286         detach();
1287         // ensure a valid iterator across the detach:
1288         iterator i = iterator{d->detachedIterator(it.i)};
1289         typename Data::Bucket bucket(i.i);
1290 
1291         d->erase(bucket);
1292         if (bucket.toBucketIndex(d) == d->numBuckets - 1 || bucket.isUnused())
1293             ++i;
1294         return i;
1295     }
1296 
1297     std::pair<iterator, iterator> equal_range(const Key &key)
1298     {
1299         return equal_range_impl(*this, key);
1300     }
1301     std::pair<const_iterator, const_iterator> equal_range(const Key &key) const noexcept
1302     {
1303         return equal_range_impl(*this, key);
1304     }
1305 private:
1306     template <typename Hash, typename K> static auto equal_range_impl(Hash &self, const K &key)
1307     {
1308         auto first = self.find(key);
1309         auto second = first;
1310         if (second != decltype(first){})
1311             ++second;
1312         return std::make_pair(first, second);
1313     }
1314 
1315     template <typename K> iterator findImpl(const K &key)
1316     {
1317         if (isEmpty()) // prevents detaching shared null
1318             return end();
1319         auto it = d->findBucket(key);
1320         size_t bucket = it.toBucketIndex(d);
1321         detach();
1322         it = typename Data::Bucket(d, bucket); // reattach in case of detach
1323         if (it.isUnused())
1324             return end();
1325         return iterator(it.toIterator(d));
1326     }
1327     template <typename K> const_iterator constFindImpl(const K &key) const noexcept
1328     {
1329         if (isEmpty())
1330             return end();
1331         auto it = d->findBucket(key);
1332         if (it.isUnused())
1333             return end();
1334         return const_iterator({d, it.toBucketIndex(d)});
1335     }
1336 
1337 public:
1338     typedef iterator Iterator;
1339     typedef const_iterator ConstIterator;
1340     inline qsizetype count() const noexcept { return d ? qsizetype(d->size) : 0; }
1341     iterator find(const Key &key)
1342     {
1343         return findImpl(key);
1344     }
1345     const_iterator find(const Key &key) const noexcept
1346     {
1347         return constFindImpl(key);
1348     }
1349     const_iterator constFind(const Key &key) const noexcept
1350     {
1351         return find(key);
1352     }
1353     iterator insert(const Key &key, const T &value)
1354     {
1355         return emplace(key, value);
1356     }
1357 
1358     void insert(const QHash &hash)
1359     {
1360         if (d == hash.d || !hash.d)
1361             return;
1362         if (!d) {
1363             *this = hash;
1364             return;
1365         }
1366 
1367         detach();
1368 
1369         for (auto it = hash.begin(); it != hash.end(); ++it)
1370             emplace(it.key(), it.value());
1371     }
1372 
1373     template <typename ...Args>
1374     iterator emplace(const Key &key, Args &&... args)
1375     {
1376         Key copy = key; // Needs to be explicit for MSVC 2019
1377         return emplace(std::move(copy), std::forward<Args>(args)...);
1378     }
1379 
1380     template <typename ...Args>
1381     iterator emplace(Key &&key, Args &&... args)
1382     {
1383         if (isDetached()) {
1384             if (d->shouldGrow()) // Construct the value now so that no dangling references are used
1385                 return emplace_helper(std::move(key), T(std::forward<Args>(args)...));
1386             return emplace_helper(std::move(key), std::forward<Args>(args)...);
1387         }
1388         // else: we must detach
1389         const auto copy = *this; // keep 'args' alive across the detach/growth
1390         detach();
1391         return emplace_helper(std::move(key), std::forward<Args>(args)...);
1392     }
1393 
1394     template <typename... Args>
1395     TryEmplaceResult tryEmplace(const Key &key, Args &&...args)
1396     {
1397         return tryEmplace_impl(key, std::forward<Args>(args)...);
1398     }
1399     template <typename... Args>
1400     TryEmplaceResult tryEmplace(Key &&key, Args &&...args)
1401     {
1402         return tryEmplace_impl(std::move(key), std::forward<Args>(args)...);
1403     }
1404 
1405     TryEmplaceResult tryInsert(const Key &key, const T &value)
1406     {
1407         return tryEmplace_impl(key, value);
1408     }
1409 
1410     template <typename... Args>
1411     std::pair<key_value_iterator, bool> try_emplace(const Key &key, Args &&...args)
1412     {
1413         return tryEmplace_impl(key, std::forward<Args>(args)...);
1414     }
1415     template <typename... Args>
1416     std::pair<key_value_iterator, bool> try_emplace(Key &&key, Args &&...args)
1417     {
1418         return tryEmplace_impl(std::move(key), std::forward<Args>(args)...);
1419     }
1420     template <typename... Args>
1421     key_value_iterator try_emplace(const_iterator /*hint*/, const Key &key, Args &&...args)
1422     {
1423         return key_value_iterator(tryEmplace_impl(key, std::forward<Args>(args)...).iterator);
1424     }
1425     template <typename... Args>
1426     key_value_iterator try_emplace(const_iterator /*hint*/, Key &&key, Args &&...args)
1427     {
1428         return key_value_iterator(tryEmplace_impl(std::move(key), std::forward<Args>(args)...).iterator);
1429     }
1430 
1431 private:
1432     template <typename K, typename... Args>
1433     TryEmplaceResult tryEmplace_impl(K &&key, Args &&...args)
1434     {
1435         if (!d)
1436             detach();
1437         QHash detachGuard;
1438 
1439         typename Data::Bucket bucket = d->findBucket(key);
1440         const bool shouldInsert = bucket.isUnused();
1441 
1442         // Even if we don't insert we may have to detach because we are
1443         // returning a non-const iterator:
1444         if (!isDetached() || (shouldInsert && d->shouldGrow())) {
1445             detachGuard = *this;
1446             const bool resized = shouldInsert && d->shouldGrow();
1447             const size_t bucketIndex = bucket.toBucketIndex(d);
1448 
1449             // Must detach from detachGuard
1450             d = resized ? Data::detached(d, d->size + 1) : Data::detached(d);
1451             bucket = resized ? d->findBucket(key) : typename Data::Bucket(d, bucketIndex);
1452         }
1453         if (shouldInsert) {
1454             Node *n = bucket.insert();
1455             using ConstructProxy = typename QHashPrivate::HeterogenousConstructProxy<Key, K>;
1456             Node::createInPlace(n, ConstructProxy(std::forward<K>(key)),
1457                                 std::forward<Args>(args)...);
1458             ++d->size;
1459         }
1460         return {iterator(bucket.toIterator(d)), shouldInsert};
1461     }
1462 public:
1463     template <typename Value>
1464     TryEmplaceResult insertOrAssign(const Key &key, Value &&value)
1465     {
1466         return insertOrAssign_impl(key, std::forward<Value>(value));
1467     }
1468     template <typename Value>
1469     TryEmplaceResult insertOrAssign(Key &&key, Value &&value)
1470     {
1471         return insertOrAssign_impl(std::move(key), std::forward<Value>(value));
1472     }
1473     template <typename Value>
1474     std::pair<key_value_iterator, bool> insert_or_assign(const Key &key, Value &&value)
1475     {
1476         return insertOrAssign_impl(key, std::forward<Value>(value));
1477     }
1478     template <typename Value>
1479     std::pair<key_value_iterator, bool> insert_or_assign(Key &&key, Value &&value)
1480     {
1481         return insertOrAssign_impl(std::move(key), std::forward<Value>(value));
1482     }
1483     template <typename Value>
1484     key_value_iterator insert_or_assign(const_iterator /*hint*/, const Key &key, Value &&value)
1485     {
1486         return key_value_iterator(insertOrAssign_impl(key, std::forward<Value>(value)).iterator);
1487     }
1488     template <typename Value>
1489     key_value_iterator insert_or_assign(const_iterator /*hint*/, Key &&key, Value &&value)
1490     {
1491         return key_value_iterator(insertOrAssign_impl(std::move(key), std::forward<Value>(value)).iterator);
1492     }
1493 
1494 private:
1495     template <typename K, typename Value>
1496     TryEmplaceResult insertOrAssign_impl(K &&key, Value &&value)
1497     {
1498         auto r = tryEmplace(std::forward<K>(key), std::forward<Value>(value));
1499         if (!r.inserted)
1500             *r.iterator = std::forward<Value>(value); // `value` is untouched if we get here
1501         return r;
1502     }
1503 
1504 public:
1505 
1506     float load_factor() const noexcept { return d ? d->loadFactor() : 0; }
1507     static float max_load_factor() noexcept { return 0.5; }
1508     size_t bucket_count() const noexcept { return d ? d->numBuckets : 0; }
1509     static size_t max_bucket_count() noexcept { return Data::maxNumBuckets(); }
1510 
1511     [[nodiscard]]
1512     inline bool empty() const noexcept { return isEmpty(); }
1513 
1514 private:
1515     template <typename ...Args>
1516     iterator emplace_helper(Key &&key, Args &&... args)
1517     {
1518         auto result = d->findOrInsert(key);
1519         if (!result.initialized)
1520             Node::createInPlace(result.it.node(), std::move(key), std::forward<Args>(args)...);
1521         else
1522             result.it.node()->emplaceValue(std::forward<Args>(args)...);
1523         return iterator(result.it);
1524     }
1525 
1526     template <typename K>
1527     using if_heterogeneously_searchable = QHashPrivate::if_heterogeneously_searchable_with<Key, K>;
1528 
1529     template <typename K>
1530     using if_key_constructible_from = std::enable_if_t<std::is_constructible_v<Key, K>, bool>;
1531 
1532 public:
1533     template <typename K, if_heterogeneously_searchable<K> = true>
1534     bool remove(const K &key)
1535     {
1536         return removeImpl(key);
1537     }
1538     template <typename K, if_heterogeneously_searchable<K> = true>
1539     T take(const K &key)
1540     {
1541         return takeImpl(key);
1542     }
1543     template <typename K, if_heterogeneously_searchable<K> = true>
1544     bool contains(const K &key) const
1545     {
1546         return d ? d->findNode(key) != nullptr : false;
1547     }
1548     template <typename K, if_heterogeneously_searchable<K> = true>
1549     qsizetype count(const K &key) const
1550     {
1551         return contains(key) ? 1 : 0;
1552     }
1553     template <typename K, if_heterogeneously_searchable<K> = true>
1554     T value(const K &key) const noexcept
1555     {
1556         if (auto *v = valueImpl(key))
1557             return *v;
1558         else
1559             return T();
1560     }
1561     template <typename K, if_heterogeneously_searchable<K> = true>
1562     T value(const K &key, const T &defaultValue) const noexcept
1563     {
1564         if (auto *v = valueImpl(key))
1565             return *v;
1566         else
1567             return defaultValue;
1568     }
1569     template <typename K, if_heterogeneously_searchable<K> = true, if_key_constructible_from<K> = true>
1570     T &operator[](const K &key)
1571     {
1572         return *tryEmplace(key).iterator;
1573     }
1574     template <typename K, if_heterogeneously_searchable<K> = true>
1575     const T operator[](const K &key) const noexcept
1576     {
1577         return value(key);
1578     }
1579     template <typename K, if_heterogeneously_searchable<K> = true>
1580     std::pair<iterator, iterator>
1581     equal_range(const K &key)
1582     {
1583         return equal_range_impl(*this, key);
1584     }
1585     template <typename K, if_heterogeneously_searchable<K> = true>
1586     std::pair<const_iterator, const_iterator>
1587     equal_range(const K &key) const noexcept
1588     {
1589         return equal_range_impl(*this, key);
1590     }
1591     template <typename K, if_heterogeneously_searchable<K> = true>
1592     iterator find(const K &key)
1593     {
1594         return findImpl(key);
1595     }
1596     template <typename K, if_heterogeneously_searchable<K> = true>
1597     const_iterator find(const K &key) const noexcept
1598     {
1599         return constFindImpl(key);
1600     }
1601     template <typename K, if_heterogeneously_searchable<K> = true>
1602     const_iterator constFind(const K &key) const noexcept
1603     {
1604         return find(key);
1605     }
1606     template <typename K, typename... Args, if_heterogeneously_searchable<K> = true, if_key_constructible_from<K> = true>
1607     TryEmplaceResult tryEmplace(K &&key, Args &&...args)
1608     {
1609         return tryEmplace_impl(std::forward<K>(key), std::forward<Args>(args)...);
1610     }
1611     template <typename K, if_heterogeneously_searchable<K> = true, if_key_constructible_from<K> = true>
1612     TryEmplaceResult tryInsert(K &&key, const T &value)
1613     {
1614         return tryEmplace_impl(std::forward<K>(key), value);
1615     }
1616     template <typename K, typename... Args, if_heterogeneously_searchable<K> = true, if_key_constructible_from<K> = true>
1617     std::pair<key_value_iterator, bool> try_emplace(K &&key, Args &&...args)
1618     {
1619         return tryEmplace_impl(std::forward<K>(key), std::forward<Args>(args)...);
1620     }
1621     template <typename K, typename... Args, if_heterogeneously_searchable<K> = true, if_key_constructible_from<K> = true>
1622     key_value_iterator try_emplace(const_iterator /*hint*/, K &&key, Args &&...args)
1623     {
1624         return key_value_iterator(tryEmplace_impl(std::forward<K>(key), std::forward<Args>(args)...).iterator);
1625     }
1626     template <typename K, typename Value, if_heterogeneously_searchable<K> = true, if_key_constructible_from<K> = true>
1627     TryEmplaceResult insertOrAssign(K &&key, Value &&value)
1628     {
1629         return insertOrAssign_impl(std::forward<K>(key), std::forward<Value>(value));
1630     }
1631     template <typename K, typename Value, if_heterogeneously_searchable<K> = true, if_key_constructible_from<K> = true>
1632     std::pair<key_value_iterator, bool> insert_or_assign(K &&key, Value &&value)
1633     {
1634         return insertOrAssign_impl(std::forward<K>(key), std::forward<Value>(value));
1635     }
1636     template <typename K, typename Value, if_heterogeneously_searchable<K> = true, if_key_constructible_from<K> = true>
1637     key_value_iterator insert_or_assign(const_iterator /*hint*/, K &&key, Value &&value)
1638     {
1639         return key_value_iterator(insertOrAssign_impl(std::forward<K>(key), std::forward<Value>(value)).iterator);
1640     }
1641 };
1642 
1643 
1644 template <typename Key, typename T>
1645 class QMultiHash
1646 {
1647     using Node = QHashPrivate::MultiNode<Key, T>;
1648     using Data = QHashPrivate::Data<Node>;
1649     using Chain = QHashPrivate::MultiNodeChain<T>;
1650 
1651     Data *d = nullptr;
1652     qsizetype m_size = 0;
1653 
1654 public:
1655     using key_type = Key;
1656     using mapped_type = T;
1657     using value_type = T;
1658     using size_type = qsizetype;
1659     using difference_type = qsizetype;
1660     using reference = T &;
1661     using const_reference = const T &;
1662 
1663     QMultiHash() noexcept = default;
1664     inline QMultiHash(std::initializer_list<std::pair<Key,T> > list)
1665         : d(new Data(list.size()))
1666     {
1667         for (typename std::initializer_list<std::pair<Key,T> >::const_iterator it = list.begin(); it != list.end(); ++it)
1668             insert(it->first, it->second);
1669     }
1670 #ifdef Q_QDOC
1671     template <typename InputIterator>
1672     QMultiHash(InputIterator f, InputIterator l);
1673 #else
1674     template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasKeyAndValue<InputIterator> = true>
1675     QMultiHash(InputIterator f, InputIterator l)
1676     {
1677         QtPrivate::reserveIfForwardIterator(this, f, l);
1678         for (; f != l; ++f)
1679             insert(f.key(), f.value());
1680     }
1681 
1682     template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasFirstAndSecond<InputIterator> = true>
1683     QMultiHash(InputIterator f, InputIterator l)
1684     {
1685         QtPrivate::reserveIfForwardIterator(this, f, l);
1686         for (; f != l; ++f) {
1687             auto &&e = *f;
1688             using V = decltype(e);
1689             insert(std::forward<V>(e).first, std::forward<V>(e).second);
1690         }
1691     }
1692 #endif
1693     QMultiHash(const QMultiHash &other) noexcept
1694         : d(other.d), m_size(other.m_size)
1695     {
1696         if (d)
1697             d->ref.ref();
1698     }
1699     ~QMultiHash()
1700     {
1701         static_assert(std::is_nothrow_destructible_v<Key>, "Types with throwing destructors are not supported in Qt containers.");
1702         static_assert(std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers.");
1703 
1704         if (d && !d->ref.deref())
1705             delete d;
1706     }
1707 
1708     QMultiHash &operator=(const QMultiHash &other) noexcept(std::is_nothrow_destructible<Node>::value)
1709     {
1710         if (d != other.d) {
1711             Data *o = other.d;
1712             if (o)
1713                 o->ref.ref();
1714             if (d && !d->ref.deref())
1715                 delete d;
1716             d = o;
1717             m_size = other.m_size;
1718         }
1719         return *this;
1720     }
1721     QMultiHash(QMultiHash &&other) noexcept
1722         : d(std::exchange(other.d, nullptr)),
1723           m_size(std::exchange(other.m_size, 0))
1724     {
1725     }
1726     QMultiHash &operator=(QMultiHash &&other) noexcept(std::is_nothrow_destructible<Node>::value)
1727     {
1728         QMultiHash moved(std::move(other));
1729         swap(moved);
1730         return *this;
1731     }
1732 
1733     explicit QMultiHash(const QHash<Key, T> &other)
1734         : QMultiHash(other.begin(), other.end())
1735     {}
1736 
1737     explicit QMultiHash(QHash<Key, T> &&other)
1738     {
1739         unite(std::move(other));
1740     }
1741 
1742     void swap(QMultiHash &other) noexcept
1743     {
1744         qt_ptr_swap(d, other.d);
1745         std::swap(m_size, other.m_size);
1746     }
1747 
1748 #ifndef Q_QDOC
1749 private:
1750     template <typename AKey = Key, typename AT = T,
1751               QTypeTraits::compare_eq_result_container<QMultiHash, AKey, AT> = true>
1752     friend bool comparesEqual(const QMultiHash &lhs, const QMultiHash &rhs) noexcept
1753     {
1754         if (lhs.d == rhs.d)
1755             return true;
1756         if (lhs.m_size != rhs.m_size)
1757             return false;
1758         if (lhs.m_size == 0)
1759             return true;
1760         // equal size, and both non-zero size => d pointers allocated for both
1761         Q_ASSERT(lhs.d);
1762         Q_ASSERT(rhs.d);
1763         if (lhs.d->size != rhs.d->size)
1764             return false;
1765         for (auto it = rhs.d->begin(); it != rhs.d->end(); ++it) {
1766             auto *n = lhs.d->findNode(it.node()->key);
1767             if (!n)
1768                 return false;
1769             Chain *e = it.node()->value;
1770             while (e) {
1771                 Chain *oe = n->value;
1772                 while (oe) {
1773                     if (oe->value == e->value)
1774                         break;
1775                     oe = oe->next;
1776                 }
1777                 if (!oe)
1778                     return false;
1779                 e = e->next;
1780             }
1781         }
1782         // all values must be the same as size is the same
1783         return true;
1784     }
1785     QT_DECLARE_EQUALITY_OPERATORS_HELPER(QMultiHash, QMultiHash, /* non-constexpr */, noexcept,
1786                  template <typename AKey = Key, typename AT = T,
1787                            QTypeTraits::compare_eq_result_container<QMultiHash, AKey, AT> = true>)
1788 public:
1789 #else
1790     friend bool operator==(const QMultiHash &lhs, const QMultiHash &rhs) noexcept;
1791     friend bool operator!=(const QMultiHash &lhs, const QMultiHash &rhs) noexcept;
1792 #endif // Q_QDOC
1793 
1794     inline qsizetype size() const noexcept { return m_size; }
1795 
1796     [[nodiscard]]
1797     inline bool isEmpty() const noexcept { return !m_size; }
1798 
1799     inline qsizetype capacity() const noexcept { return d ? qsizetype(d->numBuckets >> 1) : 0; }
1800     void reserve(qsizetype size)
1801     {
1802         // reserve(0) is used in squeeze()
1803         if (size && (this->capacity() >= size))
1804             return;
1805         if (isDetached())
1806             d->rehash(size);
1807         else
1808             d = Data::detached(d, size_t(size));
1809     }
1810     inline void squeeze() { reserve(0); }
1811 
1812     inline void detach() { if (!d || d->ref.isShared()) d = Data::detached(d); }
1813     inline bool isDetached() const noexcept { return d && !d->ref.isShared(); }
1814     bool isSharedWith(const QMultiHash &other) const noexcept { return d == other.d; }
1815 
1816     void clear() noexcept(std::is_nothrow_destructible<Node>::value)
1817     {
1818         if (d && !d->ref.deref())
1819             delete d;
1820         d = nullptr;
1821         m_size = 0;
1822     }
1823 
1824     qsizetype remove(const Key &key)
1825     {
1826         return removeImpl(key);
1827     }
1828 private:
1829     template <typename K> qsizetype removeImpl(const K &key)
1830     {
1831         if (isEmpty()) // prevents detaching shared null
1832             return 0;
1833         auto it = d->findBucket(key);
1834         size_t bucket = it.toBucketIndex(d);
1835         detach();
1836         it = typename Data::Bucket(d, bucket); // reattach in case of detach
1837 
1838         if (it.isUnused())
1839             return 0;
1840         qsizetype n = Node::freeChain(it.node());
1841         m_size -= n;
1842         Q_ASSERT(m_size >= 0);
1843         d->erase(it);
1844         return n;
1845     }
1846 
1847 public:
1848     template <typename Predicate>
1849     qsizetype removeIf(Predicate pred)
1850     {
1851         return QtPrivate::associative_erase_if(*this, pred);
1852     }
1853 
1854     T take(const Key &key)
1855     {
1856         return takeImpl(key);
1857     }
1858 private:
1859     template <typename K> T takeImpl(const K &key)
1860     {
1861         if (isEmpty()) // prevents detaching shared null
1862             return T();
1863         auto it = d->findBucket(key);
1864         size_t bucket = it.toBucketIndex(d);
1865         detach();
1866         it = typename Data::Bucket(d, bucket); // reattach in case of detach
1867 
1868         if (it.isUnused())
1869             return T();
1870         Chain *e = it.node()->value;
1871         Q_ASSERT(e);
1872         T t = std::move(e->value);
1873         if (e->next) {
1874             it.node()->value = e->next;
1875             delete e;
1876         } else {
1877             // erase() deletes the values.
1878             d->erase(it);
1879         }
1880         --m_size;
1881         Q_ASSERT(m_size >= 0);
1882         return t;
1883     }
1884 
1885 public:
1886     bool contains(const Key &key) const noexcept
1887     {
1888         if (!d)
1889             return false;
1890         return d->findNode(key) != nullptr;
1891     }
1892 
1893 private:
1894     const Key *keyImpl(const T &value) const noexcept
1895     {
1896         if (d) {
1897             auto i = d->begin();
1898             while (i != d->end()) {
1899                 Chain *e = i.node()->value;
1900                 if (e->contains(value))
1901                     return &i.node()->key;
1902                 ++i;
1903             }
1904         }
1905 
1906         return nullptr;
1907     }
1908 public:
1909     Key key(const T &value) const noexcept
1910     {
1911         if (auto *k = keyImpl(value))
1912             return *k;
1913         else
1914             return Key();
1915     }
1916     Key key(const T &value, const Key &defaultKey) const noexcept
1917     {
1918         if (auto *k = keyImpl(value))
1919             return *k;
1920         else
1921             return defaultKey;
1922     }
1923 
1924 private:
1925     template <typename K>
1926     T *valueImpl(const K &key) const noexcept
1927     {
1928         if (d) {
1929             Node *n = d->findNode(key);
1930             if (n) {
1931                 Q_ASSERT(n->value);
1932                 return &n->value->value;
1933             }
1934         }
1935         return nullptr;
1936     }
1937 public:
1938     T value(const Key &key) const noexcept
1939     {
1940         if (auto *v = valueImpl(key))
1941             return *v;
1942         else
1943             return T();
1944     }
1945     T value(const Key &key, const T &defaultValue) const noexcept
1946     {
1947         if (auto *v = valueImpl(key))
1948             return *v;
1949         else
1950             return defaultValue;
1951     }
1952 
1953     T &operator[](const Key &key)
1954     {
1955         return operatorIndexImpl(key);
1956     }
1957 private:
1958     template <typename K> T &operatorIndexImpl(const K &key)
1959     {
1960         const auto copy = isDetached() ? QMultiHash() : *this; // keep 'key' alive across the detach
1961         detach();
1962         auto result = d->findOrInsert(key);
1963         Q_ASSERT(!result.it.atEnd());
1964         if (!result.initialized) {
1965             Node::createInPlace(result.it.node(), Key(key), T());
1966             ++m_size;
1967         }
1968         return result.it.node()->value->value;
1969     }
1970 
1971 public:
1972     const T operator[](const Key &key) const noexcept
1973     {
1974         return value(key);
1975     }
1976 
1977     QList<Key> uniqueKeys() const
1978     {
1979         QList<Key> res;
1980         if (d) {
1981             auto i = d->begin();
1982             while (i != d->end()) {
1983                 res.append(i.node()->key);
1984                 ++i;
1985             }
1986         }
1987         return res;
1988     }
1989 
1990     QList<Key> keys() const { return QList<Key>(keyBegin(), keyEnd()); }
1991     QList<Key> keys(const T &value) const
1992     {
1993         QList<Key> res;
1994         const_iterator i = begin();
1995         while (i != end()) {
1996             if (i.value() == value)
1997                 res.append(i.key());
1998             ++i;
1999         }
2000         return res;
2001     }
2002 
2003     QList<T> values() const { return QList<T>(begin(), end()); }
2004     QList<T> values(const Key &key) const
2005     {
2006         return valuesImpl(key);
2007     }
2008 private:
2009     template <typename K> QList<T> valuesImpl(const K &key) const
2010     {
2011         QList<T> values;
2012         if (d) {
2013             Node *n = d->findNode(key);
2014             if (n) {
2015                 Chain *e = n->value;
2016                 while (e) {
2017                     values.append(e->value);
2018                     e = e->next;
2019                 }
2020             }
2021         }
2022         return values;
2023     }
2024 
2025 public:
2026     class const_iterator;
2027 
2028     class iterator
2029     {
2030         using piter = typename QHashPrivate::iterator<Node>;
2031         friend class const_iterator;
2032         friend class QMultiHash<Key, T>;
2033         piter i;
2034         Chain **e = nullptr;
2035         explicit inline iterator(piter it, Chain **entry = nullptr) noexcept : i(it), e(entry)
2036         {
2037             if (!it.atEnd() && !e) {
2038                 e = &it.node()->value;
2039                 Q_ASSERT(e && *e);
2040             }
2041         }
2042 
2043     public:
2044         typedef std::forward_iterator_tag iterator_category;
2045         typedef qptrdiff difference_type;
2046         typedef T value_type;
2047         typedef T *pointer;
2048         typedef T &reference;
2049 
2050         constexpr iterator() noexcept = default;
2051 
2052         inline const Key &key() const noexcept { return i.node()->key; }
2053         inline T &value() const noexcept { return (*e)->value; }
2054         inline T &operator*() const noexcept { return (*e)->value; }
2055         inline T *operator->() const noexcept { return &(*e)->value; }
2056         inline bool operator==(const iterator &o) const noexcept { return e == o.e; }
2057         inline bool operator!=(const iterator &o) const noexcept { return e != o.e; }
2058 
2059         inline iterator &operator++() noexcept {
2060             Q_ASSERT(e && *e);
2061             e = &(*e)->next;
2062             Q_ASSERT(e);
2063             if (!*e) {
2064                 ++i;
2065                 e = i.atEnd() ? nullptr : &i.node()->value;
2066             }
2067             return *this;
2068         }
2069         inline iterator operator++(int) noexcept {
2070             iterator r = *this;
2071             ++(*this);
2072             return r;
2073         }
2074 
2075         inline bool operator==(const const_iterator &o) const noexcept { return e == o.e; }
2076         inline bool operator!=(const const_iterator &o) const noexcept { return e != o.e; }
2077     };
2078     friend class iterator;
2079 
2080     class const_iterator
2081     {
2082         using piter = typename QHashPrivate::iterator<Node>;
2083         friend class iterator;
2084         friend class QMultiHash<Key, T>;
2085         piter i;
2086         Chain **e = nullptr;
2087         explicit inline const_iterator(piter it, Chain **entry = nullptr) noexcept : i(it), e(entry)
2088         {
2089             if (!it.atEnd() && !e) {
2090                 e = &it.node()->value;
2091                 Q_ASSERT(e && *e);
2092             }
2093         }
2094 
2095     public:
2096         typedef std::forward_iterator_tag iterator_category;
2097         typedef qptrdiff difference_type;
2098         typedef T value_type;
2099         typedef const T *pointer;
2100         typedef const T &reference;
2101 
2102         constexpr const_iterator() noexcept = default;
2103         inline const_iterator(const iterator &o) noexcept : i(o.i), e(o.e) { }
2104 
2105         inline const Key &key() const noexcept { return i.node()->key; }
2106         inline T &value() const noexcept { return (*e)->value; }
2107         inline T &operator*() const noexcept { return (*e)->value; }
2108         inline T *operator->() const noexcept { return &(*e)->value; }
2109         inline bool operator==(const const_iterator &o) const noexcept { return e == o.e; }
2110         inline bool operator!=(const const_iterator &o) const noexcept { return e != o.e; }
2111 
2112         inline const_iterator &operator++() noexcept {
2113             Q_ASSERT(e && *e);
2114             e = &(*e)->next;
2115             Q_ASSERT(e);
2116             if (!*e) {
2117                 ++i;
2118                 e = i.atEnd() ? nullptr : &i.node()->value;
2119             }
2120             return *this;
2121         }
2122         inline const_iterator operator++(int) noexcept
2123         {
2124             const_iterator r = *this;
2125             ++(*this);
2126             return r;
2127         }
2128     };
2129     friend class const_iterator;
2130 
2131     class key_iterator
2132     {
2133         const_iterator i;
2134 
2135     public:
2136         typedef typename const_iterator::iterator_category iterator_category;
2137         typedef qptrdiff difference_type;
2138         typedef Key value_type;
2139         typedef const Key *pointer;
2140         typedef const Key &reference;
2141 
2142         key_iterator() noexcept = default;
2143         explicit key_iterator(const_iterator o) noexcept : i(o) { }
2144 
2145         const Key &operator*() const noexcept { return i.key(); }
2146         const Key *operator->() const noexcept { return &i.key(); }
2147         bool operator==(key_iterator o) const noexcept { return i == o.i; }
2148         bool operator!=(key_iterator o) const noexcept { return i != o.i; }
2149 
2150         inline key_iterator &operator++() noexcept { ++i; return *this; }
2151         inline key_iterator operator++(int) noexcept { return key_iterator(i++);}
2152         const_iterator base() const noexcept { return i; }
2153     };
2154 
2155     typedef QKeyValueIterator<const Key&, const T&, const_iterator> const_key_value_iterator;
2156     typedef QKeyValueIterator<const Key&, T&, iterator> key_value_iterator;
2157 
2158     // STL style
2159     inline iterator begin() { if (!d) return iterator(); detach(); return iterator(d->begin()); }
2160     inline const_iterator begin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
2161     inline const_iterator cbegin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
2162     inline const_iterator constBegin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
2163     inline iterator end() noexcept { return iterator(); }
2164     inline const_iterator end() const noexcept { return const_iterator(); }
2165     inline const_iterator cend() const noexcept { return const_iterator(); }
2166     inline const_iterator constEnd() const noexcept { return const_iterator(); }
2167     inline key_iterator keyBegin() const noexcept { return key_iterator(begin()); }
2168     inline key_iterator keyEnd() const noexcept { return key_iterator(end()); }
2169     inline key_value_iterator keyValueBegin() noexcept { return key_value_iterator(begin()); }
2170     inline key_value_iterator keyValueEnd() noexcept { return key_value_iterator(end()); }
2171     inline const_key_value_iterator keyValueBegin() const noexcept { return const_key_value_iterator(begin()); }
2172     inline const_key_value_iterator constKeyValueBegin() const noexcept { return const_key_value_iterator(begin()); }
2173     inline const_key_value_iterator keyValueEnd() const noexcept { return const_key_value_iterator(end()); }
2174     inline const_key_value_iterator constKeyValueEnd() const noexcept { return const_key_value_iterator(end()); }
2175     auto asKeyValueRange() & { return QtPrivate::QKeyValueRange(*this); }
2176     auto asKeyValueRange() const & { return QtPrivate::QKeyValueRange(*this); }
2177     auto asKeyValueRange() && { return QtPrivate::QKeyValueRange(std::move(*this)); }
2178     auto asKeyValueRange() const && { return QtPrivate::QKeyValueRange(std::move(*this)); }
2179 
2180     iterator detach(const_iterator it)
2181     {
2182         auto i = it.i;
2183         Chain **e = it.e;
2184         if (d->ref.isShared()) {
2185             // need to store iterator position before detaching
2186             qsizetype n = 0;
2187             Chain *entry = i.node()->value;
2188             while (entry != *it.e) {
2189                 ++n;
2190                 entry = entry->next;
2191             }
2192             Q_ASSERT(entry);
2193             detach_helper();
2194 
2195             i = d->detachedIterator(i);
2196             e = &i.node()->value;
2197             while (n) {
2198                 e = &(*e)->next;
2199                 --n;
2200             }
2201             Q_ASSERT(e && *e);
2202         }
2203         return iterator(i, e);
2204     }
2205 
2206     iterator erase(const_iterator it)
2207     {
2208         Q_ASSERT(d);
2209         iterator iter = detach(it);
2210         iterator i = iter;
2211         Chain *e = *i.e;
2212         Chain *next = e->next;
2213         *i.e = next;
2214         delete e;
2215         if (!next) {
2216             if (i.e == &i.i.node()->value) {
2217                 // last remaining entry, erase
2218                 typename Data::Bucket bucket(i.i);
2219                 d->erase(bucket);
2220                 if (bucket.toBucketIndex(d) == d->numBuckets - 1 || bucket.isUnused())
2221                     i = iterator(++iter.i);
2222                 else // 'i' currently has a nullptr chain. So, we must recreate it
2223                     i = iterator(bucket.toIterator(d));
2224             } else {
2225                 i = iterator(++iter.i);
2226             }
2227         }
2228         --m_size;
2229         Q_ASSERT(m_size >= 0);
2230         return i;
2231     }
2232 
2233     // more Qt
2234     typedef iterator Iterator;
2235     typedef const_iterator ConstIterator;
2236     inline qsizetype count() const noexcept { return size(); }
2237 
2238 private:
2239     template <typename K> iterator findImpl(const K &key)
2240     {
2241         if (isEmpty())
2242             return end();
2243         auto it = d->findBucket(key);
2244         size_t bucket = it.toBucketIndex(d);
2245         detach();
2246         it = typename Data::Bucket(d, bucket); // reattach in case of detach
2247 
2248         if (it.isUnused())
2249             return end();
2250         return iterator(it.toIterator(d));
2251     }
2252     template <typename K> const_iterator constFindImpl(const K &key) const noexcept
2253     {
2254         if (isEmpty())
2255             return end();
2256         auto it = d->findBucket(key);
2257         if (it.isUnused())
2258             return constEnd();
2259         return const_iterator(it.toIterator(d));
2260     }
2261 public:
2262     iterator find(const Key &key)
2263     {
2264         return findImpl(key);
2265     }
2266     const_iterator constFind(const Key &key) const noexcept
2267     {
2268         return constFindImpl(key);
2269     }
2270     const_iterator find(const Key &key) const noexcept
2271     {
2272         return constFindImpl(key);
2273     }
2274 
2275     iterator insert(const Key &key, const T &value)
2276     {
2277         return emplace(key, value);
2278     }
2279 
2280     template <typename ...Args>
2281     iterator emplace(const Key &key, Args &&... args)
2282     {
2283         return emplace(Key(key), std::forward<Args>(args)...);
2284     }
2285 
2286     template <typename ...Args>
2287     iterator emplace(Key &&key, Args &&... args)
2288     {
2289         if (isDetached()) {
2290             if (d->shouldGrow()) // Construct the value now so that no dangling references are used
2291                 return emplace_helper(std::move(key), T(std::forward<Args>(args)...));
2292             return emplace_helper(std::move(key), std::forward<Args>(args)...);
2293         }
2294         // else: we must detach
2295         const auto copy = *this; // keep 'args' alive across the detach/growth
2296         detach();
2297         return emplace_helper(std::move(key), std::forward<Args>(args)...);
2298     }
2299 
2300 
2301     float load_factor() const noexcept { return d ? d->loadFactor() : 0; }
2302     static float max_load_factor() noexcept { return 0.5; }
2303     size_t bucket_count() const noexcept { return d ? d->numBuckets : 0; }
2304     static size_t max_bucket_count() noexcept { return Data::maxNumBuckets(); }
2305 
2306     [[nodiscard]]
2307     inline bool empty() const noexcept { return isEmpty(); }
2308 
2309     inline iterator replace(const Key &key, const T &value)
2310     {
2311         return emplaceReplace(key, value);
2312     }
2313 
2314     template <typename ...Args>
2315     iterator emplaceReplace(const Key &key, Args &&... args)
2316     {
2317         return emplaceReplace(Key(key), std::forward<Args>(args)...);
2318     }
2319 
2320     template <typename ...Args>
2321     iterator emplaceReplace(Key &&key, Args &&... args)
2322     {
2323         if (isDetached()) {
2324             if (d->shouldGrow()) // Construct the value now so that no dangling references are used
2325                 return emplaceReplace_helper(std::move(key), T(std::forward<Args>(args)...));
2326             return emplaceReplace_helper(std::move(key), std::forward<Args>(args)...);
2327         }
2328         // else: we must detach
2329         const auto copy = *this; // keep 'args' alive across the detach/growth
2330         detach();
2331         return emplaceReplace_helper(std::move(key), std::forward<Args>(args)...);
2332     }
2333 
2334     inline QMultiHash &operator+=(const QMultiHash &other)
2335     { this->unite(other); return *this; }
2336     inline QMultiHash operator+(const QMultiHash &other) const
2337     { QMultiHash result = *this; result += other; return result; }
2338 
2339     bool contains(const Key &key, const T &value) const noexcept
2340     {
2341         return containsImpl(key, value);
2342     }
2343 private:
2344     template <typename K> bool containsImpl(const K &key, const T &value) const noexcept
2345     {
2346         if (isEmpty())
2347             return false;
2348         auto n = d->findNode(key);
2349         if (n == nullptr)
2350             return false;
2351         return n->value->contains(value);
2352     }
2353 
2354 public:
2355     qsizetype remove(const Key &key, const T &value)
2356     {
2357         return removeImpl(key, value);
2358     }
2359 private:
2360     template <typename K> qsizetype removeImpl(const K &key, const T &value)
2361     {
2362         if (isEmpty()) // prevents detaching shared null
2363             return 0;
2364         auto it = d->findBucket(key);
2365         size_t bucket = it.toBucketIndex(d);
2366         detach();
2367         it = typename Data::Bucket(d, bucket); // reattach in case of detach
2368 
2369         if (it.isUnused())
2370             return 0;
2371         qsizetype n = 0;
2372         Chain **e = &it.node()->value;
2373         while (*e) {
2374             Chain *entry = *e;
2375             if (entry->value == value) {
2376                 *e = entry->next;
2377                 delete entry;
2378                 ++n;
2379             } else {
2380                 e = &entry->next;
2381             }
2382         }
2383         if (!it.node()->value)
2384             d->erase(it);
2385         m_size -= n;
2386         Q_ASSERT(m_size >= 0);
2387         return n;
2388     }
2389 
2390 public:
2391     qsizetype count(const Key &key) const noexcept
2392     {
2393         return countImpl(key);
2394     }
2395 private:
2396     template <typename K> qsizetype countImpl(const K &key) const noexcept
2397     {
2398         if (!d)
2399             return 0;
2400         auto it = d->findBucket(key);
2401         if (it.isUnused())
2402             return 0;
2403         qsizetype n = 0;
2404         Chain *e = it.node()->value;
2405         while (e) {
2406             ++n;
2407             e = e->next;
2408         }
2409 
2410         return n;
2411     }
2412 
2413 public:
2414     qsizetype count(const Key &key, const T &value) const noexcept
2415     {
2416         return countImpl(key, value);
2417     }
2418 private:
2419     template <typename K> qsizetype countImpl(const K &key, const T &value) const noexcept
2420     {
2421         if (!d)
2422             return 0;
2423         auto it = d->findBucket(key);
2424         if (it.isUnused())
2425             return 0;
2426         qsizetype n = 0;
2427         Chain *e = it.node()->value;
2428         while (e) {
2429             if (e->value == value)
2430                 ++n;
2431             e = e->next;
2432         }
2433 
2434         return n;
2435     }
2436 
2437     template <typename K> iterator findImpl(const K &key, const T &value)
2438     {
2439         if (isEmpty())
2440             return end();
2441         const auto copy = isDetached() ? QMultiHash() : *this; // keep 'key'/'value' alive across the detach
2442         detach();
2443         auto it = constFind(key, value);
2444         return iterator(it.i, it.e);
2445     }
2446     template <typename K> const_iterator constFindImpl(const K &key, const T &value) const noexcept
2447     {
2448         const_iterator i(constFind(key));
2449         const_iterator end(constEnd());
2450         while (i != end && i.key() == key) {
2451             if (i.value() == value)
2452                 return i;
2453             ++i;
2454         }
2455         return end;
2456     }
2457 
2458 public:
2459     iterator find(const Key &key, const T &value)
2460     {
2461         return findImpl(key, value);
2462     }
2463 
2464     const_iterator constFind(const Key &key, const T &value) const noexcept
2465     {
2466         return constFindImpl(key, value);
2467     }
2468     const_iterator find(const Key &key, const T &value) const noexcept
2469     {
2470         return constFind(key, value);
2471     }
2472 
2473     QMultiHash &unite(const QMultiHash &other)
2474     {
2475         if (isEmpty()) {
2476             *this = other;
2477         } else if (other.isEmpty()) {
2478             ;
2479         } else {
2480             QMultiHash copy(other);
2481             detach();
2482             for (auto cit = copy.cbegin(); cit != copy.cend(); ++cit)
2483                 insert(cit.key(), *cit);
2484         }
2485         return *this;
2486     }
2487 
2488     QMultiHash &unite(const QHash<Key, T> &other)
2489     {
2490         for (auto cit = other.cbegin(); cit != other.cend(); ++cit)
2491             insert(cit.key(), *cit);
2492         return *this;
2493     }
2494 
2495     QMultiHash &unite(QHash<Key, T> &&other)
2496     {
2497         if (!other.isDetached()) {
2498             unite(other);
2499             return *this;
2500         }
2501         auto it = other.d->begin();
2502         for (const auto end = other.d->end(); it != end; ++it)
2503             emplace(std::move(it.node()->key), std::move(it.node()->takeValue()));
2504         other.clear();
2505         return *this;
2506     }
2507 
2508     std::pair<iterator, iterator> equal_range(const Key &key)
2509     {
2510         return equal_range_impl(key);
2511     }
2512 private:
2513     template <typename K> std::pair<iterator, iterator> equal_range_impl(const K &key)
2514     {
2515         const auto copy = isDetached() ? QMultiHash() : *this; // keep 'key' alive across the detach
2516         detach();
2517         auto pair = std::as_const(*this).equal_range(key);
2518         return {iterator(pair.first.i), iterator(pair.second.i)};
2519     }
2520 
2521 public:
2522     std::pair<const_iterator, const_iterator> equal_range(const Key &key) const noexcept
2523     {
2524         return equal_range_impl(key);
2525     }
2526 private:
2527     template <typename K> std::pair<const_iterator, const_iterator> equal_range_impl(const K &key) const noexcept
2528     {
2529         if (!d)
2530             return {end(), end()};
2531 
2532         auto bucket = d->findBucket(key);
2533         if (bucket.isUnused())
2534             return {end(), end()};
2535         auto it = bucket.toIterator(d);
2536         auto end = it;
2537         ++end;
2538         return {const_iterator(it), const_iterator(end)};
2539     }
2540 
2541     void detach_helper()
2542     {
2543         if (!d) {
2544             d = new Data;
2545             return;
2546         }
2547         Data *dd = new Data(*d);
2548         if (!d->ref.deref())
2549             delete d;
2550         d = dd;
2551     }
2552 
2553     template<typename... Args>
2554     iterator emplace_helper(Key &&key, Args &&...args)
2555     {
2556         auto result = d->findOrInsert(key);
2557         if (!result.initialized)
2558             Node::createInPlace(result.it.node(), std::move(key), std::forward<Args>(args)...);
2559         else
2560             result.it.node()->insertMulti(std::forward<Args>(args)...);
2561         ++m_size;
2562         return iterator(result.it);
2563     }
2564 
2565     template<typename... Args>
2566     iterator emplaceReplace_helper(Key &&key, Args &&...args)
2567     {
2568         auto result = d->findOrInsert(key);
2569         if (!result.initialized) {
2570             Node::createInPlace(result.it.node(), std::move(key), std::forward<Args>(args)...);
2571             ++m_size;
2572         } else {
2573             result.it.node()->emplaceValue(std::forward<Args>(args)...);
2574         }
2575         return iterator(result.it);
2576     }
2577 
2578     template <typename K>
2579     using if_heterogeneously_searchable = QHashPrivate::if_heterogeneously_searchable_with<Key, K>;
2580 
2581     template <typename K>
2582     using if_key_constructible_from = std::enable_if_t<std::is_constructible_v<Key, K>, bool>;
2583 
2584 public:
2585     template <typename K, if_heterogeneously_searchable<K> = true>
2586     qsizetype remove(const K &key)
2587     {
2588         return removeImpl(key);
2589     }
2590     template <typename K, if_heterogeneously_searchable<K> = true>
2591     T take(const K &key)
2592     {
2593         return takeImpl(key);
2594     }
2595     template <typename K, if_heterogeneously_searchable<K> = true>
2596     bool contains(const K &key) const noexcept
2597     {
2598         if (!d)
2599             return false;
2600         return d->findNode(key) != nullptr;
2601     }
2602     template <typename K, if_heterogeneously_searchable<K> = true>
2603     T value(const K &key) const noexcept
2604     {
2605         if (auto *v = valueImpl(key))
2606             return *v;
2607         else
2608             return T();
2609     }
2610     template <typename K, if_heterogeneously_searchable<K> = true>
2611     T value(const K &key, const T &defaultValue) const noexcept
2612     {
2613         if (auto *v = valueImpl(key))
2614             return *v;
2615         else
2616             return defaultValue;
2617     }
2618     template <typename K, if_heterogeneously_searchable<K> = true, if_key_constructible_from<K> = true>
2619     T &operator[](const K &key)
2620     {
2621         return operatorIndexImpl(key);
2622     }
2623     template <typename K, if_heterogeneously_searchable<K> = true>
2624     const T operator[](const K &key) const noexcept
2625     {
2626         return value(key);
2627     }
2628     template <typename K, if_heterogeneously_searchable<K> = true>
2629     QList<T> values(const K &key)
2630     {
2631         return valuesImpl(key);
2632     }
2633     template <typename K, if_heterogeneously_searchable<K> = true>
2634     iterator find(const K &key)
2635     {
2636         return findImpl(key);
2637     }
2638     template <typename K, if_heterogeneously_searchable<K> = true>
2639     const_iterator constFind(const K &key) const noexcept
2640     {
2641         return constFindImpl(key);
2642     }
2643     template <typename K, if_heterogeneously_searchable<K> = true>
2644     const_iterator find(const K &key) const noexcept
2645     {
2646         return constFindImpl(key);
2647     }
2648     template <typename K, if_heterogeneously_searchable<K> = true>
2649     bool contains(const K &key, const T &value) const noexcept
2650     {
2651         return containsImpl(key, value);
2652     }
2653     template <typename K, if_heterogeneously_searchable<K> = true>
2654     qsizetype remove(const K &key, const T &value)
2655     {
2656         return removeImpl(key, value);
2657     }
2658     template <typename K, if_heterogeneously_searchable<K> = true>
2659     qsizetype count(const K &key) const noexcept
2660     {
2661         return countImpl(key);
2662     }
2663     template <typename K, if_heterogeneously_searchable<K> = true>
2664     qsizetype count(const K &key, const T &value) const noexcept
2665     {
2666         return countImpl(key, value);
2667     }
2668     template <typename K, if_heterogeneously_searchable<K> = true>
2669     iterator find(const K &key, const T &value)
2670     {
2671         return findImpl(key, value);
2672     }
2673     template <typename K, if_heterogeneously_searchable<K> = true>
2674     const_iterator constFind(const K &key, const T &value) const noexcept
2675     {
2676         return constFindImpl(key, value);
2677     }
2678     template <typename K, if_heterogeneously_searchable<K> = true>
2679     const_iterator find(const K &key, const T &value) const noexcept
2680     {
2681         return constFind(key, value);
2682     }
2683     template <typename K, if_heterogeneously_searchable<K> = true>
2684     std::pair<iterator, iterator>
2685     equal_range(const K &key)
2686     {
2687         return equal_range_impl(key);
2688     }
2689     template <typename K, if_heterogeneously_searchable<K> = true>
2690     std::pair<const_iterator, const_iterator>
2691     equal_range(const K &key) const noexcept
2692     {
2693         return equal_range_impl(key);
2694     }
2695 };
2696 
2697 Q_DECLARE_ASSOCIATIVE_FORWARD_ITERATOR(Hash)
2698 Q_DECLARE_MUTABLE_ASSOCIATIVE_FORWARD_ITERATOR(Hash)
2699 Q_DECLARE_ASSOCIATIVE_FORWARD_ITERATOR(MultiHash)
2700 Q_DECLARE_MUTABLE_ASSOCIATIVE_FORWARD_ITERATOR(MultiHash)
2701 
2702 template <class Key, class T>
2703 size_t qHash(const QHash<Key, T> &key, size_t seed = 0)
2704     noexcept(noexcept(qHash(std::declval<Key&>())) && noexcept(qHash(std::declval<T&>())))
2705 {
2706     size_t hash = 0;
2707     for (auto it = key.begin(), end = key.end(); it != end; ++it) {
2708         QtPrivate::QHashCombine combine;
2709         size_t h = combine(seed, it.key());
2710         // use + to keep the result independent of the ordering of the keys
2711         hash += combine(h, it.value());
2712     }
2713     return hash;
2714 }
2715 
2716 template <class Key, class T>
2717 inline size_t qHash(const QMultiHash<Key, T> &key, size_t seed = 0)
2718     noexcept(noexcept(qHash(std::declval<Key&>())) && noexcept(qHash(std::declval<T&>())))
2719 {
2720     size_t hash = 0;
2721     for (auto it = key.begin(), end = key.end(); it != end; ++it) {
2722         QtPrivate::QHashCombine combine;
2723         size_t h = combine(seed, it.key());
2724         // use + to keep the result independent of the ordering of the keys
2725         hash += combine(h, it.value());
2726     }
2727     return hash;
2728 }
2729 
2730 template <typename Key, typename T, typename Predicate>
2731 qsizetype erase_if(QHash<Key, T> &hash, Predicate pred)
2732 {
2733     return QtPrivate::associative_erase_if(hash, pred);
2734 }
2735 
2736 template <typename Key, typename T, typename Predicate>
2737 qsizetype erase_if(QMultiHash<Key, T> &hash, Predicate pred)
2738 {
2739     return QtPrivate::associative_erase_if(hash, pred);
2740 }
2741 
2742 QT_END_NAMESPACE
2743 
2744 #endif // QHASH_H