Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qhash.h
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#ifndef QHASH_H
6#define QHASH_H
7
8#include <QtCore/qalgorithms.h>
9#include <QtCore/qcontainertools_impl.h>
10#include <QtCore/qhashfunctions.h>
11#include <QtCore/qiterator.h>
12#include <QtCore/qlist.h>
13#include <QtCore/qrefcount.h>
14#include <QtCore/qttypetraits.h>
15
16#include <initializer_list>
17#include <functional> // for std::hash
18
19class tst_QHash; // for befriending
20
22
24{
25 bool operator==(const QHashDummyValue &) const noexcept { return true; }
26};
27
28namespace QHashPrivate {
29
30template <typename T, typename = void>
31constexpr inline bool HasQHashOverload = false;
32
33template <typename T>
34constexpr inline bool HasQHashOverload<T, std::enable_if_t<
35 std::is_convertible_v<decltype(qHash(std::declval<const T &>(), std::declval<size_t>())), size_t>
36>> = true;
37
38template <typename T, typename = void>
39constexpr inline bool HasStdHashSpecializationWithSeed = false;
40
41template <typename T>
42constexpr inline bool HasStdHashSpecializationWithSeed<T, std::enable_if_t<
43 std::is_convertible_v<decltype(std::hash<T>()(std::declval<const T &>(), std::declval<size_t>())), size_t>
44>> = true;
45
46template <typename T, typename = void>
47constexpr inline bool HasStdHashSpecializationWithoutSeed = false;
48
49template <typename T>
50constexpr inline bool HasStdHashSpecializationWithoutSeed<T, std::enable_if_t<
51 std::is_convertible_v<decltype(std::hash<T>()(std::declval<const T &>())), size_t>
52>> = true;
53
54template <typename T>
55size_t calculateHash(const T &t, size_t seed = 0)
56{
57 if constexpr (HasQHashOverload<T>) {
58 return qHash(t, seed);
59 } else if constexpr (HasStdHashSpecializationWithSeed<T>) {
60 return std::hash<T>()(t, seed);
61 } else if constexpr (HasStdHashSpecializationWithoutSeed<T>) {
63 return std::hash<T>()(t);
64 } else {
65 static_assert(sizeof(T) == 0, "The key type must have a qHash overload or a std::hash specialization");
66 return 0;
67 }
68}
69
70template <typename Key, typename T>
71struct Node
72{
73 using KeyType = Key;
74 using ValueType = T;
75
78 template<typename ...Args>
79 static void createInPlace(Node *n, Key &&k, Args &&... args)
80 { new (n) Node{ std::move(k), T(std::forward<Args>(args)...) }; }
81 template<typename ...Args>
82 static void createInPlace(Node *n, const Key &k, Args &&... args)
83 { new (n) Node{ Key(k), T(std::forward<Args>(args)...) }; }
84 template<typename ...Args>
85 void emplaceValue(Args &&... args)
86 {
87 value = T(std::forward<Args>(args)...);
88 }
89 T &&takeValue() noexcept(std::is_nothrow_move_assignable_v<T>)
90 {
91 return std::move(value);
92 }
93 bool valuesEqual(const Node *other) const { return value == other->value; }
94};
95
96template <typename Key>
98 using KeyType = Key;
100
102 template<typename ...Args>
103 static void createInPlace(Node *n, Key &&k, Args &&...)
104 { new (n) Node{ std::move(k) }; }
105 template<typename ...Args>
106 static void createInPlace(Node *n, const Key &k, Args &&...)
107 { new (n) Node{ k }; }
108 template<typename ...Args>
109 void emplaceValue(Args &&...)
110 {
111 }
113 bool valuesEqual(const Node *) const { return true; }
114};
115
116template <typename T>
118{
122 {
123 }
124 qsizetype free() noexcept(std::is_nothrow_destructible_v<T>)
125 {
126 qsizetype nEntries = 0;
127 MultiNodeChain *e = this;
128 while (e) {
129 MultiNodeChain *n = e->next;
130 ++nEntries;
131 delete e;
132 e = n;
133 }
134 return nEntries;
135 }
136 bool contains(const T &val) const noexcept
137 {
138 const MultiNodeChain *e = this;
139 while (e) {
140 if (e->value == val)
141 return true;
142 e = e->next;
143 }
144 return false;
145 }
146};
147
148template <typename Key, typename T>
150{
151 using KeyType = Key;
152 using ValueType = T;
153 using Chain = MultiNodeChain<T>;
154
157
158 template<typename ...Args>
159 static void createInPlace(MultiNode *n, Key &&k, Args &&... args)
160 { new (n) MultiNode(std::move(k), new Chain{ T(std::forward<Args>(args)...), nullptr }); }
161 template<typename ...Args>
162 static void createInPlace(MultiNode *n, const Key &k, Args &&... args)
163 { new (n) MultiNode(k, new Chain{ T(std::forward<Args>(args)...), nullptr }); }
164
165 MultiNode(const Key &k, Chain *c)
166 : key(k),
167 value(c)
168 {}
169 MultiNode(Key &&k, Chain *c) noexcept(std::is_nothrow_move_assignable_v<Key>)
170 : key(std::move(k)),
171 value(c)
172 {}
173
175 : key(other.key),
176 value(std::exchange(other.value, nullptr))
177 {
178 }
179
181 : key(other.key)
182 {
183 Chain *c = other.value;
184 Chain **e = &value;
185 while (c) {
186 Chain *chain = new Chain{ c->value, nullptr };
187 *e = chain;
188 e = &chain->next;
189 c = c->next;
190 }
191 }
193 {
194 if (value)
195 value->free();
196 }
197 static qsizetype freeChain(MultiNode *n) noexcept(std::is_nothrow_destructible_v<T>)
198 {
199 qsizetype size = n->value->free();
200 n->value = nullptr;
201 return size;
202 }
203 template<typename ...Args>
204 void insertMulti(Args &&... args)
205 {
206 Chain *e = new Chain{ T(std::forward<Args>(args)...), nullptr };
207 e->next = std::exchange(value, e);
208 }
209 template<typename ...Args>
210 void emplaceValue(Args &&... args)
211 {
212 value->value = T(std::forward<Args>(args)...);
213 }
214};
215
216template<typename Node>
221
223 static constexpr size_t SpanShift = 7;
224 static constexpr size_t NEntries = (1 << SpanShift);
225 static constexpr size_t LocalBucketMask = (NEntries - 1);
226 static constexpr size_t UnusedEntry = 0xff;
227
228 static_assert ((NEntries & LocalBucketMask) == 0, "NEntries must be a power of two.");
229};
230
231// Regular hash tables consist of a list of buckets that can store Nodes. But simply allocating one large array of buckets
232// would waste a lot of memory. To avoid this, we split the vector of buckets up into a vector of Spans. Each Span represents
233// NEntries buckets. To quickly find the correct Span that holds a bucket, NEntries must be a power of two.
234//
235// Inside each Span, there is an offset array that represents the actual buckets. offsets contains either an index into the
236// actual storage space for the Nodes (the 'entries' member) or 0xff (UnusedEntry) to flag that the bucket is empty.
237// As we have only 128 entries per Span, the offset array can be represented using an unsigned char. This trick makes the hash
238// table have a very small memory overhead compared to many other implementations.
239template<typename Node>
240struct Span {
241 // Entry is a slot available for storing a Node. The Span holds a pointer to
242 // an array of Entries. Upon construction of the array, those entries are
243 // unused, and nextFree() is being used to set up a singly linked list
244 // of free entries.
245 // When a node gets inserted, the first free entry is being picked, removed
246 // from the singly linked list and the Node gets constructed in place.
247 struct Entry {
248 struct { alignas(Node) unsigned char data[sizeof(Node)]; } storage;
249
250 unsigned char &nextFree() { return *reinterpret_cast<unsigned char *>(&storage); }
251 Node &node() { return *reinterpret_cast<Node *>(&storage); }
252 };
253
255 Entry *entries = nullptr;
256 unsigned char allocated = 0;
257 unsigned char nextFree = 0;
258 Span() noexcept
259 {
261 }
263 {
264 freeData();
265 }
266 void freeData() noexcept(std::is_nothrow_destructible<Node>::value)
267 {
268 if (entries) {
269 if constexpr (!std::is_trivially_destructible<Node>::value) {
270 for (auto o : offsets) {
272 entries[o].node().~Node();
273 }
274 }
275 delete[] entries;
276 entries = nullptr;
277 }
278 }
279 Node *insert(size_t i)
280 {
283 if (nextFree == allocated)
284 addStorage();
285 unsigned char entry = nextFree;
288 offsets[i] = entry;
289 return &entries[entry].node();
290 }
291 void erase(size_t bucket) noexcept(std::is_nothrow_destructible<Node>::value)
292 {
295
296 unsigned char entry = offsets[bucket];
298
299 entries[entry].node().~Node();
301 nextFree = entry;
302 }
303 size_t offset(size_t i) const noexcept
304 {
305 return offsets[i];
306 }
307 bool hasNode(size_t i) const noexcept
308 {
310 }
311 Node &at(size_t i) noexcept
312 {
315
316 return entries[offsets[i]].node();
317 }
318 const Node &at(size_t i) const noexcept
319 {
322
323 return entries[offsets[i]].node();
324 }
325 Node &atOffset(size_t o) noexcept
326 {
328
329 return entries[o].node();
330 }
331 const Node &atOffset(size_t o) const noexcept
332 {
334
335 return entries[o].node();
336 }
337 void moveLocal(size_t from, size_t to) noexcept
338 {
341 offsets[to] = offsets[from];
343 }
344 void moveFromSpan(Span &fromSpan, size_t fromIndex, size_t to) noexcept(std::is_nothrow_move_constructible_v<Node>)
345 {
349 Q_ASSERT(fromSpan.offsets[fromIndex] != SpanConstants::UnusedEntry);
350 if (nextFree == allocated)
351 addStorage();
353 offsets[to] = nextFree;
354 Entry &toEntry = entries[nextFree];
355 nextFree = toEntry.nextFree();
356
357 size_t fromOffset = fromSpan.offsets[fromIndex];
358 fromSpan.offsets[fromIndex] = SpanConstants::UnusedEntry;
359 Entry &fromEntry = fromSpan.entries[fromOffset];
360
361 if constexpr (isRelocatable<Node>()) {
362 memcpy(&toEntry, &fromEntry, sizeof(Entry));
363 } else {
364 new (&toEntry.node()) Node(std::move(fromEntry.node()));
365 fromEntry.node().~Node();
366 }
367 fromEntry.nextFree() = fromSpan.nextFree;
368 fromSpan.nextFree = static_cast<unsigned char>(fromOffset);
369 }
370
372 {
375 // the hash table should always be between 25 and 50% full
376 // this implies that we on average have between 32 and 64 entries
377 // in here. More exactly, we have a binominal distribution of the amount of
378 // occupied entries.
379 // For a 25% filled table, the average is 32 entries, with a 95% chance that we have between
380 // 23 and 41 entries.
381 // For a 50% filled table, the average is 64 entries, with a 95% chance that we have between
382 // 53 and 75 entries.
383 // Since we only resize the table once it's 50% filled and we want to avoid copies of
384 // data where possible, we initially allocate 48 entries, then resize to 80 entries, after that
385 // resize by increments of 16. That way, we usually only get one resize of the table
386 // while filling it.
387 size_t alloc;
388 static_assert(SpanConstants::NEntries % 8 == 0);
389 if (!allocated)
390 alloc = SpanConstants::NEntries / 8 * 3;
391 else if (allocated == SpanConstants::NEntries / 8 * 3)
392 alloc = SpanConstants::NEntries / 8 * 5;
393 else
395 Entry *newEntries = new Entry[alloc];
396 // we only add storage if the previous storage was fully filled, so
397 // simply copy the old data over
398 if constexpr (isRelocatable<Node>()) {
399 if (allocated)
400 memcpy(newEntries, entries, allocated * sizeof(Entry));
401 } else {
402 for (size_t i = 0; i < allocated; ++i) {
403 new (&newEntries[i].node()) Node(std::move(entries[i].node()));
404 entries[i].node().~Node();
405 }
406 }
407 for (size_t i = allocated; i < alloc; ++i) {
408 newEntries[i].nextFree() = uchar(i + 1);
409 }
410 delete[] entries;
411 entries = newEntries;
412 allocated = uchar(alloc);
413 }
414};
415
416// QHash uses a power of two growth policy.
417namespace GrowthPolicy {
418inline constexpr size_t bucketsForCapacity(size_t requestedCapacity) noexcept
419{
420 constexpr int SizeDigits = std::numeric_limits<size_t>::digits;
421
422 // We want to use at minimum a full span (128 entries), so we hardcode it for any requested
423 // capacity <= 64. Any capacity above that gets rounded to a later power of two.
424 if (requestedCapacity <= 64)
426
427 // Same as
428 // qNextPowerOfTwo(2 * requestedCapacity);
429 //
430 // but ensuring neither our multiplication nor the function overflow.
431 // Additionally, the maximum memory allocation is 2^31-1 or 2^63-1 bytes
432 // (limited by qsizetype and ptrdiff_t).
433 int count = qCountLeadingZeroBits(requestedCapacity);
434 if (count < 2)
435 return (std::numeric_limits<size_t>::max)(); // will cause std::bad_alloc
436 return size_t(1) << (SizeDigits - count + 1);
437}
438inline constexpr size_t bucketForHash(size_t nBuckets, size_t hash) noexcept
439{
440 return hash & (nBuckets - 1);
441}
442} // namespace GrowthPolicy
443
444template <typename Node>
445struct iterator;
446
447template <typename Node>
448struct Data
449{
450 using Key = typename Node::KeyType;
451 using T = typename Node::ValueType;
454
456 size_t size = 0;
457 size_t numBuckets = 0;
458 size_t seed = 0;
459 Span *spans = nullptr;
460
461 static constexpr size_t maxNumBuckets() noexcept
462 {
463 return (std::numeric_limits<ptrdiff_t>::max)() / sizeof(Span);
464 }
465
466 struct Bucket {
468 size_t index;
469
470 Bucket(Span *s, size_t i) noexcept
471 : span(s), index(i)
472 {}
473 Bucket(const Data *d, size_t bucket) noexcept
474 : span(d->spans + (bucket >> SpanConstants::SpanShift)),
476 {}
478 : Bucket(it.d, it.bucket)
479 {}
480
481 size_t toBucketIndex(const Data *d) const noexcept
482 {
483 return ((span - d->spans) << SpanConstants::SpanShift) | index;
484 }
485 iterator toIterator(const Data *d) const noexcept { return iterator{d, toBucketIndex(d)}; }
486 void advanceWrapped(const Data *d) noexcept
487 {
488 advance_impl(d, d->spans);
489 }
490 void advance(const Data *d) noexcept
491 {
492 advance_impl(d, nullptr);
493 }
494 bool isUnused() const noexcept
495 {
496 return !span->hasNode(index);
497 }
498 size_t offset() const noexcept
499 {
500 return span->offset(index);
501 }
503 {
504 return span->atOffset(offset);
505 }
507 {
508 return &span->at(index);
509 }
510 Node *insert() const
511 {
512 return span->insert(index);
513 }
514
515 private:
516 friend bool operator==(Bucket lhs, Bucket rhs) noexcept
517 {
518 return lhs.span == rhs.span && lhs.index == rhs.index;
519 }
520 friend bool operator!=(Bucket lhs, Bucket rhs) noexcept { return !(lhs == rhs); }
521
522 void advance_impl(const Data *d, Span *whenAtEnd) noexcept
523 {
524 Q_ASSERT(span);
525 ++index;
527 index = 0;
528 ++span;
529 if (span - d->spans == ptrdiff_t(d->numBuckets >> SpanConstants::SpanShift))
530 span = whenAtEnd;
531 }
532 }
533 };
534
535 static auto allocateSpans(size_t numBuckets)
536 {
537 struct R {
538 Span *spans;
539 size_t nSpans;
540 };
541
542 constexpr qptrdiff MaxSpanCount = (std::numeric_limits<qptrdiff>::max)() / sizeof(Span);
543 constexpr size_t MaxBucketCount = MaxSpanCount << SpanConstants::SpanShift;
544
545 if (numBuckets > MaxBucketCount) {
546 Q_CHECK_PTR(false);
547 Q_UNREACHABLE(); // no exceptions and no assertions -> no error reporting
548 }
549
550 size_t nSpans = numBuckets >> SpanConstants::SpanShift;
551 return R{ new Span[nSpans], nSpans };
552 }
553
560
561 void reallocationHelper(const Data &other, size_t nSpans, bool resized)
562 {
563 for (size_t s = 0; s < nSpans; ++s) {
564 const Span &span = other.spans[s];
565 for (size_t index = 0; index < SpanConstants::NEntries; ++index) {
566 if (!span.hasNode(index))
567 continue;
568 const Node &n = span.at(index);
569 auto it = resized ? findBucket(n.key) : Bucket { spans + s, index };
570 Q_ASSERT(it.isUnused());
571 Node *newNode = it.insert();
572 new (newNode) Node(n);
573 }
574 }
575 }
576
578 {
579 auto r = allocateSpans(numBuckets);
580 spans = r.spans;
581 reallocationHelper(other, r.nSpans, false);
582 }
583 Data(const Data &other, size_t reserved) : size(other.size), seed(other.seed)
584 {
587 size_t otherNSpans = other.numBuckets >> SpanConstants::SpanShift;
588 reallocationHelper(other, otherNSpans, numBuckets != other.numBuckets);
589 }
590
591 static Data *detached(Data *d)
592 {
593 if (!d)
594 return new Data;
595 Data *dd = new Data(*d);
596 if (!d->ref.deref())
597 delete d;
598 return dd;
599 }
600 static Data *detached(Data *d, size_t size)
601 {
602 if (!d)
603 return new Data(size);
604 Data *dd = new Data(*d, size);
605 if (!d->ref.deref())
606 delete d;
607 return dd;
608 }
609
610 void clear()
611 {
612 delete[] spans;
613 spans = nullptr;
614 size = 0;
615 numBuckets = 0;
616 }
617
619 {
620 return iterator{this, other.bucket};
621 }
622
623 iterator begin() const noexcept
624 {
625 iterator it{ this, 0 };
626 if (it.isUnused())
627 ++it;
628 return it;
629 }
630
631 constexpr iterator end() const noexcept
632 {
633 return iterator();
634 }
635
636 void rehash(size_t sizeHint = 0)
637 {
638 if (sizeHint == 0)
639 sizeHint = size;
640 size_t newBucketCount = GrowthPolicy::bucketsForCapacity(sizeHint);
641
642 Span *oldSpans = spans;
643 size_t oldBucketCount = numBuckets;
644 spans = allocateSpans(newBucketCount).spans;
645 numBuckets = newBucketCount;
646 size_t oldNSpans = oldBucketCount >> SpanConstants::SpanShift;
647
648 for (size_t s = 0; s < oldNSpans; ++s) {
649 Span &span = oldSpans[s];
650 for (size_t index = 0; index < SpanConstants::NEntries; ++index) {
651 if (!span.hasNode(index))
652 continue;
653 Node &n = span.at(index);
654 auto it = findBucket(n.key);
655 Q_ASSERT(it.isUnused());
656 Node *newNode = it.insert();
657 new (newNode) Node(std::move(n));
658 }
659 span.freeData();
660 }
661 delete[] oldSpans;
662 }
663
664 size_t nextBucket(size_t bucket) const noexcept
665 {
666 ++bucket;
667 if (bucket == numBuckets)
668 bucket = 0;
669 return bucket;
670 }
671
672 float loadFactor() const noexcept
673 {
674 return float(size)/numBuckets;
675 }
676 bool shouldGrow() const noexcept
677 {
678 return size >= (numBuckets >> 1);
679 }
680
681 template <typename K> Bucket findBucket(const K &key) const noexcept
682 {
683 static_assert(std::is_same_v<std::remove_cv_t<Key>, K> ||
684 QHashHeterogeneousSearch<std::remove_cv_t<Key>, K>::value);
685 Q_ASSERT(numBuckets > 0);
688 // loop over the buckets until we find the entry we search for
689 // or an empty slot, in which case we know the entry doesn't exist
690 while (true) {
691 size_t offset = bucket.offset();
693 return bucket;
694 } else {
695 Node &n = bucket.nodeAtOffset(offset);
696 if (qHashEquals(n.key, key))
697 return bucket;
698 }
699 bucket.advanceWrapped(this);
700 }
701 }
702
703 template <typename K> Node *findNode(const K &key) const noexcept
704 {
705 auto bucket = findBucket(key);
706 if (bucket.isUnused())
707 return nullptr;
708 return bucket.node();
709 }
710
712 {
715 };
716
717 template <typename K> InsertionResult findOrInsert(const K &key) noexcept
718 {
719 Bucket it(static_cast<Span *>(nullptr), 0);
720 if (numBuckets > 0) {
721 it = findBucket(key);
722 if (!it.isUnused())
723 return { it.toIterator(this), true };
724 }
725 if (shouldGrow()) {
726 rehash(size + 1);
727 it = findBucket(key); // need to get a new iterator after rehashing
728 }
729 Q_ASSERT(it.span != nullptr);
730 Q_ASSERT(it.isUnused());
731 it.insert();
732 ++size;
733 return { it.toIterator(this), false };
734 }
735
736 void erase(Bucket bucket) noexcept(std::is_nothrow_destructible<Node>::value)
737 {
738 Q_ASSERT(bucket.span->hasNode(bucket.index));
739 bucket.span->erase(bucket.index);
740 --size;
741
742 // re-insert the following entries to avoid holes
743 Bucket next = bucket;
744 while (true) {
745 next.advanceWrapped(this);
746 size_t offset = next.offset();
748 return;
749 size_t hash = QHashPrivate::calculateHash(next.nodeAtOffset(offset).key, seed);
751 while (true) {
752 if (newBucket == next) {
753 // nothing to do, item is at the right plae
754 break;
755 } else if (newBucket == bucket) {
756 // move into the hole we created earlier
757 if (next.span == bucket.span) {
758 bucket.span->moveLocal(next.index, bucket.index);
759 } else {
760 // move between spans, more expensive
761 bucket.span->moveFromSpan(*next.span, next.index, bucket.index);
762 }
763 bucket = next;
764 break;
765 }
766 newBucket.advanceWrapped(this);
767 }
768 }
769 }
770
772 {
773 delete [] spans;
774 }
775};
776
777template <typename Node>
778struct iterator {
780
781 const Data<Node> *d = nullptr;
782 size_t bucket = 0;
783
784 size_t span() const noexcept { return bucket >> SpanConstants::SpanShift; }
785 size_t index() const noexcept { return bucket & SpanConstants::LocalBucketMask; }
786 inline bool isUnused() const noexcept { return !d->spans[span()].hasNode(index()); }
787
788 inline Node *node() const noexcept
789 {
790 Q_ASSERT(!isUnused());
791 return &d->spans[span()].at(index());
792 }
793 bool atEnd() const noexcept { return !d; }
794
796 {
797 while (true) {
798 ++bucket;
799 if (bucket == d->numBuckets) {
800 d = nullptr;
801 bucket = 0;
802 break;
803 }
804 if (!isUnused())
805 break;
806 }
807 return *this;
808 }
809 bool operator==(iterator other) const noexcept
810 { return d == other.d && bucket == other.bucket; }
811 bool operator!=(iterator other) const noexcept
812 { return !(*this == other); }
813};
814
815
816
817} // namespace QHashPrivate
818
819template <typename Key, typename T>
820class QHash
821{
824 friend class QSet<Key>;
825 friend class QMultiHash<Key, T>;
826 friend tst_QHash;
827
828 Data *d = nullptr;
829
830public:
831 using key_type = Key;
832 using mapped_type = T;
833 using value_type = T;
836 using reference = T &;
837 using const_reference = const T &;
838
839 inline QHash() noexcept = default;
840 inline QHash(std::initializer_list<std::pair<Key,T> > list)
841 : d(new Data(list.size()))
842 {
843 for (typename std::initializer_list<std::pair<Key,T> >::const_iterator it = list.begin(); it != list.end(); ++it)
844 insert(it->first, it->second);
845 }
846 QHash(const QHash &other) noexcept
847 : d(other.d)
848 {
849 if (d)
850 d->ref.ref();
851 }
853 {
854 static_assert(std::is_nothrow_destructible_v<Key>, "Types with throwing destructors are not supported in Qt containers.");
855 static_assert(std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers.");
856
857 if (d && !d->ref.deref())
858 delete d;
859 }
860
861 QHash &operator=(const QHash &other) noexcept(std::is_nothrow_destructible<Node>::value)
862 {
863 if (d != other.d) {
864 Data *o = other.d;
865 if (o)
866 o->ref.ref();
867 if (d && !d->ref.deref())
868 delete d;
869 d = o;
870 }
871 return *this;
872 }
873
874 QHash(QHash &&other) noexcept
875 : d(std::exchange(other.d, nullptr))
876 {
877 }
878 QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QHash)
879#ifdef Q_QDOC
880 template <typename InputIterator>
881 QHash(InputIterator f, InputIterator l);
882#else
883 template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasKeyAndValue<InputIterator> = true>
884 QHash(InputIterator f, InputIterator l)
885 : QHash()
886 {
888 for (; f != l; ++f)
889 insert(f.key(), f.value());
890 }
891
892 template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasFirstAndSecond<InputIterator> = true>
893 QHash(InputIterator f, InputIterator l)
894 : QHash()
895 {
897 for (; f != l; ++f)
898 insert(f->first, f->second);
899 }
900#endif
901 void swap(QHash &other) noexcept { qt_ptr_swap(d, other.d); }
902
903#ifndef Q_QDOC
904 template <typename AKey = Key, typename AT = T>
906 {
907 if (d == other.d)
908 return true;
909 if (size() != other.size())
910 return false;
911
912 for (const_iterator it = other.begin(); it != other.end(); ++it) {
913 const_iterator i = find(it.key());
914 if (i == end() || !i.i.node()->valuesEqual(it.i.node()))
915 return false;
916 }
917 // all values must be the same as size is the same
918 return true;
919 }
920 template <typename AKey = Key, typename AT = T>
923#else
924 bool operator==(const QHash &other) const;
925 bool operator!=(const QHash &other) const;
926#endif // Q_QDOC
927
928 inline qsizetype size() const noexcept { return d ? qsizetype(d->size) : 0; }
929 inline bool isEmpty() const noexcept { return !d || d->size == 0; }
930
931 inline qsizetype capacity() const noexcept { return d ? qsizetype(d->numBuckets >> 1) : 0; }
933 {
934 // reserve(0) is used in squeeze()
935 if (size && (this->capacity() >= size))
936 return;
937 if (isDetached())
938 d->rehash(size);
939 else
940 d = Data::detached(d, size_t(size));
941 }
942 inline void squeeze()
943 {
944 if (capacity())
945 reserve(0);
946 }
947
948 inline void detach() { if (!d || d->ref.isShared()) d = Data::detached(d); }
949 inline bool isDetached() const noexcept { return d && !d->ref.isShared(); }
950 bool isSharedWith(const QHash &other) const noexcept { return d == other.d; }
951
952 void clear() noexcept(std::is_nothrow_destructible<Node>::value)
953 {
954 if (d && !d->ref.deref())
955 delete d;
956 d = nullptr;
957 }
958
959 bool remove(const Key &key)
960 {
961 return removeImpl(key);
962 }
963private:
964 template <typename K> bool removeImpl(const K &key)
965 {
966 if (isEmpty()) // prevents detaching shared null
967 return false;
968 auto it = d->findBucket(key);
969 size_t bucket = it.toBucketIndex(d);
970 detach();
971 it = typename Data::Bucket(d, bucket); // reattach in case of detach
972
973 if (it.isUnused())
974 return false;
975 d->erase(it);
976 return true;
977 }
978
979public:
980 template <typename Predicate>
981 qsizetype removeIf(Predicate pred)
982 {
983 return QtPrivate::associative_erase_if(*this, pred);
984 }
985
986 T take(const Key &key)
987 {
988 return takeImpl(key);
989 }
990private:
991 template <typename K> T takeImpl(const K &key)
992 {
993 if (isEmpty()) // prevents detaching shared null
994 return T();
995 auto it = d->findBucket(key);
996 size_t bucket = it.toBucketIndex(d);
997 detach();
998 it = typename Data::Bucket(d, bucket); // reattach in case of detach
999
1000 if (it.isUnused())
1001 return T();
1002 T value = it.node()->takeValue();
1003 d->erase(it);
1004 return value;
1005 }
1006
1007public:
1008 bool contains(const Key &key) const noexcept
1009 {
1010 if (!d)
1011 return false;
1012 return d->findNode(key) != nullptr;
1013 }
1014 qsizetype count(const Key &key) const noexcept
1015 {
1016 return contains(key) ? 1 : 0;
1017 }
1018
1019private:
1020 template <typename Fn> Key keyImpl(const T &value, Fn &&defaultFn) const noexcept
1021 {
1022 if (d) {
1024 while (i != end()) {
1025 if (i.value() == value)
1026 return i.key();
1027 ++i;
1028 }
1029 }
1030
1031 return defaultFn();
1032 }
1033
1034public:
1035 Key key(const T &value) const noexcept
1036 {
1037 return keyImpl(value, [] { return Key(); });
1038 }
1039 Key key(const T &value, const Key &defaultKey) const noexcept
1040 {
1041 return keyImpl(value, [&] { return defaultKey; });
1042 }
1043
1044private:
1045 template <typename K, typename Fn> T valueImpl(const K &key, Fn &&defaultValue) const noexcept
1046 {
1047 if (d) {
1048 Node *n = d->findNode(key);
1049 if (n)
1050 return n->value;
1051 }
1052 return defaultValue();
1053 }
1054public:
1055 T value(const Key &key) const noexcept
1056 {
1057 return valueImpl(key, [] { return T(); });
1058 }
1059
1060 T value(const Key &key, const T &defaultValue) const noexcept
1061 {
1062 return valueImpl(key, [&] { return defaultValue; });
1063 }
1064
1065 T &operator[](const Key &key)
1066 {
1067 return operatorIndexImpl(key);
1068 }
1069private:
1070 template <typename K> T &operatorIndexImpl(const K &key)
1071 {
1072 const auto copy = isDetached() ? QHash() : *this; // keep 'key' alive across the detach
1073 detach();
1074 auto result = d->findOrInsert(key);
1075 Q_ASSERT(!result.it.atEnd());
1076 if (!result.initialized)
1077 Node::createInPlace(result.it.node(), Key(key), T());
1078 return result.it.node()->value;
1079 }
1080
1081public:
1082 const T operator[](const Key &key) const noexcept
1083 {
1084 return value(key);
1085 }
1086
1087 QList<Key> keys() const { return QList<Key>(keyBegin(), keyEnd()); }
1088 QList<Key> keys(const T &value) const
1089 {
1090 QList<Key> res;
1092 while (i != end()) {
1093 if (i.value() == value)
1094 res.append(i.key());
1095 ++i;
1096 }
1097 return res;
1098 }
1099 QList<T> values() const { return QList<T>(begin(), end()); }
1100
1101 class const_iterator;
1102
1104 {
1105 using piter = typename QHashPrivate::iterator<Node>;
1106 friend class const_iterator;
1107 friend class QHash<Key, T>;
1108 friend class QSet<Key>;
1109 piter i;
1110 explicit inline iterator(piter it) noexcept : i(it) { }
1111
1112 public:
1113 typedef std::forward_iterator_tag iterator_category;
1115 typedef T value_type;
1116 typedef T *pointer;
1117 typedef T &reference;
1118
1119 constexpr iterator() noexcept = default;
1120
1121 inline const Key &key() const noexcept { return i.node()->key; }
1122 inline T &value() const noexcept { return i.node()->value; }
1123 inline T &operator*() const noexcept { return i.node()->value; }
1124 inline T *operator->() const noexcept { return &i.node()->value; }
1125 inline bool operator==(const iterator &o) const noexcept { return i == o.i; }
1126 inline bool operator!=(const iterator &o) const noexcept { return i != o.i; }
1127
1128 inline iterator &operator++() noexcept
1129 {
1130 ++i;
1131 return *this;
1132 }
1133 inline iterator operator++(int) noexcept
1134 {
1135 iterator r = *this;
1136 ++i;
1137 return r;
1138 }
1139
1140 inline bool operator==(const const_iterator &o) const noexcept { return i == o.i; }
1141 inline bool operator!=(const const_iterator &o) const noexcept { return i != o.i; }
1142 };
1143 friend class iterator;
1144
1146 {
1147 using piter = typename QHashPrivate::iterator<Node>;
1148 friend class iterator;
1149 friend class QHash<Key, T>;
1150 friend class QSet<Key>;
1151 piter i;
1152 explicit inline const_iterator(piter it) : i(it) { }
1153
1154 public:
1155 typedef std::forward_iterator_tag iterator_category;
1157 typedef T value_type;
1158 typedef const T *pointer;
1159 typedef const T &reference;
1160
1161 constexpr const_iterator() noexcept = default;
1162 inline const_iterator(const iterator &o) noexcept : i(o.i) { }
1163
1164 inline const Key &key() const noexcept { return i.node()->key; }
1165 inline const T &value() const noexcept { return i.node()->value; }
1166 inline const T &operator*() const noexcept { return i.node()->value; }
1167 inline const T *operator->() const noexcept { return &i.node()->value; }
1168 inline bool operator==(const const_iterator &o) const noexcept { return i == o.i; }
1169 inline bool operator!=(const const_iterator &o) const noexcept { return i != o.i; }
1170
1171 inline const_iterator &operator++() noexcept
1172 {
1173 ++i;
1174 return *this;
1175 }
1176 inline const_iterator operator++(int) noexcept
1177 {
1178 const_iterator r = *this;
1179 ++i;
1180 return r;
1181 }
1182 };
1183 friend class const_iterator;
1184
1186 {
1188
1189 public:
1193 typedef const Key *pointer;
1194 typedef const Key &reference;
1195
1196 key_iterator() noexcept = default;
1198
1199 const Key &operator*() const noexcept { return i.key(); }
1200 const Key *operator->() const noexcept { return &i.key(); }
1201 bool operator==(key_iterator o) const noexcept { return i == o.i; }
1202 bool operator!=(key_iterator o) const noexcept { return i != o.i; }
1203
1204 inline key_iterator &operator++() noexcept { ++i; return *this; }
1205 inline key_iterator operator++(int) noexcept { return key_iterator(i++);}
1206 const_iterator base() const noexcept { return i; }
1207 };
1208
1209 typedef QKeyValueIterator<const Key&, const T&, const_iterator> const_key_value_iterator;
1210 typedef QKeyValueIterator<const Key&, T&, iterator> key_value_iterator;
1211
1212 // STL style
1213 inline iterator begin() { detach(); return iterator(d->begin()); }
1214 inline const_iterator begin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
1215 inline const_iterator cbegin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
1216 inline const_iterator constBegin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
1217 inline iterator end() noexcept { return iterator(); }
1218 inline const_iterator end() const noexcept { return const_iterator(); }
1219 inline const_iterator cend() const noexcept { return const_iterator(); }
1220 inline const_iterator constEnd() const noexcept { return const_iterator(); }
1221 inline key_iterator keyBegin() const noexcept { return key_iterator(begin()); }
1222 inline key_iterator keyEnd() const noexcept { return key_iterator(end()); }
1229 auto asKeyValueRange() & { return QtPrivate::QKeyValueRange(*this); }
1230 auto asKeyValueRange() const & { return QtPrivate::QKeyValueRange(*this); }
1231 auto asKeyValueRange() && { return QtPrivate::QKeyValueRange(std::move(*this)); }
1232 auto asKeyValueRange() const && { return QtPrivate::QKeyValueRange(std::move(*this)); }
1233
1235 {
1236 Q_ASSERT(it != constEnd());
1237 detach();
1238 // ensure a valid iterator across the detach:
1239 iterator i = iterator{d->detachedIterator(it.i)};
1240 typename Data::Bucket bucket(i.i);
1241
1242 d->erase(bucket);
1243 if (bucket.toBucketIndex(d) == d->numBuckets - 1 || bucket.isUnused())
1244 ++i;
1245 return i;
1246 }
1247
1248 std::pair<iterator, iterator> equal_range(const Key &key)
1249 {
1250 return equal_range_impl(*this, key);
1251 }
1252 std::pair<const_iterator, const_iterator> equal_range(const Key &key) const noexcept
1253 {
1254 return equal_range_impl(*this, key);
1255 }
1256private:
1257 template <typename Hash, typename K> static auto equal_range_impl(Hash &self, const K &key)
1258 {
1259 auto first = self.find(key);
1260 auto second = first;
1261 if (second != decltype(first){})
1262 ++second;
1263 return std::make_pair(first, second);
1264 }
1265
1266 template <typename K> iterator findImpl(const K &key)
1267 {
1268 if (isEmpty()) // prevents detaching shared null
1269 return end();
1270 auto it = d->findBucket(key);
1271 size_t bucket = it.toBucketIndex(d);
1272 detach();
1273 it = typename Data::Bucket(d, bucket); // reattach in case of detach
1274 if (it.isUnused())
1275 return end();
1276 return iterator(it.toIterator(d));
1277 }
1278 template <typename K> const_iterator constFindImpl(const K &key) const noexcept
1279 {
1280 if (isEmpty())
1281 return end();
1282 auto it = d->findBucket(key);
1283 if (it.isUnused())
1284 return end();
1285 return const_iterator({d, it.toBucketIndex(d)});
1286 }
1287
1288public:
1291 inline qsizetype count() const noexcept { return d ? qsizetype(d->size) : 0; }
1293 {
1294 return findImpl(key);
1295 }
1296 const_iterator find(const Key &key) const noexcept
1297 {
1298 return constFindImpl(key);
1299 }
1300 const_iterator constFind(const Key &key) const noexcept
1301 {
1302 return find(key);
1303 }
1304 iterator insert(const Key &key, const T &value)
1305 {
1306 return emplace(key, value);
1307 }
1308
1309 void insert(const QHash &hash)
1310 {
1311 if (d == hash.d || !hash.d)
1312 return;
1313 if (!d) {
1314 *this = hash;
1315 return;
1316 }
1317
1318 detach();
1319
1320 for (auto it = hash.begin(); it != hash.end(); ++it)
1321 emplace(it.key(), it.value());
1322 }
1323
1324 template <typename ...Args>
1325 iterator emplace(const Key &key, Args &&... args)
1326 {
1327 Key copy = key; // Needs to be explicit for MSVC 2019
1328 return emplace(std::move(copy), std::forward<Args>(args)...);
1329 }
1330
1331 template <typename ...Args>
1332 iterator emplace(Key &&key, Args &&... args)
1333 {
1334 if (isDetached()) {
1335 if (d->shouldGrow()) // Construct the value now so that no dangling references are used
1336 return emplace_helper(std::move(key), T(std::forward<Args>(args)...));
1337 return emplace_helper(std::move(key), std::forward<Args>(args)...);
1338 }
1339 // else: we must detach
1340 const auto copy = *this; // keep 'args' alive across the detach/growth
1341 detach();
1342 return emplace_helper(std::move(key), std::forward<Args>(args)...);
1343 }
1344
1345 float load_factor() const noexcept { return d ? d->loadFactor() : 0; }
1346 static float max_load_factor() noexcept { return 0.5; }
1347 size_t bucket_count() const noexcept { return d ? d->numBuckets : 0; }
1348 static size_t max_bucket_count() noexcept { return Data::maxNumBuckets(); }
1349
1350 inline bool empty() const noexcept { return isEmpty(); }
1351
1352private:
1353 template <typename ...Args>
1354 iterator emplace_helper(Key &&key, Args &&... args)
1355 {
1356 auto result = d->findOrInsert(key);
1357 if (!result.initialized)
1358 Node::createInPlace(result.it.node(), std::move(key), std::forward<Args>(args)...);
1359 else
1360 result.it.node()->emplaceValue(std::forward<Args>(args)...);
1361 return iterator(result.it);
1362 }
1363
1364public:
1365#ifdef __cpp_concepts
1366 bool remove(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
1367 {
1368 return removeImpl(key);
1369 }
1370 T take(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
1371 {
1372 return takeImpl(key);
1373 }
1374 bool contains(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
1375 {
1376 return d ? d->findNode(key) != nullptr : false;
1377 }
1378 qsizetype count(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
1379 {
1380 return contains(key) ? 1 : 0;
1381 }
1382 T value(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
1383 {
1384 return valueImpl(key, [] { return T(); });
1385 }
1386 T value(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &defaultValue) const noexcept
1387 {
1388 return valueImpl(key, [&] { return defaultValue; });
1389 }
1390 T &operator[](const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
1391 {
1392 return operatorIndexImpl(key);
1393 }
1394 const T operator[](const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
1395 {
1396 return value(key);
1397 }
1398 std::pair<iterator, iterator>
1399 equal_range(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
1400 {
1401 return equal_range_impl(*this, key);
1402 }
1403 std::pair<const_iterator, const_iterator>
1404 equal_range(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
1405 {
1406 return equal_range_impl(*this, key);
1407 }
1408 iterator find(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
1409 {
1410 return findImpl(key);
1411 }
1412 const_iterator find(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
1413 {
1414 return constFindImpl(key);
1415 }
1416 const_iterator constFind(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
1417 {
1418 return find(key);
1419 }
1420#endif // __cpp_concepts
1421};
1422
1423
1424template <typename Key, typename T>
1426{
1430
1431 Data *d = nullptr;
1432 qsizetype m_size = 0;
1433
1434public:
1435 using key_type = Key;
1436 using mapped_type = T;
1437 using value_type = T;
1440 using reference = T &;
1441 using const_reference = const T &;
1442
1443 QMultiHash() noexcept = default;
1444 inline QMultiHash(std::initializer_list<std::pair<Key,T> > list)
1445 : d(new Data(list.size()))
1446 {
1447 for (typename std::initializer_list<std::pair<Key,T> >::const_iterator it = list.begin(); it != list.end(); ++it)
1448 insert(it->first, it->second);
1449 }
1450#ifdef Q_QDOC
1451 template <typename InputIterator>
1452 QMultiHash(InputIterator f, InputIterator l);
1453#else
1454 template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasKeyAndValue<InputIterator> = true>
1455 QMultiHash(InputIterator f, InputIterator l)
1456 {
1458 for (; f != l; ++f)
1459 insert(f.key(), f.value());
1460 }
1461
1462 template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasFirstAndSecond<InputIterator> = true>
1463 QMultiHash(InputIterator f, InputIterator l)
1464 {
1466 for (; f != l; ++f)
1467 insert(f->first, f->second);
1468 }
1469#endif
1470 QMultiHash(const QMultiHash &other) noexcept
1471 : d(other.d), m_size(other.m_size)
1472 {
1473 if (d)
1474 d->ref.ref();
1475 }
1477 {
1478 static_assert(std::is_nothrow_destructible_v<Key>, "Types with throwing destructors are not supported in Qt containers.");
1479 static_assert(std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers.");
1480
1481 if (d && !d->ref.deref())
1482 delete d;
1483 }
1484
1485 QMultiHash &operator=(const QMultiHash &other) noexcept(std::is_nothrow_destructible<Node>::value)
1486 {
1487 if (d != other.d) {
1488 Data *o = other.d;
1489 if (o)
1490 o->ref.ref();
1491 if (d && !d->ref.deref())
1492 delete d;
1493 d = o;
1494 m_size = other.m_size;
1495 }
1496 return *this;
1497 }
1499 : d(std::exchange(other.d, nullptr)),
1500 m_size(std::exchange(other.m_size, 0))
1501 {
1502 }
1503 QMultiHash &operator=(QMultiHash &&other) noexcept(std::is_nothrow_destructible<Node>::value)
1504 {
1505 QMultiHash moved(std::move(other));
1506 swap(moved);
1507 return *this;
1508 }
1509
1510 explicit QMultiHash(const QHash<Key, T> &other)
1512 {}
1513
1514 explicit QMultiHash(QHash<Key, T> &&other)
1515 {
1516 unite(std::move(other));
1517 }
1518
1519 void swap(QMultiHash &other) noexcept
1520 {
1521 qt_ptr_swap(d, other.d);
1522 std::swap(m_size, other.m_size);
1523 }
1524
1525#ifndef Q_QDOC
1526 template <typename AKey = Key, typename AT = T>
1528 {
1529 if (d == other.d)
1530 return true;
1531 if (m_size != other.m_size)
1532 return false;
1533 if (m_size == 0)
1534 return true;
1535 // equal size, and both non-zero size => d pointers allocated for both
1536 Q_ASSERT(d);
1537 Q_ASSERT(other.d);
1538 if (d->size != other.d->size)
1539 return false;
1540 for (auto it = other.d->begin(); it != other.d->end(); ++it) {
1541 auto *n = d->findNode(it.node()->key);
1542 if (!n)
1543 return false;
1544 Chain *e = it.node()->value;
1545 while (e) {
1546 Chain *oe = n->value;
1547 while (oe) {
1548 if (oe->value == e->value)
1549 break;
1550 oe = oe->next;
1551 }
1552 if (!oe)
1553 return false;
1554 e = e->next;
1555 }
1556 }
1557 // all values must be the same as size is the same
1558 return true;
1559 }
1560 template <typename AKey = Key, typename AT = T>
1563#else
1564 bool operator==(const QMultiHash &other) const;
1565 bool operator!=(const QMultiHash &other) const;
1566#endif // Q_QDOC
1567
1568 inline qsizetype size() const noexcept { return m_size; }
1569
1570 inline bool isEmpty() const noexcept { return !m_size; }
1571
1572 inline qsizetype capacity() const noexcept { return d ? qsizetype(d->numBuckets >> 1) : 0; }
1574 {
1575 // reserve(0) is used in squeeze()
1576 if (size && (this->capacity() >= size))
1577 return;
1578 if (isDetached())
1579 d->rehash(size);
1580 else
1581 d = Data::detached(d, size_t(size));
1582 }
1583 inline void squeeze() { reserve(0); }
1584
1585 inline void detach() { if (!d || d->ref.isShared()) d = Data::detached(d); }
1586 inline bool isDetached() const noexcept { return d && !d->ref.isShared(); }
1587 bool isSharedWith(const QMultiHash &other) const noexcept { return d == other.d; }
1588
1589 void clear() noexcept(std::is_nothrow_destructible<Node>::value)
1590 {
1591 if (d && !d->ref.deref())
1592 delete d;
1593 d = nullptr;
1594 m_size = 0;
1595 }
1596
1598 {
1599 return removeImpl(key);
1600 }
1601private:
1602 template <typename K> qsizetype removeImpl(const K &key)
1603 {
1604 if (isEmpty()) // prevents detaching shared null
1605 return 0;
1606 auto it = d->findBucket(key);
1607 size_t bucket = it.toBucketIndex(d);
1608 detach();
1609 it = typename Data::Bucket(d, bucket); // reattach in case of detach
1610
1611 if (it.isUnused())
1612 return 0;
1613 qsizetype n = Node::freeChain(it.node());
1614 m_size -= n;
1615 Q_ASSERT(m_size >= 0);
1616 d->erase(it);
1617 return n;
1618 }
1619
1620public:
1621 template <typename Predicate>
1622 qsizetype removeIf(Predicate pred)
1623 {
1624 return QtPrivate::associative_erase_if(*this, pred);
1625 }
1626
1627 T take(const Key &key)
1628 {
1629 return takeImpl(key);
1630 }
1631private:
1632 template <typename K> T takeImpl(const K &key)
1633 {
1634 if (isEmpty()) // prevents detaching shared null
1635 return T();
1636 auto it = d->findBucket(key);
1637 size_t bucket = it.toBucketIndex(d);
1638 detach();
1639 it = typename Data::Bucket(d, bucket); // reattach in case of detach
1640
1641 if (it.isUnused())
1642 return T();
1643 Chain *e = it.node()->value;
1644 Q_ASSERT(e);
1645 T t = std::move(e->value);
1646 if (e->next) {
1647 it.node()->value = e->next;
1648 delete e;
1649 } else {
1650 // erase() deletes the values.
1651 d->erase(it);
1652 }
1653 --m_size;
1654 Q_ASSERT(m_size >= 0);
1655 return t;
1656 }
1657
1658public:
1659 bool contains(const Key &key) const noexcept
1660 {
1661 if (!d)
1662 return false;
1663 return d->findNode(key) != nullptr;
1664 }
1665
1666private:
1667 template <typename Fn> Key keyImpl(const T &value, Fn &&defaultValue) const noexcept
1668 {
1669 if (d) {
1670 auto i = d->begin();
1671 while (i != d->end()) {
1672 Chain *e = i.node()->value;
1673 if (e->contains(value))
1674 return i.node()->key;
1675 ++i;
1676 }
1677 }
1678
1679 return defaultValue();
1680 }
1681public:
1682 Key key(const T &value) const noexcept
1683 {
1684 return keyImpl(value, [] { return Key(); });
1685 }
1686 Key key(const T &value, const Key &defaultKey) const noexcept
1687 {
1688 return keyImpl(value, [&] { return defaultKey; });
1689 }
1690
1691private:
1692 template <typename K, typename Fn> T valueImpl(const K &key, Fn &&defaultValue) const noexcept
1693 {
1694 if (d) {
1695 Node *n = d->findNode(key);
1696 if (n) {
1697 Q_ASSERT(n->value);
1698 return n->value->value;
1699 }
1700 }
1701 return defaultValue();
1702 }
1703public:
1704 T value(const Key &key) const noexcept
1705 {
1706 return valueImpl(key, [] { return T(); });
1707 }
1708 T value(const Key &key, const T &defaultValue) const noexcept
1709 {
1710 return valueImpl(key, [&] { return defaultValue; });
1711 }
1712
1713 T &operator[](const Key &key)
1714 {
1715 return operatorIndexImpl(key);
1716 }
1717private:
1718 template <typename K> T &operatorIndexImpl(const K &key)
1719 {
1720 const auto copy = isDetached() ? QMultiHash() : *this; // keep 'key' alive across the detach
1721 detach();
1722 auto result = d->findOrInsert(key);
1723 Q_ASSERT(!result.it.atEnd());
1724 if (!result.initialized) {
1725 Node::createInPlace(result.it.node(), Key(key), T());
1726 ++m_size;
1727 }
1728 return result.it.node()->value->value;
1729 }
1730
1731public:
1732 const T operator[](const Key &key) const noexcept
1733 {
1734 return value(key);
1735 }
1736
1737 QList<Key> uniqueKeys() const
1738 {
1739 QList<Key> res;
1740 if (d) {
1741 auto i = d->begin();
1742 while (i != d->end()) {
1743 res.append(i.node()->key);
1744 ++i;
1745 }
1746 }
1747 return res;
1748 }
1749
1750 QList<Key> keys() const { return QList<Key>(keyBegin(), keyEnd()); }
1751 QList<Key> keys(const T &value) const
1752 {
1753 QList<Key> res;
1755 while (i != end()) {
1756 if (i.value() == value)
1757 res.append(i.key());
1758 ++i;
1759 }
1760 return res;
1761 }
1762
1763 QList<T> values() const { return QList<T>(begin(), end()); }
1764 QList<T> values(const Key &key) const
1765 {
1766 return valuesImpl(key);
1767 }
1768private:
1769 template <typename K> QList<T> valuesImpl(const K &key) const
1770 {
1771 QList<T> values;
1772 if (d) {
1773 Node *n = d->findNode(key);
1774 if (n) {
1775 Chain *e = n->value;
1776 while (e) {
1777 values.append(e->value);
1778 e = e->next;
1779 }
1780 }
1781 }
1782 return values;
1783 }
1784
1785public:
1786 class const_iterator;
1787
1789 {
1790 using piter = typename QHashPrivate::iterator<Node>;
1791 friend class const_iterator;
1792 friend class QMultiHash<Key, T>;
1793 piter i;
1794 Chain **e = nullptr;
1795 explicit inline iterator(piter it, Chain **entry = nullptr) noexcept : i(it), e(entry)
1796 {
1797 if (!it.atEnd() && !e) {
1798 e = &it.node()->value;
1799 Q_ASSERT(e && *e);
1800 }
1801 }
1802
1803 public:
1804 typedef std::forward_iterator_tag iterator_category;
1806 typedef T value_type;
1807 typedef T *pointer;
1808 typedef T &reference;
1809
1810 constexpr iterator() noexcept = default;
1811
1812 inline const Key &key() const noexcept { return i.node()->key; }
1813 inline T &value() const noexcept { return (*e)->value; }
1814 inline T &operator*() const noexcept { return (*e)->value; }
1815 inline T *operator->() const noexcept { return &(*e)->value; }
1816 inline bool operator==(const iterator &o) const noexcept { return e == o.e; }
1817 inline bool operator!=(const iterator &o) const noexcept { return e != o.e; }
1818
1819 inline iterator &operator++() noexcept {
1820 Q_ASSERT(e && *e);
1821 e = &(*e)->next;
1822 Q_ASSERT(e);
1823 if (!*e) {
1824 ++i;
1825 e = i.atEnd() ? nullptr : &i.node()->value;
1826 }
1827 return *this;
1828 }
1829 inline iterator operator++(int) noexcept {
1830 iterator r = *this;
1831 ++(*this);
1832 return r;
1833 }
1834
1835 inline bool operator==(const const_iterator &o) const noexcept { return e == o.e; }
1836 inline bool operator!=(const const_iterator &o) const noexcept { return e != o.e; }
1837 };
1838 friend class iterator;
1839
1841 {
1842 using piter = typename QHashPrivate::iterator<Node>;
1843 friend class iterator;
1844 friend class QMultiHash<Key, T>;
1845 piter i;
1846 Chain **e = nullptr;
1847 explicit inline const_iterator(piter it, Chain **entry = nullptr) noexcept : i(it), e(entry)
1848 {
1849 if (!it.atEnd() && !e) {
1850 e = &it.node()->value;
1851 Q_ASSERT(e && *e);
1852 }
1853 }
1854
1855 public:
1856 typedef std::forward_iterator_tag iterator_category;
1858 typedef T value_type;
1859 typedef const T *pointer;
1860 typedef const T &reference;
1861
1862 constexpr const_iterator() noexcept = default;
1863 inline const_iterator(const iterator &o) noexcept : i(o.i), e(o.e) { }
1864
1865 inline const Key &key() const noexcept { return i.node()->key; }
1866 inline T &value() const noexcept { return (*e)->value; }
1867 inline T &operator*() const noexcept { return (*e)->value; }
1868 inline T *operator->() const noexcept { return &(*e)->value; }
1869 inline bool operator==(const const_iterator &o) const noexcept { return e == o.e; }
1870 inline bool operator!=(const const_iterator &o) const noexcept { return e != o.e; }
1871
1872 inline const_iterator &operator++() noexcept {
1873 Q_ASSERT(e && *e);
1874 e = &(*e)->next;
1875 Q_ASSERT(e);
1876 if (!*e) {
1877 ++i;
1878 e = i.atEnd() ? nullptr : &i.node()->value;
1879 }
1880 return *this;
1881 }
1882 inline const_iterator operator++(int) noexcept
1883 {
1884 const_iterator r = *this;
1885 ++(*this);
1886 return r;
1887 }
1888 };
1889 friend class const_iterator;
1890
1892 {
1894
1895 public:
1899 typedef const Key *pointer;
1900 typedef const Key &reference;
1901
1902 key_iterator() noexcept = default;
1904
1905 const Key &operator*() const noexcept { return i.key(); }
1906 const Key *operator->() const noexcept { return &i.key(); }
1907 bool operator==(key_iterator o) const noexcept { return i == o.i; }
1908 bool operator!=(key_iterator o) const noexcept { return i != o.i; }
1909
1910 inline key_iterator &operator++() noexcept { ++i; return *this; }
1911 inline key_iterator operator++(int) noexcept { return key_iterator(i++);}
1912 const_iterator base() const noexcept { return i; }
1913 };
1914
1915 typedef QKeyValueIterator<const Key&, const T&, const_iterator> const_key_value_iterator;
1916 typedef QKeyValueIterator<const Key&, T&, iterator> key_value_iterator;
1917
1918 // STL style
1919 inline iterator begin() { detach(); return iterator(d->begin()); }
1920 inline const_iterator begin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
1921 inline const_iterator cbegin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
1922 inline const_iterator constBegin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
1923 inline iterator end() noexcept { return iterator(); }
1924 inline const_iterator end() const noexcept { return const_iterator(); }
1925 inline const_iterator cend() const noexcept { return const_iterator(); }
1926 inline const_iterator constEnd() const noexcept { return const_iterator(); }
1927 inline key_iterator keyBegin() const noexcept { return key_iterator(begin()); }
1928 inline key_iterator keyEnd() const noexcept { return key_iterator(end()); }
1930 inline key_value_iterator keyValueEnd() noexcept { return key_value_iterator(end()); }
1935 auto asKeyValueRange() & { return QtPrivate::QKeyValueRange(*this); }
1936 auto asKeyValueRange() const & { return QtPrivate::QKeyValueRange(*this); }
1937 auto asKeyValueRange() && { return QtPrivate::QKeyValueRange(std::move(*this)); }
1938 auto asKeyValueRange() const && { return QtPrivate::QKeyValueRange(std::move(*this)); }
1939
1941 {
1942 auto i = it.i;
1943 Chain **e = it.e;
1944 if (d->ref.isShared()) {
1945 // need to store iterator position before detaching
1946 qsizetype n = 0;
1947 Chain *entry = i.node()->value;
1948 while (entry != *it.e) {
1949 ++n;
1950 entry = entry->next;
1951 }
1952 Q_ASSERT(entry);
1953 detach_helper();
1954
1955 i = d->detachedIterator(i);
1956 e = &i.node()->value;
1957 while (n) {
1958 e = &(*e)->next;
1959 --n;
1960 }
1961 Q_ASSERT(e && *e);
1962 }
1963 return iterator(i, e);
1964 }
1965
1967 {
1968 Q_ASSERT(d);
1970 iterator i = iter;
1971 Chain *e = *i.e;
1972 Chain *next = e->next;
1973 *i.e = next;
1974 delete e;
1975 if (!next) {
1976 if (i.e == &i.i.node()->value) {
1977 // last remaining entry, erase
1978 typename Data::Bucket bucket(i.i);
1979 d->erase(bucket);
1980 if (bucket.toBucketIndex(d) == d->numBuckets - 1 || bucket.isUnused())
1981 i = iterator(++iter.i);
1982 else // 'i' currently has a nullptr chain. So, we must recreate it
1983 i = iterator(bucket.toIterator(d));
1984 } else {
1985 i = iterator(++iter.i);
1986 }
1987 }
1988 --m_size;
1989 Q_ASSERT(m_size >= 0);
1990 return i;
1991 }
1992
1993 // more Qt
1996 inline qsizetype count() const noexcept { return size(); }
1997
1998private:
1999 template <typename K> iterator findImpl(const K &key)
2000 {
2001 if (isEmpty())
2002 return end();
2003 auto it = d->findBucket(key);
2004 size_t bucket = it.toBucketIndex(d);
2005 detach();
2006 it = typename Data::Bucket(d, bucket); // reattach in case of detach
2007
2008 if (it.isUnused())
2009 return end();
2010 return iterator(it.toIterator(d));
2011 }
2012 template <typename K> const_iterator constFindImpl(const K &key) const noexcept
2013 {
2014 if (isEmpty())
2015 return end();
2016 auto it = d->findBucket(key);
2017 if (it.isUnused())
2018 return constEnd();
2019 return const_iterator(it.toIterator(d));
2020 }
2021public:
2023 {
2024 return findImpl(key);
2025 }
2026 const_iterator constFind(const Key &key) const noexcept
2027 {
2028 return constFindImpl(key);
2029 }
2030 const_iterator find(const Key &key) const noexcept
2031 {
2032 return constFindImpl(key);
2033 }
2034
2035 iterator insert(const Key &key, const T &value)
2036 {
2037 return emplace(key, value);
2038 }
2039
2040 template <typename ...Args>
2041 iterator emplace(const Key &key, Args &&... args)
2042 {
2043 return emplace(Key(key), std::forward<Args>(args)...);
2044 }
2045
2046 template <typename ...Args>
2047 iterator emplace(Key &&key, Args &&... args)
2048 {
2049 if (isDetached()) {
2050 if (d->shouldGrow()) // Construct the value now so that no dangling references are used
2051 return emplace_helper(std::move(key), T(std::forward<Args>(args)...));
2052 return emplace_helper(std::move(key), std::forward<Args>(args)...);
2053 }
2054 // else: we must detach
2055 const auto copy = *this; // keep 'args' alive across the detach/growth
2056 detach();
2057 return emplace_helper(std::move(key), std::forward<Args>(args)...);
2058 }
2059
2060
2061 float load_factor() const noexcept { return d ? d->loadFactor() : 0; }
2062 static float max_load_factor() noexcept { return 0.5; }
2063 size_t bucket_count() const noexcept { return d ? d->numBuckets : 0; }
2064 static size_t max_bucket_count() noexcept { return Data::maxNumBuckets(); }
2065
2066 inline bool empty() const noexcept { return isEmpty(); }
2067
2068 inline iterator replace(const Key &key, const T &value)
2069 {
2070 return emplaceReplace(key, value);
2071 }
2072
2073 template <typename ...Args>
2074 iterator emplaceReplace(const Key &key, Args &&... args)
2075 {
2076 return emplaceReplace(Key(key), std::forward<Args>(args)...);
2077 }
2078
2079 template <typename ...Args>
2081 {
2082 if (isDetached()) {
2083 if (d->shouldGrow()) // Construct the value now so that no dangling references are used
2084 return emplaceReplace_helper(std::move(key), T(std::forward<Args>(args)...));
2085 return emplaceReplace_helper(std::move(key), std::forward<Args>(args)...);
2086 }
2087 // else: we must detach
2088 const auto copy = *this; // keep 'args' alive across the detach/growth
2089 detach();
2090 return emplaceReplace_helper(std::move(key), std::forward<Args>(args)...);
2091 }
2092
2094 { this->unite(other); return *this; }
2096 { QMultiHash result = *this; result += other; return result; }
2097
2098 bool contains(const Key &key, const T &value) const noexcept
2099 {
2100 return containsImpl(key, value);
2101 }
2102private:
2103 template <typename K> bool containsImpl(const K &key, const T &value) const noexcept
2104 {
2105 if (isEmpty())
2106 return false;
2107 auto n = d->findNode(key);
2108 if (n == nullptr)
2109 return false;
2110 return n->value->contains(value);
2111 }
2112
2113public:
2114 qsizetype remove(const Key &key, const T &value)
2115 {
2116 return removeImpl(key, value);
2117 }
2118private:
2119 template <typename K> qsizetype removeImpl(const K &key, const T &value)
2120 {
2121 if (isEmpty()) // prevents detaching shared null
2122 return 0;
2123 auto it = d->findBucket(key);
2124 size_t bucket = it.toBucketIndex(d);
2125 detach();
2126 it = typename Data::Bucket(d, bucket); // reattach in case of detach
2127
2128 if (it.isUnused())
2129 return 0;
2130 qsizetype n = 0;
2131 Chain **e = &it.node()->value;
2132 while (*e) {
2133 Chain *entry = *e;
2134 if (entry->value == value) {
2135 *e = entry->next;
2136 delete entry;
2137 ++n;
2138 } else {
2139 e = &entry->next;
2140 }
2141 }
2142 if (!it.node()->value)
2143 d->erase(it);
2144 m_size -= n;
2145 Q_ASSERT(m_size >= 0);
2146 return n;
2147 }
2148
2149public:
2150 qsizetype count(const Key &key) const noexcept
2151 {
2152 return countImpl(key);
2153 }
2154private:
2155 template <typename K> qsizetype countImpl(const K &key) const noexcept
2156 {
2157 if (!d)
2158 return 0;
2159 auto it = d->findBucket(key);
2160 if (it.isUnused())
2161 return 0;
2162 qsizetype n = 0;
2163 Chain *e = it.node()->value;
2164 while (e) {
2165 ++n;
2166 e = e->next;
2167 }
2168
2169 return n;
2170 }
2171
2172public:
2173 qsizetype count(const Key &key, const T &value) const noexcept
2174 {
2175 return countImpl(key, value);
2176 }
2177private:
2178 template <typename K> qsizetype countImpl(const K &key, const T &value) const noexcept
2179 {
2180 if (!d)
2181 return 0;
2182 auto it = d->findBucket(key);
2183 if (it.isUnused())
2184 return 0;
2185 qsizetype n = 0;
2186 Chain *e = it.node()->value;
2187 while (e) {
2188 if (e->value == value)
2189 ++n;
2190 e = e->next;
2191 }
2192
2193 return n;
2194 }
2195
2196 template <typename K> iterator findImpl(const K &key, const T &value)
2197 {
2198 if (isEmpty())
2199 return end();
2200 const auto copy = isDetached() ? QMultiHash() : *this; // keep 'key'/'value' alive across the detach
2201 detach();
2202 auto it = constFind(key, value);
2203 return iterator(it.i, it.e);
2204 }
2205 template <typename K> const_iterator constFindImpl(const K &key, const T &value) const noexcept
2206 {
2209 while (i != end && i.key() == key) {
2210 if (i.value() == value)
2211 return i;
2212 ++i;
2213 }
2214 return end;
2215 }
2216
2217public:
2218 iterator find(const Key &key, const T &value)
2219 {
2220 return findImpl(key, value);
2221 }
2222
2223 const_iterator constFind(const Key &key, const T &value) const noexcept
2224 {
2225 return constFindImpl(key, value);
2226 }
2227 const_iterator find(const Key &key, const T &value) const noexcept
2228 {
2229 return constFind(key, value);
2230 }
2231
2233 {
2234 if (isEmpty()) {
2235 *this = other;
2236 } else if (other.isEmpty()) {
2237 ;
2238 } else {
2240 detach();
2241 for (auto cit = copy.cbegin(); cit != copy.cend(); ++cit)
2242 insert(cit.key(), *cit);
2243 }
2244 return *this;
2245 }
2246
2247 QMultiHash &unite(const QHash<Key, T> &other)
2248 {
2249 for (auto cit = other.cbegin(); cit != other.cend(); ++cit)
2250 insert(cit.key(), *cit);
2251 return *this;
2252 }
2253
2254 QMultiHash &unite(QHash<Key, T> &&other)
2255 {
2256 if (!other.isDetached()) {
2257 unite(other);
2258 return *this;
2259 }
2260 auto it = other.d->begin();
2261 for (const auto end = other.d->end(); it != end; ++it)
2262 emplace(std::move(it.node()->key), std::move(it.node()->takeValue()));
2263 other.clear();
2264 return *this;
2265 }
2266
2267 std::pair<iterator, iterator> equal_range(const Key &key)
2268 {
2269 return equal_range_impl(key);
2270 }
2271private:
2272 template <typename K> std::pair<iterator, iterator> equal_range_impl(const K &key)
2273 {
2274 const auto copy = isDetached() ? QMultiHash() : *this; // keep 'key' alive across the detach
2275 detach();
2276 auto pair = std::as_const(*this).equal_range(key);
2277 return {iterator(pair.first.i), iterator(pair.second.i)};
2278 }
2279
2280public:
2281 std::pair<const_iterator, const_iterator> equal_range(const Key &key) const noexcept
2282 {
2283 return equal_range_impl(key);
2284 }
2285private:
2286 template <typename K> std::pair<const_iterator, const_iterator> equal_range_impl(const K &key) const noexcept
2287 {
2288 if (!d)
2289 return {end(), end()};
2290
2291 auto bucket = d->findBucket(key);
2292 if (bucket.isUnused())
2293 return {end(), end()};
2294 auto it = bucket.toIterator(d);
2295 auto end = it;
2296 ++end;
2297 return {const_iterator(it), const_iterator(end)};
2298 }
2299
2300 void detach_helper()
2301 {
2302 if (!d) {
2303 d = new Data;
2304 return;
2305 }
2306 Data *dd = new Data(*d);
2307 if (!d->ref.deref())
2308 delete d;
2309 d = dd;
2310 }
2311
2312 template<typename... Args>
2313 iterator emplace_helper(Key &&key, Args &&...args)
2314 {
2315 auto result = d->findOrInsert(key);
2316 if (!result.initialized)
2317 Node::createInPlace(result.it.node(), std::move(key), std::forward<Args>(args)...);
2318 else
2319 result.it.node()->insertMulti(std::forward<Args>(args)...);
2320 ++m_size;
2321 return iterator(result.it);
2322 }
2323
2324 template<typename... Args>
2325 iterator emplaceReplace_helper(Key &&key, Args &&...args)
2326 {
2327 auto result = d->findOrInsert(key);
2328 if (!result.initialized) {
2329 Node::createInPlace(result.it.node(), std::move(key), std::forward<Args>(args)...);
2330 ++m_size;
2331 } else {
2332 result.it.node()->emplaceValue(std::forward<Args>(args)...);
2333 }
2334 return iterator(result.it);
2335 }
2336
2337public:
2338#ifdef __cpp_concepts
2339 qsizetype remove(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
2340 {
2341 return removeImpl(key);
2342 }
2343 T take(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
2344 {
2345 return takeImpl(key);
2346 }
2347 bool contains(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
2348 {
2349 if (!d)
2350 return false;
2351 return d->findNode(key) != nullptr;
2352 }
2353 T value(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
2354 {
2355 return valueImpl(key, [] { return T(); });
2356 }
2357 T value(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &defaultValue) const noexcept
2358 {
2359 return valueImpl(key, [&] { return defaultValue; });
2360 }
2361 T &operator[](const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
2362 {
2363 return operatorIndexImpl(key);
2364 }
2365 const T operator[](const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
2366 {
2367 return value(key);
2368 }
2369 QList<T> values(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
2370 {
2371 return valuesImpl(key);
2372 }
2373 iterator find(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
2374 {
2375 return findImpl(key);
2376 }
2377 const_iterator constFind(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
2378 {
2379 return constFindImpl(key);
2380 }
2381 const_iterator find(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
2382 {
2383 return constFindImpl(key);
2384 }
2385 bool contains(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &value) const noexcept
2386 {
2387 return containsImpl(key, value);
2388 }
2389 qsizetype remove(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &value)
2390 {
2391 return removeImpl(key, value);
2392 }
2393 qsizetype count(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
2394 {
2395 return countImpl(key);
2396 }
2397 qsizetype count(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &value) const noexcept
2398 {
2399 return countImpl(key, value);
2400 }
2401 iterator find(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &value)
2402 {
2403 return findImpl(key, value);
2404 }
2405 const_iterator constFind(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &value) const noexcept
2406 {
2407 return constFindImpl(key, value);
2408 }
2409 const_iterator find(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &value) const noexcept
2410 {
2411 return constFind(key, value);
2412 }
2413 std::pair<iterator, iterator>
2414 equal_range(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
2415 {
2416 return equal_range_impl(key);
2417 }
2418 std::pair<const_iterator, const_iterator>
2419 equal_range(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
2420 {
2421 return equal_range_impl(key);
2422 }
2423#endif // __cpp_concepts
2424};
2425
2430
2431template <class Key, class T>
2432size_t qHash(const QHash<Key, T> &key, size_t seed = 0)
2433 noexcept(noexcept(qHash(std::declval<Key&>())) && noexcept(qHash(std::declval<T&>())))
2434{
2435 size_t hash = 0;
2436 for (auto it = key.begin(), end = key.end(); it != end; ++it) {
2438 size_t h = combine(seed, it.key());
2439 // use + to keep the result independent of the ordering of the keys
2440 hash += combine(h, it.value());
2441 }
2442 return hash;
2443}
2444
2445template <class Key, class T>
2446inline size_t qHash(const QMultiHash<Key, T> &key, size_t seed = 0)
2447 noexcept(noexcept(qHash(std::declval<Key&>())) && noexcept(qHash(std::declval<T&>())))
2448{
2449 size_t hash = 0;
2450 for (auto it = key.begin(), end = key.end(); it != end; ++it) {
2452 size_t h = combine(seed, it.key());
2453 // use + to keep the result independent of the ordering of the keys
2454 hash += combine(h, it.value());
2455 }
2456 return hash;
2457}
2458
2459template <typename Key, typename T, typename Predicate>
2460qsizetype erase_if(QHash<Key, T> &hash, Predicate pred)
2461{
2463}
2464
2465template <typename Key, typename T, typename Predicate>
2466qsizetype erase_if(QMultiHash<Key, T> &hash, Predicate pred)
2467{
2469}
2470
2472
2473#endif // QHASH_H
Definition lalr.h:136
\inmodule QtCore
Definition qhash.h:1146
const_iterator operator++(int) noexcept
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qhash.h:1176
bool operator==(const const_iterator &o) const noexcept
Returns true if other points to the same item as this iterator; otherwise returns false.
Definition qhash.h:1168
bool operator!=(const const_iterator &o) const noexcept
Returns true if other points to a different item than this iterator; otherwise returns false.
Definition qhash.h:1169
const T & value() const noexcept
Returns the current item's value.
Definition qhash.h:1165
const T & reference
Definition qhash.h:1159
const T & operator*() const noexcept
Returns the current item's value.
Definition qhash.h:1166
const T * pointer
Definition qhash.h:1158
qptrdiff difference_type
Definition qhash.h:1156
std::forward_iterator_tag iterator_category
Definition qhash.h:1155
constexpr const_iterator() noexcept=default
Constructs an uninitialized iterator.
const_iterator & operator++() noexcept
The prefix ++ operator ({++i}) advances the iterator to the next item in the hash and returns an iter...
Definition qhash.h:1171
const T * operator->() const noexcept
Returns a pointer to the current item's value.
Definition qhash.h:1167
const Key & key() const noexcept
Returns the current item's key.
Definition qhash.h:1164
\inmodule QtCore
Definition qhash.h:1104
bool operator==(const const_iterator &o) const noexcept
Returns true if other points to the same item as this iterator; otherwise returns false.
Definition qhash.h:1140
bool operator!=(const const_iterator &o) const noexcept
Returns true if other points to a different item than this iterator; otherwise returns false.
Definition qhash.h:1141
T & value() const noexcept
Returns a modifiable reference to the current item's value.
Definition qhash.h:1122
constexpr iterator() noexcept=default
Constructs an uninitialized iterator.
bool operator==(const iterator &o) const noexcept
Definition qhash.h:1125
std::forward_iterator_tag iterator_category
Definition qhash.h:1113
T * operator->() const noexcept
Returns a pointer to the current item's value.
Definition qhash.h:1124
T & operator*() const noexcept
Returns a modifiable reference to the current item's value.
Definition qhash.h:1123
iterator & operator++() noexcept
The prefix ++ operator ({++i}) advances the iterator to the next item in the hash and returns an iter...
Definition qhash.h:1128
qptrdiff difference_type
Definition qhash.h:1114
iterator operator++(int) noexcept
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qhash.h:1133
bool operator!=(const iterator &o) const noexcept
Definition qhash.h:1126
\inmodule QtCore
Definition qhash.h:1186
key_iterator() noexcept=default
key_iterator & operator++() noexcept
The prefix ++ operator ({++i}) advances the iterator to the next item in the hash and returns an iter...
Definition qhash.h:1204
const Key & reference
Definition qhash.h:1194
const_iterator base() const noexcept
Returns the underlying const_iterator this key_iterator is based on.
Definition qhash.h:1206
const Key * operator->() const noexcept
Returns a pointer to the current item's key.
Definition qhash.h:1200
bool operator!=(key_iterator o) const noexcept
Returns true if other points to a different item than this iterator; otherwise returns false.
Definition qhash.h:1202
key_iterator operator++(int) noexcept
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qhash.h:1205
const_iterator::iterator_category iterator_category
Definition qhash.h:1190
bool operator==(key_iterator o) const noexcept
Returns true if other points to the same item as this iterator; otherwise returns false.
Definition qhash.h:1201
qptrdiff difference_type
Definition qhash.h:1191
const Key & operator*() const noexcept
Returns the current item's key.
Definition qhash.h:1199
const Key * pointer
Definition qhash.h:1193
\inmodule QtCore
Definition qhash.h:821
T value_type
Definition qhash.h:833
key_iterator keyEnd() const noexcept
Definition qhash.h:1222
void squeeze()
Reduces the size of the QHash's internal hash table to save memory.
Definition qhash.h:942
std::pair< const_iterator, const_iterator > equal_range(const Key &key) const noexcept
Definition qhash.h:1252
bool remove(const Key &key)
Removes the item that has the key from the hash.
Definition qhash.h:959
iterator begin()
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1213
const_iterator cbegin() const noexcept
Definition qhash.h:1215
qsizetype size() const noexcept
Returns the number of items in the hash.
Definition qhash.h:928
T & operator[](const Key &key)
Returns the value associated with the key as a modifiable reference.
Definition qhash.h:1065
float load_factor() const noexcept
Returns the current load factor of the QHash's internal hash table.
Definition qhash.h:1345
T & reference
Definition qhash.h:836
~QHash()
Destroys the hash.
Definition qhash.h:852
QHash(QHash &&other) noexcept
Move-constructs a QHash instance, making it point at the same object that other was pointing to.
Definition qhash.h:874
QHash(InputIterator f, InputIterator l)
Definition qhash.h:893
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1300
const_iterator begin() const noexcept
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qhash.h:1214
const_iterator constEnd() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the ...
Definition qhash.h:1220
iterator find(const Key &key)
Returns an iterator pointing to the item with the key in the hash.
Definition qhash.h:1292
qsizetype size_type
Typedef for int.
Definition qhash.h:834
key_value_iterator keyValueEnd()
Definition qhash.h:1224
QList< T > values() const
Returns a list containing all the values in the hash, in an arbitrary order.
Definition qhash.h:1099
key_value_iterator keyValueBegin()
Definition qhash.h:1223
auto asKeyValueRange() const &&
Definition qhash.h:1232
void reserve(qsizetype size)
Ensures that the QHash's internal hash table has space to store at least size items without having to...
Definition qhash.h:932
iterator emplace(const Key &key, Args &&... args)
Definition qhash.h:1325
T value(const Key &key, const T &defaultValue) const noexcept
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qhash.h:1060
const T operator[](const Key &key) const noexcept
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qhash.h:1082
const_iterator constBegin() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1216
T take(const Key &key)
Removes the item with the key from the hash and returns the value associated with it.
Definition qhash.h:986
void detach()
Definition qhash.h:948
const_key_value_iterator constKeyValueEnd() const noexcept
Definition qhash.h:1228
qsizetype difference_type
Typedef for ptrdiff_t.
Definition qhash.h:835
bool isDetached() const noexcept
Definition qhash.h:949
Key key_type
Typedef for Key.
Definition qhash.h:831
QKeyValueIterator< const Key &, T &, iterator > key_value_iterator
\inmodule QtCore
Definition qhash.h:1210
friend class iterator
Definition qhash.h:1143
iterator Iterator
Qt-style synonym for QHash::iterator.
Definition qhash.h:1289
QKeyValueIterator< const Key &, const T &, const_iterator > const_key_value_iterator
\inmodule QtCore
Definition qhash.h:1209
Key key(const T &value, const Key &defaultKey) const noexcept
Definition qhash.h:1039
static float max_load_factor() noexcept
Definition qhash.h:1346
QList< Key > keys() const
Returns a list containing all the keys in the hash, in an arbitrary order.
Definition qhash.h:1087
auto asKeyValueRange() &
Definition qhash.h:1229
iterator erase(const_iterator it)
Definition qhash.h:1234
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
Definition qhash.h:1008
QTypeTraits::compare_eq_result_container< QHash, AKey, AT > operator==(const QHash &other) const noexcept
Returns true if other is equal to this hash; otherwise returns false.
Definition qhash.h:905
const_key_value_iterator keyValueEnd() const noexcept
Definition qhash.h:1227
key_iterator keyBegin() const noexcept
Definition qhash.h:1221
const_key_value_iterator keyValueBegin() const noexcept
Definition qhash.h:1225
qsizetype count() const noexcept
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qhash.h:1291
qsizetype removeIf(Predicate pred)
Definition qhash.h:981
T value(const Key &key) const noexcept
Definition qhash.h:1055
void swap(QHash &other) noexcept
Definition qhash.h:901
bool isSharedWith(const QHash &other) const noexcept
Definition qhash.h:950
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1217
QHash & operator=(const QHash &other) noexcept(std::is_nothrow_destructible< Node >::value)
Assigns other to this hash and returns a reference to this hash.
Definition qhash.h:861
size_t bucket_count() const noexcept
Definition qhash.h:1347
void insert(const QHash &hash)
Definition qhash.h:1309
auto asKeyValueRange() &&
Definition qhash.h:1231
friend class const_iterator
Definition qhash.h:1183
const_iterator cend() const noexcept
Definition qhash.h:1219
auto asKeyValueRange() const &
Definition qhash.h:1230
QHash(InputIterator f, InputIterator l)
Definition qhash.h:884
std::pair< iterator, iterator > equal_range(const Key &key)
Definition qhash.h:1248
const_iterator ConstIterator
Qt-style synonym for QHash::const_iterator.
Definition qhash.h:1290
T mapped_type
Typedef for T.
Definition qhash.h:832
static size_t max_bucket_count() noexcept
Definition qhash.h:1348
const_iterator find(const Key &key) const noexcept
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qhash.h:1296
Key key(const T &value) const noexcept
Definition qhash.h:1035
const T & const_reference
Definition qhash.h:837
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Removes all items from the hash and frees up all memory used by it.
Definition qhash.h:952
QList< Key > keys(const T &value) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qhash.h:1088
QTypeTraits::compare_eq_result_container< QHash, AKey, AT > operator!=(const QHash &other) const noexcept
Returns true if other is not equal to this hash; otherwise returns false.
Definition qhash.h:921
bool empty() const noexcept
This function is provided for STL compatibility.
Definition qhash.h:1350
qsizetype count(const Key &key) const noexcept
Returns the number of items associated with the key.
Definition qhash.h:1014
qsizetype capacity() const noexcept
Returns the number of buckets in the QHash's internal hash table.
Definition qhash.h:931
bool isEmpty() const noexcept
Returns true if the hash contains no items; otherwise returns false.
Definition qhash.h:929
const_iterator end() const noexcept
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qhash.h:1218
QHash() noexcept=default
Constructs an empty hash.
iterator emplace(Key &&key, Args &&... args)
Inserts a new element into the container.
Definition qhash.h:1332
QHash(const QHash &other) noexcept
Constructs a copy of other.
Definition qhash.h:846
const_key_value_iterator constKeyValueBegin() const noexcept
Definition qhash.h:1226
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1304
iterator end()
Definition qlist.h:627
iterator begin()
Definition qlist.h:626
\inmodule QtCore
Definition qhash.h:1841
const_iterator & operator++() noexcept
The prefix ++ operator ({++i}) advances the iterator to the next item in the hash and returns an iter...
Definition qhash.h:1872
T & value() const noexcept
Returns the current item's value.
Definition qhash.h:1866
const Key & key() const noexcept
Returns the current item's key.
Definition qhash.h:1865
std::forward_iterator_tag iterator_category
Definition qhash.h:1856
bool operator!=(const const_iterator &o) const noexcept
Returns true if other points to a different item than this iterator; otherwise returns false.
Definition qhash.h:1870
T * operator->() const noexcept
Returns a pointer to the current item's value.
Definition qhash.h:1868
constexpr const_iterator() noexcept=default
Constructs an uninitialized iterator.
T & operator*() const noexcept
Returns the current item's value.
Definition qhash.h:1867
const_iterator operator++(int) noexcept
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qhash.h:1882
bool operator==(const const_iterator &o) const noexcept
Returns true if other points to the same item as this iterator; otherwise returns false.
Definition qhash.h:1869
\inmodule QtCore
Definition qhash.h:1789
T & value() const noexcept
Returns a modifiable reference to the current item's value.
Definition qhash.h:1813
constexpr iterator() noexcept=default
Constructs an uninitialized iterator.
bool operator==(const const_iterator &o) const noexcept
Returns true if other points to the same item as this iterator; otherwise returns false.
Definition qhash.h:1835
T * operator->() const noexcept
Returns a pointer to the current item's value.
Definition qhash.h:1815
bool operator!=(const iterator &o) const noexcept
Definition qhash.h:1817
bool operator!=(const const_iterator &o) const noexcept
Returns true if other points to a different item than this iterator; otherwise returns false.
Definition qhash.h:1836
bool operator==(const iterator &o) const noexcept
Definition qhash.h:1816
T & operator*() const noexcept
Returns a modifiable reference to the current item's value.
Definition qhash.h:1814
iterator & operator++() noexcept
The prefix ++ operator ({++i}) advances the iterator to the next item in the hash and returns an iter...
Definition qhash.h:1819
iterator operator++(int) noexcept
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qhash.h:1829
std::forward_iterator_tag iterator_category
Definition qhash.h:1804
qptrdiff difference_type
Definition qhash.h:1805
\inmodule QtCore
Definition qhash.h:1892
const_iterator base() const noexcept
Returns the underlying const_iterator this key_iterator is based on.
Definition qhash.h:1912
const_iterator::iterator_category iterator_category
Definition qhash.h:1896
key_iterator() noexcept=default
const Key * operator->() const noexcept
Returns a pointer to the current item's key.
Definition qhash.h:1906
bool operator!=(key_iterator o) const noexcept
Returns true if other points to a different item than this iterator; otherwise returns false.
Definition qhash.h:1908
const Key & reference
Definition qhash.h:1900
key_iterator operator++(int) noexcept
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qhash.h:1911
const Key * pointer
Definition qhash.h:1899
key_iterator & operator++() noexcept
The prefix ++ operator ({++i}) advances the iterator to the next item in the hash and returns an iter...
Definition qhash.h:1910
bool operator==(key_iterator o) const noexcept
Returns true if other points to the same item as this iterator; otherwise returns false.
Definition qhash.h:1907
const Key & operator*() const noexcept
Returns the current item's key.
Definition qhash.h:1905
\inmodule QtCore
Definition qhash.h:1426
const_key_value_iterator constKeyValueEnd() const noexcept
Definition qhash.h:1934
const_iterator find(const Key &key) const noexcept
Definition qhash.h:2030
const_key_value_iterator keyValueBegin() const noexcept
Definition qhash.h:1931
const_iterator find(const Key &key, const T &value) const noexcept
Definition qhash.h:2227
const_key_value_iterator keyValueEnd() const noexcept
Definition qhash.h:1933
QMultiHash & unite(const QHash< Key, T > &other)
Definition qhash.h:2247
iterator find(const Key &key, const T &value)
Definition qhash.h:2218
iterator Iterator
Definition qhash.h:1994
QMultiHash(const QHash< Key, T > &other)
Constructs a copy of other (which can be a QHash or a QMultiHash).
Definition qhash.h:1510
bool contains(const Key &key, const T &value) const noexcept
Definition qhash.h:2098
const_iterator constFind(const Key &key, const T &value) const noexcept
Definition qhash.h:2223
std::pair< iterator, iterator > equal_range(const Key &key)
Definition qhash.h:2267
QMultiHash(QHash< Key, T > &&other)
Definition qhash.h:1514
QTypeTraits::compare_eq_result_container< QMultiHash, AKey, AT > operator==(const QMultiHash &other) const noexcept
Definition qhash.h:1527
iterator find(const Key &key)
Definition qhash.h:2022
auto asKeyValueRange() &&
Definition qhash.h:1937
float load_factor() const noexcept
Definition qhash.h:2061
bool isSharedWith(const QMultiHash &other) const noexcept
Definition qhash.h:1587
key_value_iterator keyValueBegin() noexcept
Definition qhash.h:1929
QMultiHash(const QMultiHash &other) noexcept
Definition qhash.h:1470
std::pair< const_iterator, const_iterator > equal_range(const Key &key) const noexcept
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qhash.h:2281
iterator detach(const_iterator it)
Definition qhash.h:1940
QMultiHash & operator=(QMultiHash &&other) noexcept(std::is_nothrow_destructible< Node >::value)
Definition qhash.h:1503
qsizetype count(const Key &key, const T &value) const noexcept
Definition qhash.h:2173
const_iterator cbegin() const noexcept
Definition qhash.h:1921
T value_type
Definition qhash.h:1437
key_iterator keyBegin() const noexcept
Definition qhash.h:1927
const_iterator constBegin() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1922
iterator emplace(const Key &key, Args &&... args)
Definition qhash.h:2041
QMultiHash(QMultiHash &&other) noexcept
Definition qhash.h:1498
bool empty() const noexcept
Definition qhash.h:2066
auto asKeyValueRange() const &
Definition qhash.h:1936
const_iterator constEnd() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the ...
Definition qhash.h:1926
static size_t max_bucket_count() noexcept
Definition qhash.h:2064
QMultiHash(InputIterator f, InputIterator l)
Definition qhash.h:1463
T & reference
Definition qhash.h:1440
QKeyValueIterator< const Key &, const T &, const_iterator > const_key_value_iterator
\inmodule QtCore
Definition qhash.h:1915
bool isDetached() const noexcept
Definition qhash.h:1586
T value(const Key &key) const noexcept
Definition qhash.h:1704
QList< T > values(const Key &key) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qhash.h:1764
friend class iterator
Definition qhash.h:1838
iterator emplace(Key &&key, Args &&... args)
Inserts a new element into the container.
Definition qhash.h:2047
const_iterator cend() const noexcept
Definition qhash.h:1925
qsizetype removeIf(Predicate pred)
Definition qhash.h:1622
iterator emplaceReplace(Key &&key, Args &&... args)
Inserts a new element into the container.
Definition qhash.h:2080
QMultiHash & unite(const QMultiHash &other)
Definition qhash.h:2232
QList< T > values() const
Returns a list containing all the values in the hash, in an arbitrary order.
Definition qhash.h:1763
T mapped_type
Definition qhash.h:1436
Key key(const T &value) const noexcept
Definition qhash.h:1682
QMultiHash(InputIterator f, InputIterator l)
Definition qhash.h:1455
qsizetype capacity() const noexcept
Definition qhash.h:1572
bool contains(const Key &key) const noexcept
Definition qhash.h:1659
qsizetype difference_type
Definition qhash.h:1439
QMultiHash & operator+=(const QMultiHash &other)
Inserts all the items in the other hash into this hash and returns a reference to this hash.
Definition qhash.h:2093
Key key_type
Definition qhash.h:1435
QMultiHash & operator=(const QMultiHash &other) noexcept(std::is_nothrow_destructible< Node >::value)
Definition qhash.h:1485
void swap(QMultiHash &other) noexcept
Definition qhash.h:1519
const_iterator begin() const noexcept
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qhash.h:1920
T & operator[](const Key &key)
Returns the value associated with the key as a modifiable reference.
Definition qhash.h:1713
T take(const Key &key)
Removes the item with the key from the hash and returns the value associated with it.
Definition qhash.h:1627
qsizetype remove(const Key &key)
Definition qhash.h:1597
const_iterator ConstIterator
Definition qhash.h:1995
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:2035
QMultiHash & unite(QHash< Key, T > &&other)
Definition qhash.h:2254
void detach()
Definition qhash.h:1585
const T & const_reference
Definition qhash.h:1441
size_t bucket_count() const noexcept
Definition qhash.h:2063
void reserve(qsizetype size)
Definition qhash.h:1573
bool isEmpty() const noexcept
Definition qhash.h:1570
iterator emplaceReplace(const Key &key, Args &&... args)
Definition qhash.h:2074
iterator begin()
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1919
QList< Key > keys() const
Returns a list containing all the keys in the hash, in an arbitrary order.
Definition qhash.h:1750
QMultiHash() noexcept=default
Constructs an empty hash.
QList< Key > uniqueKeys() const
Definition qhash.h:1737
QList< Key > keys(const T &value) const
Definition qhash.h:1751
T value(const Key &key, const T &defaultValue) const noexcept
Returns the value associated with the key.
Definition qhash.h:1708
qsizetype size() const noexcept
Definition qhash.h:1568
key_value_iterator keyValueEnd() noexcept
Definition qhash.h:1930
iterator replace(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:2068
const_iterator end() const noexcept
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qhash.h:1924
friend class const_iterator
Definition qhash.h:1889
qsizetype count(const Key &key) const noexcept
Definition qhash.h:2150
Key key(const T &value, const Key &defaultKey) const noexcept
Definition qhash.h:1686
qsizetype size_type
Definition qhash.h:1438
const_key_value_iterator constKeyValueBegin() const noexcept
Definition qhash.h:1932
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Definition qhash.h:1589
iterator erase(const_iterator it)
Definition qhash.h:1966
qsizetype remove(const Key &key, const T &value)
Definition qhash.h:2114
key_iterator keyEnd() const noexcept
Definition qhash.h:1928
void squeeze()
Definition qhash.h:1583
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1923
~QMultiHash()
Definition qhash.h:1476
qsizetype count() const noexcept
Definition qhash.h:1996
QKeyValueIterator< const Key &, T &, iterator > key_value_iterator
\inmodule QtCore
Definition qhash.h:1916
QTypeTraits::compare_eq_result_container< QMultiHash, AKey, AT > operator!=(const QMultiHash &other) const noexcept
Definition qhash.h:1561
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:2026
QMultiHash operator+(const QMultiHash &other) const
Returns a hash that contains all the items in this hash in addition to all the items in other.
Definition qhash.h:2095
auto asKeyValueRange() const &&
Definition qhash.h:1938
auto asKeyValueRange() &
Definition qhash.h:1935
static float max_load_factor() noexcept
Definition qhash.h:2062
const T operator[](const Key &key) const noexcept
Definition qhash.h:1732
Definition qset.h:19
iterator begin()
Definition qset.h:137
iterator erase(const_iterator i)
Definition qset.h:146
iterator insert(const T &value)
Definition qset.h:156
\inmodule QtCore
Definition qrefcount.h:16
#define this
Definition dialogs.cpp:9
QHash< int, QWidget * > hash
[35multi]
QSet< QString >::iterator it
set reserve(20000)
short next
Definition keywords.cpp:445
constexpr size_t bucketForHash(size_t nBuckets, size_t hash) noexcept
Definition qhash.h:438
constexpr size_t bucketsForCapacity(size_t requestedCapacity) noexcept
Definition qhash.h:418
constexpr bool isRelocatable()
Definition qhash.h:217
constexpr bool HasStdHashSpecializationWithoutSeed
Definition qhash.h:47
size_t calculateHash(const T &t, size_t seed=0)
Definition qhash.h:55
constexpr bool HasQHashOverload
Definition qhash.h:31
constexpr bool HasStdHashSpecializationWithSeed
Definition qhash.h:39
Combined button and popup list for selecting options.
std::enable_if_t< std::conjunction_v< QTypeTraits::has_operator_equal_container< Container, T >... >, bool > compare_eq_result_container
auto associative_erase_if(Container &c, Predicate &pred)
QKeyValueRange(Map &) -> QKeyValueRange< Map & >
void reserveIfForwardIterator(Container *, InputIterator, InputIterator)
QT_POPCOUNT_RELAXED_CONSTEXPR uint qCountLeadingZeroBits(quint32 v) noexcept
static jboolean copy(JNIEnv *, jobject)
#define Q_UNLIKELY(x)
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter * iter
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
size_t qHash(const QFileSystemWatcherPathKey &key, size_t seed=0)
size_t qHash(const QHash< Key, T > &key, size_t seed=0) noexcept(noexcept(qHash(std::declval< Key & >())) &&noexcept(qHash(std::declval< T & >())))
Definition qhash.h:2432
qsizetype erase_if(QHash< Key, T > &hash, Predicate pred)
Definition qhash.h:2460
bool qHashEquals(const T &a, const T &b)
#define Q_DECLARE_MUTABLE_ASSOCIATIVE_FORWARD_ITERATOR(C)
Definition qiterator.h:198
#define Q_DECLARE_ASSOCIATIVE_FORWARD_ITERATOR(C)
Definition qiterator.h:172
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:21
GLenum GLsizei GLsizei GLint * values
[15]
GLuint64 key
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLboolean r
[2]
GLuint GLuint end
GLenum GLenum GLsizei count
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat f
GLenum GLuint GLintptr offset
GLint ref
GLint first
GLfloat n
GLfloat GLfloat GLfloat GLfloat h
GLdouble s
[6]
Definition qopenglext.h:235
GLuint res
const GLubyte * c
GLuint GLfloat * val
GLuint entry
GLuint GLsizei const GLuint const GLintptr * offsets
GLdouble GLdouble t
Definition qopenglext.h:243
GLenum GLenum GLsizei void GLsizei void void * span
GLuint64EXT * result
[6]
static Q_CONSTINIT QBasicAtomicInteger< unsigned > seed
Definition qrandom.cpp:196
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
constexpr void qt_ptr_swap(T *&lhs, T *&rhs) noexcept
Definition qswap.h:29
#define Q_UNUSED(x)
unsigned char uchar
Definition qtypes.h:32
ptrdiff_t qptrdiff
Definition qtypes.h:164
ptrdiff_t qsizetype
Definition qtypes.h:165
#define explicit
QList< int > list
[14]
Q_CHECK_PTR(a=new int[80])
QObject::connect nullptr
QSharedPointer< T > other(t)
[5]
QJSValueList args
bool operator==(const QHashDummyValue &) const noexcept
Definition qhash.h:25
friend bool operator==(Bucket lhs, Bucket rhs) noexcept
Definition qhash.h:516
size_t offset() const noexcept
Definition qhash.h:498
bool isUnused() const noexcept
Definition qhash.h:494
Bucket(const Data *d, size_t bucket) noexcept
Definition qhash.h:473
Bucket(Span *s, size_t i) noexcept
Definition qhash.h:470
Node * insert() const
Definition qhash.h:510
void advance(const Data *d) noexcept
Definition qhash.h:490
Node & nodeAtOffset(size_t offset)
Definition qhash.h:502
iterator toIterator(const Data *d) const noexcept
Definition qhash.h:485
friend bool operator!=(Bucket lhs, Bucket rhs) noexcept
Definition qhash.h:520
void advanceWrapped(const Data *d) noexcept
Definition qhash.h:486
Bucket(iterator it) noexcept
Definition qhash.h:477
size_t toBucketIndex(const Data *d) const noexcept
Definition qhash.h:481
void reallocationHelper(const Data &other, size_t nSpans, bool resized)
Definition qhash.h:561
iterator begin() const noexcept
Definition qhash.h:623
QHashPrivate::Span< Node > Span
Definition qhash.h:452
size_t nextBucket(size_t bucket) const noexcept
Definition qhash.h:664
typename Node::ValueType T
Definition qhash.h:451
InsertionResult findOrInsert(const K &key) noexcept
Definition qhash.h:717
Node * findNode(const K &key) const noexcept
Definition qhash.h:703
QHashPrivate::iterator< Node > iterator
Definition qhash.h:453
static Data * detached(Data *d)
Definition qhash.h:591
iterator detachedIterator(iterator other) const noexcept
Definition qhash.h:618
constexpr iterator end() const noexcept
Definition qhash.h:631
bool shouldGrow() const noexcept
Definition qhash.h:676
typename Node::KeyType Key
Definition qhash.h:450
void rehash(size_t sizeHint=0)
Definition qhash.h:636
void erase(Bucket bucket) noexcept(std::is_nothrow_destructible< Node >::value)
Definition qhash.h:736
static Data * detached(Data *d, size_t size)
Definition qhash.h:600
float loadFactor() const noexcept
Definition qhash.h:672
Data(size_t reserve=0)
Definition qhash.h:554
static auto allocateSpans(size_t numBuckets)
Definition qhash.h:535
Data(const Data &other, size_t reserved)
Definition qhash.h:583
Data(const Data &other)
Definition qhash.h:577
static constexpr size_t maxNumBuckets() noexcept
Definition qhash.h:461
Bucket findBucket(const K &key) const noexcept
Definition qhash.h:681
size_t numBuckets
Definition qhash.h:457
qsizetype free() noexcept(std::is_nothrow_destructible_v< T >)
Definition qhash.h:124
bool contains(const T &val) const noexcept
Definition qhash.h:136
MultiNodeChain * next
Definition qhash.h:120
static qsizetype freeChain(MultiNode *n) noexcept(std::is_nothrow_destructible_v< T >)
Definition qhash.h:197
MultiNode(MultiNode &&other)
Definition qhash.h:174
void insertMulti(Args &&... args)
Definition qhash.h:204
MultiNode(const MultiNode &other)
Definition qhash.h:180
static void createInPlace(MultiNode *n, const Key &k, Args &&... args)
Definition qhash.h:162
MultiNode(const Key &k, Chain *c)
Definition qhash.h:165
static void createInPlace(MultiNode *n, Key &&k, Args &&... args)
Definition qhash.h:159
MultiNode(Key &&k, Chain *c) noexcept(std::is_nothrow_move_assignable_v< Key >)
Definition qhash.h:169
MultiNodeChain< T > Chain
Definition qhash.h:153
void emplaceValue(Args &&... args)
Definition qhash.h:210
static void createInPlace(Node *n, const Key &k, Args &&...)
Definition qhash.h:106
bool valuesEqual(const Node *) const
Definition qhash.h:113
static void createInPlace(Node *n, Key &&k, Args &&...)
Definition qhash.h:103
void emplaceValue(Args &&... args)
Definition qhash.h:85
bool valuesEqual(const Node *other) const
Definition qhash.h:93
static void createInPlace(Node *n, const Key &k, Args &&... args)
Definition qhash.h:82
static void createInPlace(Node *n, Key &&k, Args &&... args)
Definition qhash.h:79
T && takeValue() noexcept(std::is_nothrow_move_assignable_v< T >)
Definition qhash.h:89
static constexpr size_t SpanShift
Definition qhash.h:223
static constexpr size_t LocalBucketMask
Definition qhash.h:225
static constexpr size_t UnusedEntry
Definition qhash.h:226
static constexpr size_t NEntries
Definition qhash.h:224
struct QHashPrivate::Span::Entry::@178 storage
unsigned char & nextFree()
Definition qhash.h:250
const Node & at(size_t i) const noexcept
Definition qhash.h:318
void moveLocal(size_t from, size_t to) noexcept
Definition qhash.h:337
void addStorage()
Definition qhash.h:371
void freeData() noexcept(std::is_nothrow_destructible< Node >::value)
Definition qhash.h:266
void erase(size_t bucket) noexcept(std::is_nothrow_destructible< Node >::value)
Definition qhash.h:291
unsigned char nextFree
Definition qhash.h:257
Span() noexcept
Definition qhash.h:258
unsigned char allocated
Definition qhash.h:256
unsigned char offsets[SpanConstants::NEntries]
Definition qhash.h:254
Entry * entries
Definition qhash.h:255
Node & atOffset(size_t o) noexcept
Definition qhash.h:325
size_t offset(size_t i) const noexcept
Definition qhash.h:303
Node * insert(size_t i)
Definition qhash.h:279
bool hasNode(size_t i) const noexcept
Definition qhash.h:307
void moveFromSpan(Span &fromSpan, size_t fromIndex, size_t to) noexcept(std::is_nothrow_move_constructible_v< Node >)
Definition qhash.h:344
const Node & atOffset(size_t o) const noexcept
Definition qhash.h:331
Node & at(size_t i) noexcept
Definition qhash.h:311
Node * node() const noexcept
Definition qhash.h:788
size_t span() const noexcept
Definition qhash.h:784
iterator operator++() noexcept
Definition qhash.h:795
size_t index() const noexcept
Definition qhash.h:785
const Data< Node > * d
Definition qhash.h:781
bool isUnused() const noexcept
Definition qhash.h:786
bool operator!=(iterator other) const noexcept
Definition qhash.h:811
bool atEnd() const noexcept
Definition qhash.h:793
bool operator==(iterator other) const noexcept
Definition qhash.h:809
static Q_CORE_EXPORT QHashSeed globalSeed() noexcept
\threadsafe
Definition qhash.cpp:1216