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