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