QtBase  v6.3.1
qhash.h
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2020 The Qt Company Ltd.
4 ** Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of the QtCore module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU Lesser General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU Lesser
20 ** General Public License version 3 as published by the Free Software
21 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
22 ** packaging of this file. Please review the following information to
23 ** ensure the GNU Lesser General Public License version 3 requirements
24 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25 **
26 ** GNU General Public License Usage
27 ** Alternatively, this file may be used under the terms of the GNU
28 ** General Public License version 2.0 or (at your option) the GNU General
29 ** Public license version 3 or any later version approved by the KDE Free
30 ** Qt Foundation. The licenses are as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32 ** included in the packaging of this file. Please review the following
33 ** information to ensure the GNU General Public License requirements will
34 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35 ** https://www.gnu.org/licenses/gpl-3.0.html.
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40 
41 #ifndef QHASH_H
42 #define QHASH_H
43 
44 #include <QtCore/qcontainertools_impl.h>
45 #include <QtCore/qhashfunctions.h>
46 #include <QtCore/qiterator.h>
47 #include <QtCore/qlist.h>
48 #include <QtCore/qmath.h>
49 #include <QtCore/qrefcount.h>
50 
51 #include <initializer_list>
52 #include <functional> // for std::hash
53 
54 class tst_QHash; // for befriending
55 
57 
59 {
60  bool operator==(const QHashDummyValue &) const noexcept { return true; }
61 };
62 
63 namespace QHashPrivate {
64 
65 template <typename T, typename = void>
66 constexpr inline bool HasQHashOverload = false;
67 
68 template <typename T>
69 constexpr inline bool HasQHashOverload<T, std::enable_if_t<
70  std::is_convertible_v<decltype(qHash(std::declval<const T &>(), std::declval<size_t>())), size_t>
71 >> = true;
72 
73 template <typename T, typename = void>
74 constexpr inline bool HasStdHashSpecializationWithSeed = false;
75 
76 template <typename T>
77 constexpr inline bool HasStdHashSpecializationWithSeed<T, std::enable_if_t<
78  std::is_convertible_v<decltype(std::hash<T>()(std::declval<const T &>(), std::declval<size_t>())), size_t>
79 >> = true;
80 
81 template <typename T, typename = void>
82 constexpr inline bool HasStdHashSpecializationWithoutSeed = false;
83 
84 template <typename T>
85 constexpr inline bool HasStdHashSpecializationWithoutSeed<T, std::enable_if_t<
86  std::is_convertible_v<decltype(std::hash<T>()(std::declval<const T &>())), size_t>
87 >> = true;
88 
89 template <typename T>
90 size_t calculateHash(const T &t, size_t seed = 0)
91 {
92  if constexpr (HasQHashOverload<T>) {
93  return qHash(t, seed);
94  } else if constexpr (HasStdHashSpecializationWithSeed<T>) {
95  return std::hash<T>()(t, seed);
96  } else if constexpr (HasStdHashSpecializationWithoutSeed<T>) {
97  Q_UNUSED(seed);
98  return std::hash<T>()(t);
99  } else {
100  static_assert(sizeof(T) == 0, "The key type must have a qHash overload or a std::hash specialization");
101  return 0;
102  }
103 }
104 
105 template <typename Key, typename T>
106 struct Node
107 {
108  using KeyType = Key;
109  using ValueType = T;
110 
111  Key key;
113  template<typename ...Args>
114  static void createInPlace(Node *n, Key &&k, Args &&... args)
115  { new (n) Node{ std::move(k), T(std::forward<Args>(args)...) }; }
116  template<typename ...Args>
117  static void createInPlace(Node *n, const Key &k, Args &&... args)
118  { new (n) Node{ Key(k), T(std::forward<Args>(args)...) }; }
119  template<typename ...Args>
120  void emplaceValue(Args &&... args)
121  {
122  value = T(std::forward<Args>(args)...);
123  }
124  T &&takeValue() noexcept(std::is_nothrow_move_assignable_v<T>)
125  {
126  return std::move(value);
127  }
128  bool valuesEqual(const Node *other) const { return value == other->value; }
129 };
130 
131 template <typename Key>
133  using KeyType = Key;
135 
136  Key key;
137  template<typename ...Args>
138  static void createInPlace(Node *n, Key &&k, Args &&...)
139  { new (n) Node{ std::move(k) }; }
140  template<typename ...Args>
141  static void createInPlace(Node *n, const Key &k, Args &&...)
142  { new (n) Node{ k }; }
143  template<typename ...Args>
144  void emplaceValue(Args &&...)
145  {
146  }
148  bool valuesEqual(const Node *) const { return true; }
149 };
150 
151 template <typename T>
153 {
155  MultiNodeChain *next = nullptr;
157  {
158  }
159  qsizetype free() noexcept(std::is_nothrow_destructible_v<T>)
160  {
161  qsizetype nEntries = 0;
162  MultiNodeChain *e = this;
163  while (e) {
164  MultiNodeChain *n = e->next;
165  ++nEntries;
166  delete e;
167  e = n;
168  }
169  return nEntries;
170  }
171  bool contains(const T &val) const noexcept
172  {
173  const MultiNodeChain *e = this;
174  while (e) {
175  if (e->value == val)
176  return true;
177  e = e->next;
178  }
179  return false;
180  }
181 };
182 
183 template <typename Key, typename T>
184 struct MultiNode
185 {
186  using KeyType = Key;
187  using ValueType = T;
189 
190  Key key;
192 
193  template<typename ...Args>
194  static void createInPlace(MultiNode *n, Key &&k, Args &&... args)
195  { new (n) MultiNode(std::move(k), new Chain{ T(std::forward<Args>(args)...), nullptr }); }
196  template<typename ...Args>
197  static void createInPlace(MultiNode *n, const Key &k, Args &&... args)
198  { new (n) MultiNode(k, new Chain{ T(std::forward<Args>(args)...), nullptr }); }
199 
200  MultiNode(const Key &k, Chain *c)
201  : key(k),
202  value(c)
203  {}
204  MultiNode(Key &&k, Chain *c) noexcept(std::is_nothrow_move_assignable_v<Key>)
205  : key(std::move(k)),
206  value(c)
207  {}
208 
210  : key(other.key),
211  value(qExchange(other.value, nullptr))
212  {
213  }
214 
216  : key(other.key)
217  {
218  Chain *c = other.value;
219  Chain **e = &value;
220  while (c) {
221  Chain *chain = new Chain{ c->value, nullptr };
222  *e = chain;
223  e = &chain->next;
224  c = c->next;
225  }
226  }
228  {
229  if (value)
230  value->free();
231  }
232  static qsizetype freeChain(MultiNode *n) noexcept(std::is_nothrow_destructible_v<T>)
233  {
234  qsizetype size = n->value->free();
235  n->value = nullptr;
236  return size;
237  }
238  template<typename ...Args>
239  void insertMulti(Args &&... args)
240  {
241  Chain *e = new Chain{ T(std::forward<Args>(args)...), nullptr };
242  e->next = qExchange(value, e);
243  }
244  template<typename ...Args>
245  void emplaceValue(Args &&... args)
246  {
247  value->value = T(std::forward<Args>(args)...);
248  }
249 };
250 
251 template<typename Node>
252 constexpr bool isRelocatable()
253 {
255 }
256 
257 // Regular hash tables consist of a list of buckets that can store Nodes. But simply allocating one large array of buckets
258 // would waste a lot of memory. To avoid this, we split the vector of buckets up into a vector of Spans. Each Span represents
259 // NEntries buckets. To quickly find the correct Span that holds a bucket, NEntries must be a power of two.
260 //
261 // Inside each Span, there is an offset array that represents the actual buckets. offsets contains either an index into the
262 // actual storage space for the Nodes (the 'entries' member) or 0xff (UnusedEntry) to flag that the bucket is empty.
263 // As we have only 128 entries per Span, the offset array can be represented using an unsigned char. This trick makes the hash
264 // table have a very small memory overhead compared to many other implementations.
265 template<typename Node>
266 struct Span {
267  enum {
268  NEntries = 128,
270  UnusedEntry = 0xff
271  };
272  static_assert ((NEntries & LocalBucketMask) == 0, "EntriesPerSpan must be a power of two.");
273 
274  // Entry is a slot available for storing a Node. The Span holds a pointer to
275  // an array of Entries. Upon construction of the array, those entries are
276  // unused, and nextFree() is being used to set up a singly linked list
277  // of free entries.
278  // When a node gets inserted, the first free entry is being picked, removed
279  // from the singly linked list and the Node gets constructed in place.
280  struct Entry {
281  struct { alignas(Node) unsigned char data[sizeof(Node)]; } storage;
282 
283  unsigned char &nextFree() { return *reinterpret_cast<unsigned char *>(&storage); }
284  Node &node() { return *reinterpret_cast<Node *>(&storage); }
285  };
286 
287  unsigned char offsets[NEntries];
288  Entry *entries = nullptr;
289  unsigned char allocated = 0;
290  unsigned char nextFree = 0;
291  Span() noexcept
292  {
293  memset(offsets, UnusedEntry, sizeof(offsets));
294  }
296  {
297  freeData();
298  }
299  void freeData() noexcept(std::is_nothrow_destructible<Node>::value)
300  {
301  if (entries) {
303  for (auto o : offsets) {
304  if (o != UnusedEntry)
305  entries[o].node().~Node();
306  }
307  }
308  delete[] entries;
309  entries = nullptr;
310  }
311  }
312  Node *insert(size_t i)
313  {
314  Q_ASSERT(i < NEntries);
316  if (nextFree == allocated)
317  addStorage();
318  unsigned char entry = nextFree;
321  offsets[i] = entry;
322  return &entries[entry].node();
323  }
324  void erase(size_t bucket) noexcept(std::is_nothrow_destructible<Node>::value)
325  {
326  Q_ASSERT(bucket < NEntries);
327  Q_ASSERT(offsets[bucket] != UnusedEntry);
328 
329  unsigned char entry = offsets[bucket];
330  offsets[bucket] = UnusedEntry;
331 
332  entries[entry].node().~Node();
334  nextFree = entry;
335  }
336  size_t offset(size_t i) const noexcept
337  {
338  return offsets[i];
339  }
340  bool hasNode(size_t i) const noexcept
341  {
342  return (offsets[i] != UnusedEntry);
343  }
344  Node &at(size_t i) noexcept
345  {
346  Q_ASSERT(i < NEntries);
348 
349  return entries[offsets[i]].node();
350  }
351  const Node &at(size_t i) const noexcept
352  {
353  Q_ASSERT(i < NEntries);
355 
356  return entries[offsets[i]].node();
357  }
358  Node &atOffset(size_t o) noexcept
359  {
360  Q_ASSERT(o < allocated);
361 
362  return entries[o].node();
363  }
364  const Node &atOffset(size_t o) const noexcept
365  {
366  Q_ASSERT(o < allocated);
367 
368  return entries[o].node();
369  }
370  void moveLocal(size_t from, size_t to) noexcept
371  {
372  Q_ASSERT(offsets[from] != UnusedEntry);
373  Q_ASSERT(offsets[to] == UnusedEntry);
374  offsets[to] = offsets[from];
375  offsets[from] = UnusedEntry;
376  }
377  void moveFromSpan(Span &fromSpan, size_t fromIndex, size_t to) noexcept(std::is_nothrow_move_constructible_v<Node>)
378  {
379  Q_ASSERT(to < NEntries);
380  Q_ASSERT(offsets[to] == UnusedEntry);
381  Q_ASSERT(fromIndex < NEntries);
382  Q_ASSERT(fromSpan.offsets[fromIndex] != UnusedEntry);
383  if (nextFree == allocated)
384  addStorage();
386  offsets[to] = nextFree;
387  Entry &toEntry = entries[nextFree];
388  nextFree = toEntry.nextFree();
389 
390  size_t fromOffset = fromSpan.offsets[fromIndex];
391  fromSpan.offsets[fromIndex] = UnusedEntry;
392  Entry &fromEntry = fromSpan.entries[fromOffset];
393 
394  if constexpr (isRelocatable<Node>()) {
395  memcpy(&toEntry, &fromEntry, sizeof(Entry));
396  } else {
397  new (&toEntry.node()) Node(std::move(fromEntry.node()));
398  fromEntry.node().~Node();
399  }
400  fromEntry.nextFree() = fromSpan.nextFree;
401  fromSpan.nextFree = static_cast<unsigned char>(fromOffset);
402  }
403 
404  void addStorage()
405  {
408  // the hash table should always be between 25 and 50% full
409  // this implies that we on average have between 32 and 64 entries
410  // in here. The likelihood of having below 16 entries is very small,
411  // so start with that and increment by 16 each time we need to add
412  // some more space
413  const size_t increment = NEntries / 8;
414  size_t alloc = allocated + increment;
415  Entry *newEntries = new Entry[alloc];
416  // we only add storage if the previous storage was fully filled, so
417  // simply copy the old data over
418  if constexpr (isRelocatable<Node>()) {
419  if (allocated)
420  memcpy(newEntries, entries, allocated * sizeof(Entry));
421  } else {
422  for (size_t i = 0; i < allocated; ++i) {
423  new (&newEntries[i].node()) Node(std::move(entries[i].node()));
424  entries[i].node().~Node();
425  }
426  }
427  for (size_t i = allocated; i < allocated + increment; ++i) {
428  newEntries[i].nextFree() = uchar(i + 1);
429  }
430  delete[] entries;
431  entries = newEntries;
432  allocated = uchar(alloc);
433  }
434 };
435 
436 // QHash uses a power of two growth policy.
437 namespace GrowthPolicy {
438 inline constexpr size_t maxNumBuckets() noexcept
439 {
440  // ensure the size of a Span does not depend on the template parameters
441  using Node1 = Node<int, int>;
442  using Node2 = Node<char, void *>;
443  using Node3 = Node<qsizetype, QHashDummyValue>;
444  static_assert(sizeof(Span<Node1>) == sizeof(Span<Node2>));
445  static_assert(sizeof(Span<Node1>) == sizeof(Span<Node3>));
446  static_assert(int(Span<Node1>::NEntries) == int(Span<Node2>::NEntries));
447  static_assert(int(Span<Node1>::NEntries) == int(Span<Node3>::NEntries));
448 
449  // Maximum is 2^31-1 or 2^63-1 bytes (limited by qsizetype and ptrdiff_t)
450  size_t max = (std::numeric_limits<ptrdiff_t>::max)();
451  return max / sizeof(Span<Node1>) * Span<Node1>::NEntries;
452 }
453 inline constexpr size_t bucketsForCapacity(size_t requestedCapacity) noexcept
454 {
455  if (requestedCapacity <= 8)
456  return 16;
457  if (requestedCapacity >= maxNumBuckets())
458  return maxNumBuckets();
459  return qNextPowerOfTwo(QIntegerForSize<sizeof(size_t)>::Unsigned(2 * requestedCapacity - 1));
460 }
461 inline constexpr size_t bucketForHash(size_t nBuckets, size_t hash) noexcept
462 {
463  return hash & (nBuckets - 1);
464 }
465 } // namespace GrowthPolicy
466 
467 template <typename Node>
468 struct iterator;
469 
470 template <typename Node>
471 struct Data
472 {
473  using Key = typename Node::KeyType;
474  using T = typename Node::ValueType;
477 
479  size_t size = 0;
480  size_t numBuckets = 0;
481  size_t seed = 0;
482 
483 
484  Span *spans = nullptr;
485 
486  Data(size_t reserve = 0)
487  {
489  size_t nSpans = (numBuckets + Span::LocalBucketMask) / Span::NEntries;
490  spans = new Span[nSpans];
492  }
493 
494  void reallocationHelper(const Data &other, size_t nSpans, bool resized)
495  {
496  for (size_t s = 0; s < nSpans; ++s) {
497  const Span &span = other.spans[s];
498  for (size_t index = 0; index < Span::NEntries; ++index) {
499  if (!span.hasNode(index))
500  continue;
501  const Node &n = span.at(index);
502  iterator it = resized ? find(n.key) : iterator { this, s * Span::NEntries + index };
503  Q_ASSERT(it.isUnused());
504  Node *newNode = spans[it.span()].insert(it.index());
505  new (newNode) Node(n);
506  }
507  }
508  }
509 
511  {
512  size_t nSpans = (numBuckets + Span::LocalBucketMask) / Span::NEntries;
513  spans = new Span[nSpans];
514  reallocationHelper(other, nSpans, false);
515  }
516  Data(const Data &other, size_t reserved) : size(other.size), seed(other.seed)
517  {
519  size_t nSpans = (numBuckets + Span::LocalBucketMask) / Span::NEntries;
520  spans = new Span[nSpans];
521  size_t otherNSpans = (other.numBuckets + Span::LocalBucketMask) / Span::NEntries;
522  reallocationHelper(other, otherNSpans, true);
523  }
524 
525  static Data *detached(Data *d)
526  {
527  if (!d)
528  return new Data;
529  Data *dd = new Data(*d);
530  if (!d->ref.deref())
531  delete d;
532  return dd;
533  }
534  static Data *detached(Data *d, size_t size)
535  {
536  if (!d)
537  return new Data(size);
538  Data *dd = new Data(*d, size);
539  if (!d->ref.deref())
540  delete d;
541  return dd;
542  }
543 
544  void clear()
545  {
546  delete[] spans;
547  spans = nullptr;
548  size = 0;
549  numBuckets = 0;
550  }
551 
553  {
554  return iterator{this, other.bucket};
555  }
556 
557  iterator begin() const noexcept
558  {
559  iterator it{ this, 0 };
560  if (it.isUnused())
561  ++it;
562  return it;
563  }
564 
565  constexpr iterator end() const noexcept
566  {
567  return iterator();
568  }
569 
570  void rehash(size_t sizeHint = 0)
571  {
572  if (sizeHint == 0)
573  sizeHint = size;
574  size_t newBucketCount = GrowthPolicy::bucketsForCapacity(sizeHint);
575 
576  Span *oldSpans = spans;
577  size_t oldBucketCount = numBuckets;
578  size_t nSpans = (newBucketCount + Span::LocalBucketMask) / Span::NEntries;
579  spans = new Span[nSpans];
580  numBuckets = newBucketCount;
581  size_t oldNSpans = (oldBucketCount + Span::LocalBucketMask) / Span::NEntries;
582 
583  for (size_t s = 0; s < oldNSpans; ++s) {
584  Span &span = oldSpans[s];
585  for (size_t index = 0; index < Span::NEntries; ++index) {
586  if (!span.hasNode(index))
587  continue;
588  Node &n = span.at(index);
589  iterator it = find(n.key);
590  Q_ASSERT(it.isUnused());
591  Node *newNode = spans[it.span()].insert(it.index());
592  new (newNode) Node(std::move(n));
593  }
594  span.freeData();
595  }
596  delete[] oldSpans;
597  }
598 
599  size_t nextBucket(size_t bucket) const noexcept
600  {
601  ++bucket;
602  if (bucket == numBuckets)
603  bucket = 0;
604  return bucket;
605  }
606 
607  float loadFactor() const noexcept
608  {
609  return float(size)/numBuckets;
610  }
611  bool shouldGrow() const noexcept
612  {
613  return size >= (numBuckets >> 1);
614  }
615 
616  iterator find(const Key &key) const noexcept
617  {
618  Q_ASSERT(numBuckets > 0);
620  size_t bucket = GrowthPolicy::bucketForHash(numBuckets, hash);
621  // loop over the buckets until we find the entry we search for
622  // or an empty slot, in which case we know the entry doesn't exist
623  while (true) {
624  // Split the bucket into the indexex of span array, and the local
625  // offset inside the span
626  size_t span = bucket / Span::NEntries;
627  size_t index = bucket & Span::LocalBucketMask;
628  Span &s = spans[span];
629  size_t offset = s.offset(index);
630  if (offset == Span::UnusedEntry) {
631  return iterator{ this, bucket };
632  } else {
633  Node &n = s.atOffset(offset);
634  if (qHashEquals(n.key, key))
635  return iterator{ this, bucket };
636  }
637  bucket = nextBucket(bucket);
638  }
639  }
640 
641  Node *findNode(const Key &key) const noexcept
642  {
643  if (!size)
644  return nullptr;
645  iterator it = find(key);
646  if (it.isUnused())
647  return nullptr;
648  return it.node();
649  }
650 
652  {
655  };
656 
658  {
659  iterator it;
660  if (numBuckets > 0) {
661  it = find(key);
662  if (!it.isUnused())
663  return { it, true };
664  }
665  if (shouldGrow()) {
666  rehash(size + 1);
667  it = find(key); // need to get a new iterator after rehashing
668  }
669  Q_ASSERT(it.d);
670  Q_ASSERT(it.isUnused());
671  spans[it.span()].insert(it.index());
672  ++size;
673  return { it, false };
674  }
675 
677  {
678  size_t bucket = it.bucket;
679  size_t span = bucket / Span::NEntries;
680  size_t index = bucket & Span::LocalBucketMask;
681  Q_ASSERT(spans[span].hasNode(index));
682  spans[span].erase(index);
683  --size;
684 
685  // re-insert the following entries to avoid holes
686  size_t hole = bucket;
687  size_t next = bucket;
688  while (true) {
689  next = nextBucket(next);
690  size_t nextSpan = next / Span::NEntries;
691  size_t nextIndex = next & Span::LocalBucketMask;
692  if (!spans[nextSpan].hasNode(nextIndex))
693  break;
694  size_t hash = QHashPrivate::calculateHash(spans[nextSpan].at(nextIndex).key, seed);
695  size_t newBucket = GrowthPolicy::bucketForHash(numBuckets, hash);
696  while (true) {
697  if (newBucket == next) {
698  // nothing to do, item is at the right plae
699  break;
700  } else if (newBucket == hole) {
701  // move into hole
702  size_t holeSpan = hole / Span::NEntries;
703  size_t holeIndex = hole & Span::LocalBucketMask;
704  if (nextSpan == holeSpan) {
705  spans[holeSpan].moveLocal(nextIndex, holeIndex);
706  } else {
707  // move between spans, more expensive
708  spans[holeSpan].moveFromSpan(spans[nextSpan], nextIndex, holeIndex);
709  }
710  hole = next;
711  break;
712  }
713  newBucket = nextBucket(newBucket);
714  }
715  }
716 
717  // return correct position of the next element
718  if (bucket == numBuckets - 1 || !spans[span].hasNode(index))
719  ++it;
720  return it;
721  }
722 
724  {
725  delete [] spans;
726  }
727 };
728 
729 template <typename Node>
730 struct iterator {
732 
733  const Data<Node> *d = nullptr;
734  size_t bucket = 0;
735 
736  size_t span() const noexcept { return bucket / Span::NEntries; }
737  size_t index() const noexcept { return bucket & Span::LocalBucketMask; }
738  inline bool isUnused() const noexcept { return !d->spans[span()].hasNode(index()); }
739 
740  inline Node *node() const noexcept
741  {
742  Q_ASSERT(!isUnused());
743  return &d->spans[span()].at(index());
744  }
745  bool atEnd() const noexcept { return !d; }
746 
747  iterator operator++() noexcept
748  {
749  while (true) {
750  ++bucket;
751  if (bucket == d->numBuckets) {
752  d = nullptr;
753  bucket = 0;
754  break;
755  }
756  if (!isUnused())
757  break;
758  }
759  return *this;
760  }
761  bool operator==(iterator other) const noexcept
762  { return d == other.d && bucket == other.bucket; }
763  bool operator!=(iterator other) const noexcept
764  { return !(*this == other); }
765 };
766 
767 
768 
769 } // namespace QHashPrivate
770 
771 template <typename Key, typename T>
772 class QHash
773 {
776  friend class QSet<Key>;
777  friend class QMultiHash<Key, T>;
778  friend tst_QHash;
779 
780  Data *d = nullptr;
781 
782 public:
783  using key_type = Key;
784  using mapped_type = T;
785  using value_type = T;
788  using reference = T &;
789  using const_reference = const T &;
790 
791  inline QHash() noexcept = default;
792  inline QHash(std::initializer_list<std::pair<Key,T> > list)
793  : d(new Data(list.size()))
794  {
795  for (typename std::initializer_list<std::pair<Key,T> >::const_iterator it = list.begin(); it != list.end(); ++it)
796  insert(it->first, it->second);
797  }
798  QHash(const QHash &other) noexcept
799  : d(other.d)
800  {
801  if (d)
802  d->ref.ref();
803  }
805  {
806  static_assert(std::is_nothrow_destructible_v<Key>, "Types with throwing destructors are not supported in Qt containers.");
807  static_assert(std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers.");
808 
809  if (d && !d->ref.deref())
810  delete d;
811  }
812 
814  {
815  if (d != other.d) {
816  Data *o = other.d;
817  if (o)
818  o->ref.ref();
819  if (d && !d->ref.deref())
820  delete d;
821  d = o;
822  }
823  return *this;
824  }
825 
826  QHash(QHash &&other) noexcept
827  : d(std::exchange(other.d, nullptr))
828  {
829  }
831 #ifdef Q_QDOC
832  template <typename InputIterator>
833  QHash(InputIterator f, InputIterator l);
834 #else
835  template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasKeyAndValue<InputIterator> = true>
836  QHash(InputIterator f, InputIterator l)
837  : QHash()
838  {
840  for (; f != l; ++f)
841  insert(f.key(), f.value());
842  }
843 
844  template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasFirstAndSecond<InputIterator> = true>
845  QHash(InputIterator f, InputIterator l)
846  : QHash()
847  {
849  for (; f != l; ++f)
850  insert(f->first, f->second);
851  }
852 #endif
853  void swap(QHash &other) noexcept { qt_ptr_swap(d, other.d); }
854 
855 #ifndef Q_CLANG_QDOC
856  template <typename AKey = Key, typename AT = T>
858  {
859  if (d == other.d)
860  return true;
861  if (size() != other.size())
862  return false;
863 
864  for (const_iterator it = other.begin(); it != other.end(); ++it) {
865  const_iterator i = find(it.key());
866  if (i == end() || !i.i.node()->valuesEqual(it.i.node()))
867  return false;
868  }
869  // all values must be the same as size is the same
870  return true;
871  }
872  template <typename AKey = Key, typename AT = T>
874  { return !(*this == other); }
875 #else
876  bool operator==(const QHash &other) const;
877  bool operator!=(const QHash &other) const;
878 #endif // Q_CLANG_QDOC
879 
880  inline qsizetype size() const noexcept { return d ? qsizetype(d->size) : 0; }
881  inline bool isEmpty() const noexcept { return !d || d->size == 0; }
882 
883  inline qsizetype capacity() const noexcept { return d ? qsizetype(d->numBuckets >> 1) : 0; }
885  {
886  // reserve(0) is used in squeeze()
887  if (size && (this->capacity() >= size))
888  return;
889  if (isDetached())
890  d->rehash(size);
891  else
892  d = Data::detached(d, size_t(size));
893  }
894  inline void squeeze()
895  {
896  if (capacity())
897  reserve(0);
898  }
899 
900  inline void detach() { if (!d || d->ref.isShared()) d = Data::detached(d); }
901  inline bool isDetached() const noexcept { return d && !d->ref.isShared(); }
902  bool isSharedWith(const QHash &other) const noexcept { return d == other.d; }
903 
904  void clear() noexcept(std::is_nothrow_destructible<Node>::value)
905  {
906  if (d && !d->ref.deref())
907  delete d;
908  d = nullptr;
909  }
910 
911  bool remove(const Key &key)
912  {
913  if (isEmpty()) // prevents detaching shared null
914  return false;
915  auto it = d->find(key);
916  detach();
917  it = d->detachedIterator(it);
918 
919  if (it.isUnused())
920  return false;
921  d->erase(it);
922  return true;
923  }
924  template <typename Predicate>
926  {
927  return QtPrivate::associative_erase_if(*this, pred);
928  }
929  T take(const Key &key)
930  {
931  if (isEmpty()) // prevents detaching shared null
932  return T();
933  auto it = d->find(key);
934  detach();
935  it = d->detachedIterator(it);
936 
937  if (it.isUnused())
938  return T();
939  T value = it.node()->takeValue();
940  d->erase(it);
941  return value;
942  }
943 
944  bool contains(const Key &key) const noexcept
945  {
946  if (!d)
947  return false;
948  return d->findNode(key) != nullptr;
949  }
950  qsizetype count(const Key &key) const noexcept
951  {
952  return contains(key) ? 1 : 0;
953  }
954 
955 private:
956  const Key *keyImpl(const T &value) const noexcept
957  {
958  if (d) {
959  const_iterator i = begin();
960  while (i != end()) {
961  if (i.value() == value)
962  return &i.key();
963  ++i;
964  }
965  }
966 
967  return nullptr;
968  }
969 
970 public:
971  Key key(const T &value) const noexcept
972  {
973  if (auto *k = keyImpl(value))
974  return *k;
975  else
976  return Key();
977  }
978  Key key(const T &value, const Key &defaultKey) const noexcept
979  {
980  if (auto *k = keyImpl(value))
981  return *k;
982  else
983  return defaultKey;
984  }
985 
986 private:
987  T *valueImpl(const Key &key) const noexcept
988  {
989  if (d) {
990  Node *n = d->findNode(key);
991  if (n)
992  return &n->value;
993  }
994  return nullptr;
995  }
996 public:
997  T value(const Key &key) const noexcept
998  {
999  if (T *v = valueImpl(key))
1000  return *v;
1001  else
1002  return T();
1003  }
1004 
1005  T value(const Key &key, const T &defaultValue) const noexcept
1006  {
1007  if (T *v = valueImpl(key))
1008  return *v;
1009  else
1010  return defaultValue;
1011  }
1012 
1013  T &operator[](const Key &key)
1014  {
1015  const auto copy = isDetached() ? QHash() : *this; // keep 'key' alive across the detach
1016  detach();
1017  auto result = d->findOrInsert(key);
1018  Q_ASSERT(!result.it.atEnd());
1019  if (!result.initialized)
1020  Node::createInPlace(result.it.node(), key, T());
1021  return result.it.node()->value;
1022  }
1023 
1024  const T operator[](const Key &key) const noexcept
1025  {
1026  return value(key);
1027  }
1028 
1029  QList<Key> keys() const { return QList<Key>(keyBegin(), keyEnd()); }
1030  QList<Key> keys(const T &value) const
1031  {
1032  QList<Key> res;
1033  const_iterator i = begin();
1034  while (i != end()) {
1035  if (i.value() == value)
1036  res.append(i.key());
1037  ++i;
1038  }
1039  return res;
1040  }
1041  QList<T> values() const { return QList<T>(begin(), end()); }
1042 
1043  class const_iterator;
1044 
1045  class iterator
1046  {
1047  using piter = typename QHashPrivate::iterator<Node>;
1048  friend class const_iterator;
1049  friend class QHash<Key, T>;
1050  friend class QSet<Key>;
1051  piter i;
1052  explicit inline iterator(piter it) noexcept : i(it) { }
1053 
1054  public:
1055  typedef std::forward_iterator_tag iterator_category;
1057  typedef T value_type;
1058  typedef T *pointer;
1059  typedef T &reference;
1060 
1061  constexpr iterator() noexcept = default;
1062 
1063  inline const Key &key() const noexcept { return i.node()->key; }
1064  inline T &value() const noexcept { return i.node()->value; }
1065  inline T &operator*() const noexcept { return i.node()->value; }
1066  inline T *operator->() const noexcept { return &i.node()->value; }
1067  inline bool operator==(const iterator &o) const noexcept { return i == o.i; }
1068  inline bool operator!=(const iterator &o) const noexcept { return i != o.i; }
1069 
1070  inline iterator &operator++() noexcept
1071  {
1072  ++i;
1073  return *this;
1074  }
1075  inline iterator operator++(int) noexcept
1076  {
1077  iterator r = *this;
1078  ++i;
1079  return r;
1080  }
1081 
1082  inline bool operator==(const const_iterator &o) const noexcept { return i == o.i; }
1083  inline bool operator!=(const const_iterator &o) const noexcept { return i != o.i; }
1084  };
1085  friend class iterator;
1086 
1088  {
1089  using piter = typename QHashPrivate::iterator<Node>;
1090  friend class iterator;
1091  friend class QHash<Key, T>;
1092  friend class QSet<Key>;
1093  piter i;
1094  explicit inline const_iterator(piter it) : i(it) { }
1095 
1096  public:
1097  typedef std::forward_iterator_tag iterator_category;
1099  typedef T value_type;
1100  typedef const T *pointer;
1101  typedef const T &reference;
1102 
1103  constexpr const_iterator() noexcept = default;
1104  inline const_iterator(const iterator &o) noexcept : i(o.i) { }
1105 
1106  inline const Key &key() const noexcept { return i.node()->key; }
1107  inline const T &value() const noexcept { return i.node()->value; }
1108  inline const T &operator*() const noexcept { return i.node()->value; }
1109  inline const T *operator->() const noexcept { return &i.node()->value; }
1110  inline bool operator==(const const_iterator &o) const noexcept { return i == o.i; }
1111  inline bool operator!=(const const_iterator &o) const noexcept { return i != o.i; }
1112 
1113  inline const_iterator &operator++() noexcept
1114  {
1115  ++i;
1116  return *this;
1117  }
1118  inline const_iterator operator++(int) noexcept
1119  {
1120  const_iterator r = *this;
1121  ++i;
1122  return r;
1123  }
1124  };
1125  friend class const_iterator;
1126 
1128  {
1129  const_iterator i;
1130 
1131  public:
1134  typedef Key value_type;
1135  typedef const Key *pointer;
1136  typedef const Key &reference;
1137 
1138  key_iterator() noexcept = default;
1140 
1141  const Key &operator*() const noexcept { return i.key(); }
1142  const Key *operator->() const noexcept { return &i.key(); }
1143  bool operator==(key_iterator o) const noexcept { return i == o.i; }
1144  bool operator!=(key_iterator o) const noexcept { return i != o.i; }
1145 
1146  inline key_iterator &operator++() noexcept { ++i; return *this; }
1147  inline key_iterator operator++(int) noexcept { return key_iterator(i++);}
1148  const_iterator base() const noexcept { return i; }
1149  };
1150 
1153 
1154  // STL style
1155  inline iterator begin() { detach(); return iterator(d->begin()); }
1156  inline const_iterator begin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
1157  inline const_iterator cbegin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
1158  inline const_iterator constBegin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
1159  inline iterator end() noexcept { return iterator(); }
1160  inline const_iterator end() const noexcept { return const_iterator(); }
1161  inline const_iterator cend() const noexcept { return const_iterator(); }
1162  inline const_iterator constEnd() const noexcept { return const_iterator(); }
1163  inline key_iterator keyBegin() const noexcept { return key_iterator(begin()); }
1164  inline key_iterator keyEnd() const noexcept { return key_iterator(end()); }
1171 
1173  {
1174  Q_ASSERT(it != constEnd());
1175  detach();
1176  // ensure a valid iterator across the detach:
1177  iterator i = iterator{d->detachedIterator(it.i)};
1178 
1179  i.i = d->erase(i.i);
1180  return i;
1181  }
1182 
1184  {
1185  auto first = find(key);
1186  auto second = first;
1187  if (second != iterator())
1188  ++second;
1189  return qMakePair(first, second);
1190  }
1191 
1193  {
1194  auto first = find(key);
1195  auto second = first;
1196  if (second != iterator())
1197  ++second;
1198  return qMakePair(first, second);
1199  }
1200 
1203  inline qsizetype count() const noexcept { return d ? qsizetype(d->size) : 0; }
1205  {
1206  if (isEmpty()) // prevents detaching shared null
1207  return end();
1208  auto it = d->find(key);
1209  detach();
1210  it = d->detachedIterator(it);
1211  if (it.isUnused())
1212  it = d->end();
1213  return iterator(it);
1214  }
1215  const_iterator find(const Key &key) const noexcept
1216  {
1217  if (isEmpty())
1218  return end();
1219  auto it = d->find(key);
1220  if (it.isUnused())
1221  it = d->end();
1222  return const_iterator(it);
1223  }
1224  const_iterator constFind(const Key &key) const noexcept
1225  {
1226  return find(key);
1227  }
1228  iterator insert(const Key &key, const T &value)
1229  {
1230  return emplace(key, value);
1231  }
1232 
1233  void insert(const QHash &hash)
1234  {
1235  if (d == hash.d || !hash.d)
1236  return;
1237  if (!d) {
1238  *this = hash;
1239  return;
1240  }
1241 
1242  detach();
1243 
1244  for (auto it = hash.begin(); it != hash.end(); ++it)
1245  emplace(it.key(), it.value());
1246  }
1247 
1248  template <typename ...Args>
1249  iterator emplace(const Key &key, Args &&... args)
1250  {
1251  Key copy = key; // Needs to be explicit for MSVC 2019
1252  return emplace(std::move(copy), std::forward<Args>(args)...);
1253  }
1254 
1255  template <typename ...Args>
1257  {
1258  if (isDetached()) {
1259  if (d->shouldGrow()) // Construct the value now so that no dangling references are used
1260  return emplace_helper(std::move(key), T(std::forward<Args>(args)...));
1261  return emplace_helper(std::move(key), std::forward<Args>(args)...);
1262  }
1263  // else: we must detach
1264  const auto copy = *this; // keep 'args' alive across the detach/growth
1265  detach();
1266  return emplace_helper(std::move(key), std::forward<Args>(args)...);
1267  }
1268 
1269  float load_factor() const noexcept { return d ? d->loadFactor() : 0; }
1270  static float max_load_factor() noexcept { return 0.5; }
1271  size_t bucket_count() const noexcept { return d ? d->numBuckets : 0; }
1272  static size_t max_bucket_count() noexcept { return QHashPrivate::GrowthPolicy::maxNumBuckets(); }
1273 
1274  inline bool empty() const noexcept { return isEmpty(); }
1275 
1276 private:
1277  template <typename ...Args>
1278  iterator emplace_helper(Key &&key, Args &&... args)
1279  {
1280  auto result = d->findOrInsert(key);
1281  if (!result.initialized)
1282  Node::createInPlace(result.it.node(), std::move(key), std::forward<Args>(args)...);
1283  else
1284  result.it.node()->emplaceValue(std::forward<Args>(args)...);
1285  return iterator(result.it);
1286  }
1287 };
1288 
1289 
1290 
1291 template <typename Key, typename T>
1293 {
1297 
1298  Data *d = nullptr;
1299  qsizetype m_size = 0;
1300 
1301 public:
1302  using key_type = Key;
1303  using mapped_type = T;
1304  using value_type = T;
1307  using reference = T &;
1308  using const_reference = const T &;
1309 
1310  QMultiHash() noexcept = default;
1311  inline QMultiHash(std::initializer_list<std::pair<Key,T> > list)
1312  : d(new Data(list.size()))
1313  {
1314  for (typename std::initializer_list<std::pair<Key,T> >::const_iterator it = list.begin(); it != list.end(); ++it)
1315  insert(it->first, it->second);
1316  }
1317 #ifdef Q_QDOC
1318  template <typename InputIterator>
1319  QMultiHash(InputIterator f, InputIterator l);
1320 #else
1321  template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasKeyAndValue<InputIterator> = true>
1322  QMultiHash(InputIterator f, InputIterator l)
1323  {
1325  for (; f != l; ++f)
1326  insert(f.key(), f.value());
1327  }
1328 
1329  template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasFirstAndSecond<InputIterator> = true>
1330  QMultiHash(InputIterator f, InputIterator l)
1331  {
1333  for (; f != l; ++f)
1334  insert(f->first, f->second);
1335  }
1336 #endif
1337  QMultiHash(const QMultiHash &other) noexcept
1338  : d(other.d), m_size(other.m_size)
1339  {
1340  if (d)
1341  d->ref.ref();
1342  }
1344  {
1345  static_assert(std::is_nothrow_destructible_v<Key>, "Types with throwing destructors are not supported in Qt containers.");
1346  static_assert(std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers.");
1347 
1348  if (d && !d->ref.deref())
1349  delete d;
1350  }
1351 
1353  {
1354  if (d != other.d) {
1355  Data *o = other.d;
1356  if (o)
1357  o->ref.ref();
1358  if (d && !d->ref.deref())
1359  delete d;
1360  d = o;
1361  m_size = other.m_size;
1362  }
1363  return *this;
1364  }
1366  : d(qExchange(other.d, nullptr)),
1367  m_size(qExchange(other.m_size, 0))
1368  {
1369  }
1371  {
1372  QMultiHash moved(std::move(other));
1373  swap(moved);
1374  return *this;
1375  }
1376 
1377  explicit QMultiHash(const QHash<Key, T> &other)
1378  : QMultiHash(other.begin(), other.end())
1379  {}
1380 
1382  {
1383  unite(std::move(other));
1384  }
1385 
1386  void swap(QMultiHash &other) noexcept
1387  {
1388  qt_ptr_swap(d, other.d);
1389  std::swap(m_size, other.m_size);
1390  }
1391 
1392 #ifndef Q_CLANG_QDOC
1393  template <typename AKey = Key, typename AT = T>
1395  {
1396  if (d == other.d)
1397  return true;
1398  if (m_size != other.m_size)
1399  return false;
1400  if (m_size == 0)
1401  return true;
1402  // equal size, and both non-zero size => d pointers allocated for both
1403  Q_ASSERT(d);
1404  Q_ASSERT(other.d);
1405  if (d->size != other.d->size)
1406  return false;
1407  for (auto it = other.d->begin(); it != other.d->end(); ++it) {
1408  auto *n = d->findNode(it.node()->key);
1409  if (!n)
1410  return false;
1411  Chain *e = it.node()->value;
1412  while (e) {
1413  Chain *oe = n->value;
1414  while (oe) {
1415  if (oe->value == e->value)
1416  break;
1417  oe = oe->next;
1418  }
1419  if (!oe)
1420  return false;
1421  e = e->next;
1422  }
1423  }
1424  // all values must be the same as size is the same
1425  return true;
1426  }
1427  template <typename AKey = Key, typename AT = T>
1429  { return !(*this == other); }
1430 #else
1431  bool operator==(const QMultiHash &other) const;
1432  bool operator!=(const QMultiHash &other) const;
1433 #endif // Q_CLANG_QDOC
1434 
1435  inline qsizetype size() const noexcept { return m_size; }
1436 
1437  inline bool isEmpty() const noexcept { return !m_size; }
1438 
1439  inline qsizetype capacity() const noexcept { return d ? qsizetype(d->numBuckets >> 1) : 0; }
1441  {
1442  // reserve(0) is used in squeeze()
1443  if (size && (this->capacity() >= size))
1444  return;
1445  if (isDetached())
1446  d->rehash(size);
1447  else
1448  d = Data::detached(d, size_t(size));
1449  }
1450  inline void squeeze() { reserve(0); }
1451 
1452  inline void detach() { if (!d || d->ref.isShared()) d = Data::detached(d); }
1453  inline bool isDetached() const noexcept { return d && !d->ref.isShared(); }
1454  bool isSharedWith(const QMultiHash &other) const noexcept { return d == other.d; }
1455 
1456  void clear() noexcept(std::is_nothrow_destructible<Node>::value)
1457  {
1458  if (d && !d->ref.deref())
1459  delete d;
1460  d = nullptr;
1461  m_size = 0;
1462  }
1463 
1465  {
1466  if (isEmpty()) // prevents detaching shared null
1467  return 0;
1468  auto it = d->find(key);
1469  detach();
1470  it = d->detachedIterator(it);
1471 
1472  if (it.isUnused())
1473  return 0;
1474  qsizetype n = Node::freeChain(it.node());
1475  m_size -= n;
1476  Q_ASSERT(m_size >= 0);
1477  d->erase(it);
1478  return n;
1479  }
1480  template <typename Predicate>
1482  {
1483  return QtPrivate::associative_erase_if(*this, pred);
1484  }
1485  T take(const Key &key)
1486  {
1487  if (isEmpty()) // prevents detaching shared null
1488  return T();
1489  auto it = d->find(key);
1490  detach();
1491  it = d->detachedIterator(it);
1492 
1493  if (it.isUnused())
1494  return T();
1495  Chain *e = it.node()->value;
1496  Q_ASSERT(e);
1497  T t = std::move(e->value);
1498  if (e->next) {
1499  it.node()->value = e->next;
1500  delete e;
1501  } else {
1502  // erase() deletes the values.
1503  d->erase(it);
1504  }
1505  --m_size;
1506  Q_ASSERT(m_size >= 0);
1507  return t;
1508  }
1509 
1510  bool contains(const Key &key) const noexcept
1511  {
1512  if (!d)
1513  return false;
1514  return d->findNode(key) != nullptr;
1515  }
1516 
1517 private:
1518  const Key *keyImpl(const T &value) const noexcept
1519  {
1520  if (d) {
1521  auto i = d->begin();
1522  while (i != d->end()) {
1523  Chain *e = i.node()->value;
1524  if (e->contains(value))
1525  return &i.node()->key;
1526  ++i;
1527  }
1528  }
1529 
1530  return nullptr;
1531  }
1532 public:
1533  Key key(const T &value) const noexcept
1534  {
1535  if (auto *k = keyImpl(value))
1536  return *k;
1537  else
1538  return Key();
1539  }
1540  Key key(const T &value, const Key &defaultKey) const noexcept
1541  {
1542  if (auto *k = keyImpl(value))
1543  return *k;
1544  else
1545  return defaultKey;
1546  }
1547 
1548 private:
1549  T *valueImpl(const Key &key) const noexcept
1550  {
1551  if (d) {
1552  Node *n = d->findNode(key);
1553  if (n) {
1554  Q_ASSERT(n->value);
1555  return &n->value->value;
1556  }
1557  }
1558  return nullptr;
1559  }
1560 public:
1561  T value(const Key &key) const noexcept
1562  {
1563  if (auto *v = valueImpl(key))
1564  return *v;
1565  else
1566  return T();
1567  }
1568  T value(const Key &key, const T &defaultValue) const noexcept
1569  {
1570  if (auto *v = valueImpl(key))
1571  return *v;
1572  else
1573  return defaultValue;
1574  }
1575 
1576  T &operator[](const Key &key)
1577  {
1578  const auto copy = isDetached() ? QMultiHash() : *this; // keep 'key' alive across the detach
1579  detach();
1580  auto result = d->findOrInsert(key);
1581  Q_ASSERT(!result.it.atEnd());
1582  if (!result.initialized)
1583  Node::createInPlace(result.it.node(), key, T());
1584  return result.it.node()->value->value;
1585  }
1586 
1587  const T operator[](const Key &key) const noexcept
1588  {
1589  return value(key);
1590  }
1591 
1593  {
1594  QList<Key> res;
1595  if (d) {
1596  auto i = d->begin();
1597  while (i != d->end()) {
1598  res.append(i.node()->key);
1599  ++i;
1600  }
1601  }
1602  return res;
1603  }
1604 
1605  QList<Key> keys() const { return QList<Key>(keyBegin(), keyEnd()); }
1606  QList<Key> keys(const T &value) const
1607  {
1608  QList<Key> res;
1609  const_iterator i = begin();
1610  while (i != end()) {
1611  if (i.value() == value)
1612  res.append(i.key());
1613  ++i;
1614  }
1615  return res;
1616  }
1617  QList<T> values() const { return QList<T>(begin(), end()); }
1618  QList<T> values(const Key &key) const
1619  {
1620  QList<T> values;
1621  if (d) {
1622  Node *n = d->findNode(key);
1623  if (n) {
1624  Chain *e = n->value;
1625  while (e) {
1626  values.append(e->value);
1627  e = e->next;
1628  }
1629  }
1630  }
1631  return values;
1632  }
1633 
1634  class const_iterator;
1635 
1636  class iterator
1637  {
1638  using piter = typename QHashPrivate::iterator<Node>;
1639  friend class const_iterator;
1640  friend class QMultiHash<Key, T>;
1641  piter i;
1642  Chain **e = nullptr;
1643  explicit inline iterator(piter it, Chain **entry = nullptr) noexcept : i(it), e(entry)
1644  {
1645  if (!it.atEnd() && !e) {
1646  e = &it.node()->value;
1647  Q_ASSERT(e && *e);
1648  }
1649  }
1650 
1651  public:
1652  typedef std::forward_iterator_tag iterator_category;
1654  typedef T value_type;
1655  typedef T *pointer;
1656  typedef T &reference;
1657 
1658  constexpr iterator() noexcept = default;
1659 
1660  inline const Key &key() const noexcept { return i.node()->key; }
1661  inline T &value() const noexcept { return (*e)->value; }
1662  inline T &operator*() const noexcept { return (*e)->value; }
1663  inline T *operator->() const noexcept { return &(*e)->value; }
1664  inline bool operator==(const iterator &o) const noexcept { return e == o.e; }
1665  inline bool operator!=(const iterator &o) const noexcept { return e != o.e; }
1666 
1667  inline iterator &operator++() noexcept {
1668  Q_ASSERT(e && *e);
1669  e = &(*e)->next;
1670  Q_ASSERT(e);
1671  if (!*e) {
1672  ++i;
1673  e = i.atEnd() ? nullptr : &i.node()->value;
1674  }
1675  return *this;
1676  }
1677  inline iterator operator++(int) noexcept {
1678  iterator r = *this;
1679  ++(*this);
1680  return r;
1681  }
1682 
1683  inline bool operator==(const const_iterator &o) const noexcept { return e == o.e; }
1684  inline bool operator!=(const const_iterator &o) const noexcept { return e != o.e; }
1685  };
1686  friend class iterator;
1687 
1689  {
1690  using piter = typename QHashPrivate::iterator<Node>;
1691  friend class iterator;
1692  friend class QMultiHash<Key, T>;
1693  piter i;
1694  Chain **e = nullptr;
1695  explicit inline const_iterator(piter it, Chain **entry = nullptr) noexcept : i(it), e(entry)
1696  {
1697  if (!it.atEnd() && !e) {
1698  e = &it.node()->value;
1699  Q_ASSERT(e && *e);
1700  }
1701  }
1702 
1703  public:
1704  typedef std::forward_iterator_tag iterator_category;
1706  typedef T value_type;
1707  typedef const T *pointer;
1708  typedef const T &reference;
1709 
1710  constexpr const_iterator() noexcept = default;
1711  inline const_iterator(const iterator &o) noexcept : i(o.i), e(o.e) { }
1712 
1713  inline const Key &key() const noexcept { return i.node()->key; }
1714  inline T &value() const noexcept { return (*e)->value; }
1715  inline T &operator*() const noexcept { return (*e)->value; }
1716  inline T *operator->() const noexcept { return &(*e)->value; }
1717  inline bool operator==(const const_iterator &o) const noexcept { return e == o.e; }
1718  inline bool operator!=(const const_iterator &o) const noexcept { return e != o.e; }
1719 
1720  inline const_iterator &operator++() noexcept {
1721  Q_ASSERT(e && *e);
1722  e = &(*e)->next;
1723  Q_ASSERT(e);
1724  if (!*e) {
1725  ++i;
1726  e = i.atEnd() ? nullptr : &i.node()->value;
1727  }
1728  return *this;
1729  }
1730  inline const_iterator operator++(int) noexcept
1731  {
1732  const_iterator r = *this;
1733  ++(*this);
1734  return r;
1735  }
1736  };
1737  friend class const_iterator;
1738 
1740  {
1741  const_iterator i;
1742 
1743  public:
1746  typedef Key value_type;
1747  typedef const Key *pointer;
1748  typedef const Key &reference;
1749 
1750  key_iterator() noexcept = default;
1752 
1753  const Key &operator*() const noexcept { return i.key(); }
1754  const Key *operator->() const noexcept { return &i.key(); }
1755  bool operator==(key_iterator o) const noexcept { return i == o.i; }
1756  bool operator!=(key_iterator o) const noexcept { return i != o.i; }
1757 
1758  inline key_iterator &operator++() noexcept { ++i; return *this; }
1759  inline key_iterator operator++(int) noexcept { return key_iterator(i++);}
1760  const_iterator base() const noexcept { return i; }
1761  };
1762 
1765 
1766  // STL style
1767  inline iterator begin() { detach(); return iterator(d->begin()); }
1768  inline const_iterator begin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
1769  inline const_iterator cbegin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
1770  inline const_iterator constBegin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
1771  inline iterator end() noexcept { return iterator(); }
1772  inline const_iterator end() const noexcept { return const_iterator(); }
1773  inline const_iterator cend() const noexcept { return const_iterator(); }
1774  inline const_iterator constEnd() const noexcept { return const_iterator(); }
1775  inline key_iterator keyBegin() const noexcept { return key_iterator(begin()); }
1776  inline key_iterator keyEnd() const noexcept { return key_iterator(end()); }
1778  inline key_value_iterator keyValueEnd() noexcept { return key_value_iterator(end()); }
1783 
1785  {
1786  auto i = it.i;
1787  Chain **e = it.e;
1788  if (d->ref.isShared()) {
1789  // need to store iterator position before detaching
1790  qsizetype n = 0;
1791  Chain *entry = i.node()->value;
1792  while (entry != *it.e) {
1793  ++n;
1794  entry = entry->next;
1795  }
1796  Q_ASSERT(entry);
1797  detach_helper();
1798 
1799  i = d->detachedIterator(i);
1800  e = &i.node()->value;
1801  while (n) {
1802  e = &(*e)->next;
1803  --n;
1804  }
1805  Q_ASSERT(e && *e);
1806  }
1807  return iterator(i, e);
1808  }
1809 
1811  {
1812  Q_ASSERT(d);
1813  iterator iter = detach(it);
1814  iterator i = iter;
1815  Chain *e = *i.e;
1816  Chain *next = e->next;
1817  *i.e = next;
1818  delete e;
1819  if (!next) {
1820  if (i.e == &i.i.node()->value) {
1821  // last remaining entry, erase
1822  i = iterator(d->erase(i.i));
1823  } else {
1824  i = iterator(++iter.i);
1825  }
1826  }
1827  --m_size;
1828  Q_ASSERT(m_size >= 0);
1829  return i;
1830  }
1831 
1832  // more Qt
1835  inline qsizetype count() const noexcept { return size(); }
1837  {
1838  if (isEmpty())
1839  return end();
1840  auto it = d->find(key);
1841  detach();
1842  it = d->detachedIterator(it);
1843 
1844  if (it.isUnused())
1845  it = d->end();
1846  return iterator(it);
1847  }
1848  const_iterator find(const Key &key) const noexcept
1849  {
1850  return constFind(key);
1851  }
1852  const_iterator constFind(const Key &key) const noexcept
1853  {
1854  if (isEmpty())
1855  return end();
1856  auto it = d->find(key);
1857  if (it.isUnused())
1858  it = d->end();
1859  return const_iterator(it);
1860  }
1861  iterator insert(const Key &key, const T &value)
1862  {
1863  return emplace(key, value);
1864  }
1865 
1866  template <typename ...Args>
1867  iterator emplace(const Key &key, Args &&... args)
1868  {
1869  return emplace(Key(key), std::forward<Args>(args)...);
1870  }
1871 
1872  template <typename ...Args>
1874  {
1875  if (isDetached()) {
1876  if (d->shouldGrow()) // Construct the value now so that no dangling references are used
1877  return emplace_helper(std::move(key), T(std::forward<Args>(args)...));
1878  return emplace_helper(std::move(key), std::forward<Args>(args)...);
1879  }
1880  // else: we must detach
1881  const auto copy = *this; // keep 'args' alive across the detach/growth
1882  detach();
1883  return emplace_helper(std::move(key), std::forward<Args>(args)...);
1884  }
1885 
1886 
1887  float load_factor() const noexcept { return d ? d->loadFactor() : 0; }
1888  static float max_load_factor() noexcept { return 0.5; }
1889  size_t bucket_count() const noexcept { return d ? d->numBuckets : 0; }
1890  static size_t max_bucket_count() noexcept { return QHashPrivate::GrowthPolicy::maxNumBuckets(); }
1891 
1892  inline bool empty() const noexcept { return isEmpty(); }
1893 
1894  inline iterator replace(const Key &key, const T &value)
1895  {
1896  return emplaceReplace(key, value);
1897  }
1898 
1899  template <typename ...Args>
1901  {
1902  return emplaceReplace(Key(key), std::forward<Args>(args)...);
1903  }
1904 
1905  template <typename ...Args>
1907  {
1908  if (isDetached()) {
1909  if (d->shouldGrow()) // Construct the value now so that no dangling references are used
1910  return emplaceReplace_helper(std::move(key), T(std::forward<Args>(args)...));
1911  return emplaceReplace_helper(std::move(key), std::forward<Args>(args)...);
1912  }
1913  // else: we must detach
1914  const auto copy = *this; // keep 'args' alive across the detach/growth
1915  detach();
1916  return emplaceReplace_helper(std::move(key), std::forward<Args>(args)...);
1917  }
1918 
1920  { this->unite(other); return *this; }
1921  inline QMultiHash operator+(const QMultiHash &other) const
1922  { QMultiHash result = *this; result += other; return result; }
1923 
1924  bool contains(const Key &key, const T &value) const noexcept
1925  {
1926  if (isEmpty())
1927  return false;
1928  auto n = d->findNode(key);
1929  if (n == nullptr)
1930  return false;
1931  return n->value->contains(value);
1932  }
1933 
1934  qsizetype remove(const Key &key, const T &value)
1935  {
1936  if (isEmpty()) // prevents detaching shared null
1937  return 0;
1938  auto it = d->find(key);
1939  detach();
1940  it = d->detachedIterator(it);
1941 
1942  if (it.isUnused())
1943  return 0;
1944  qsizetype n = 0;
1945  Chain **e = &it.node()->value;
1946  while (*e) {
1947  Chain *entry = *e;
1948  if (entry->value == value) {
1949  *e = entry->next;
1950  delete entry;
1951  ++n;
1952  } else {
1953  e = &entry->next;
1954  }
1955  }
1956  if (!it.node()->value)
1957  d->erase(it);
1958  m_size -= n;
1959  Q_ASSERT(m_size >= 0);
1960  return n;
1961  }
1962 
1963  qsizetype count(const Key &key) const noexcept
1964  {
1965  if (!d)
1966  return 0;
1967  auto it = d->find(key);
1968  if (it.isUnused())
1969  return 0;
1970  qsizetype n = 0;
1971  Chain *e = it.node()->value;
1972  while (e) {
1973  ++n;
1974  e = e->next;
1975  }
1976 
1977  return n;
1978  }
1979 
1980  qsizetype count(const Key &key, const T &value) const noexcept
1981  {
1982  if (!d)
1983  return 0;
1984  auto it = d->find(key);
1985  if (it.isUnused())
1986  return 0;
1987  qsizetype n = 0;
1988  Chain *e = it.node()->value;
1989  while (e) {
1990  if (e->value == value)
1991  ++n;
1992  e = e->next;
1993  }
1994 
1995  return n;
1996  }
1997 
1998  iterator find(const Key &key, const T &value)
1999  {
2000  if (isEmpty())
2001  return end();
2002  const auto copy = isDetached() ? QMultiHash() : *this; // keep 'key'/'value' alive across the detach
2003  detach();
2004  auto it = constFind(key, value);
2005  return iterator(it.i, it.e);
2006  }
2007  const_iterator find(const Key &key, const T &value) const noexcept
2008  {
2009  return constFind(key, value);
2010  }
2011  const_iterator constFind(const Key &key, const T &value) const noexcept
2012  {
2015  while (i != end && i.key() == key) {
2016  if (i.value() == value)
2017  return i;
2018  ++i;
2019  }
2020  return end;
2021  }
2022 
2024  {
2025  if (isEmpty()) {
2026  *this = other;
2027  } else if (other.isEmpty()) {
2028  ;
2029  } else {
2030  QMultiHash copy(other);
2031  detach();
2032  for (auto cit = copy.cbegin(); cit != copy.cend(); ++cit)
2033  insert(cit.key(), *cit);
2034  }
2035  return *this;
2036  }
2037 
2039  {
2040  for (auto cit = other.cbegin(); cit != other.cend(); ++cit)
2041  insert(cit.key(), *cit);
2042  return *this;
2043  }
2044 
2046  {
2047  if (!other.isDetached()) {
2048  unite(other);
2049  return *this;
2050  }
2051  auto it = other.d->begin();
2052  for (const auto end = other.d->end(); it != end; ++it)
2053  emplace(std::move(it.node()->key), std::move(it.node()->takeValue()));
2054  other.clear();
2055  return *this;
2056  }
2057 
2059  {
2060  const auto copy = isDetached() ? QMultiHash() : *this; // keep 'key' alive across the detach
2061  detach();
2062  auto pair = qAsConst(*this).equal_range(key);
2063  return qMakePair(iterator(pair.first.i), iterator(pair.second.i));
2064  }
2065 
2067  {
2068  if (!d)
2069  return qMakePair(end(), end());
2070 
2071  auto it = d->find(key);
2072  if (it.isUnused())
2073  return qMakePair(end(), end());
2074  auto end = it;
2075  ++end;
2077  }
2078 
2079 private:
2080  void detach_helper()
2081  {
2082  if (!d) {
2083  d = new Data;
2084  return;
2085  }
2086  Data *dd = new Data(*d);
2087  if (!d->ref.deref())
2088  delete d;
2089  d = dd;
2090  }
2091 
2092  template<typename... Args>
2093  iterator emplace_helper(Key &&key, Args &&...args)
2094  {
2095  auto result = d->findOrInsert(key);
2096  if (!result.initialized)
2097  Node::createInPlace(result.it.node(), std::move(key), std::forward<Args>(args)...);
2098  else
2099  result.it.node()->insertMulti(std::forward<Args>(args)...);
2100  ++m_size;
2101  return iterator(result.it);
2102  }
2103 
2104  template<typename... Args>
2105  iterator emplaceReplace_helper(Key &&key, Args &&...args)
2106  {
2107  auto result = d->findOrInsert(key);
2108  if (!result.initialized) {
2109  Node::createInPlace(result.it.node(), std::move(key), std::forward<Args>(args)...);
2110  ++m_size;
2111  } else {
2112  result.it.node()->emplaceValue(std::forward<Args>(args)...);
2113  }
2114  return iterator(result.it);
2115  }
2116 };
2117 
2122 
2123 template <class Key, class T>
2124 size_t qHash(const QHash<Key, T> &key, size_t seed = 0)
2125  noexcept(noexcept(qHash(std::declval<Key&>())) && noexcept(qHash(std::declval<T&>())))
2126 {
2127  size_t hash = 0;
2128  for (auto it = key.begin(), end = key.end(); it != end; ++it) {
2129  QtPrivate::QHashCombine combine;
2130  size_t h = combine(seed, it.key());
2131  // use + to keep the result independent of the ordering of the keys
2132  hash += combine(h, it.value());
2133  }
2134  return hash;
2135 }
2136 
2137 template <class Key, class T>
2138 inline size_t qHash(const QMultiHash<Key, T> &key, size_t seed = 0)
2139  noexcept(noexcept(qHash(std::declval<Key&>())) && noexcept(qHash(std::declval<T&>())))
2140 {
2141  size_t hash = 0;
2142  for (auto it = key.begin(), end = key.end(); it != end; ++it) {
2143  QtPrivate::QHashCombine combine;
2144  size_t h = combine(seed, it.key());
2145  // use + to keep the result independent of the ordering of the keys
2146  hash += combine(h, it.value());
2147  }
2148  return hash;
2149 }
2150 
2151 template <typename Key, typename T, typename Predicate>
2153 {
2154  return QtPrivate::associative_erase_if(hash, pred);
2155 }
2156 
2157 template <typename Key, typename T, typename Predicate>
2159 {
2160  return QtPrivate::associative_erase_if(hash, pred);
2161 }
2162 
2164 
2165 #endif // QHASH_H
small capitals from c petite p scientific i
[1]
Definition: afcover.h:80
#define value
[5]
[0]
Definition: node.h:62
template< typename Predicate > qsizetype erase_if(QByteArray &ba, Predicate pred)
Definition: qbytearray.h:676
template< typename Enum > size_t qHash(QFlags< Enum > flags, size_t seed=0) noexcept
The QHash::const_iterator class provides an STL-style const iterator for QHash.
Definition: qhash.h:1088
const_iterator operator++(int) noexcept
Definition: qhash.h:1118
bool operator==(const const_iterator &o) const noexcept
Definition: qhash.h:1110
bool operator!=(const const_iterator &o) const noexcept
Definition: qhash.h:1111
const T & value() const noexcept
Definition: qhash.h:1107
const T & reference
Definition: qhash.h:1101
const T & operator*() const noexcept
Definition: qhash.h:1108
const T * pointer
Definition: qhash.h:1100
qptrdiff difference_type
Definition: qhash.h:1098
std::forward_iterator_tag iterator_category
Definition: qhash.h:1097
constexpr const_iterator() noexcept=default
const_iterator & operator++() noexcept
Definition: qhash.h:1113
const T * operator->() const noexcept
Definition: qhash.h:1109
const Key & key() const noexcept
Definition: qhash.h:1106
The QHash::iterator class provides an STL-style non-const iterator for QHash.
Definition: qhash.h:1046
bool operator==(const const_iterator &o) const noexcept
Definition: qhash.h:1082
bool operator!=(const const_iterator &o) const noexcept
Definition: qhash.h:1083
T & value() const noexcept
Definition: qhash.h:1064
constexpr iterator() noexcept=default
bool operator==(const iterator &o) const noexcept
Definition: qhash.h:1067
std::forward_iterator_tag iterator_category
Definition: qhash.h:1055
T * operator->() const noexcept
Definition: qhash.h:1066
T & operator*() const noexcept
Definition: qhash.h:1065
iterator & operator++() noexcept
Definition: qhash.h:1070
qptrdiff difference_type
Definition: qhash.h:1056
iterator operator++(int) noexcept
Definition: qhash.h:1075
bool operator!=(const iterator &o) const noexcept
Definition: qhash.h:1068
The QHash::key_iterator class provides an STL-style const iterator for QHash keys.
Definition: qhash.h:1128
key_iterator() noexcept=default
key_iterator & operator++() noexcept
Definition: qhash.h:1146
const Key & reference
Definition: qhash.h:1136
const_iterator base() const noexcept
Definition: qhash.h:1148
const Key * operator->() const noexcept
Definition: qhash.h:1142
bool operator!=(key_iterator o) const noexcept
Definition: qhash.h:1144
key_iterator operator++(int) noexcept
Definition: qhash.h:1147
const_iterator::iterator_category iterator_category
Definition: qhash.h:1132
bool operator==(key_iterator o) const noexcept
Definition: qhash.h:1143
qptrdiff difference_type
Definition: qhash.h:1133
const Key & operator*() const noexcept
Definition: qhash.h:1141
const Key * pointer
Definition: qhash.h:1135
The QHash class is a template class that provides a hash-table-based dictionary.
Definition: qhash.h:773
key_iterator keyEnd() const noexcept
Definition: qhash.h:1164
void squeeze()
Definition: qhash.h:894
qsizetype size_type
Definition: qhash.h:786
bool remove(const Key &key)
Definition: qhash.h:911
iterator begin()
Definition: qhash.h:1155
const_iterator cbegin() const noexcept
Definition: qhash.h:1157
qsizetype size() const noexcept
Definition: qhash.h:880
T & operator[](const Key &key)
Definition: qhash.h:1013
float load_factor() const noexcept
Definition: qhash.h:1269
~QHash()
Definition: qhash.h:804
QHash(QHash &&other) noexcept
Definition: qhash.h:826
QHash(InputIterator f, InputIterator l)
Definition: qhash.h:845
const_iterator constFind(const Key &key) const noexcept
Definition: qhash.h:1224
const_iterator begin() const noexcept
Definition: qhash.h:1156
const_iterator constEnd() const noexcept
Definition: qhash.h:1162
iterator find(const Key &key)
Definition: qhash.h:1204
key_value_iterator keyValueEnd()
Definition: qhash.h:1166
QList< T > values() const
Definition: qhash.h:1041
key_value_iterator keyValueBegin()
Definition: qhash.h:1165
void reserve(qsizetype size)
Definition: qhash.h:884
iterator emplace(const Key &key, Args &&... args)
Definition: qhash.h:1249
T value(const Key &key, const T &defaultValue) const noexcept
Definition: qhash.h:1005
const T operator[](const Key &key) const noexcept
Definition: qhash.h:1024
const_iterator constBegin() const noexcept
Definition: qhash.h:1158
T take(const Key &key)
Definition: qhash.h:929
void detach()
Definition: qhash.h:900
const_key_value_iterator constKeyValueEnd() const noexcept
Definition: qhash.h:1170
bool isDetached() const noexcept
Definition: qhash.h:901
QKeyValueIterator< const Key &, T &, iterator > key_value_iterator
The QHash::key_value_iterator typedef provides an STL-style iterator for QHash.
Definition: qhash.h:1152
friend class iterator
Definition: qhash.h:1085
iterator Iterator
Definition: qhash.h:1201
QKeyValueIterator< const Key &, const T &, const_iterator > const_key_value_iterator
The QHash::const_key_value_iterator typedef provides an STL-style const iterator for QHash.
Definition: qhash.h:1151
Key key(const T &value, const Key &defaultKey) const noexcept
Definition: qhash.h:978
static float max_load_factor() noexcept
Definition: qhash.h:1270
QList< Key > keys() const
Definition: qhash.h:1029
iterator erase(const_iterator it)
Definition: qhash.h:1172
qsizetype difference_type
Definition: qhash.h:787
bool contains(const Key &key) const noexcept
Definition: qhash.h:944
QTypeTraits::compare_eq_result_container< QHash, AKey, AT > operator==(const QHash &other) const noexcept
Definition: qhash.h:857
const_key_value_iterator keyValueEnd() const noexcept
Definition: qhash.h:1169
key_iterator keyBegin() const noexcept
Definition: qhash.h:1163
const_key_value_iterator keyValueBegin() const noexcept
Definition: qhash.h:1167
qsizetype count() const noexcept
Definition: qhash.h:1203
qsizetype removeIf(Predicate pred)
Definition: qhash.h:925
T value(const Key &key) const noexcept
Definition: qhash.h:997
void swap(QHash &other) noexcept
Definition: qhash.h:853
bool isSharedWith(const QHash &other) const noexcept
Definition: qhash.h:902
iterator end() noexcept
Definition: qhash.h:1159
QHash & operator=(const QHash &other) noexcept(std::is_nothrow_destructible< Node >::value)
Definition: qhash.h:813
size_t bucket_count() const noexcept
Definition: qhash.h:1271
void insert(const QHash &hash)
Definition: qhash.h:1233
friend class const_iterator
Definition: qhash.h:1125
const_iterator cend() const noexcept
Definition: qhash.h:1161
QHash(InputIterator f, InputIterator l)
Definition: qhash.h:836
QPair< const_iterator, const_iterator > equal_range(const Key &key) const noexcept
Definition: qhash.h:1192
const_iterator ConstIterator
Definition: qhash.h:1202
static size_t max_bucket_count() noexcept
Definition: qhash.h:1272
const_iterator find(const Key &key) const noexcept
Definition: qhash.h:1215
Key key(const T &value) const noexcept
Definition: qhash.h:971
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Definition: qhash.h:904
QList< Key > keys(const T &value) const
Definition: qhash.h:1030
QTypeTraits::compare_eq_result_container< QHash, AKey, AT > operator!=(const QHash &other) const noexcept
Definition: qhash.h:873
bool empty() const noexcept
Definition: qhash.h:1274
qsizetype count(const Key &key) const noexcept
Definition: qhash.h:950
qsizetype capacity() const noexcept
Definition: qhash.h:883
QPair< iterator, iterator > equal_range(const Key &key)
Definition: qhash.h:1183
Key key_type
Definition: qhash.h:783
bool isEmpty() const noexcept
Definition: qhash.h:881
const_iterator end() const noexcept
Definition: qhash.h:1160
QHash() noexcept=default
iterator emplace(Key &&key, Args &&... args)
Definition: qhash.h:1256
QHash(const QHash &other) noexcept
Definition: qhash.h:798
const_key_value_iterator constKeyValueBegin() const noexcept
Definition: qhash.h:1168
iterator insert(const Key &key, const T &value)
Definition: qhash.h:1228
Definition: qlist.h:108
The QMultiHash::const_iterator class provides an STL-style const iterator for QMultiHash.
Definition: qhash.h:1689
const_iterator & operator++() noexcept
Definition: qhash.h:1720
T & value() const noexcept
Definition: qhash.h:1714
const Key & key() const noexcept
Definition: qhash.h:1713
std::forward_iterator_tag iterator_category
Definition: qhash.h:1704
bool operator!=(const const_iterator &o) const noexcept
Definition: qhash.h:1718
T * operator->() const noexcept
Definition: qhash.h:1716
constexpr const_iterator() noexcept=default
T & operator*() const noexcept
Definition: qhash.h:1715
const_iterator operator++(int) noexcept
Definition: qhash.h:1730
bool operator==(const const_iterator &o) const noexcept
Definition: qhash.h:1717
The QMultiHash::iterator class provides an STL-style non-const iterator for QMultiHash.
Definition: qhash.h:1637
T & value() const noexcept
Definition: qhash.h:1661
constexpr iterator() noexcept=default
bool operator==(const const_iterator &o) const noexcept
Definition: qhash.h:1683
T * operator->() const noexcept
Definition: qhash.h:1663
bool operator!=(const iterator &o) const noexcept
Definition: qhash.h:1665
bool operator!=(const const_iterator &o) const noexcept
Definition: qhash.h:1684
bool operator==(const iterator &o) const noexcept
Definition: qhash.h:1664
T & operator*() const noexcept
Definition: qhash.h:1662
iterator & operator++() noexcept
Definition: qhash.h:1667
iterator operator++(int) noexcept
Definition: qhash.h:1677
std::forward_iterator_tag iterator_category
Definition: qhash.h:1652
qptrdiff difference_type
Definition: qhash.h:1653
The QMultiHash::key_iterator class provides an STL-style const iterator for QMultiHash keys.
Definition: qhash.h:1740
const_iterator base() const noexcept
Definition: qhash.h:1760
const_iterator::iterator_category iterator_category
Definition: qhash.h:1744
key_iterator() noexcept=default
const Key * operator->() const noexcept
Definition: qhash.h:1754
bool operator!=(key_iterator o) const noexcept
Definition: qhash.h:1756
const Key & reference
Definition: qhash.h:1748
key_iterator operator++(int) noexcept
Definition: qhash.h:1759
const Key * pointer
Definition: qhash.h:1747
qptrdiff difference_type
Definition: qhash.h:1745
key_iterator & operator++() noexcept
Definition: qhash.h:1758
bool operator==(key_iterator o) const noexcept
Definition: qhash.h:1755
const Key & operator*() const noexcept
Definition: qhash.h:1753
The QMultiHash class is a convenience QHash subclass that provides multi-valued hashes.
Definition: qhash.h:1293
const_key_value_iterator constKeyValueEnd() const noexcept
Definition: qhash.h:1782
const_iterator find(const Key &key) const noexcept
Definition: qhash.h:1848
const_key_value_iterator keyValueBegin() const noexcept
Definition: qhash.h:1779
const_iterator find(const Key &key, const T &value) const noexcept
Definition: qhash.h:2007
const_key_value_iterator keyValueEnd() const noexcept
Definition: qhash.h:1781
QMultiHash & unite(const QHash< Key, T > &other)
Definition: qhash.h:2038
iterator find(const Key &key, const T &value)
Definition: qhash.h:1998
iterator Iterator
Definition: qhash.h:1833
QTypeTraits::compare_eq_result_container< QMultiHash, AKey, AT > operator==(const QMultiHash &other) const noexcept
Definition: qhash.h:1394
QMultiHash(const QHash< Key, T > &other)
Definition: qhash.h:1377
QMultiHash & operator=(const QMultiHash &other) noexcept(std::is_nothrow_destructible< Node >::value)
Definition: qhash.h:1352
bool contains(const Key &key, const T &value) const noexcept
Definition: qhash.h:1924
QList< Key > keys(const T &value) const
Definition: qhash.h:1606
const_iterator constFind(const Key &key, const T &value) const noexcept
Definition: qhash.h:2011
QMultiHash(QHash< Key, T > &&other)
Definition: qhash.h:1381
iterator find(const Key &key)
Definition: qhash.h:1836
float load_factor() const noexcept
Definition: qhash.h:1887
bool isSharedWith(const QMultiHash &other) const noexcept
Definition: qhash.h:1454
key_value_iterator keyValueBegin() noexcept
Definition: qhash.h:1777
QMultiHash(const QMultiHash &other) noexcept
Definition: qhash.h:1337
iterator detach(const_iterator it)
Definition: qhash.h:1784
qsizetype count(const Key &key, const T &value) const noexcept
Definition: qhash.h:1980
const_iterator cbegin() const noexcept
Definition: qhash.h:1769
key_iterator keyBegin() const noexcept
Definition: qhash.h:1775
const_iterator constBegin() const noexcept
Definition: qhash.h:1770
iterator emplace(const Key &key, Args &&... args)
Definition: qhash.h:1867
QMultiHash(QMultiHash &&other) noexcept
Definition: qhash.h:1365
bool empty() const noexcept
Definition: qhash.h:1892
const_iterator constEnd() const noexcept
Definition: qhash.h:1774
static size_t max_bucket_count() noexcept
Definition: qhash.h:1890
QMultiHash(InputIterator f, InputIterator l)
Definition: qhash.h:1330
QKeyValueIterator< const Key &, const T &, const_iterator > const_key_value_iterator
The QMap::const_key_value_iterator typedef provides an STL-style const iterator for QMultiHash and QM...
Definition: qhash.h:1763
bool isDetached() const noexcept
Definition: qhash.h:1453
T value(const Key &key) const noexcept
Definition: qhash.h:1561
QPair< iterator, iterator > equal_range(const Key &key)
Definition: qhash.h:2058
QList< T > values(const Key &key) const
Definition: qhash.h:1618
friend class iterator
Definition: qhash.h:1686
iterator emplace(Key &&key, Args &&... args)
Definition: qhash.h:1873
const_iterator cend() const noexcept
Definition: qhash.h:1773
qsizetype removeIf(Predicate pred)
Definition: qhash.h:1481
iterator emplaceReplace(Key &&key, Args &&... args)
Definition: qhash.h:1906
QMultiHash & unite(const QMultiHash &other)
Definition: qhash.h:2023
QList< T > values() const
Definition: qhash.h:1617
Key key(const T &value) const noexcept
Definition: qhash.h:1533
QMultiHash(InputIterator f, InputIterator l)
Definition: qhash.h:1322
qsizetype capacity() const noexcept
Definition: qhash.h:1439
bool contains(const Key &key) const noexcept
Definition: qhash.h:1510
qsizetype difference_type
Definition: qhash.h:1306
QMultiHash & operator+=(const QMultiHash &other)
Definition: qhash.h:1919
QMultiHash & operator=(QMultiHash &&other) noexcept(std::is_nothrow_destructible< Node >::value)
Definition: qhash.h:1370
Key key_type
Definition: qhash.h:1302
void swap(QMultiHash &other) noexcept
Definition: qhash.h:1386
const_iterator begin() const noexcept
Definition: qhash.h:1768
T & operator[](const Key &key)
Definition: qhash.h:1576
T take(const Key &key)
Definition: qhash.h:1485
qsizetype remove(const Key &key)
Definition: qhash.h:1464
const_iterator ConstIterator
Definition: qhash.h:1834
iterator insert(const Key &key, const T &value)
Definition: qhash.h:1861
void detach()
Definition: qhash.h:1452
size_t bucket_count() const noexcept
Definition: qhash.h:1889
void reserve(qsizetype size)
Definition: qhash.h:1440
bool isEmpty() const noexcept
Definition: qhash.h:1437
iterator emplaceReplace(const Key &key, Args &&... args)
Definition: qhash.h:1900
iterator begin()
Definition: qhash.h:1767
QList< Key > keys() const
Definition: qhash.h:1605
QMultiHash() noexcept=default
QList< Key > uniqueKeys() const
Definition: qhash.h:1592
T value(const Key &key, const T &defaultValue) const noexcept
Definition: qhash.h:1568
qsizetype size() const noexcept
Definition: qhash.h:1435
key_value_iterator keyValueEnd() noexcept
Definition: qhash.h:1778
iterator replace(const Key &key, const T &value)
Definition: qhash.h:1894
const_iterator end() const noexcept
Definition: qhash.h:1772
friend class const_iterator
Definition: qhash.h:1737
qsizetype count(const Key &key) const noexcept
Definition: qhash.h:1963
Key key(const T &value, const Key &defaultKey) const noexcept
Definition: qhash.h:1540
qsizetype size_type
Definition: qhash.h:1305
const_key_value_iterator constKeyValueBegin() const noexcept
Definition: qhash.h:1780
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Definition: qhash.h:1456
iterator erase(const_iterator it)
Definition: qhash.h:1810
qsizetype remove(const Key &key, const T &value)
Definition: qhash.h:1934
key_iterator keyEnd() const noexcept
Definition: qhash.h:1776
QTypeTraits::compare_eq_result_container< QMultiHash, AKey, AT > operator!=(const QMultiHash &other) const noexcept
Definition: qhash.h:1428
void squeeze()
Definition: qhash.h:1450
iterator end() noexcept
Definition: qhash.h:1771
~QMultiHash()
Definition: qhash.h:1343
qsizetype count() const noexcept
Definition: qhash.h:1835
QKeyValueIterator< const Key &, T &, iterator > key_value_iterator
The QMap::key_value_iterator typedef provides an STL-style iterator for QMultiHash and QMultiHash.
Definition: qhash.h:1764
const_iterator constFind(const Key &key) const noexcept
Definition: qhash.h:1852
QMultiHash operator+(const QMultiHash &other) const
Definition: qhash.h:1921
static float max_load_factor() noexcept
Definition: qhash.h:1888
QPair< const_iterator, const_iterator > equal_range(const Key &key) const noexcept
Definition: qhash.h:2066
const T operator[](const Key &key) const noexcept
Definition: qhash.h:1587
QMultiHash & unite(QHash< Key, T > &&other)
Definition: qhash.h:2045
Definition: qset.h:54
#define T(x)
Definition: main.cpp:42
QHash< int, QWidget * > hash
[35multi]
double e
set reserve(20000)
auto it unsigned count const
Definition: hb-iter.hh:848
short next
Definition: keywords.cpp:454
#define inline
Definition: md4c.c:45
Generic::PredicateMatcher< T > Predicate(std::function< bool(T const &)> const &predicate, std::string const &description="")
Definition: catch_p_p.h:3521
QPainterPath node()
Definition: paths.cpp:574
typename C::iterator iterator
constexpr size_t maxNumBuckets() noexcept
Definition: qhash.h:438
constexpr size_t bucketForHash(size_t nBuckets, size_t hash) noexcept
Definition: qhash.h:461
constexpr size_t bucketsForCapacity(size_t requestedCapacity) noexcept
Definition: qhash.h:453
constexpr bool isRelocatable()
Definition: qhash.h:252
constexpr bool HasStdHashSpecializationWithoutSeed
Definition: qhash.h:82
size_t calculateHash(const T &t, size_t seed=0)
Definition: qhash.h:90
constexpr Q_DECL_CONST_FUNCTION size_t hash(size_t key, size_t seed) noexcept
constexpr bool HasQHashOverload
Definition: qhash.h:66
constexpr bool HasStdHashSpecializationWithSeed
Definition: qhash.h:74
std::enable_if_t< std::conjunction_v< QTypeTraits::has_operator_equal_container< Container, T >... >, bool > compare_eq_result_container
Definition: qtypeinfo.h:347
auto associative_erase_if(Container &c, Predicate &pred)
void reserveIfForwardIterator(Container *, InputIterator, InputIterator)
default
Definition: devices.py:76
Definition: qfloat16.h:381
void swap(SimpleVector< T > &v1, SimpleVector< T > &v2)
Definition: simplevector.h:331
std::pair< T1, T2 > QPair
Definition: qcontainerfwd.h:56
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter * iter
EGLOutputLayerEXT EGLint EGLAttrib value
QT_BEGIN_INCLUDE_NAMESPACE typedef unsigned char uchar
Definition: qglobal.h:332
ptrdiff_t qptrdiff
Definition: qglobal.h:307
ptrdiff_t qsizetype
Definition: qglobal.h:308
#define QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(Class)
Definition: qglobal.h:556
size_t qHash(const QHash< Key, T > &key, size_t seed=0) noexcept(noexcept(qHash(std::declval< Key & >())) &&noexcept(qHash(std::declval< T & >())))
Definition: qhash.h:2124
bool qHashEquals(const T &a, const T &b)
#define Q_DECLARE_MUTABLE_ASSOCIATIVE_FORWARD_ITERATOR(C)
Definition: qiterator.h:210
#define Q_DECLARE_ASSOCIATIVE_FORWARD_ITERATOR(C)
Definition: qiterator.h:184
constexpr quint32 qNextPowerOfTwo(quint32 v)
Definition: qmath.h:371
GLenum GLsizei GLsizei GLint * values
[16]
GLsizei const GLfloat * v
[13]
GLuint64 key
GLboolean r
[2]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLuint GLuint end
GLfloat GLfloat f
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint GLintptr offset
GLint ref
GLint first
GLfloat n
GLfloat GLfloat GLfloat GLfloat h
GLuint res
Definition: qopenglext.h:8867
const GLubyte * c
Definition: qopenglext.h:12701
GLuint GLfloat * val
Definition: qopenglext.h:1513
GLuint entry
Definition: qopenglext.h:11002
GLuint GLsizei const GLuint const GLintptr * offsets
Definition: qopenglext.h:2587
GLdouble GLdouble t
[9]
Definition: qopenglext.h:243
GLenum GLenum GLsizei void GLsizei void void * span
Definition: qopenglext.h:2747
GLuint64EXT * result
[6]
Definition: qopenglext.h:10932
GLdouble s
[6]
Definition: qopenglext.h:235
constexpr decltype(auto) qMakePair(T1 &&value1, T2 &&value2) noexcept(noexcept(std::make_pair(std::forward< T1 >(value1), std::forward< T2 >(value2))))
Definition: qpair.h:55
#define Q_ASSERT(cond)
Definition: qrandom.cpp:84
#define explicit
Q_UNUSED(salary)
[21]
QtConcurrent::task([]{ qDebug("Hello, world!");}).spawn(FutureResult void increment(QPromise< int > &promise, int i)
[10]
QObject::connect nullptr
QSharedPointer< T > other(t)
[5]
QAction * at
QStringList::Iterator it
QStringList list
[0]
Definition: main.cpp:58
bool operator==(const QHashDummyValue &) const noexcept
Definition: qhash.h:60
void reallocationHelper(const Data &other, size_t nSpans, bool resized)
Definition: qhash.h:494
Span * spans
Definition: qhash.h:484
iterator begin() const noexcept
Definition: qhash.h:557
void clear()
Definition: qhash.h:544
size_t nextBucket(size_t bucket) const noexcept
Definition: qhash.h:599
static Data * detached(Data *d)
Definition: qhash.h:525
iterator find(const Key &key) const noexcept
Definition: qhash.h:616
QHashPrivate::iterator< Node > iterator
Definition: qhash.h:476
iterator detachedIterator(iterator other) const noexcept
Definition: qhash.h:552
constexpr iterator end() const noexcept
Definition: qhash.h:565
iterator erase(iterator it) noexcept(std::is_nothrow_destructible< Node >::value)
Definition: qhash.h:676
bool shouldGrow() const noexcept
Definition: qhash.h:611
typename Node::KeyType Key
Definition: qhash.h:473
void rehash(size_t sizeHint=0)
Definition: qhash.h:570
InsertionResult findOrInsert(const Key &key) noexcept
Definition: qhash.h:657
Node * findNode(const Key &key) const noexcept
Definition: qhash.h:641
static Data * detached(Data *d, size_t size)
Definition: qhash.h:534
float loadFactor() const noexcept
Definition: qhash.h:607
Data(size_t reserve=0)
Definition: qhash.h:486
Data(const Data &other, size_t reserved)
Definition: qhash.h:516
Data(const Data &other)
Definition: qhash.h:510
size_t numBuckets
Definition: qhash.h:480
qsizetype free() noexcept(std::is_nothrow_destructible_v< T >)
Definition: qhash.h:159
bool contains(const T &val) const noexcept
Definition: qhash.h:171
MultiNodeChain * next
Definition: qhash.h:155
static qsizetype freeChain(MultiNode *n) noexcept(std::is_nothrow_destructible_v< T >)
Definition: qhash.h:232
MultiNode(MultiNode &&other)
Definition: qhash.h:209
void insertMulti(Args &&... args)
Definition: qhash.h:239
MultiNode(const MultiNode &other)
Definition: qhash.h:215
static void createInPlace(MultiNode *n, const Key &k, Args &&... args)
Definition: qhash.h:197
MultiNode(const Key &k, Chain *c)
Definition: qhash.h:200
static void createInPlace(MultiNode *n, Key &&k, Args &&... args)
Definition: qhash.h:194
MultiNode(Key &&k, Chain *c) noexcept(std::is_nothrow_move_assignable_v< Key >)
Definition: qhash.h:204
void emplaceValue(Args &&... args)
Definition: qhash.h:245
static void createInPlace(Node *n, const Key &k, Args &&...)
Definition: qhash.h:141
bool valuesEqual(const Node *) const
Definition: qhash.h:148
static void createInPlace(Node *n, Key &&k, Args &&...)
Definition: qhash.h:138
void emplaceValue(Args &&... args)
Definition: qhash.h:120
T && takeValue() noexcept(std::is_nothrow_move_assignable_v< T >)
Definition: qhash.h:124
bool valuesEqual(const Node *other) const
Definition: qhash.h:128
static void createInPlace(Node *n, const Key &k, Args &&... args)
Definition: qhash.h:117
static void createInPlace(Node *n, Key &&k, Args &&... args)
Definition: qhash.h:114
Definition: qhash.h:280
Node & node()
Definition: qhash.h:284
struct QHashPrivate::Span::Entry::@496 storage
unsigned char & nextFree()
Definition: qhash.h:283
const Node & atOffset(size_t o) const noexcept
Definition: qhash.h:364
void moveLocal(size_t from, size_t to) noexcept
Definition: qhash.h:370
void addStorage()
Definition: qhash.h:404
void freeData() noexcept(std::is_nothrow_destructible< Node >::value)
Definition: qhash.h:299
void erase(size_t bucket) noexcept(std::is_nothrow_destructible< Node >::value)
Definition: qhash.h:324
unsigned char nextFree
Definition: qhash.h:290
Node & at(size_t i) noexcept
Definition: qhash.h:344
Node & atOffset(size_t o) noexcept
Definition: qhash.h:358
Span() noexcept
Definition: qhash.h:291
unsigned char allocated
Definition: qhash.h:289
Entry * entries
Definition: qhash.h:288
size_t offset(size_t i) const noexcept
Definition: qhash.h:336
bool hasNode(size_t i) const noexcept
Definition: qhash.h:340
void moveFromSpan(Span &fromSpan, size_t fromIndex, size_t to) noexcept(std::is_nothrow_move_constructible_v< Node >)
Definition: qhash.h:377
Node * insert(size_t i)
Definition: qhash.h:312
const Node & at(size_t i) const noexcept
Definition: qhash.h:351
Node * node() const noexcept
Definition: qhash.h:740
size_t span() const noexcept
Definition: qhash.h:736
iterator operator++() noexcept
Definition: qhash.h:747
size_t index() const noexcept
Definition: qhash.h:737
const Data< Node > * d
Definition: qhash.h:733
bool isUnused() const noexcept
Definition: qhash.h:738
bool operator!=(iterator other) const noexcept
Definition: qhash.h:763
bool atEnd() const noexcept
Definition: qhash.h:745
bool operator==(iterator other) const noexcept
Definition: qhash.h:761
static Q_CORE_EXPORT QHashSeed globalSeed() noexcept
Definition: qhash.cpp:888
Definition: main.cpp:38