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