File indexing completed on 2025-01-18 10:07:25
0001
0002
0003
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
0015 #include <initializer_list>
0016 #include <functional> // for std::hash
0017
0018 class tst_QHash;
0019
0020 QT_BEGIN_NAMESPACE
0021
0022 struct QHashDummyValue
0023 {
0024 bool operator==(const QHashDummyValue &) const noexcept { return true; }
0025 };
0026
0027 namespace QHashPrivate {
0028
0029 template <typename T, typename = void>
0030 constexpr inline bool HasQHashOverload = false;
0031
0032 template <typename T>
0033 constexpr inline bool HasQHashOverload<T, std::enable_if_t<
0034 std::is_convertible_v<decltype(qHash(std::declval<const T &>(), std::declval<size_t>())), size_t>
0035 >> = true;
0036
0037 template <typename T, typename = void>
0038 constexpr inline bool HasStdHashSpecializationWithSeed = false;
0039
0040 template <typename T>
0041 constexpr inline bool HasStdHashSpecializationWithSeed<T, std::enable_if_t<
0042 std::is_convertible_v<decltype(std::hash<T>()(std::declval<const T &>(), std::declval<size_t>())), size_t>
0043 >> = true;
0044
0045 template <typename T, typename = void>
0046 constexpr inline bool HasStdHashSpecializationWithoutSeed = false;
0047
0048 template <typename T>
0049 constexpr inline bool HasStdHashSpecializationWithoutSeed<T, std::enable_if_t<
0050 std::is_convertible_v<decltype(std::hash<T>()(std::declval<const T &>())), size_t>
0051 >> = true;
0052
0053 template <typename T>
0054 size_t calculateHash(const T &t, size_t seed = 0)
0055 {
0056 if constexpr (HasQHashOverload<T>) {
0057 return qHash(t, seed);
0058 } else if constexpr (HasStdHashSpecializationWithSeed<T>) {
0059 return std::hash<T>()(t, seed);
0060 } else if constexpr (HasStdHashSpecializationWithoutSeed<T>) {
0061 Q_UNUSED(seed);
0062 return std::hash<T>()(t);
0063 } else {
0064 static_assert(sizeof(T) == 0, "The key type must have a qHash overload or a std::hash specialization");
0065 return 0;
0066 }
0067 }
0068
0069 template <typename Key, typename T>
0070 struct Node
0071 {
0072 using KeyType = Key;
0073 using ValueType = T;
0074
0075 Key key;
0076 T value;
0077 template<typename ...Args>
0078 static void createInPlace(Node *n, Key &&k, Args &&... args)
0079 { new (n) Node{ std::move(k), T(std::forward<Args>(args)...) }; }
0080 template<typename ...Args>
0081 static void createInPlace(Node *n, const Key &k, Args &&... args)
0082 { new (n) Node{ Key(k), T(std::forward<Args>(args)...) }; }
0083 template<typename ...Args>
0084 void emplaceValue(Args &&... args)
0085 {
0086 value = T(std::forward<Args>(args)...);
0087 }
0088 T &&takeValue() noexcept(std::is_nothrow_move_assignable_v<T>)
0089 {
0090 return std::move(value);
0091 }
0092 bool valuesEqual(const Node *other) const { return value == other->value; }
0093 };
0094
0095 template <typename Key>
0096 struct Node<Key, QHashDummyValue> {
0097 using KeyType = Key;
0098 using ValueType = QHashDummyValue;
0099
0100 Key key;
0101 template<typename ...Args>
0102 static void createInPlace(Node *n, Key &&k, Args &&...)
0103 { new (n) Node{ std::move(k) }; }
0104 template<typename ...Args>
0105 static void createInPlace(Node *n, const Key &k, Args &&...)
0106 { new (n) Node{ k }; }
0107 template<typename ...Args>
0108 void emplaceValue(Args &&...)
0109 {
0110 }
0111 ValueType takeValue() { return QHashDummyValue(); }
0112 bool valuesEqual(const Node *) const { return true; }
0113 };
0114
0115 template <typename T>
0116 struct MultiNodeChain
0117 {
0118 T value;
0119 MultiNodeChain *next = nullptr;
0120 ~MultiNodeChain()
0121 {
0122 }
0123 qsizetype free() noexcept(std::is_nothrow_destructible_v<T>)
0124 {
0125 qsizetype nEntries = 0;
0126 MultiNodeChain *e = this;
0127 while (e) {
0128 MultiNodeChain *n = e->next;
0129 ++nEntries;
0130 delete e;
0131 e = n;
0132 }
0133 return nEntries;
0134 }
0135 bool contains(const T &val) const noexcept
0136 {
0137 const MultiNodeChain *e = this;
0138 while (e) {
0139 if (e->value == val)
0140 return true;
0141 e = e->next;
0142 }
0143 return false;
0144 }
0145 };
0146
0147 template <typename Key, typename T>
0148 struct MultiNode
0149 {
0150 using KeyType = Key;
0151 using ValueType = T;
0152 using Chain = MultiNodeChain<T>;
0153
0154 Key key;
0155 Chain *value;
0156
0157 template<typename ...Args>
0158 static void createInPlace(MultiNode *n, Key &&k, Args &&... args)
0159 { new (n) MultiNode(std::move(k), new Chain{ T(std::forward<Args>(args)...), nullptr }); }
0160 template<typename ...Args>
0161 static void createInPlace(MultiNode *n, const Key &k, Args &&... args)
0162 { new (n) MultiNode(k, new Chain{ T(std::forward<Args>(args)...), nullptr }); }
0163
0164 MultiNode(const Key &k, Chain *c)
0165 : key(k),
0166 value(c)
0167 {}
0168 MultiNode(Key &&k, Chain *c) noexcept(std::is_nothrow_move_assignable_v<Key>)
0169 : key(std::move(k)),
0170 value(c)
0171 {}
0172
0173 MultiNode(MultiNode &&other)
0174 : key(other.key),
0175 value(std::exchange(other.value, nullptr))
0176 {
0177 }
0178
0179 MultiNode(const MultiNode &other)
0180 : key(other.key)
0181 {
0182 Chain *c = other.value;
0183 Chain **e = &value;
0184 while (c) {
0185 Chain *chain = new Chain{ c->value, nullptr };
0186 *e = chain;
0187 e = &chain->next;
0188 c = c->next;
0189 }
0190 }
0191 ~MultiNode()
0192 {
0193 if (value)
0194 value->free();
0195 }
0196 static qsizetype freeChain(MultiNode *n) noexcept(std::is_nothrow_destructible_v<T>)
0197 {
0198 qsizetype size = n->value->free();
0199 n->value = nullptr;
0200 return size;
0201 }
0202 template<typename ...Args>
0203 void insertMulti(Args &&... args)
0204 {
0205 Chain *e = new Chain{ T(std::forward<Args>(args)...), nullptr };
0206 e->next = std::exchange(value, e);
0207 }
0208 template<typename ...Args>
0209 void emplaceValue(Args &&... args)
0210 {
0211 value->value = T(std::forward<Args>(args)...);
0212 }
0213 };
0214
0215 template<typename Node>
0216 constexpr bool isRelocatable()
0217 {
0218 return QTypeInfo<typename Node::KeyType>::isRelocatable && QTypeInfo<typename Node::ValueType>::isRelocatable;
0219 }
0220
0221 struct SpanConstants {
0222 static constexpr size_t SpanShift = 7;
0223 static constexpr size_t NEntries = (1 << SpanShift);
0224 static constexpr size_t LocalBucketMask = (NEntries - 1);
0225 static constexpr size_t UnusedEntry = 0xff;
0226
0227 static_assert ((NEntries & LocalBucketMask) == 0, "NEntries must be a power of two.");
0228 };
0229
0230
0231
0232
0233
0234
0235
0236
0237
0238 template<typename Node>
0239 struct Span {
0240
0241
0242
0243
0244
0245
0246 struct Entry {
0247 struct { alignas(Node) unsigned char data[sizeof(Node)]; } storage;
0248
0249 unsigned char &nextFree() { return *reinterpret_cast<unsigned char *>(&storage); }
0250 Node &node() { return *reinterpret_cast<Node *>(&storage); }
0251 };
0252
0253 unsigned char offsets[SpanConstants::NEntries];
0254 Entry *entries = nullptr;
0255 unsigned char allocated = 0;
0256 unsigned char nextFree = 0;
0257 Span() noexcept
0258 {
0259 memset(offsets, SpanConstants::UnusedEntry, sizeof(offsets));
0260 }
0261 ~Span()
0262 {
0263 freeData();
0264 }
0265 void freeData() noexcept(std::is_nothrow_destructible<Node>::value)
0266 {
0267 if (entries) {
0268 if constexpr (!std::is_trivially_destructible<Node>::value) {
0269 for (auto o : offsets) {
0270 if (o != SpanConstants::UnusedEntry)
0271 entries[o].node().~Node();
0272 }
0273 }
0274 delete[] entries;
0275 entries = nullptr;
0276 }
0277 }
0278 Node *insert(size_t i)
0279 {
0280 Q_ASSERT(i < SpanConstants::NEntries);
0281 Q_ASSERT(offsets[i] == SpanConstants::UnusedEntry);
0282 if (nextFree == allocated)
0283 addStorage();
0284 unsigned char entry = nextFree;
0285 Q_ASSERT(entry < allocated);
0286 nextFree = entries[entry].nextFree();
0287 offsets[i] = entry;
0288 return &entries[entry].node();
0289 }
0290 void erase(size_t bucket) noexcept(std::is_nothrow_destructible<Node>::value)
0291 {
0292 Q_ASSERT(bucket < SpanConstants::NEntries);
0293 Q_ASSERT(offsets[bucket] != SpanConstants::UnusedEntry);
0294
0295 unsigned char entry = offsets[bucket];
0296 offsets[bucket] = SpanConstants::UnusedEntry;
0297
0298 entries[entry].node().~Node();
0299 entries[entry].nextFree() = nextFree;
0300 nextFree = entry;
0301 }
0302 size_t offset(size_t i) const noexcept
0303 {
0304 return offsets[i];
0305 }
0306 bool hasNode(size_t i) const noexcept
0307 {
0308 return (offsets[i] != SpanConstants::UnusedEntry);
0309 }
0310 Node &at(size_t i) noexcept
0311 {
0312 Q_ASSERT(i < SpanConstants::NEntries);
0313 Q_ASSERT(offsets[i] != SpanConstants::UnusedEntry);
0314
0315 return entries[offsets[i]].node();
0316 }
0317 const Node &at(size_t i) const noexcept
0318 {
0319 Q_ASSERT(i < SpanConstants::NEntries);
0320 Q_ASSERT(offsets[i] != SpanConstants::UnusedEntry);
0321
0322 return entries[offsets[i]].node();
0323 }
0324 Node &atOffset(size_t o) noexcept
0325 {
0326 Q_ASSERT(o < allocated);
0327
0328 return entries[o].node();
0329 }
0330 const Node &atOffset(size_t o) const noexcept
0331 {
0332 Q_ASSERT(o < allocated);
0333
0334 return entries[o].node();
0335 }
0336 void moveLocal(size_t from, size_t to) noexcept
0337 {
0338 Q_ASSERT(offsets[from] != SpanConstants::UnusedEntry);
0339 Q_ASSERT(offsets[to] == SpanConstants::UnusedEntry);
0340 offsets[to] = offsets[from];
0341 offsets[from] = SpanConstants::UnusedEntry;
0342 }
0343 void moveFromSpan(Span &fromSpan, size_t fromIndex, size_t to) noexcept(std::is_nothrow_move_constructible_v<Node>)
0344 {
0345 Q_ASSERT(to < SpanConstants::NEntries);
0346 Q_ASSERT(offsets[to] == SpanConstants::UnusedEntry);
0347 Q_ASSERT(fromIndex < SpanConstants::NEntries);
0348 Q_ASSERT(fromSpan.offsets[fromIndex] != SpanConstants::UnusedEntry);
0349 if (nextFree == allocated)
0350 addStorage();
0351 Q_ASSERT(nextFree < allocated);
0352 offsets[to] = nextFree;
0353 Entry &toEntry = entries[nextFree];
0354 nextFree = toEntry.nextFree();
0355
0356 size_t fromOffset = fromSpan.offsets[fromIndex];
0357 fromSpan.offsets[fromIndex] = SpanConstants::UnusedEntry;
0358 Entry &fromEntry = fromSpan.entries[fromOffset];
0359
0360 if constexpr (isRelocatable<Node>()) {
0361 memcpy(&toEntry, &fromEntry, sizeof(Entry));
0362 } else {
0363 new (&toEntry.node()) Node(std::move(fromEntry.node()));
0364 fromEntry.node().~Node();
0365 }
0366 fromEntry.nextFree() = fromSpan.nextFree;
0367 fromSpan.nextFree = static_cast<unsigned char>(fromOffset);
0368 }
0369
0370 void addStorage()
0371 {
0372 Q_ASSERT(allocated < SpanConstants::NEntries);
0373 Q_ASSERT(nextFree == allocated);
0374
0375
0376
0377
0378
0379
0380
0381
0382
0383
0384
0385
0386 size_t alloc;
0387 static_assert(SpanConstants::NEntries % 8 == 0);
0388 if (!allocated)
0389 alloc = SpanConstants::NEntries / 8 * 3;
0390 else if (allocated == SpanConstants::NEntries / 8 * 3)
0391 alloc = SpanConstants::NEntries / 8 * 5;
0392 else
0393 alloc = allocated + SpanConstants::NEntries/8;
0394 Entry *newEntries = new Entry[alloc];
0395
0396
0397 if constexpr (isRelocatable<Node>()) {
0398 if (allocated)
0399 memcpy(newEntries, entries, allocated * sizeof(Entry));
0400 } else {
0401 for (size_t i = 0; i < allocated; ++i) {
0402 new (&newEntries[i].node()) Node(std::move(entries[i].node()));
0403 entries[i].node().~Node();
0404 }
0405 }
0406 for (size_t i = allocated; i < alloc; ++i) {
0407 newEntries[i].nextFree() = uchar(i + 1);
0408 }
0409 delete[] entries;
0410 entries = newEntries;
0411 allocated = uchar(alloc);
0412 }
0413 };
0414
0415
0416 namespace GrowthPolicy {
0417 inline constexpr size_t bucketsForCapacity(size_t requestedCapacity) noexcept
0418 {
0419 constexpr int SizeDigits = std::numeric_limits<size_t>::digits;
0420
0421
0422
0423 if (requestedCapacity <= 64)
0424 return SpanConstants::NEntries;
0425
0426
0427
0428
0429
0430
0431
0432 int count = qCountLeadingZeroBits(requestedCapacity);
0433 if (count < 2)
0434 return (std::numeric_limits<size_t>::max)();
0435 return size_t(1) << (SizeDigits - count + 1);
0436 }
0437 inline constexpr size_t bucketForHash(size_t nBuckets, size_t hash) noexcept
0438 {
0439 return hash & (nBuckets - 1);
0440 }
0441 }
0442
0443 template <typename Node>
0444 struct iterator;
0445
0446 template <typename Node>
0447 struct Data
0448 {
0449 using Key = typename Node::KeyType;
0450 using T = typename Node::ValueType;
0451 using Span = QHashPrivate::Span<Node>;
0452 using iterator = QHashPrivate::iterator<Node>;
0453
0454 QtPrivate::RefCount ref = {{1}};
0455 size_t size = 0;
0456 size_t numBuckets = 0;
0457 size_t seed = 0;
0458 Span *spans = nullptr;
0459
0460 static constexpr size_t maxNumBuckets() noexcept
0461 {
0462 return (std::numeric_limits<ptrdiff_t>::max)() / sizeof(Span);
0463 }
0464
0465 struct Bucket {
0466 Span *span;
0467 size_t index;
0468
0469 Bucket(Span *s, size_t i) noexcept
0470 : span(s), index(i)
0471 {}
0472 Bucket(const Data *d, size_t bucket) noexcept
0473 : span(d->spans + (bucket >> SpanConstants::SpanShift)),
0474 index(bucket & SpanConstants::LocalBucketMask)
0475 {}
0476 Bucket(iterator it) noexcept
0477 : Bucket(it.d, it.bucket)
0478 {}
0479
0480 size_t toBucketIndex(const Data *d) const noexcept
0481 {
0482 return ((span - d->spans) << SpanConstants::SpanShift) | index;
0483 }
0484 iterator toIterator(const Data *d) const noexcept { return iterator{d, toBucketIndex(d)}; }
0485 void advanceWrapped(const Data *d) noexcept
0486 {
0487 advance_impl(d, d->spans);
0488 }
0489 void advance(const Data *d) noexcept
0490 {
0491 advance_impl(d, nullptr);
0492 }
0493 bool isUnused() const noexcept
0494 {
0495 return !span->hasNode(index);
0496 }
0497 size_t offset() const noexcept
0498 {
0499 return span->offset(index);
0500 }
0501 Node &nodeAtOffset(size_t offset)
0502 {
0503 return span->atOffset(offset);
0504 }
0505 Node *node()
0506 {
0507 return &span->at(index);
0508 }
0509 Node *insert() const
0510 {
0511 return span->insert(index);
0512 }
0513
0514 private:
0515 friend bool operator==(Bucket lhs, Bucket rhs) noexcept
0516 {
0517 return lhs.span == rhs.span && lhs.index == rhs.index;
0518 }
0519 friend bool operator!=(Bucket lhs, Bucket rhs) noexcept { return !(lhs == rhs); }
0520
0521 void advance_impl(const Data *d, Span *whenAtEnd) noexcept
0522 {
0523 Q_ASSERT(span);
0524 ++index;
0525 if (Q_UNLIKELY(index == SpanConstants::NEntries)) {
0526 index = 0;
0527 ++span;
0528 if (span - d->spans == ptrdiff_t(d->numBuckets >> SpanConstants::SpanShift))
0529 span = whenAtEnd;
0530 }
0531 }
0532 };
0533
0534 static auto allocateSpans(size_t numBuckets)
0535 {
0536 struct R {
0537 Span *spans;
0538 size_t nSpans;
0539 };
0540
0541 constexpr qptrdiff MaxSpanCount = (std::numeric_limits<qptrdiff>::max)() / sizeof(Span);
0542 constexpr size_t MaxBucketCount = MaxSpanCount << SpanConstants::SpanShift;
0543
0544 if (numBuckets > MaxBucketCount) {
0545 Q_CHECK_PTR(false);
0546 Q_UNREACHABLE();
0547 }
0548
0549 size_t nSpans = numBuckets >> SpanConstants::SpanShift;
0550 return R{ new Span[nSpans], nSpans };
0551 }
0552
0553 Data(size_t reserve = 0)
0554 {
0555 numBuckets = GrowthPolicy::bucketsForCapacity(reserve);
0556 spans = allocateSpans(numBuckets).spans;
0557 seed = QHashSeed::globalSeed();
0558 }
0559
0560 void reallocationHelper(const Data &other, size_t nSpans, bool resized)
0561 {
0562 for (size_t s = 0; s < nSpans; ++s) {
0563 const Span &span = other.spans[s];
0564 for (size_t index = 0; index < SpanConstants::NEntries; ++index) {
0565 if (!span.hasNode(index))
0566 continue;
0567 const Node &n = span.at(index);
0568 auto it = resized ? findBucket(n.key) : Bucket { spans + s, index };
0569 Q_ASSERT(it.isUnused());
0570 Node *newNode = it.insert();
0571 new (newNode) Node(n);
0572 }
0573 }
0574 }
0575
0576 Data(const Data &other) : size(other.size), numBuckets(other.numBuckets), seed(other.seed)
0577 {
0578 auto r = allocateSpans(numBuckets);
0579 spans = r.spans;
0580 reallocationHelper(other, r.nSpans, false);
0581 }
0582 Data(const Data &other, size_t reserved) : size(other.size), seed(other.seed)
0583 {
0584 numBuckets = GrowthPolicy::bucketsForCapacity(qMax(size, reserved));
0585 spans = allocateSpans(numBuckets).spans;
0586 size_t otherNSpans = other.numBuckets >> SpanConstants::SpanShift;
0587 reallocationHelper(other, otherNSpans, true);
0588 }
0589
0590 static Data *detached(Data *d)
0591 {
0592 if (!d)
0593 return new Data;
0594 Data *dd = new Data(*d);
0595 if (!d->ref.deref())
0596 delete d;
0597 return dd;
0598 }
0599 static Data *detached(Data *d, size_t size)
0600 {
0601 if (!d)
0602 return new Data(size);
0603 Data *dd = new Data(*d, size);
0604 if (!d->ref.deref())
0605 delete d;
0606 return dd;
0607 }
0608
0609 void clear()
0610 {
0611 delete[] spans;
0612 spans = nullptr;
0613 size = 0;
0614 numBuckets = 0;
0615 }
0616
0617 iterator detachedIterator(iterator other) const noexcept
0618 {
0619 return iterator{this, other.bucket};
0620 }
0621
0622 iterator begin() const noexcept
0623 {
0624 iterator it{ this, 0 };
0625 if (it.isUnused())
0626 ++it;
0627 return it;
0628 }
0629
0630 constexpr iterator end() const noexcept
0631 {
0632 return iterator();
0633 }
0634
0635 void rehash(size_t sizeHint = 0)
0636 {
0637 if (sizeHint == 0)
0638 sizeHint = size;
0639 size_t newBucketCount = GrowthPolicy::bucketsForCapacity(sizeHint);
0640
0641 Span *oldSpans = spans;
0642 size_t oldBucketCount = numBuckets;
0643 spans = allocateSpans(newBucketCount).spans;
0644 numBuckets = newBucketCount;
0645 size_t oldNSpans = oldBucketCount >> SpanConstants::SpanShift;
0646
0647 for (size_t s = 0; s < oldNSpans; ++s) {
0648 Span &span = oldSpans[s];
0649 for (size_t index = 0; index < SpanConstants::NEntries; ++index) {
0650 if (!span.hasNode(index))
0651 continue;
0652 Node &n = span.at(index);
0653 auto it = findBucket(n.key);
0654 Q_ASSERT(it.isUnused());
0655 Node *newNode = it.insert();
0656 new (newNode) Node(std::move(n));
0657 }
0658 span.freeData();
0659 }
0660 delete[] oldSpans;
0661 }
0662
0663 size_t nextBucket(size_t bucket) const noexcept
0664 {
0665 ++bucket;
0666 if (bucket == numBuckets)
0667 bucket = 0;
0668 return bucket;
0669 }
0670
0671 float loadFactor() const noexcept
0672 {
0673 return float(size)/numBuckets;
0674 }
0675 bool shouldGrow() const noexcept
0676 {
0677 return size >= (numBuckets >> 1);
0678 }
0679
0680 Bucket findBucket(const Key &key) const noexcept
0681 {
0682 Q_ASSERT(numBuckets > 0);
0683 size_t hash = QHashPrivate::calculateHash(key, seed);
0684 Bucket bucket(this, GrowthPolicy::bucketForHash(numBuckets, hash));
0685
0686
0687 while (true) {
0688 size_t offset = bucket.offset();
0689 if (offset == SpanConstants::UnusedEntry) {
0690 return bucket;
0691 } else {
0692 Node &n = bucket.nodeAtOffset(offset);
0693 if (qHashEquals(n.key, key))
0694 return bucket;
0695 }
0696 bucket.advanceWrapped(this);
0697 }
0698 }
0699
0700 Node *findNode(const Key &key) const noexcept
0701 {
0702 auto bucket = findBucket(key);
0703 if (bucket.isUnused())
0704 return nullptr;
0705 return bucket.node();
0706 }
0707
0708 struct InsertionResult
0709 {
0710 iterator it;
0711 bool initialized;
0712 };
0713
0714 InsertionResult findOrInsert(const Key &key) noexcept
0715 {
0716 Bucket it(static_cast<Span *>(nullptr), 0);
0717 if (numBuckets > 0) {
0718 it = findBucket(key);
0719 if (!it.isUnused())
0720 return { it.toIterator(this), true };
0721 }
0722 if (shouldGrow()) {
0723 rehash(size + 1);
0724 it = findBucket(key);
0725 }
0726 Q_ASSERT(it.span != nullptr);
0727 Q_ASSERT(it.isUnused());
0728 it.insert();
0729 ++size;
0730 return { it.toIterator(this), false };
0731 }
0732
0733 void erase(Bucket bucket) noexcept(std::is_nothrow_destructible<Node>::value)
0734 {
0735 Q_ASSERT(bucket.span->hasNode(bucket.index));
0736 bucket.span->erase(bucket.index);
0737 --size;
0738
0739
0740 Bucket next = bucket;
0741 while (true) {
0742 next.advanceWrapped(this);
0743 size_t offset = next.offset();
0744 if (offset == SpanConstants::UnusedEntry)
0745 return;
0746 size_t hash = QHashPrivate::calculateHash(next.nodeAtOffset(offset).key, seed);
0747 Bucket newBucket(this, GrowthPolicy::bucketForHash(numBuckets, hash));
0748 while (true) {
0749 if (newBucket == next) {
0750
0751 break;
0752 } else if (newBucket == bucket) {
0753
0754 if (next.span == bucket.span) {
0755 bucket.span->moveLocal(next.index, bucket.index);
0756 } else {
0757
0758 bucket.span->moveFromSpan(*next.span, next.index, bucket.index);
0759 }
0760 bucket = next;
0761 break;
0762 }
0763 newBucket.advanceWrapped(this);
0764 }
0765 }
0766 }
0767
0768 ~Data()
0769 {
0770 delete [] spans;
0771 }
0772 };
0773
0774 template <typename Node>
0775 struct iterator {
0776 using Span = QHashPrivate::Span<Node>;
0777
0778 const Data<Node> *d = nullptr;
0779 size_t bucket = 0;
0780
0781 size_t span() const noexcept { return bucket >> SpanConstants::SpanShift; }
0782 size_t index() const noexcept { return bucket & SpanConstants::LocalBucketMask; }
0783 inline bool isUnused() const noexcept { return !d->spans[span()].hasNode(index()); }
0784
0785 inline Node *node() const noexcept
0786 {
0787 Q_ASSERT(!isUnused());
0788 return &d->spans[span()].at(index());
0789 }
0790 bool atEnd() const noexcept { return !d; }
0791
0792 iterator operator++() noexcept
0793 {
0794 while (true) {
0795 ++bucket;
0796 if (bucket == d->numBuckets) {
0797 d = nullptr;
0798 bucket = 0;
0799 break;
0800 }
0801 if (!isUnused())
0802 break;
0803 }
0804 return *this;
0805 }
0806 bool operator==(iterator other) const noexcept
0807 { return d == other.d && bucket == other.bucket; }
0808 bool operator!=(iterator other) const noexcept
0809 { return !(*this == other); }
0810 };
0811
0812
0813
0814 }
0815
0816 template <typename Key, typename T>
0817 class QHash
0818 {
0819 using Node = QHashPrivate::Node<Key, T>;
0820 using Data = QHashPrivate::Data<Node>;
0821 friend class QSet<Key>;
0822 friend class QMultiHash<Key, T>;
0823 friend tst_QHash;
0824
0825 Data *d = nullptr;
0826
0827 public:
0828 using key_type = Key;
0829 using mapped_type = T;
0830 using value_type = T;
0831 using size_type = qsizetype;
0832 using difference_type = qsizetype;
0833 using reference = T &;
0834 using const_reference = const T &;
0835
0836 inline QHash() noexcept = default;
0837 inline QHash(std::initializer_list<std::pair<Key,T> > list)
0838 : d(new Data(list.size()))
0839 {
0840 for (typename std::initializer_list<std::pair<Key,T> >::const_iterator it = list.begin(); it != list.end(); ++it)
0841 insert(it->first, it->second);
0842 }
0843 QHash(const QHash &other) noexcept
0844 : d(other.d)
0845 {
0846 if (d)
0847 d->ref.ref();
0848 }
0849 ~QHash()
0850 {
0851 static_assert(std::is_nothrow_destructible_v<Key>, "Types with throwing destructors are not supported in Qt containers.");
0852 static_assert(std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers.");
0853
0854 if (d && !d->ref.deref())
0855 delete d;
0856 }
0857
0858 QHash &operator=(const QHash &other) noexcept(std::is_nothrow_destructible<Node>::value)
0859 {
0860 if (d != other.d) {
0861 Data *o = other.d;
0862 if (o)
0863 o->ref.ref();
0864 if (d && !d->ref.deref())
0865 delete d;
0866 d = o;
0867 }
0868 return *this;
0869 }
0870
0871 QHash(QHash &&other) noexcept
0872 : d(std::exchange(other.d, nullptr))
0873 {
0874 }
0875 QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QHash)
0876 #ifdef Q_QDOC
0877 template <typename InputIterator>
0878 QHash(InputIterator f, InputIterator l);
0879 #else
0880 template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasKeyAndValue<InputIterator> = true>
0881 QHash(InputIterator f, InputIterator l)
0882 : QHash()
0883 {
0884 QtPrivate::reserveIfForwardIterator(this, f, l);
0885 for (; f != l; ++f)
0886 insert(f.key(), f.value());
0887 }
0888
0889 template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasFirstAndSecond<InputIterator> = true>
0890 QHash(InputIterator f, InputIterator l)
0891 : QHash()
0892 {
0893 QtPrivate::reserveIfForwardIterator(this, f, l);
0894 for (; f != l; ++f)
0895 insert(f->first, f->second);
0896 }
0897 #endif
0898 void swap(QHash &other) noexcept { qt_ptr_swap(d, other.d); }
0899
0900 #ifndef Q_QDOC
0901 template <typename AKey = Key, typename AT = T>
0902 QTypeTraits::compare_eq_result_container<QHash, AKey, AT> operator==(const QHash &other) const noexcept
0903 {
0904 if (d == other.d)
0905 return true;
0906 if (size() != other.size())
0907 return false;
0908
0909 for (const_iterator it = other.begin(); it != other.end(); ++it) {
0910 const_iterator i = find(it.key());
0911 if (i == end() || !i.i.node()->valuesEqual(it.i.node()))
0912 return false;
0913 }
0914
0915 return true;
0916 }
0917 template <typename AKey = Key, typename AT = T>
0918 QTypeTraits::compare_eq_result_container<QHash, AKey, AT> operator!=(const QHash &other) const noexcept
0919 { return !(*this == other); }
0920 #else
0921 bool operator==(const QHash &other) const;
0922 bool operator!=(const QHash &other) const;
0923 #endif
0924
0925 inline qsizetype size() const noexcept { return d ? qsizetype(d->size) : 0; }
0926 inline bool isEmpty() const noexcept { return !d || d->size == 0; }
0927
0928 inline qsizetype capacity() const noexcept { return d ? qsizetype(d->numBuckets >> 1) : 0; }
0929 void reserve(qsizetype size)
0930 {
0931
0932 if (size && (this->capacity() >= size))
0933 return;
0934 if (isDetached())
0935 d->rehash(size);
0936 else
0937 d = Data::detached(d, size_t(size));
0938 }
0939 inline void squeeze()
0940 {
0941 if (capacity())
0942 reserve(0);
0943 }
0944
0945 inline void detach() { if (!d || d->ref.isShared()) d = Data::detached(d); }
0946 inline bool isDetached() const noexcept { return d && !d->ref.isShared(); }
0947 bool isSharedWith(const QHash &other) const noexcept { return d == other.d; }
0948
0949 void clear() noexcept(std::is_nothrow_destructible<Node>::value)
0950 {
0951 if (d && !d->ref.deref())
0952 delete d;
0953 d = nullptr;
0954 }
0955
0956 bool remove(const Key &key)
0957 {
0958 if (isEmpty())
0959 return false;
0960 auto it = d->findBucket(key);
0961 size_t bucket = it.toBucketIndex(d);
0962 detach();
0963 it = typename Data::Bucket(d, bucket);
0964
0965 if (it.isUnused())
0966 return false;
0967 d->erase(it);
0968 return true;
0969 }
0970 template <typename Predicate>
0971 qsizetype removeIf(Predicate pred)
0972 {
0973 return QtPrivate::associative_erase_if(*this, pred);
0974 }
0975 T take(const Key &key)
0976 {
0977 if (isEmpty())
0978 return T();
0979 auto it = d->findBucket(key);
0980 size_t bucket = it.toBucketIndex(d);
0981 detach();
0982 it = typename Data::Bucket(d, bucket);
0983
0984 if (it.isUnused())
0985 return T();
0986 T value = it.node()->takeValue();
0987 d->erase(it);
0988 return value;
0989 }
0990
0991 bool contains(const Key &key) const noexcept
0992 {
0993 if (!d)
0994 return false;
0995 return d->findNode(key) != nullptr;
0996 }
0997 qsizetype count(const Key &key) const noexcept
0998 {
0999 return contains(key) ? 1 : 0;
1000 }
1001
1002 private:
1003 const Key *keyImpl(const T &value) const noexcept
1004 {
1005 if (d) {
1006 const_iterator i = begin();
1007 while (i != end()) {
1008 if (i.value() == value)
1009 return &i.key();
1010 ++i;
1011 }
1012 }
1013
1014 return nullptr;
1015 }
1016
1017 public:
1018 Key key(const T &value) const noexcept
1019 {
1020 if (auto *k = keyImpl(value))
1021 return *k;
1022 else
1023 return Key();
1024 }
1025 Key key(const T &value, const Key &defaultKey) const noexcept
1026 {
1027 if (auto *k = keyImpl(value))
1028 return *k;
1029 else
1030 return defaultKey;
1031 }
1032
1033 private:
1034 T *valueImpl(const Key &key) const noexcept
1035 {
1036 if (d) {
1037 Node *n = d->findNode(key);
1038 if (n)
1039 return &n->value;
1040 }
1041 return nullptr;
1042 }
1043 public:
1044 T value(const Key &key) const noexcept
1045 {
1046 if (T *v = valueImpl(key))
1047 return *v;
1048 else
1049 return T();
1050 }
1051
1052 T value(const Key &key, const T &defaultValue) const noexcept
1053 {
1054 if (T *v = valueImpl(key))
1055 return *v;
1056 else
1057 return defaultValue;
1058 }
1059
1060 T &operator[](const Key &key)
1061 {
1062 const auto copy = isDetached() ? QHash() : *this;
1063 detach();
1064 auto result = d->findOrInsert(key);
1065 Q_ASSERT(!result.it.atEnd());
1066 if (!result.initialized)
1067 Node::createInPlace(result.it.node(), key, T());
1068 return result.it.node()->value;
1069 }
1070
1071 const T operator[](const Key &key) const noexcept
1072 {
1073 return value(key);
1074 }
1075
1076 QList<Key> keys() const { return QList<Key>(keyBegin(), keyEnd()); }
1077 QList<Key> keys(const T &value) const
1078 {
1079 QList<Key> res;
1080 const_iterator i = begin();
1081 while (i != end()) {
1082 if (i.value() == value)
1083 res.append(i.key());
1084 ++i;
1085 }
1086 return res;
1087 }
1088 QList<T> values() const { return QList<T>(begin(), end()); }
1089
1090 class const_iterator;
1091
1092 class iterator
1093 {
1094 using piter = typename QHashPrivate::iterator<Node>;
1095 friend class const_iterator;
1096 friend class QHash<Key, T>;
1097 friend class QSet<Key>;
1098 piter i;
1099 explicit inline iterator(piter it) noexcept : i(it) { }
1100
1101 public:
1102 typedef std::forward_iterator_tag iterator_category;
1103 typedef qptrdiff difference_type;
1104 typedef T value_type;
1105 typedef T *pointer;
1106 typedef T &reference;
1107
1108 constexpr iterator() noexcept = default;
1109
1110 inline const Key &key() const noexcept { return i.node()->key; }
1111 inline T &value() const noexcept { return i.node()->value; }
1112 inline T &operator*() const noexcept { return i.node()->value; }
1113 inline T *operator->() const noexcept { return &i.node()->value; }
1114 inline bool operator==(const iterator &o) const noexcept { return i == o.i; }
1115 inline bool operator!=(const iterator &o) const noexcept { return i != o.i; }
1116
1117 inline iterator &operator++() noexcept
1118 {
1119 ++i;
1120 return *this;
1121 }
1122 inline iterator operator++(int) noexcept
1123 {
1124 iterator r = *this;
1125 ++i;
1126 return r;
1127 }
1128
1129 inline bool operator==(const const_iterator &o) const noexcept { return i == o.i; }
1130 inline bool operator!=(const const_iterator &o) const noexcept { return i != o.i; }
1131 };
1132 friend class iterator;
1133
1134 class const_iterator
1135 {
1136 using piter = typename QHashPrivate::iterator<Node>;
1137 friend class iterator;
1138 friend class QHash<Key, T>;
1139 friend class QSet<Key>;
1140 piter i;
1141 explicit inline const_iterator(piter it) : i(it) { }
1142
1143 public:
1144 typedef std::forward_iterator_tag iterator_category;
1145 typedef qptrdiff difference_type;
1146 typedef T value_type;
1147 typedef const T *pointer;
1148 typedef const T &reference;
1149
1150 constexpr const_iterator() noexcept = default;
1151 inline const_iterator(const iterator &o) noexcept : i(o.i) { }
1152
1153 inline const Key &key() const noexcept { return i.node()->key; }
1154 inline const T &value() const noexcept { return i.node()->value; }
1155 inline const T &operator*() const noexcept { return i.node()->value; }
1156 inline const T *operator->() const noexcept { return &i.node()->value; }
1157 inline bool operator==(const const_iterator &o) const noexcept { return i == o.i; }
1158 inline bool operator!=(const const_iterator &o) const noexcept { return i != o.i; }
1159
1160 inline const_iterator &operator++() noexcept
1161 {
1162 ++i;
1163 return *this;
1164 }
1165 inline const_iterator operator++(int) noexcept
1166 {
1167 const_iterator r = *this;
1168 ++i;
1169 return r;
1170 }
1171 };
1172 friend class const_iterator;
1173
1174 class key_iterator
1175 {
1176 const_iterator i;
1177
1178 public:
1179 typedef typename const_iterator::iterator_category iterator_category;
1180 typedef qptrdiff difference_type;
1181 typedef Key value_type;
1182 typedef const Key *pointer;
1183 typedef const Key &reference;
1184
1185 key_iterator() noexcept = default;
1186 explicit key_iterator(const_iterator o) noexcept : i(o) { }
1187
1188 const Key &operator*() const noexcept { return i.key(); }
1189 const Key *operator->() const noexcept { return &i.key(); }
1190 bool operator==(key_iterator o) const noexcept { return i == o.i; }
1191 bool operator!=(key_iterator o) const noexcept { return i != o.i; }
1192
1193 inline key_iterator &operator++() noexcept { ++i; return *this; }
1194 inline key_iterator operator++(int) noexcept { return key_iterator(i++);}
1195 const_iterator base() const noexcept { return i; }
1196 };
1197
1198 typedef QKeyValueIterator<const Key&, const T&, const_iterator> const_key_value_iterator;
1199 typedef QKeyValueIterator<const Key&, T&, iterator> key_value_iterator;
1200
1201
1202 inline iterator begin() { detach(); return iterator(d->begin()); }
1203 inline const_iterator begin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
1204 inline const_iterator cbegin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
1205 inline const_iterator constBegin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
1206 inline iterator end() noexcept { return iterator(); }
1207 inline const_iterator end() const noexcept { return const_iterator(); }
1208 inline const_iterator cend() const noexcept { return const_iterator(); }
1209 inline const_iterator constEnd() const noexcept { return const_iterator(); }
1210 inline key_iterator keyBegin() const noexcept { return key_iterator(begin()); }
1211 inline key_iterator keyEnd() const noexcept { return key_iterator(end()); }
1212 inline key_value_iterator keyValueBegin() { return key_value_iterator(begin()); }
1213 inline key_value_iterator keyValueEnd() { return key_value_iterator(end()); }
1214 inline const_key_value_iterator keyValueBegin() const noexcept { return const_key_value_iterator(begin()); }
1215 inline const_key_value_iterator constKeyValueBegin() const noexcept { return const_key_value_iterator(begin()); }
1216 inline const_key_value_iterator keyValueEnd() const noexcept { return const_key_value_iterator(end()); }
1217 inline const_key_value_iterator constKeyValueEnd() const noexcept { return const_key_value_iterator(end()); }
1218 auto asKeyValueRange() & { return QtPrivate::QKeyValueRange(*this); }
1219 auto asKeyValueRange() const & { return QtPrivate::QKeyValueRange(*this); }
1220 auto asKeyValueRange() && { return QtPrivate::QKeyValueRange(std::move(*this)); }
1221 auto asKeyValueRange() const && { return QtPrivate::QKeyValueRange(std::move(*this)); }
1222
1223 iterator erase(const_iterator it)
1224 {
1225 Q_ASSERT(it != constEnd());
1226 detach();
1227
1228 iterator i = iterator{d->detachedIterator(it.i)};
1229 typename Data::Bucket bucket(i.i);
1230
1231 d->erase(bucket);
1232 if (bucket.toBucketIndex(d) == d->numBuckets - 1 || bucket.isUnused())
1233 ++i;
1234 return i;
1235 }
1236
1237 std::pair<iterator, iterator> equal_range(const Key &key)
1238 {
1239 auto first = find(key);
1240 auto second = first;
1241 if (second != iterator())
1242 ++second;
1243 return {first, second};
1244 }
1245
1246 std::pair<const_iterator, const_iterator> equal_range(const Key &key) const noexcept
1247 {
1248 auto first = find(key);
1249 auto second = first;
1250 if (second != iterator())
1251 ++second;
1252 return {first, second};
1253 }
1254
1255 typedef iterator Iterator;
1256 typedef const_iterator ConstIterator;
1257 inline qsizetype count() const noexcept { return d ? qsizetype(d->size) : 0; }
1258 iterator find(const Key &key)
1259 {
1260 if (isEmpty())
1261 return end();
1262 auto it = d->findBucket(key);
1263 size_t bucket = it.toBucketIndex(d);
1264 detach();
1265 it = typename Data::Bucket(d, bucket);
1266 if (it.isUnused())
1267 return end();
1268 return iterator(it.toIterator(d));
1269 }
1270 const_iterator find(const Key &key) const noexcept
1271 {
1272 if (isEmpty())
1273 return end();
1274 auto it = d->findBucket(key);
1275 if (it.isUnused())
1276 return end();
1277 return const_iterator({d, it.toBucketIndex(d)});
1278 }
1279 const_iterator constFind(const Key &key) const noexcept
1280 {
1281 return find(key);
1282 }
1283 iterator insert(const Key &key, const T &value)
1284 {
1285 return emplace(key, value);
1286 }
1287
1288 void insert(const QHash &hash)
1289 {
1290 if (d == hash.d || !hash.d)
1291 return;
1292 if (!d) {
1293 *this = hash;
1294 return;
1295 }
1296
1297 detach();
1298
1299 for (auto it = hash.begin(); it != hash.end(); ++it)
1300 emplace(it.key(), it.value());
1301 }
1302
1303 template <typename ...Args>
1304 iterator emplace(const Key &key, Args &&... args)
1305 {
1306 Key copy = key;
1307 return emplace(std::move(copy), std::forward<Args>(args)...);
1308 }
1309
1310 template <typename ...Args>
1311 iterator emplace(Key &&key, Args &&... args)
1312 {
1313 if (isDetached()) {
1314 if (d->shouldGrow())
1315 return emplace_helper(std::move(key), T(std::forward<Args>(args)...));
1316 return emplace_helper(std::move(key), std::forward<Args>(args)...);
1317 }
1318
1319 const auto copy = *this;
1320 detach();
1321 return emplace_helper(std::move(key), std::forward<Args>(args)...);
1322 }
1323
1324 float load_factor() const noexcept { return d ? d->loadFactor() : 0; }
1325 static float max_load_factor() noexcept { return 0.5; }
1326 size_t bucket_count() const noexcept { return d ? d->numBuckets : 0; }
1327 static size_t max_bucket_count() noexcept { return Data::maxNumBuckets(); }
1328
1329 inline bool empty() const noexcept { return isEmpty(); }
1330
1331 private:
1332 template <typename ...Args>
1333 iterator emplace_helper(Key &&key, Args &&... args)
1334 {
1335 auto result = d->findOrInsert(key);
1336 if (!result.initialized)
1337 Node::createInPlace(result.it.node(), std::move(key), std::forward<Args>(args)...);
1338 else
1339 result.it.node()->emplaceValue(std::forward<Args>(args)...);
1340 return iterator(result.it);
1341 }
1342 };
1343
1344
1345
1346 template <typename Key, typename T>
1347 class QMultiHash
1348 {
1349 using Node = QHashPrivate::MultiNode<Key, T>;
1350 using Data = QHashPrivate::Data<Node>;
1351 using Chain = QHashPrivate::MultiNodeChain<T>;
1352
1353 Data *d = nullptr;
1354 qsizetype m_size = 0;
1355
1356 public:
1357 using key_type = Key;
1358 using mapped_type = T;
1359 using value_type = T;
1360 using size_type = qsizetype;
1361 using difference_type = qsizetype;
1362 using reference = T &;
1363 using const_reference = const T &;
1364
1365 QMultiHash() noexcept = default;
1366 inline QMultiHash(std::initializer_list<std::pair<Key,T> > list)
1367 : d(new Data(list.size()))
1368 {
1369 for (typename std::initializer_list<std::pair<Key,T> >::const_iterator it = list.begin(); it != list.end(); ++it)
1370 insert(it->first, it->second);
1371 }
1372 #ifdef Q_QDOC
1373 template <typename InputIterator>
1374 QMultiHash(InputIterator f, InputIterator l);
1375 #else
1376 template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasKeyAndValue<InputIterator> = true>
1377 QMultiHash(InputIterator f, InputIterator l)
1378 {
1379 QtPrivate::reserveIfForwardIterator(this, f, l);
1380 for (; f != l; ++f)
1381 insert(f.key(), f.value());
1382 }
1383
1384 template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasFirstAndSecond<InputIterator> = true>
1385 QMultiHash(InputIterator f, InputIterator l)
1386 {
1387 QtPrivate::reserveIfForwardIterator(this, f, l);
1388 for (; f != l; ++f)
1389 insert(f->first, f->second);
1390 }
1391 #endif
1392 QMultiHash(const QMultiHash &other) noexcept
1393 : d(other.d), m_size(other.m_size)
1394 {
1395 if (d)
1396 d->ref.ref();
1397 }
1398 ~QMultiHash()
1399 {
1400 static_assert(std::is_nothrow_destructible_v<Key>, "Types with throwing destructors are not supported in Qt containers.");
1401 static_assert(std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers.");
1402
1403 if (d && !d->ref.deref())
1404 delete d;
1405 }
1406
1407 QMultiHash &operator=(const QMultiHash &other) noexcept(std::is_nothrow_destructible<Node>::value)
1408 {
1409 if (d != other.d) {
1410 Data *o = other.d;
1411 if (o)
1412 o->ref.ref();
1413 if (d && !d->ref.deref())
1414 delete d;
1415 d = o;
1416 m_size = other.m_size;
1417 }
1418 return *this;
1419 }
1420 QMultiHash(QMultiHash &&other) noexcept
1421 : d(std::exchange(other.d, nullptr)),
1422 m_size(std::exchange(other.m_size, 0))
1423 {
1424 }
1425 QMultiHash &operator=(QMultiHash &&other) noexcept(std::is_nothrow_destructible<Node>::value)
1426 {
1427 QMultiHash moved(std::move(other));
1428 swap(moved);
1429 return *this;
1430 }
1431
1432 explicit QMultiHash(const QHash<Key, T> &other)
1433 : QMultiHash(other.begin(), other.end())
1434 {}
1435
1436 explicit QMultiHash(QHash<Key, T> &&other)
1437 {
1438 unite(std::move(other));
1439 }
1440
1441 void swap(QMultiHash &other) noexcept
1442 {
1443 qt_ptr_swap(d, other.d);
1444 std::swap(m_size, other.m_size);
1445 }
1446
1447 #ifndef Q_QDOC
1448 template <typename AKey = Key, typename AT = T>
1449 QTypeTraits::compare_eq_result_container<QMultiHash, AKey, AT> operator==(const QMultiHash &other) const noexcept
1450 {
1451 if (d == other.d)
1452 return true;
1453 if (m_size != other.m_size)
1454 return false;
1455 if (m_size == 0)
1456 return true;
1457
1458 Q_ASSERT(d);
1459 Q_ASSERT(other.d);
1460 if (d->size != other.d->size)
1461 return false;
1462 for (auto it = other.d->begin(); it != other.d->end(); ++it) {
1463 auto *n = d->findNode(it.node()->key);
1464 if (!n)
1465 return false;
1466 Chain *e = it.node()->value;
1467 while (e) {
1468 Chain *oe = n->value;
1469 while (oe) {
1470 if (oe->value == e->value)
1471 break;
1472 oe = oe->next;
1473 }
1474 if (!oe)
1475 return false;
1476 e = e->next;
1477 }
1478 }
1479
1480 return true;
1481 }
1482 template <typename AKey = Key, typename AT = T>
1483 QTypeTraits::compare_eq_result_container<QMultiHash, AKey, AT> operator!=(const QMultiHash &other) const noexcept
1484 { return !(*this == other); }
1485 #else
1486 bool operator==(const QMultiHash &other) const;
1487 bool operator!=(const QMultiHash &other) const;
1488 #endif
1489
1490 inline qsizetype size() const noexcept { return m_size; }
1491
1492 inline bool isEmpty() const noexcept { return !m_size; }
1493
1494 inline qsizetype capacity() const noexcept { return d ? qsizetype(d->numBuckets >> 1) : 0; }
1495 void reserve(qsizetype size)
1496 {
1497
1498 if (size && (this->capacity() >= size))
1499 return;
1500 if (isDetached())
1501 d->rehash(size);
1502 else
1503 d = Data::detached(d, size_t(size));
1504 }
1505 inline void squeeze() { reserve(0); }
1506
1507 inline void detach() { if (!d || d->ref.isShared()) d = Data::detached(d); }
1508 inline bool isDetached() const noexcept { return d && !d->ref.isShared(); }
1509 bool isSharedWith(const QMultiHash &other) const noexcept { return d == other.d; }
1510
1511 void clear() noexcept(std::is_nothrow_destructible<Node>::value)
1512 {
1513 if (d && !d->ref.deref())
1514 delete d;
1515 d = nullptr;
1516 m_size = 0;
1517 }
1518
1519 qsizetype remove(const Key &key)
1520 {
1521 if (isEmpty())
1522 return 0;
1523 auto it = d->findBucket(key);
1524 size_t bucket = it.toBucketIndex(d);
1525 detach();
1526 it = typename Data::Bucket(d, bucket);
1527
1528 if (it.isUnused())
1529 return 0;
1530 qsizetype n = Node::freeChain(it.node());
1531 m_size -= n;
1532 Q_ASSERT(m_size >= 0);
1533 d->erase(it);
1534 return n;
1535 }
1536 template <typename Predicate>
1537 qsizetype removeIf(Predicate pred)
1538 {
1539 return QtPrivate::associative_erase_if(*this, pred);
1540 }
1541 T take(const Key &key)
1542 {
1543 if (isEmpty())
1544 return T();
1545 auto it = d->findBucket(key);
1546 size_t bucket = it.toBucketIndex(d);
1547 detach();
1548 it = typename Data::Bucket(d, bucket);
1549
1550 if (it.isUnused())
1551 return T();
1552 Chain *e = it.node()->value;
1553 Q_ASSERT(e);
1554 T t = std::move(e->value);
1555 if (e->next) {
1556 it.node()->value = e->next;
1557 delete e;
1558 } else {
1559
1560 d->erase(it);
1561 }
1562 --m_size;
1563 Q_ASSERT(m_size >= 0);
1564 return t;
1565 }
1566
1567 bool contains(const Key &key) const noexcept
1568 {
1569 if (!d)
1570 return false;
1571 return d->findNode(key) != nullptr;
1572 }
1573
1574 private:
1575 const Key *keyImpl(const T &value) const noexcept
1576 {
1577 if (d) {
1578 auto i = d->begin();
1579 while (i != d->end()) {
1580 Chain *e = i.node()->value;
1581 if (e->contains(value))
1582 return &i.node()->key;
1583 ++i;
1584 }
1585 }
1586
1587 return nullptr;
1588 }
1589 public:
1590 Key key(const T &value) const noexcept
1591 {
1592 if (auto *k = keyImpl(value))
1593 return *k;
1594 else
1595 return Key();
1596 }
1597 Key key(const T &value, const Key &defaultKey) const noexcept
1598 {
1599 if (auto *k = keyImpl(value))
1600 return *k;
1601 else
1602 return defaultKey;
1603 }
1604
1605 private:
1606 T *valueImpl(const Key &key) const noexcept
1607 {
1608 if (d) {
1609 Node *n = d->findNode(key);
1610 if (n) {
1611 Q_ASSERT(n->value);
1612 return &n->value->value;
1613 }
1614 }
1615 return nullptr;
1616 }
1617 public:
1618 T value(const Key &key) const noexcept
1619 {
1620 if (auto *v = valueImpl(key))
1621 return *v;
1622 else
1623 return T();
1624 }
1625 T value(const Key &key, const T &defaultValue) const noexcept
1626 {
1627 if (auto *v = valueImpl(key))
1628 return *v;
1629 else
1630 return defaultValue;
1631 }
1632
1633 T &operator[](const Key &key)
1634 {
1635 const auto copy = isDetached() ? QMultiHash() : *this;
1636 detach();
1637 auto result = d->findOrInsert(key);
1638 Q_ASSERT(!result.it.atEnd());
1639 if (!result.initialized) {
1640 Node::createInPlace(result.it.node(), key, T());
1641 ++m_size;
1642 }
1643 return result.it.node()->value->value;
1644 }
1645
1646 const T operator[](const Key &key) const noexcept
1647 {
1648 return value(key);
1649 }
1650
1651 QList<Key> uniqueKeys() const
1652 {
1653 QList<Key> res;
1654 if (d) {
1655 auto i = d->begin();
1656 while (i != d->end()) {
1657 res.append(i.node()->key);
1658 ++i;
1659 }
1660 }
1661 return res;
1662 }
1663
1664 QList<Key> keys() const { return QList<Key>(keyBegin(), keyEnd()); }
1665 QList<Key> keys(const T &value) const
1666 {
1667 QList<Key> res;
1668 const_iterator i = begin();
1669 while (i != end()) {
1670 if (i.value() == value)
1671 res.append(i.key());
1672 ++i;
1673 }
1674 return res;
1675 }
1676 QList<T> values() const { return QList<T>(begin(), end()); }
1677 QList<T> values(const Key &key) const
1678 {
1679 QList<T> values;
1680 if (d) {
1681 Node *n = d->findNode(key);
1682 if (n) {
1683 Chain *e = n->value;
1684 while (e) {
1685 values.append(e->value);
1686 e = e->next;
1687 }
1688 }
1689 }
1690 return values;
1691 }
1692
1693 class const_iterator;
1694
1695 class iterator
1696 {
1697 using piter = typename QHashPrivate::iterator<Node>;
1698 friend class const_iterator;
1699 friend class QMultiHash<Key, T>;
1700 piter i;
1701 Chain **e = nullptr;
1702 explicit inline iterator(piter it, Chain **entry = nullptr) noexcept : i(it), e(entry)
1703 {
1704 if (!it.atEnd() && !e) {
1705 e = &it.node()->value;
1706 Q_ASSERT(e && *e);
1707 }
1708 }
1709
1710 public:
1711 typedef std::forward_iterator_tag iterator_category;
1712 typedef qptrdiff difference_type;
1713 typedef T value_type;
1714 typedef T *pointer;
1715 typedef T &reference;
1716
1717 constexpr iterator() noexcept = default;
1718
1719 inline const Key &key() const noexcept { return i.node()->key; }
1720 inline T &value() const noexcept { return (*e)->value; }
1721 inline T &operator*() const noexcept { return (*e)->value; }
1722 inline T *operator->() const noexcept { return &(*e)->value; }
1723 inline bool operator==(const iterator &o) const noexcept { return e == o.e; }
1724 inline bool operator!=(const iterator &o) const noexcept { return e != o.e; }
1725
1726 inline iterator &operator++() noexcept {
1727 Q_ASSERT(e && *e);
1728 e = &(*e)->next;
1729 Q_ASSERT(e);
1730 if (!*e) {
1731 ++i;
1732 e = i.atEnd() ? nullptr : &i.node()->value;
1733 }
1734 return *this;
1735 }
1736 inline iterator operator++(int) noexcept {
1737 iterator r = *this;
1738 ++(*this);
1739 return r;
1740 }
1741
1742 inline bool operator==(const const_iterator &o) const noexcept { return e == o.e; }
1743 inline bool operator!=(const const_iterator &o) const noexcept { return e != o.e; }
1744 };
1745 friend class iterator;
1746
1747 class const_iterator
1748 {
1749 using piter = typename QHashPrivate::iterator<Node>;
1750 friend class iterator;
1751 friend class QMultiHash<Key, T>;
1752 piter i;
1753 Chain **e = nullptr;
1754 explicit inline const_iterator(piter it, Chain **entry = nullptr) noexcept : i(it), e(entry)
1755 {
1756 if (!it.atEnd() && !e) {
1757 e = &it.node()->value;
1758 Q_ASSERT(e && *e);
1759 }
1760 }
1761
1762 public:
1763 typedef std::forward_iterator_tag iterator_category;
1764 typedef qptrdiff difference_type;
1765 typedef T value_type;
1766 typedef const T *pointer;
1767 typedef const T &reference;
1768
1769 constexpr const_iterator() noexcept = default;
1770 inline const_iterator(const iterator &o) noexcept : i(o.i), e(o.e) { }
1771
1772 inline const Key &key() const noexcept { return i.node()->key; }
1773 inline T &value() const noexcept { return (*e)->value; }
1774 inline T &operator*() const noexcept { return (*e)->value; }
1775 inline T *operator->() const noexcept { return &(*e)->value; }
1776 inline bool operator==(const const_iterator &o) const noexcept { return e == o.e; }
1777 inline bool operator!=(const const_iterator &o) const noexcept { return e != o.e; }
1778
1779 inline const_iterator &operator++() noexcept {
1780 Q_ASSERT(e && *e);
1781 e = &(*e)->next;
1782 Q_ASSERT(e);
1783 if (!*e) {
1784 ++i;
1785 e = i.atEnd() ? nullptr : &i.node()->value;
1786 }
1787 return *this;
1788 }
1789 inline const_iterator operator++(int) noexcept
1790 {
1791 const_iterator r = *this;
1792 ++(*this);
1793 return r;
1794 }
1795 };
1796 friend class const_iterator;
1797
1798 class key_iterator
1799 {
1800 const_iterator i;
1801
1802 public:
1803 typedef typename const_iterator::iterator_category iterator_category;
1804 typedef qptrdiff difference_type;
1805 typedef Key value_type;
1806 typedef const Key *pointer;
1807 typedef const Key &reference;
1808
1809 key_iterator() noexcept = default;
1810 explicit key_iterator(const_iterator o) noexcept : i(o) { }
1811
1812 const Key &operator*() const noexcept { return i.key(); }
1813 const Key *operator->() const noexcept { return &i.key(); }
1814 bool operator==(key_iterator o) const noexcept { return i == o.i; }
1815 bool operator!=(key_iterator o) const noexcept { return i != o.i; }
1816
1817 inline key_iterator &operator++() noexcept { ++i; return *this; }
1818 inline key_iterator operator++(int) noexcept { return key_iterator(i++);}
1819 const_iterator base() const noexcept { return i; }
1820 };
1821
1822 typedef QKeyValueIterator<const Key&, const T&, const_iterator> const_key_value_iterator;
1823 typedef QKeyValueIterator<const Key&, T&, iterator> key_value_iterator;
1824
1825
1826 inline iterator begin() { detach(); return iterator(d->begin()); }
1827 inline const_iterator begin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
1828 inline const_iterator cbegin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
1829 inline const_iterator constBegin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
1830 inline iterator end() noexcept { return iterator(); }
1831 inline const_iterator end() const noexcept { return const_iterator(); }
1832 inline const_iterator cend() const noexcept { return const_iterator(); }
1833 inline const_iterator constEnd() const noexcept { return const_iterator(); }
1834 inline key_iterator keyBegin() const noexcept { return key_iterator(begin()); }
1835 inline key_iterator keyEnd() const noexcept { return key_iterator(end()); }
1836 inline key_value_iterator keyValueBegin() noexcept { return key_value_iterator(begin()); }
1837 inline key_value_iterator keyValueEnd() noexcept { return key_value_iterator(end()); }
1838 inline const_key_value_iterator keyValueBegin() const noexcept { return const_key_value_iterator(begin()); }
1839 inline const_key_value_iterator constKeyValueBegin() const noexcept { return const_key_value_iterator(begin()); }
1840 inline const_key_value_iterator keyValueEnd() const noexcept { return const_key_value_iterator(end()); }
1841 inline const_key_value_iterator constKeyValueEnd() const noexcept { return const_key_value_iterator(end()); }
1842 auto asKeyValueRange() & { return QtPrivate::QKeyValueRange(*this); }
1843 auto asKeyValueRange() const & { return QtPrivate::QKeyValueRange(*this); }
1844 auto asKeyValueRange() && { return QtPrivate::QKeyValueRange(std::move(*this)); }
1845 auto asKeyValueRange() const && { return QtPrivate::QKeyValueRange(std::move(*this)); }
1846
1847 iterator detach(const_iterator it)
1848 {
1849 auto i = it.i;
1850 Chain **e = it.e;
1851 if (d->ref.isShared()) {
1852
1853 qsizetype n = 0;
1854 Chain *entry = i.node()->value;
1855 while (entry != *it.e) {
1856 ++n;
1857 entry = entry->next;
1858 }
1859 Q_ASSERT(entry);
1860 detach_helper();
1861
1862 i = d->detachedIterator(i);
1863 e = &i.node()->value;
1864 while (n) {
1865 e = &(*e)->next;
1866 --n;
1867 }
1868 Q_ASSERT(e && *e);
1869 }
1870 return iterator(i, e);
1871 }
1872
1873 iterator erase(const_iterator it)
1874 {
1875 Q_ASSERT(d);
1876 iterator iter = detach(it);
1877 iterator i = iter;
1878 Chain *e = *i.e;
1879 Chain *next = e->next;
1880 *i.e = next;
1881 delete e;
1882 if (!next) {
1883 if (i.e == &i.i.node()->value) {
1884
1885 typename Data::Bucket bucket(i.i);
1886 d->erase(bucket);
1887 if (bucket.toBucketIndex(d) == d->numBuckets - 1 || bucket.isUnused())
1888 i = iterator(++iter.i);
1889 else
1890 i = iterator(bucket.toIterator(d));
1891 } else {
1892 i = iterator(++iter.i);
1893 }
1894 }
1895 --m_size;
1896 Q_ASSERT(m_size >= 0);
1897 return i;
1898 }
1899
1900
1901 typedef iterator Iterator;
1902 typedef const_iterator ConstIterator;
1903 inline qsizetype count() const noexcept { return size(); }
1904 iterator find(const Key &key)
1905 {
1906 if (isEmpty())
1907 return end();
1908 auto it = d->findBucket(key);
1909 size_t bucket = it.toBucketIndex(d);
1910 detach();
1911 it = typename Data::Bucket(d, bucket);
1912
1913 if (it.isUnused())
1914 return end();
1915 return iterator(it.toIterator(d));
1916 }
1917 const_iterator find(const Key &key) const noexcept
1918 {
1919 return constFind(key);
1920 }
1921 const_iterator constFind(const Key &key) const noexcept
1922 {
1923 if (isEmpty())
1924 return end();
1925 auto it = d->findBucket(key);
1926 if (it.isUnused())
1927 return constEnd();
1928 return const_iterator(it.toIterator(d));
1929 }
1930 iterator insert(const Key &key, const T &value)
1931 {
1932 return emplace(key, value);
1933 }
1934
1935 template <typename ...Args>
1936 iterator emplace(const Key &key, Args &&... args)
1937 {
1938 return emplace(Key(key), std::forward<Args>(args)...);
1939 }
1940
1941 template <typename ...Args>
1942 iterator emplace(Key &&key, Args &&... args)
1943 {
1944 if (isDetached()) {
1945 if (d->shouldGrow())
1946 return emplace_helper(std::move(key), T(std::forward<Args>(args)...));
1947 return emplace_helper(std::move(key), std::forward<Args>(args)...);
1948 }
1949
1950 const auto copy = *this;
1951 detach();
1952 return emplace_helper(std::move(key), std::forward<Args>(args)...);
1953 }
1954
1955
1956 float load_factor() const noexcept { return d ? d->loadFactor() : 0; }
1957 static float max_load_factor() noexcept { return 0.5; }
1958 size_t bucket_count() const noexcept { return d ? d->numBuckets : 0; }
1959 static size_t max_bucket_count() noexcept { return Data::maxNumBuckets(); }
1960
1961 inline bool empty() const noexcept { return isEmpty(); }
1962
1963 inline iterator replace(const Key &key, const T &value)
1964 {
1965 return emplaceReplace(key, value);
1966 }
1967
1968 template <typename ...Args>
1969 iterator emplaceReplace(const Key &key, Args &&... args)
1970 {
1971 return emplaceReplace(Key(key), std::forward<Args>(args)...);
1972 }
1973
1974 template <typename ...Args>
1975 iterator emplaceReplace(Key &&key, Args &&... args)
1976 {
1977 if (isDetached()) {
1978 if (d->shouldGrow())
1979 return emplaceReplace_helper(std::move(key), T(std::forward<Args>(args)...));
1980 return emplaceReplace_helper(std::move(key), std::forward<Args>(args)...);
1981 }
1982
1983 const auto copy = *this;
1984 detach();
1985 return emplaceReplace_helper(std::move(key), std::forward<Args>(args)...);
1986 }
1987
1988 inline QMultiHash &operator+=(const QMultiHash &other)
1989 { this->unite(other); return *this; }
1990 inline QMultiHash operator+(const QMultiHash &other) const
1991 { QMultiHash result = *this; result += other; return result; }
1992
1993 bool contains(const Key &key, const T &value) const noexcept
1994 {
1995 if (isEmpty())
1996 return false;
1997 auto n = d->findNode(key);
1998 if (n == nullptr)
1999 return false;
2000 return n->value->contains(value);
2001 }
2002
2003 qsizetype remove(const Key &key, const T &value)
2004 {
2005 if (isEmpty())
2006 return 0;
2007 auto it = d->findBucket(key);
2008 size_t bucket = it.toBucketIndex(d);
2009 detach();
2010 it = typename Data::Bucket(d, bucket);
2011
2012 if (it.isUnused())
2013 return 0;
2014 qsizetype n = 0;
2015 Chain **e = &it.node()->value;
2016 while (*e) {
2017 Chain *entry = *e;
2018 if (entry->value == value) {
2019 *e = entry->next;
2020 delete entry;
2021 ++n;
2022 } else {
2023 e = &entry->next;
2024 }
2025 }
2026 if (!it.node()->value)
2027 d->erase(it);
2028 m_size -= n;
2029 Q_ASSERT(m_size >= 0);
2030 return n;
2031 }
2032
2033 qsizetype count(const Key &key) const noexcept
2034 {
2035 if (!d)
2036 return 0;
2037 auto it = d->findBucket(key);
2038 if (it.isUnused())
2039 return 0;
2040 qsizetype n = 0;
2041 Chain *e = it.node()->value;
2042 while (e) {
2043 ++n;
2044 e = e->next;
2045 }
2046
2047 return n;
2048 }
2049
2050 qsizetype count(const Key &key, const T &value) const noexcept
2051 {
2052 if (!d)
2053 return 0;
2054 auto it = d->findBucket(key);
2055 if (it.isUnused())
2056 return 0;
2057 qsizetype n = 0;
2058 Chain *e = it.node()->value;
2059 while (e) {
2060 if (e->value == value)
2061 ++n;
2062 e = e->next;
2063 }
2064
2065 return n;
2066 }
2067
2068 iterator find(const Key &key, const T &value)
2069 {
2070 if (isEmpty())
2071 return end();
2072 const auto copy = isDetached() ? QMultiHash() : *this;
2073 detach();
2074 auto it = constFind(key, value);
2075 return iterator(it.i, it.e);
2076 }
2077 const_iterator find(const Key &key, const T &value) const noexcept
2078 {
2079 return constFind(key, value);
2080 }
2081 const_iterator constFind(const Key &key, const T &value) const noexcept
2082 {
2083 const_iterator i(constFind(key));
2084 const_iterator end(constEnd());
2085 while (i != end && i.key() == key) {
2086 if (i.value() == value)
2087 return i;
2088 ++i;
2089 }
2090 return end;
2091 }
2092
2093 QMultiHash &unite(const QMultiHash &other)
2094 {
2095 if (isEmpty()) {
2096 *this = other;
2097 } else if (other.isEmpty()) {
2098 ;
2099 } else {
2100 QMultiHash copy(other);
2101 detach();
2102 for (auto cit = copy.cbegin(); cit != copy.cend(); ++cit)
2103 insert(cit.key(), *cit);
2104 }
2105 return *this;
2106 }
2107
2108 QMultiHash &unite(const QHash<Key, T> &other)
2109 {
2110 for (auto cit = other.cbegin(); cit != other.cend(); ++cit)
2111 insert(cit.key(), *cit);
2112 return *this;
2113 }
2114
2115 QMultiHash &unite(QHash<Key, T> &&other)
2116 {
2117 if (!other.isDetached()) {
2118 unite(other);
2119 return *this;
2120 }
2121 auto it = other.d->begin();
2122 for (const auto end = other.d->end(); it != end; ++it)
2123 emplace(std::move(it.node()->key), std::move(it.node()->takeValue()));
2124 other.clear();
2125 return *this;
2126 }
2127
2128 std::pair<iterator, iterator> equal_range(const Key &key)
2129 {
2130 const auto copy = isDetached() ? QMultiHash() : *this;
2131 detach();
2132 auto pair = std::as_const(*this).equal_range(key);
2133 return {iterator(pair.first.i), iterator(pair.second.i)};
2134 }
2135
2136 std::pair<const_iterator, const_iterator> equal_range(const Key &key) const noexcept
2137 {
2138 if (!d)
2139 return {end(), end()};
2140
2141 auto bucket = d->findBucket(key);
2142 if (bucket.isUnused())
2143 return {end(), end()};
2144 auto it = bucket.toIterator(d);
2145 auto end = it;
2146 ++end;
2147 return {const_iterator(it), const_iterator(end)};
2148 }
2149
2150 private:
2151 void detach_helper()
2152 {
2153 if (!d) {
2154 d = new Data;
2155 return;
2156 }
2157 Data *dd = new Data(*d);
2158 if (!d->ref.deref())
2159 delete d;
2160 d = dd;
2161 }
2162
2163 template<typename... Args>
2164 iterator emplace_helper(Key &&key, Args &&...args)
2165 {
2166 auto result = d->findOrInsert(key);
2167 if (!result.initialized)
2168 Node::createInPlace(result.it.node(), std::move(key), std::forward<Args>(args)...);
2169 else
2170 result.it.node()->insertMulti(std::forward<Args>(args)...);
2171 ++m_size;
2172 return iterator(result.it);
2173 }
2174
2175 template<typename... Args>
2176 iterator emplaceReplace_helper(Key &&key, Args &&...args)
2177 {
2178 auto result = d->findOrInsert(key);
2179 if (!result.initialized) {
2180 Node::createInPlace(result.it.node(), std::move(key), std::forward<Args>(args)...);
2181 ++m_size;
2182 } else {
2183 result.it.node()->emplaceValue(std::forward<Args>(args)...);
2184 }
2185 return iterator(result.it);
2186 }
2187 };
2188
2189 Q_DECLARE_ASSOCIATIVE_FORWARD_ITERATOR(Hash)
2190 Q_DECLARE_MUTABLE_ASSOCIATIVE_FORWARD_ITERATOR(Hash)
2191 Q_DECLARE_ASSOCIATIVE_FORWARD_ITERATOR(MultiHash)
2192 Q_DECLARE_MUTABLE_ASSOCIATIVE_FORWARD_ITERATOR(MultiHash)
2193
2194 template <class Key, class T>
2195 size_t qHash(const QHash<Key, T> &key, size_t seed = 0)
2196 noexcept(noexcept(qHash(std::declval<Key&>())) && noexcept(qHash(std::declval<T&>())))
2197 {
2198 size_t hash = 0;
2199 for (auto it = key.begin(), end = key.end(); it != end; ++it) {
2200 QtPrivate::QHashCombine combine;
2201 size_t h = combine(seed, it.key());
2202
2203 hash += combine(h, it.value());
2204 }
2205 return hash;
2206 }
2207
2208 template <class Key, class T>
2209 inline size_t qHash(const QMultiHash<Key, T> &key, size_t seed = 0)
2210 noexcept(noexcept(qHash(std::declval<Key&>())) && noexcept(qHash(std::declval<T&>())))
2211 {
2212 size_t hash = 0;
2213 for (auto it = key.begin(), end = key.end(); it != end; ++it) {
2214 QtPrivate::QHashCombine combine;
2215 size_t h = combine(seed, it.key());
2216
2217 hash += combine(h, it.value());
2218 }
2219 return hash;
2220 }
2221
2222 template <typename Key, typename T, typename Predicate>
2223 qsizetype erase_if(QHash<Key, T> &hash, Predicate pred)
2224 {
2225 return QtPrivate::associative_erase_if(hash, pred);
2226 }
2227
2228 template <typename Key, typename T, typename Predicate>
2229 qsizetype erase_if(QMultiHash<Key, T> &hash, Predicate pred)
2230 {
2231 return QtPrivate::associative_erase_if(hash, pred);
2232 }
2233
2234 QT_END_NAMESPACE
2235
2236 #endif