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