summaryrefslogtreecommitdiffstats
path: root/src/corelib/tools/qhash.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/tools/qhash.h')
-rw-r--r--src/corelib/tools/qhash.h2570
1 files changed, 1564 insertions, 1006 deletions
diff --git a/src/corelib/tools/qhash.h b/src/corelib/tools/qhash.h
index d03f301306..cdf5577fcb 100644
--- a/src/corelib/tools/qhash.h
+++ b/src/corelib/tools/qhash.h
@@ -1,7 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
+** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -41,218 +40,769 @@
#ifndef QHASH_H
#define QHASH_H
-#include <QtCore/qchar.h>
#include <QtCore/qiterator.h>
-#include <QtCore/qlist.h>
+#include <QtCore/qvector.h>
#include <QtCore/qrefcount.h>
#include <QtCore/qhashfunctions.h>
#include <QtCore/qcontainertools_impl.h>
+#include <QtCore/qmath.h>
-#include <algorithm>
#include <initializer_list>
-#if defined(Q_CC_MSVC)
-#pragma warning( push )
-#pragma warning( disable : 4311 ) // disable pointer truncation warning
-#pragma warning( disable : 4127 ) // conditional expression is constant
-#endif
-
QT_BEGIN_NAMESPACE
-struct Q_CORE_EXPORT QHashData
+struct QHashDummyValue
{
- struct Node {
- Node *next;
- uint h;
- };
+ bool operator==(const QHashDummyValue &) const noexcept { return true; }
+};
- Node *fakeNext;
- Node **buckets;
- QtPrivate::RefCount ref;
- int size;
- int nodeSize;
- short userNumBits;
- short numBits;
- int numBuckets;
- uint seed;
- uint sharable : 1;
- uint strictAlignment : 1;
- uint reserved : 30;
-
- void *allocateNode(int nodeAlign);
- void freeNode(void *node);
- QHashData *detach_helper(void (*node_duplicate)(Node *, void *), void (*node_delete)(Node *),
- int nodeSize, int nodeAlign);
- bool willGrow();
- void hasShrunk();
- void rehash(int hint);
- void free_helper(void (*node_delete)(Node *));
- Node *firstNode();
-#ifdef QT_QHASH_DEBUG
- void dump();
- void checkSanity();
-#endif
- static Node *nextNode(Node *node);
- static Node *previousNode(Node *node);
+namespace QHashPrivate {
- static const QHashData shared_null;
-};
+// QHash uses a power of two growth policy.
+namespace GrowthPolicy
+{
+inline constexpr size_t maxNumBuckets() noexcept
+{
+ return size_t(1) << (8*sizeof(size_t) - 1);
+}
+inline constexpr size_t bucketsForCapacity(size_t requestedCapacity) noexcept
+{
+ if (requestedCapacity <= 8)
+ return 16;
+ if (requestedCapacity >= maxNumBuckets())
+ return maxNumBuckets();
+ return qNextPowerOfTwo(QIntegerForSize<sizeof(size_t)>::Unsigned(2*requestedCapacity - 1));
+}
+inline constexpr size_t bucketForHash(size_t nBuckets, size_t hash) noexcept
+{
+ return hash & (nBuckets - 1);
+}
+}
-inline bool QHashData::willGrow()
+template <typename Key, typename T>
+struct Node
{
- if (size >= numBuckets) {
- rehash(numBits + 1);
- return true;
- } else {
- return false;
+ using KeyType = Key;
+ using ValueType = T;
+
+ Key key;
+ T value;
+ static Node create(Key &&k, T &&t) noexcept(std::is_nothrow_move_assignable_v<Key> && std::is_nothrow_move_assignable_v<T>)
+ {
+ return Node{ std::move(k), std::move(t) };
}
-}
+ static Node create(const Key &k, const T &t) noexcept(std::is_nothrow_copy_constructible_v<Key> && std::is_nothrow_copy_constructible_v<T>)
+ {
+ return Node{ k, t };
+ }
+ void replace(const T &t) noexcept(std::is_nothrow_assignable_v<T>)
+ {
+ value = t;
+ }
+ void replace(T &&t) noexcept(std::is_nothrow_move_assignable_v<T>)
+ {
+ value = std::move(t);
+ }
+ T &&takeValue() noexcept(std::is_nothrow_move_assignable_v<T>)
+ {
+ return std::move(value);
+ }
+};
-inline void QHashData::hasShrunk()
+template <typename T>
+struct MultiNodeChain
{
- if (size <= (numBuckets >> 3) && numBits > userNumBits) {
- QT_TRY {
- rehash(qMax(int(numBits) - 2, int(userNumBits)));
- } QT_CATCH(const std::bad_alloc &) {
- // ignore bad allocs - shrinking shouldn't throw. rehash is exception safe.
+ T value;
+ MultiNodeChain *next = nullptr;
+ ~MultiNodeChain()
+ {
+ }
+ qsizetype free() noexcept(std::is_nothrow_destructible_v<T>)
+ {
+ qsizetype nEntries = 0;
+ MultiNodeChain *e = this;
+ while (e) {
+ MultiNodeChain *n = e->next;
+ ++nEntries;
+ delete e;
+ e = n;
}
+ return nEntries;
}
-}
+ bool contains(const T &val) const noexcept
+ {
+ const MultiNodeChain *e = this;
+ while (e) {
+ if (e->value == val)
+ return true;
+ e = e->next;
+ }
+ return false;
+ }
+};
-inline QHashData::Node *QHashData::firstNode()
+template <typename Key, typename T>
+struct MultiNode
{
- Node *e = reinterpret_cast<Node *>(this);
- Node **bucket = buckets;
- int n = numBuckets;
- while (n--) {
- if (*bucket != e)
- return *bucket;
- ++bucket;
+ using KeyType = Key;
+ using ValueType = T;
+ using Chain = MultiNodeChain<T>;
+
+ Key key;
+ Chain *value;
+
+ static MultiNode create(Key &&k, T &&t)
+ {
+ Chain *c = new Chain{ std::move(t), nullptr };
+ return MultiNode(std::move(k), c);
+ }
+ static MultiNode create(const Key &k, const T &t)
+ {
+ Chain *c = new Chain{ t, nullptr };
+ return MultiNode(k, c);
}
- return e;
-}
-struct QHashDummyValue
-{
+ MultiNode(const Key &k, Chain *c)
+ : key(k),
+ value(c)
+ {}
+ MultiNode(Key &&k, Chain *c) noexcept(std::is_nothrow_move_assignable_v<Key>)
+ : key(std::move(k)),
+ value(c)
+ {}
+
+ MultiNode(MultiNode &&other)
+ : key(other.key),
+ value(other.value)
+ {
+ other.value = nullptr;
+ }
+
+ MultiNode(const MultiNode &other)
+ : key(other.key)
+ {
+ Chain *c = other.value;
+ Chain **e = &value;
+ while (c) {
+ Chain *chain = new Chain{ c->value, nullptr };
+ *e = chain;
+ e = &chain->next;
+ c = c->next;
+ }
+ }
+ ~MultiNode()
+ {
+ if (value)
+ value->free();
+ }
+ static qsizetype freeChain(MultiNode *n) noexcept(std::is_nothrow_destructible_v<T>)
+ {
+ qsizetype size = n->value->free();
+ n->value = nullptr;
+ return size;
+ }
+ void replace(const T &t) noexcept(std::is_nothrow_assignable_v<T, T>)
+ {
+ value->value = t;
+ }
+ void replace(T &&t) noexcept(std::is_nothrow_move_assignable_v<T>)
+ {
+ value->value = std::move(t);
+ }
+ void insertMulti(const T &t)
+ {
+ Chain *e = new Chain{ t, nullptr };
+ e->next = value;
+ value = e;
+ }
+
+ // compiler generated move operators are fine
};
-constexpr bool operator==(const QHashDummyValue &, const QHashDummyValue &) noexcept
+template<typename Node>
+constexpr bool isRelocatable()
{
- return true;
+ return QTypeInfo<typename Node::KeyType>::isRelocatable && QTypeInfo<typename Node::ValueType>::isRelocatable;
}
-Q_DECLARE_TYPEINFO(QHashDummyValue, Q_MOVABLE_TYPE | Q_DUMMY_TYPE);
+// Regular hash tables consist of a list of buckets that can store Nodes. But simply allocating one large array of buckets
+// would waste a lot of memory. To avoid this, we split the vector of buckets up into a vector of Spans. Each Span represents
+// NEntries buckets. To quickly find the correct Span that holds a bucket, NEntries must be a power of two.
+//
+// Inside each Span, there is an offset array that represents the actual buckets. offsets contains either an index into the
+// actual storage space for the Nodes (the 'entries' member) or 0xff (UnusedEntry) to flag that the bucket is empty.
+// As we have only 128 entries per Span, the offset array can be represented using an unsigned char. This trick makes the hash
+// table have a very small memory overhead compared to many other implementations.
+template<typename Node>
+struct Span {
+ enum {
+ NEntries = 128,
+ LocalBucketMask = (NEntries - 1),
+ UnusedEntry = 0xff
+ };
+ static_assert ((NEntries & LocalBucketMask) == 0, "EntriesPerSpan must be a power of two.");
+
+ // Entry is a slot available for storing a Node. The Span holds a pointer to
+ // an array of Entries. Upon construction of the array, those entries are
+ // unused, and nextFree() is being used to set up a singly linked list
+ // of free entries.
+ // When a node gets inserted, the first free entry is being picked, removed
+ // from the singly linked list and the Node gets constructed in place.
+ struct Entry {
+ typename std::aligned_storage<sizeof(Node), alignof(Node)>::type storage;
+
+ unsigned char &nextFree() { return *reinterpret_cast<unsigned char *>(&storage); }
+ Node &node() { return *reinterpret_cast<Node *>(&storage); }
+ };
-template <class Key, class T>
-struct QHashNode
-{
- QHashNode *next;
- const uint h;
- const Key key;
- T value;
+ unsigned char offsets[NEntries];
+ Entry *entries = nullptr;
+ unsigned char allocated = 0;
+ unsigned char nextFree = 0;
+ Span() noexcept
+ {
+ memset(offsets, UnusedEntry, sizeof(offsets));
+ }
+ ~Span()
+ {
+ freeData();
+ }
+ void freeData() noexcept(std::is_nothrow_destructible<Node>::value)
+ {
+ if (entries) {
+ if constexpr (!std::is_trivially_destructible<Node>::value) {
+ for (auto o : offsets) {
+ if (o != UnusedEntry)
+ entries[o].node().~Node();
+ }
+ }
+ delete [] entries;
+ entries = nullptr;
+ }
+ }
+ void insert(size_t i, Node &&n)
+ {
+ Q_ASSERT(i <= NEntries);
+ Q_ASSERT(offsets[i] == UnusedEntry);
+ if (nextFree == allocated)
+ addStorage();
+ unsigned char entry = nextFree;
+ Q_ASSERT(entry < allocated);
+ nextFree = entries[entry].nextFree();
+ offsets[i] = entry;
+ new (&entries[entry].node()) Node(std::move(n));
+ }
+ void erase(size_t bucket) noexcept(std::is_nothrow_destructible<Node>::value)
+ {
+ Q_ASSERT(bucket <= NEntries);
+ Q_ASSERT(offsets[bucket] != UnusedEntry);
- inline QHashNode(const Key &key0, const T &value0, uint hash, QHashNode *n)
- : next(n), h(hash), key(key0), value(value0) {}
- inline bool same_key(uint h0, const Key &key0) const { return h0 == h && key0 == key; }
+ unsigned char entry = offsets[bucket];
+ offsets[bucket] = UnusedEntry;
-private:
- Q_DISABLE_COPY(QHashNode)
+ entries[entry].node().~Node();
+ entries[entry].nextFree() = nextFree;
+ nextFree = entry;
+ }
+ size_t offset(size_t i) const noexcept
+ {
+ return offsets[i];
+ }
+ bool hasNode(size_t i) const noexcept
+ {
+ return (offsets[i] != UnusedEntry);
+ }
+ Node &at(size_t i) noexcept
+ {
+ Q_ASSERT(i <= NEntries);
+ Q_ASSERT(offsets[i] != UnusedEntry);
+
+ return entries[offsets[i]].node();
+ }
+ const Node &at(size_t i) const noexcept
+ {
+ Q_ASSERT(i <= NEntries);
+ Q_ASSERT(offsets[i] != UnusedEntry);
+
+ return entries[offsets[i]].node();
+ }
+ Node &atOffset(size_t o) noexcept
+ {
+ Q_ASSERT(o < allocated);
+
+ return entries[o].node();
+ }
+ const Node &atOffset(size_t o) const noexcept
+ {
+ Q_ASSERT(o < allocated);
+
+ return entries[o].node();
+ }
+ void moveLocal(size_t from, size_t to) noexcept
+ {
+ Q_ASSERT(offsets[from] != UnusedEntry);
+ Q_ASSERT(offsets[to] == UnusedEntry);
+ offsets[to] = offsets[from];
+ offsets[from] = UnusedEntry;
+ }
+ void moveFromSpan(Span &fromSpan, size_t fromIndex, size_t to) noexcept(std::is_nothrow_move_constructible_v<Node>)
+ {
+ Q_ASSERT(to <= NEntries);
+ Q_ASSERT(offsets[to] == UnusedEntry);
+ Q_ASSERT(fromIndex <= NEntries);
+ Q_ASSERT(fromSpan.offsets[fromIndex] != UnusedEntry);
+ if (nextFree == allocated)
+ addStorage();
+ Q_ASSERT(nextFree < allocated);
+ offsets[to] = nextFree;
+ Entry &toEntry = entries[nextFree];
+ nextFree = toEntry.nextFree();
+
+ size_t fromOffset = fromSpan.offsets[fromIndex];
+ fromSpan.offsets[fromIndex] = UnusedEntry;
+ Entry &fromEntry = fromSpan.entries[fromOffset];
+
+ if constexpr (isRelocatable<Node>()) {
+ memcpy(&toEntry, &fromEntry, sizeof(Entry));
+ } else {
+ new (&toEntry.node()) Node(std::move(fromEntry.node()));
+ fromEntry.node().~Node();
+ }
+ fromEntry.nextFree() = fromSpan.nextFree;
+ fromSpan.nextFree = static_cast<unsigned char>(fromOffset);
+ }
+
+ void addStorage()
+ {
+ Q_ASSERT(allocated < NEntries);
+ Q_ASSERT(nextFree == allocated);
+ // the hash table should always be between 25 and 50% full
+ // this implies that we on average have between 32 and 64 entries
+ // in here. The likelihood of having below 16 entries is very small,
+ // so start with that and increment by 16 each time we need to add
+ // some more space
+ const size_t increment = NEntries/8;
+ size_t alloc = allocated + increment;
+ Entry *newEntries = new Entry[alloc];
+ // we only add storage if the previous storage was fully filled, so
+ // simply copy the old data over
+ if constexpr (isRelocatable<Node>()) {
+ memcpy(newEntries, entries, allocated*sizeof(Entry));
+ } else {
+ for (size_t i = 0; i < allocated; ++i) {
+ new (&newEntries[i].node()) Node(std::move(entries[i].node()));
+ entries[i].node().~Node();
+ }
+ }
+ for (size_t i = allocated; i < allocated + increment; ++i) {
+ newEntries[i].nextFree() = uchar(i + 1);
+ }
+ delete [] entries;
+ entries = newEntries;
+ allocated = uchar(alloc);
+ }
};
-// Specialize for QHashDummyValue in order to save some memory
-template <class Key>
-struct QHashNode<Key, QHashDummyValue>
+template <typename Node>
+struct iterator;
+
+template <typename Node>
+struct Data
{
- union {
- QHashNode *next;
- QHashDummyValue value;
- };
- const uint h;
- const Key key;
+ using Key = typename Node::KeyType;
+ using T = typename Node::ValueType;
+ using Span = QHashPrivate::Span<Node>;
+ using iterator = QHashPrivate::iterator<Node>;
- inline QHashNode(const Key &key0, const QHashDummyValue &, uint hash, QHashNode *n)
- : next(n), h(hash), key(key0) {}
- inline bool same_key(uint h0, const Key &key0) const { return h0 == h && key0 == key; }
+ QtPrivate::RefCount ref = {{1}};
+ size_t size = 0;
+ size_t numBuckets = 0;
+ size_t seed = 0;
-private:
- Q_DISABLE_COPY(QHashNode)
+
+ Span *spans = nullptr;
+
+ Data(size_t reserve = 0)
+ {
+ numBuckets = GrowthPolicy::bucketsForCapacity(reserve);
+ size_t nSpans = (numBuckets + Span::LocalBucketMask) / Span::NEntries;
+ spans = new Span[nSpans];
+ seed = qGlobalQHashSeed();
+ }
+ Data(const Data &other)
+ : size(other.size),
+ numBuckets(other.numBuckets),
+ seed(other.seed)
+ {
+ size_t nSpans = (other.numBuckets + Span::LocalBucketMask) / Span::NEntries;
+ spans = new Span[nSpans];
+
+ for (size_t s = 0; s < nSpans; ++s) {
+ const Span &span = other.spans[s];
+ for (size_t index = 0; index < Span::NEntries; ++index) {
+ if (!span.hasNode(index))
+ continue;
+ const Node &n = span.at(index);
+ iterator it{ this, s*Span::NEntries + index };
+ Q_ASSERT(it.isUnused());
+
+ spans[it.span()].insert(it.index(), std::move(Node(n)));
+ }
+ }
+ }
+ Data(const Data &other, size_t reserved)
+ : size(other.size),
+ seed(other.seed)
+ {
+ numBuckets = GrowthPolicy::bucketsForCapacity(qMax(size, reserved));
+ size_t nSpans = (numBuckets + Span::LocalBucketMask) / Span::NEntries;
+ spans = new Span[nSpans];
+
+ for (size_t s = 0; s < nSpans; ++s) {
+ const Span &span = other.spans[s];
+ for (size_t index = 0; index < Span::NEntries; ++index) {
+ if (!span.hasNode(index))
+ continue;
+ const Node &n = span.at(index);
+ iterator it = find(n.key);
+ Q_ASSERT(it.isUnused());
+ spans[it.span()].insert(it.index(), std::move(Node(n)));
+ }
+ }
+ }
+
+ static Data *detached(Data *d)
+ {
+ if (!d)
+ return new Data;
+ Data *dd = new Data(*d);
+ if (!d->ref.deref())
+ delete d;
+ return dd;
+ }
+ static Data *detached(Data *d, size_t size)
+ {
+ if (!d)
+ return new Data(size);
+ Data *dd = new Data(*d, size);
+ if (!d->ref.deref())
+ delete d;
+ return dd;
+ }
+
+
+ iterator detachedIterator(iterator other) const noexcept
+ {
+ return iterator{this, other.bucket};
+ }
+
+ iterator begin() const noexcept
+ {
+ iterator it{ this, 0 };
+ if (it.isUnused())
+ ++it;
+ return it;
+ }
+
+ constexpr iterator end() const noexcept
+ {
+ return iterator();
+ }
+
+ void rehash(size_t sizeHint = 0)
+ {
+ if (sizeHint == 0)
+ sizeHint = size;
+ size_t newBucketCount = GrowthPolicy::bucketsForCapacity(sizeHint);
+
+ Span *oldSpans = spans;
+ size_t oldBucketCount = numBuckets;
+ size_t nSpans = (newBucketCount + Span::LocalBucketMask) / Span::NEntries;
+ spans = new Span[nSpans];
+ numBuckets = newBucketCount;
+ size_t oldNSpans = (oldBucketCount + Span::LocalBucketMask) / Span::NEntries;
+
+ for (size_t s = 0; s < oldNSpans; ++s) {
+ Span &span = oldSpans[s];
+ for (size_t index = 0; index < Span::NEntries; ++index) {
+ if (!span.hasNode(index))
+ continue;
+ Node &n = span.at(index);
+ iterator it = find(n.key);
+ Q_ASSERT(it.isUnused());
+ spans[it.span()].insert(it.index(), std::move(n));
+ }
+ span.freeData();
+ }
+ delete [] oldSpans;
+ }
+
+ size_t nextBucket(size_t bucket) const noexcept
+ {
+ ++bucket;
+ if (bucket == numBuckets)
+ bucket = 0;
+ return bucket;
+ }
+
+ float loadFactor() const noexcept
+ {
+ return float(size)/numBuckets;
+ }
+ bool shouldGrow() const noexcept
+ {
+ return size >= (numBuckets >> 1);
+ }
+
+ iterator find(const Key &key) const noexcept
+ {
+ Q_ASSERT(numBuckets > 0);
+ size_t hash = qHash(key, seed);
+ size_t bucket = GrowthPolicy::bucketForHash(numBuckets, hash);
+ // loop over the buckets until we find the entry we search for
+ // or an empty slot, in which case we know the entry doesn't exist
+ while (true) {
+ // Split the bucket into the indexex of span array, and the local
+ // offset inside the span
+ size_t span = bucket / Span::NEntries;
+ size_t index = bucket & Span::LocalBucketMask;
+ Span &s = spans[span];
+ size_t offset = s.offset(index);
+ if (offset == Span::UnusedEntry) {
+ return iterator{ this, bucket };
+ } else {
+ Node &n = s.atOffset(offset);
+ if (n.key == key)
+ return iterator{ this, bucket };
+ }
+ bucket = nextBucket(bucket);
+ }
+ }
+
+ Node *findNode(const Key &key) const noexcept
+ {
+ if (!size)
+ return nullptr;
+ iterator it = find(key);
+ if (it.isUnused())
+ return nullptr;
+ return it.node();
+ }
+
+ Node *findAndInsertNode(const Key &key) noexcept
+ {
+ if (shouldGrow())
+ rehash(size + 1);
+ iterator it = find(key);
+ if (it.isUnused()) {
+ spans[it.span()].insert(it.index(), Node::create(key, T()));
+ ++size;
+ }
+ return it.node();
+ }
+
+
+ iterator insert(Node &&n)
+ {
+ if (shouldGrow())
+ rehash(size + 1);
+ iterator it = find(n.key);
+ if (it.isUnused()) {
+ spans[it.span()].insert(it.index(), std::move(n));
+ ++size;
+ } else {
+ it.node()->replace(std::move(n.takeValue()));
+ }
+ return it;
+ }
+
+ iterator insert(const Key &key, const T &value)
+ {
+ if (shouldGrow())
+ rehash(size + 1);
+ auto it = find(key);
+ if (it.isUnused()) {
+ spans[it.span()].insert(it.index(), Node::create(key, value));
+ ++size;
+ } else {
+ it.node()->replace(value);
+ }
+ return it;
+ }
+
+ iterator insertMulti(const Key &key, const T &value)
+ {
+ if (shouldGrow())
+ rehash(size + 1);
+ auto it = find(key);
+ if (it.isUnused()) {
+ spans[it.span()].insert(it.index(), std::move(Node::create(key, value)));
+ ++size;
+ } else {
+ it.node()->insertMulti(value);
+ }
+ return it;
+ }
+
+
+ iterator erase(iterator it) noexcept(std::is_nothrow_destructible<Node>::value)
+ {
+ size_t bucket = it.bucket;
+ size_t span = bucket / Span::NEntries;
+ size_t index = bucket & Span::LocalBucketMask;
+ Q_ASSERT(spans[span].hasNode(index));
+ spans[span].erase(index);
+ --size;
+
+ // re-insert the following entries to avoid holes
+ size_t hole = bucket;
+ size_t next = bucket;
+ while (true) {
+ next = nextBucket(next);
+ size_t nextSpan = next / Span::NEntries;
+ size_t nextIndex = next & Span::LocalBucketMask;
+ if (!spans[nextSpan].hasNode(nextIndex))
+ break;
+ size_t hash = qHash(spans[nextSpan].at(nextIndex).key, seed);
+ size_t newBucket = GrowthPolicy::bucketForHash(numBuckets, hash);
+ while (true) {
+ if (newBucket == next) {
+ // nothing to do, item is at the right plae
+ break;
+ } else if (newBucket == hole) {
+ // move into hole
+ size_t holeSpan = hole / Span::NEntries;
+ size_t holeIndex = hole & Span::LocalBucketMask;
+ if (nextSpan == holeSpan) {
+ spans[holeSpan].moveLocal(nextIndex, holeIndex);
+ } else {
+ // move between spans, more expensive
+ spans[holeSpan].moveFromSpan(spans[nextSpan], nextIndex, holeIndex);
+ }
+ hole = next;
+ break;
+ }
+ newBucket = nextBucket(newBucket);
+ }
+ }
+
+ // return correct position of the next element
+ if (!spans[span].hasNode(index))
+ ++it;
+ return it;
+ }
+
+ ~Data()
+ {
+ delete [] spans;
+ }
};
+template <typename Node>
+struct iterator {
+ using Span = QHashPrivate::Span<Node>;
-#if 0
-// ###
-// The introduction of the QHash random seed breaks this optimization, as it
-// relies on qHash(int i) = i. If the hash value is salted, then the hash
-// table becomes corrupted.
-//
-// A bit more research about whether it makes sense or not to salt integer
-// keys (and in general keys whose hash value is easy to invert)
-// is needed, or about how keep this optimization and the seed (f.i. by
-// specializing QHash for integer values, and re-apply the seed during lookup).
-//
-// Be aware that such changes can easily be binary incompatible, and therefore
-// cannot be made during the Qt 5 lifetime.
-#define Q_HASH_DECLARE_INT_NODES(key_type) \
- template <class T> \
- struct QHashDummyNode<key_type, T> { \
- QHashDummyNode *next; \
- union { uint h; key_type key; }; \
-\
- inline QHashDummyNode(key_type /* key0 */) {} \
- }; \
-\
- template <class T> \
- struct QHashNode<key_type, T> { \
- QHashNode *next; \
- union { uint h; key_type key; }; \
- T value; \
-\
- inline QHashNode(key_type /* key0 */) {} \
- inline QHashNode(key_type /* key0 */, const T &value0) : value(value0) {} \
- inline bool same_key(uint h0, key_type) const { return h0 == h; } \
- }
-
-#if defined(Q_BYTE_ORDER) && Q_BYTE_ORDER == Q_LITTLE_ENDIAN
-Q_HASH_DECLARE_INT_NODES(short);
-Q_HASH_DECLARE_INT_NODES(ushort);
-#endif
-Q_HASH_DECLARE_INT_NODES(int);
-Q_HASH_DECLARE_INT_NODES(uint);
-#undef Q_HASH_DECLARE_INT_NODES
-#endif // #if 0
+ const Data<Node> *d = nullptr;
+ size_t bucket = 0;
-template <class Key, class T>
-class QHash
-{
- typedef QHashNode<Key, T> Node;
+ size_t span() const noexcept { return bucket / Span::NEntries; }
+ size_t index() const noexcept { return bucket & Span::LocalBucketMask; }
+ inline bool isUnused() const noexcept { return !d->spans[span()].hasNode(index()); }
- union {
- QHashData *d;
- QHashNode<Key, T> *e;
- };
+ inline Node *node() const noexcept
+ {
+ Q_ASSERT(!isUnused());
+ return &d->spans[span()].at(index());
+ }
+ bool atEnd() const noexcept { return !d; }
- static inline Node *concrete(QHashData::Node *node) {
- return reinterpret_cast<Node *>(node);
+ iterator operator++() noexcept
+ {
+ while (true) {
+ ++bucket;
+ if (bucket == d->numBuckets) {
+ d = nullptr;
+ bucket = 0;
+ break;
+ }
+ if (!isUnused())
+ break;
+ }
+ return *this;
}
+ bool operator==(iterator other) const noexcept
+ { return d == other.d && bucket == other.bucket; }
+ bool operator!=(iterator other) const noexcept
+ { return !(*this == other); }
+};
+
- static inline int alignOfNode() { return qMax<int>(sizeof(void*), alignof(Node)); }
+
+} // namespace QHashPrivate
+
+template <class Key, class T>
+class QHash
+{
+ using Node = QHashPrivate::Node<Key, T>;
+ using Data = QHashPrivate::Data<Node>;
+ friend class QSet<Key>;
+
+ Data *d = nullptr;
public:
- inline QHash() noexcept : d(const_cast<QHashData *>(&QHashData::shared_null)) { }
+ using key_type = Key;
+ using mapped_type = T;
+ using value_type = T;
+ using size_type = qsizetype;
+ using difference_type = qsizetype;
+ using reference = T &;
+ using const_reference = const T &;
+
+ inline QHash() noexcept = default;
inline QHash(std::initializer_list<std::pair<Key,T> > list)
- : d(const_cast<QHashData *>(&QHashData::shared_null))
+ : d(new Data(list.size()))
{
- reserve(int(list.size()));
for (typename std::initializer_list<std::pair<Key,T> >::const_iterator it = list.begin(); it != list.end(); ++it)
insert(it->first, it->second);
}
- QHash(const QHash &other) : d(other.d) { d->ref.ref(); if (!d->sharable) detach(); }
- ~QHash() { if (!d->ref.deref()) freeData(d); }
+ QHash(const QHash &other) noexcept
+ : d(other.d)
+ {
+ if (d)
+ d->ref.ref();
+ }
+ ~QHash()
+ {
+ if (d && !d->ref.deref())
+ delete d;
+ }
+
+ QHash &operator=(const QHash &other) noexcept(std::is_nothrow_destructible<Node>::value)
+ {
+ if (d != other.d) {
+ Data *o = other.d;
+ if (o)
+ o->ref.ref();
+ if (d && !d->ref.deref())
+ delete d;
+ d = o;
+ }
+ return *this;
+ }
- QHash &operator=(const QHash &other);
- QHash(QHash &&other) noexcept : d(other.d) { other.d = const_cast<QHashData *>(&QHashData::shared_null); }
- QHash &operator=(QHash &&other) noexcept
- { QHash moved(std::move(other)); swap(moved); return *this; }
+ QHash(QHash &&other) noexcept
+ : d(std::exchange(other.d, nullptr))
+ {
+ }
+ QHash &operator=(QHash &&other) noexcept(std::is_nothrow_destructible<Node>::value)
+ {
+ if (d != other.d) {
+ if (d && !d->ref.deref())
+ delete d;
+ d = std::exchange(other.d, nullptr);
+ }
+ return *this;
+ }
#ifdef Q_QDOC
template <typename InputIterator>
QHash(InputIterator f, InputIterator l);
@@ -277,170 +827,220 @@ public:
#endif
void swap(QHash &other) noexcept { qSwap(d, other.d); }
- bool operator==(const QHash &other) const;
- bool operator!=(const QHash &other) const { return !(*this == other); }
+ bool operator==(const QHash &other) const noexcept
+ {
+ if (d == other.d)
+ return true;
+ if (size() != other.size())
+ return false;
- inline int size() const { return d->size; }
+ for (const_iterator it = other.begin(); it != other.end(); ++it) {
+ const_iterator i = find(it.key());
+ if (i == end() || !(i.value() == it.value()))
+ return false;
+ }
+ // all values must be the same as size is the same
+ return true;
+ }
+ bool operator!=(const QHash &other) const noexcept { return !(*this == other); }
- inline bool isEmpty() const { return d->size == 0; }
+ inline qsizetype size() const noexcept { return d ? qsizetype(d->size) : 0; }
+ inline bool isEmpty() const noexcept { return !d || d->size == 0; }
- inline int capacity() const { return d->numBuckets; }
- void reserve(int size);
- inline void squeeze() { reserve(1); }
+ inline qsizetype capacity() const noexcept { return d ? qsizetype(d->numBuckets >> 1) : 0; }
+ void reserve(qsizetype size)
+ {
+ if (isDetached())
+ d->rehash(size);
+ else
+ d = Data::detached(d, size_t(size));
+ }
+ inline void squeeze() { reserve(0); }
+
+ inline void detach() { if (!d || d->ref.isShared()) d = Data::detached(d); }
+ inline bool isDetached() const noexcept { return d && !d->ref.isShared(); }
+ bool isSharedWith(const QHash &other) const noexcept { return d == other.d; }
+
+ void clear() noexcept(std::is_nothrow_destructible<Node>::value)
+ {
+ if (d && !d->ref.deref())
+ delete d;
+ d = nullptr;
+ }
- inline void detach() { if (d->ref.isShared()) detach_helper(); }
- inline bool isDetached() const { return !d->ref.isShared(); }
- bool isSharedWith(const QHash &other) const { return d == other.d; }
+ bool remove(const Key &key)
+ {
+ if (isEmpty()) // prevents detaching shared null
+ return false;
+ detach();
- void clear();
+ auto it = d->find(key);
+ if (it.isUnused())
+ return false;
+ d->erase(it);
+ return true;
+ }
+ T take(const Key &key)
+ {
+ if (isEmpty()) // prevents detaching shared null
+ return T();
+ detach();
- int remove(const Key &key);
- T take(const Key &key);
+ auto it = d->find(key);
+ if (it.isUnused())
+ return T();
+ T value = it.node()->takeValue();
+ d->erase(it);
+ return value;
+ }
- bool contains(const Key &key) const;
- Key key(const T &value) const;
- Key key(const T &value, const Key &defaultKey) const;
- T value(const Key &key) const;
- T value(const Key &key, const T &defaultValue) const;
- T &operator[](const Key &key);
- const T operator[](const Key &key) const;
+ bool contains(const Key &key) const noexcept
+ {
+ if (!d)
+ return false;
+ return d->findNode(key) != nullptr;
+ }
+ qsizetype count(const Key &key) const noexcept
+ {
+ return contains(key) ? 1 : 0;
+ }
- QList<Key> keys() const;
- QList<Key> keys(const T &value) const;
- QList<T> values() const;
-#if QT_DEPRECATED_SINCE(5, 15)
- QT_DEPRECATED_VERSION_X_5_15("Use QMultiHash for hashes storing multiple values with the same key.") QList<Key> uniqueKeys() const;
- QT_DEPRECATED_VERSION_X_5_15("Use QMultiHash for hashes storing multiple values with the same key.") QList<T> values(const Key &key) const;
-#endif
- int count(const Key &key) const;
+ Key key(const T &value, const Key &defaultKey = Key()) const noexcept
+ {
+ if (d) {
+ const_iterator i = begin();
+ while (i != end()) {
+ if (i.value() == value)
+ return i.key();
+ ++i;
+ }
+ }
+
+ return defaultKey;
+ }
+ T value(const Key &key, const T &defaultValue = T()) const noexcept
+ {
+ if (d) {
+ Node *n = d->findNode(key);
+ if (n)
+ return n->value;
+ }
+ return defaultValue;
+ }
+ T &operator[](const Key &key)
+ {
+ detach();
+ Node *n = d->findAndInsertNode(key);
+ Q_ASSERT(n);
+ return n->value;
+ }
+
+ const T operator[](const Key &key) const noexcept
+ {
+ return value(key);
+ }
+
+ QVector<Key> keys() const
+ {
+ return QVector<Key>(keyBegin(), keyEnd());
+ }
+ QVector<Key> keys(const T &value) const
+ {
+ QVector<Key> res;
+ const_iterator i = begin();
+ while (i != end()) {
+ if (i.value() == value)
+ res.append(i.key());
+ ++i;
+ }
+ return res;
+ }
+ QVector<T> values() const
+ {
+ return QVector<T>(begin(), end());
+ }
class const_iterator;
class iterator
{
+ using piter = typename QHashPrivate::iterator<Node>;
friend class const_iterator;
friend class QHash<Key, T>;
friend class QSet<Key>;
- QHashData::Node *i;
+ piter i;
+ explicit inline iterator(piter it) noexcept : i(it) { }
public:
-#if QT_DEPRECATED_WARNINGS_SINCE < QT_VERSION_CHECK(5, 15, 0)
- typedef std::bidirectional_iterator_tag iterator_category;
-#else
typedef std::forward_iterator_tag iterator_category;
-#endif
typedef qptrdiff difference_type;
typedef T value_type;
typedef T *pointer;
typedef T &reference;
- inline iterator() : i(nullptr) { }
- explicit inline iterator(void *node) : i(reinterpret_cast<QHashData::Node *>(node)) { }
+ constexpr iterator() noexcept = default;
- inline const Key &key() const { return concrete(i)->key; }
- inline T &value() const { return concrete(i)->value; }
- inline T &operator*() const { return concrete(i)->value; }
- inline T *operator->() const { return &concrete(i)->value; }
- inline bool operator==(const iterator &o) const { return i == o.i; }
- inline bool operator!=(const iterator &o) const { return i != o.i; }
+ inline const Key &key() const noexcept { return i.node()->key; }
+ inline T &value() const noexcept { return i.node()->value; }
+ inline T &operator*() const noexcept { return i.node()->value; }
+ inline T *operator->() const noexcept { return &i.node()->value; }
+ inline bool operator==(const iterator &o) const noexcept { return i == o.i; }
+ inline bool operator!=(const iterator &o) const noexcept { return i != o.i; }
- inline iterator &operator++() {
- i = QHashData::nextNode(i);
- return *this;
- }
- inline iterator operator++(int) {
- iterator r = *this;
- i = QHashData::nextNode(i);
- return r;
- }
-#if QT_DEPRECATED_SINCE(5, 15)
- inline QT_DEPRECATED_VERSION_5_15 iterator &operator--()
+ inline iterator &operator++() noexcept
{
- i = QHashData::previousNode(i);
+ ++i;
return *this;
}
- inline QT_DEPRECATED_VERSION_5_15 iterator operator--(int)
+ inline iterator operator++(int) noexcept
{
iterator r = *this;
- i = QHashData::previousNode(i);
+ ++i;
return r;
}
- inline QT_DEPRECATED_VERSION_5_15 iterator operator+(int j) const
- { iterator r = *this; if (j > 0) while (j--) ++r; else while (j++) --r; return r; }
- inline QT_DEPRECATED_VERSION_5_15 iterator operator-(int j) const { return operator+(-j); }
- inline QT_DEPRECATED_VERSION_5_15 iterator &operator+=(int j) { return *this = *this + j; }
- inline QT_DEPRECATED_VERSION_5_15 iterator &operator-=(int j) { return *this = *this - j; }
- friend inline QT_DEPRECATED_VERSION_5_15 iterator operator+(int j, iterator k) { return k + j; }
-#endif
- inline bool operator==(const const_iterator &o) const { return i == o.i; }
- inline bool operator!=(const const_iterator &o) const { return i != o.i; }
+ inline bool operator==(const const_iterator &o) const noexcept { return i == o.i; }
+ inline bool operator!=(const const_iterator &o) const noexcept { return i != o.i; }
};
friend class iterator;
class const_iterator
{
+ using piter = typename QHashPrivate::iterator<Node>;
friend class iterator;
friend class QHash<Key, T>;
- friend class QMultiHash<Key, T>;
friend class QSet<Key>;
- QHashData::Node *i;
+ piter i;
+ explicit inline const_iterator(piter it) : i(it) { }
public:
-#if QT_DEPRECATED_WARNINGS_SINCE < QT_VERSION_CHECK(5, 15, 0)
- typedef std::bidirectional_iterator_tag iterator_category;
-#else
typedef std::forward_iterator_tag iterator_category;
-#endif
typedef qptrdiff difference_type;
typedef T value_type;
typedef const T *pointer;
typedef const T &reference;
- Q_DECL_CONSTEXPR inline const_iterator() : i(nullptr) { }
- explicit inline const_iterator(void *node)
- : i(reinterpret_cast<QHashData::Node *>(node)) { }
- inline const_iterator(const iterator &o)
- { i = o.i; }
-
- inline const Key &key() const { return concrete(i)->key; }
- inline const T &value() const { return concrete(i)->value; }
- inline const T &operator*() const { return concrete(i)->value; }
- inline const T *operator->() const { return &concrete(i)->value; }
- Q_DECL_CONSTEXPR inline bool operator==(const const_iterator &o) const { return i == o.i; }
- Q_DECL_CONSTEXPR inline bool operator!=(const const_iterator &o) const { return i != o.i; }
-
- inline const_iterator &operator++() {
- i = QHashData::nextNode(i);
- return *this;
- }
- inline const_iterator operator++(int) {
- const_iterator r = *this;
- i = QHashData::nextNode(i);
- return r;
- }
-#if QT_DEPRECATED_SINCE(5, 15)
- inline QT_DEPRECATED_VERSION_5_15 const_iterator &operator--()
+ constexpr const_iterator() noexcept = default;
+ inline const_iterator(const iterator &o) noexcept : i(o.i) { }
+
+ inline const Key &key() const noexcept { return i.node()->key; }
+ inline const T &value() const noexcept { return i.node()->value; }
+ inline const T &operator*() const noexcept { return i.node()->value; }
+ inline const T *operator->() const noexcept { return &i.node()->value; }
+ inline bool operator==(const const_iterator &o) const noexcept { return i == o.i; }
+ inline bool operator!=(const const_iterator &o) const noexcept { return i != o.i; }
+
+ inline const_iterator &operator++() noexcept
{
- i = QHashData::previousNode(i);
+ ++i;
return *this;
}
- inline QT_DEPRECATED_VERSION_5_15 const_iterator operator--(int)
+ inline const_iterator operator++(int) noexcept
{
const_iterator r = *this;
- i = QHashData::previousNode(i);
+ ++i;
return r;
}
- inline QT_DEPRECATED_VERSION_5_15 const_iterator operator+(int j) const
- { const_iterator r = *this; if (j > 0) while (j--) ++r; else while (j++) --r; return r; }
- inline QT_DEPRECATED_VERSION_5_15 const_iterator operator-(int j) const { return operator+(-j); }
- inline QT_DEPRECATED_VERSION_5_15 const_iterator &operator+=(int j) { return *this = *this + j; }
- inline QT_DEPRECATED_VERSION_5_15 const_iterator &operator-=(int j) { return *this = *this - j; }
- friend inline QT_DEPRECATED_VERSION_5_15 const_iterator operator+(int j, const_iterator k)
- {
- return k + j;
- }
-#endif
};
friend class const_iterator;
@@ -450,641 +1050,744 @@ public:
public:
typedef typename const_iterator::iterator_category iterator_category;
- typedef typename const_iterator::difference_type difference_type;
+ typedef qptrdiff difference_type;
typedef Key value_type;
typedef const Key *pointer;
typedef const Key &reference;
- key_iterator() = default;
- explicit key_iterator(const_iterator o) : i(o) { }
+ key_iterator() noexcept = default;
+ explicit key_iterator(const_iterator o) noexcept : i(o) { }
- const Key &operator*() const { return i.key(); }
- const Key *operator->() const { return &i.key(); }
- bool operator==(key_iterator o) const { return i == o.i; }
- bool operator!=(key_iterator o) const { return i != o.i; }
+ const Key &operator*() const noexcept { return i.key(); }
+ const Key *operator->() const noexcept { return &i.key(); }
+ bool operator==(key_iterator o) const noexcept { return i == o.i; }
+ bool operator!=(key_iterator o) const noexcept { return i != o.i; }
- inline key_iterator &operator++() { ++i; return *this; }
- inline key_iterator operator++(int) { return key_iterator(i++);}
-#if QT_DEPRECATED_SINCE(5, 15)
- inline QT_DEPRECATED_VERSION_5_15 key_iterator &operator--()
- {
- --i;
- return *this;
- }
- inline QT_DEPRECATED_VERSION_5_15 key_iterator operator--(int) { return key_iterator(i--); }
-#endif
- const_iterator base() const { return i; }
+ inline key_iterator &operator++() noexcept { ++i; return *this; }
+ inline key_iterator operator++(int) noexcept { return key_iterator(i++);}
+ const_iterator base() const noexcept { return i; }
};
typedef QKeyValueIterator<const Key&, const T&, const_iterator> const_key_value_iterator;
typedef QKeyValueIterator<const Key&, T&, iterator> key_value_iterator;
// STL style
- inline iterator begin() { detach(); return iterator(d->firstNode()); }
- inline const_iterator begin() const { return const_iterator(d->firstNode()); }
- inline const_iterator cbegin() const { return const_iterator(d->firstNode()); }
- inline const_iterator constBegin() const { return const_iterator(d->firstNode()); }
- inline iterator end() { detach(); return iterator(e); }
- inline const_iterator end() const { return const_iterator(e); }
- inline const_iterator cend() const { return const_iterator(e); }
- inline const_iterator constEnd() const { return const_iterator(e); }
- inline key_iterator keyBegin() const { return key_iterator(begin()); }
- inline key_iterator keyEnd() const { return key_iterator(end()); }
+ inline iterator begin() { detach(); return iterator(d->begin()); }
+ inline const_iterator begin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
+ inline const_iterator cbegin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
+ inline const_iterator constBegin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
+ inline iterator end() noexcept { return iterator(); }
+ inline const_iterator end() const noexcept { return const_iterator(); }
+ inline const_iterator cend() const noexcept { return const_iterator(); }
+ inline const_iterator constEnd() const noexcept { return const_iterator(); }
+ inline key_iterator keyBegin() const noexcept { return key_iterator(begin()); }
+ inline key_iterator keyEnd() const noexcept { return key_iterator(end()); }
inline key_value_iterator keyValueBegin() { return key_value_iterator(begin()); }
inline key_value_iterator keyValueEnd() { return key_value_iterator(end()); }
- inline const_key_value_iterator keyValueBegin() const { return const_key_value_iterator(begin()); }
- inline const_key_value_iterator constKeyValueBegin() const { return const_key_value_iterator(begin()); }
- inline const_key_value_iterator keyValueEnd() const { return const_key_value_iterator(end()); }
- inline const_key_value_iterator constKeyValueEnd() const { return const_key_value_iterator(end()); }
+ inline const_key_value_iterator keyValueBegin() const noexcept { return const_key_value_iterator(begin()); }
+ inline const_key_value_iterator constKeyValueBegin() const noexcept { return const_key_value_iterator(begin()); }
+ inline const_key_value_iterator keyValueEnd() const noexcept { return const_key_value_iterator(end()); }
+ inline const_key_value_iterator constKeyValueEnd() const noexcept { return const_key_value_iterator(end()); }
- QPair<iterator, iterator> equal_range(const Key &key);
- QPair<const_iterator, const_iterator> equal_range(const Key &key) const noexcept;
- iterator erase(iterator it) { return erase(const_iterator(it.i)); }
- iterator erase(const_iterator it);
+ iterator erase(const_iterator it)
+ {
+ Q_ASSERT(it != constEnd());
+ detach();
+ // ensure a valid iterator across the detach:
+ iterator i = iterator{d->detachedIterator(it.i)};
- // more Qt
- typedef iterator Iterator;
- typedef const_iterator ConstIterator;
- inline int count() const { return d->size; }
- iterator find(const Key &key);
- const_iterator find(const Key &key) const;
- const_iterator constFind(const Key &key) const;
- iterator insert(const Key &key, const T &value);
- void insert(const QHash &hash);
-#if QT_DEPRECATED_SINCE(5, 15)
- QT_DEPRECATED_VERSION_X_5_15("Use QMultiHash for hashes storing multiple values with the same key.") iterator insertMulti(const Key &key, const T &value);
- QT_DEPRECATED_VERSION_X_5_15("Use QMultiHash for hashes storing multiple values with the same key.") QHash &unite(const QHash &other);
-#endif
+ i.i = d->erase(i.i);
+ return i;
+ }
- // STL compatibility
- typedef T mapped_type;
- typedef Key key_type;
- typedef qptrdiff difference_type;
- typedef int size_type;
+ QPair<iterator, iterator> equal_range(const Key &key)
+ {
+ auto first = find(key);
+ auto second = first;
+ if (second != iterator())
+ ++second;
+ return qMakePair(first, second);
+ }
- inline bool empty() const { return isEmpty(); }
+ QPair<const_iterator, const_iterator> equal_range(const Key &key) const noexcept
+ {
+ auto first = find(key);
+ auto second = first;
+ if (second != iterator())
+ ++second;
+ return qMakePair(first, second);
+ }
-#ifdef QT_QHASH_DEBUG
- inline void dump() const { d->dump(); }
- inline void checkSanity() const { d->checkSanity(); }
-#endif
+ typedef iterator Iterator;
+ typedef const_iterator ConstIterator;
+ inline qsizetype count() const noexcept { return d ? qsizetype(d->size) : 0; }
+ iterator find(const Key &key)
+ {
+ if (isEmpty()) // prevents detaching shared null
+ return end();
+ detach();
+ auto it = d->find(key);
+ if (it.isUnused())
+ it = d->end();
+ return iterator(it);
+ }
+ const_iterator find(const Key &key) const noexcept
+ {
+ if (isEmpty())
+ return end();
+ auto it = d->find(key);
+ if (it.isUnused())
+ it = d->end();
+ return const_iterator(it);
+ }
+ const_iterator constFind(const Key &key) const noexcept
+ {
+ return find(key);
+ }
+ iterator insert(const Key &key, const T &value)
+ {
+ detach();
-private:
- void detach_helper();
- void freeData(QHashData *d);
- Node **findNode(const Key &key, uint *hp = nullptr) const;
- Node **findNode(const Key &key, uint h) const;
- Node *createNode(uint h, const Key &key, const T &value, Node **nextNode);
- void deleteNode(Node *node);
- static void deleteNode2(QHashData::Node *node);
-
- static void duplicateNode(QHashData::Node *originalNode, void *newNode);
-
- bool isValidIterator(const iterator &it) const noexcept
- { return isValidNode(it.i); }
- bool isValidIterator(const const_iterator &it) const noexcept
- { return isValidNode(it.i); }
- bool isValidNode(QHashData::Node *node) const noexcept
- {
-#if defined(QT_DEBUG) && !defined(Q_HASH_NO_ITERATOR_DEBUG)
- while (node->next)
- node = node->next;
- return (static_cast<void *>(node) == d);
-#else
- Q_UNUSED(node);
- return true;
-#endif
+ auto i = d->insert(Node{key, value});
+ return iterator(i);
}
- friend class QSet<Key>;
- friend class QMultiHash<Key, T>;
-};
+ void insert(const QHash &hash)
+ {
+ if (d == hash.d || !hash.d)
+ return;
+ if (!d) {
+ *this = hash;
+ return;
+ }
+ detach();
-template <class Key, class T>
-Q_INLINE_TEMPLATE void QHash<Key, T>::deleteNode(Node *node)
-{
- deleteNode2(reinterpret_cast<QHashData::Node*>(node));
- d->freeNode(node);
-}
+ for (auto it = hash.begin(); it != hash.end(); ++it)
+ insert(it.key(), it.value());
+ }
-template <class Key, class T>
-Q_INLINE_TEMPLATE void QHash<Key, T>::deleteNode2(QHashData::Node *node)
-{
-#ifdef Q_CC_BOR
- concrete(node)->~QHashNode<Key, T>();
-#else
- concrete(node)->~Node();
-#endif
-}
+ float load_factor() const noexcept { return d ? d->loadFactor() : 0; }
+ static float max_load_factor() noexcept { return 0.5; }
+ size_t bucket_count() const noexcept { return d ? d->numBuckets : 0; }
+ static size_t max_bucket_count() noexcept { return QHashPrivate::GrowthPolicy::maxNumBuckets(); }
-template <class Key, class T>
-Q_INLINE_TEMPLATE void QHash<Key, T>::duplicateNode(QHashData::Node *node, void *newNode)
-{
- Node *concreteNode = concrete(node);
- new (newNode) Node(concreteNode->key, concreteNode->value, concreteNode->h, nullptr);
-}
+ inline bool empty() const noexcept { return isEmpty(); }
+};
-template <class Key, class T>
-Q_INLINE_TEMPLATE typename QHash<Key, T>::Node *
-QHash<Key, T>::createNode(uint ah, const Key &akey, const T &avalue, Node **anextNode)
-{
- Node *node = new (d->allocateNode(alignOfNode())) Node(akey, avalue, ah, *anextNode);
- *anextNode = node;
- ++d->size;
- return node;
-}
-template <class Key, class T>
-Q_OUTOFLINE_TEMPLATE void QHash<Key, T>::freeData(QHashData *x)
-{
- x->free_helper(deleteNode2);
-}
template <class Key, class T>
-Q_INLINE_TEMPLATE void QHash<Key, T>::clear()
+class QMultiHash
{
- *this = QHash();
-}
+ using Node = QHashPrivate::MultiNode<Key, T>;
+ using Data = QHashPrivate::Data<Node>;
+ using Chain = QHashPrivate::MultiNodeChain<T>;
-template <class Key, class T>
-Q_OUTOFLINE_TEMPLATE void QHash<Key, T>::detach_helper()
-{
- QHashData *x = d->detach_helper(duplicateNode, deleteNode2, sizeof(Node), alignOfNode());
- if (!d->ref.deref())
- freeData(d);
- d = x;
-}
+ Data *d = nullptr;
+ qsizetype m_size = 0;
-template <class Key, class T>
-Q_INLINE_TEMPLATE QHash<Key, T> &QHash<Key, T>::operator=(const QHash &other)
-{
- if (d != other.d) {
- QHashData *o = other.d;
- o->ref.ref();
- if (!d->ref.deref())
- freeData(d);
- d = o;
- if (!d->sharable)
- detach_helper();
+public:
+ using key_type = Key;
+ using mapped_type = T;
+ using value_type = T;
+ using size_type = qsizetype;
+ using difference_type = qsizetype;
+ using reference = T &;
+ using const_reference = const T &;
+
+ QMultiHash() noexcept = default;
+ inline QMultiHash(std::initializer_list<std::pair<Key,T> > list)
+ : d(new Data(list.size()))
+ {
+ for (typename std::initializer_list<std::pair<Key,T> >::const_iterator it = list.begin(); it != list.end(); ++it)
+ insert(it->first, it->second);
}
- return *this;
-}
-
-template <class Key, class T>
-Q_INLINE_TEMPLATE T QHash<Key, T>::value(const Key &akey) const
-{
- Node *node;
- if (d->size == 0 || (node = *findNode(akey)) == e) {
- return T();
- } else {
- return node->value;
+#ifdef Q_QDOC
+ template <typename InputIterator>
+ QMultiHash(InputIterator f, InputIterator l);
+#else
+ template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasKeyAndValue<InputIterator> = true>
+ QMultiHash(InputIterator f, InputIterator l)
+ {
+ QtPrivate::reserveIfForwardIterator(this, f, l);
+ for (; f != l; ++f)
+ insert(f.key(), f.value());
}
-}
-template <class Key, class T>
-Q_INLINE_TEMPLATE T QHash<Key, T>::value(const Key &akey, const T &adefaultValue) const
-{
- Node *node;
- if (d->size == 0 || (node = *findNode(akey)) == e) {
- return adefaultValue;
- } else {
- return node->value;
+ template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasFirstAndSecond<InputIterator> = true>
+ QMultiHash(InputIterator f, InputIterator l)
+ {
+ QtPrivate::reserveIfForwardIterator(this, f, l);
+ for (; f != l; ++f)
+ insert(f->first, f->second);
+ }
+#endif
+ QMultiHash(const QMultiHash &other) noexcept
+ : d(other.d), m_size(other.m_size)
+ {
+ if (d)
+ d->ref.ref();
+ }
+ ~QMultiHash()
+ {
+ if (d && !d->ref.deref())
+ delete d;
}
-}
-
-template <class Key, class T>
-Q_OUTOFLINE_TEMPLATE QList<Key> QHash<Key, T>::keys() const
-{
- QList<Key> res;
- res.reserve(size());
- const_iterator i = begin();
- while (i != end()) {
- res.append(i.key());
- ++i;
- }
- return res;
-}
-template <class Key, class T>
-Q_OUTOFLINE_TEMPLATE QList<Key> QHash<Key, T>::keys(const T &avalue) const
-{
- QList<Key> res;
- const_iterator i = begin();
- while (i != end()) {
- if (i.value() == avalue)
- res.append(i.key());
- ++i;
- }
- return res;
-}
+ QMultiHash &operator=(const QMultiHash &other) noexcept(std::is_nothrow_destructible<Node>::value)
+ {
+ if (d != other.d) {
+ Data *o = other.d;
+ if (o)
+ o->ref.ref();
+ if (d && !d->ref.deref())
+ delete d;
+ d = o;
+ m_size = other.m_size;
+ }
+ return *this;
+ }
+ QMultiHash(QMultiHash &&other) noexcept : d(other.d), m_size(other.m_size)
+ {
+ other.d = nullptr;
+ other.m_size = 0;
+ }
+ QMultiHash &operator=(QMultiHash &&other) noexcept(std::is_nothrow_destructible<Node>::value)
+ {
+ QMultiHash moved(std::move(other));
+ swap(moved);
+ return *this;
+ }
-template <class Key, class T>
-Q_OUTOFLINE_TEMPLATE Key QHash<Key, T>::key(const T &avalue) const
-{
- return key(avalue, Key());
-}
+ QMultiHash(const QHash<Key, T> &other)
+ : QMultiHash(other.begin(), other.end())
+ {}
+ void swap(QMultiHash &other) noexcept { qSwap(d, other.d); qSwap(m_size, other.m_size); }
-template <class Key, class T>
-Q_OUTOFLINE_TEMPLATE Key QHash<Key, T>::key(const T &avalue, const Key &defaultValue) const
-{
- const_iterator i = begin();
- while (i != end()) {
- if (i.value() == avalue)
- return i.key();
- ++i;
+ bool operator==(const QMultiHash &other) const noexcept
+ {
+ if (d == other.d)
+ return true;
+ if (!d || ! other.d)
+ return false;
+ if (m_size != other.m_size || d->size != other.d->size)
+ return false;
+ for (auto it = other.d->begin(); it != other.d->end(); ++it) {
+ auto i = d->find(it.node()->key);
+ if (i == d->end())
+ return false;
+ Chain *e = it.node()->value;
+ while (e) {
+ Chain *oe = i.node()->value;
+ while (oe) {
+ if (oe->value == e->value)
+ break;
+ oe = oe->next;
+ }
+ if (!oe)
+ return false;
+ e = e->next;
+ }
+ }
+ // all values must be the same as size is the same
+ return true;
}
+ bool operator!=(const QMultiHash &other) const noexcept { return !(*this == other); }
- return defaultValue;
-}
+ inline qsizetype size() const noexcept { return m_size; }
-template <class Key, class T>
-Q_OUTOFLINE_TEMPLATE QList<T> QHash<Key, T>::values() const
-{
- QList<T> res;
- res.reserve(size());
- const_iterator i = begin();
- while (i != end()) {
- res.append(i.value());
- ++i;
- }
- return res;
-}
+ inline bool isEmpty() const noexcept { return !m_size; }
-template <class Key, class T>
-Q_OUTOFLINE_TEMPLATE int QHash<Key, T>::count(const Key &akey) const
-{
- int cnt = 0;
- Node *node = *findNode(akey);
- if (node != e) {
- do {
- ++cnt;
- } while ((node = node->next) != e && node->key == akey);
- }
- return cnt;
-}
+ inline qsizetype capacity() const noexcept { return d ? qsizetype(d->numBuckets >> 1) : 0; }
+ void reserve(qsizetype size)
+ {
+ if (isDetached())
+ d->rehash(size);
+ else
+ d = Data::detached(d, size_t(size));
+ }
+ inline void squeeze() { reserve(0); }
-template <class Key, class T>
-Q_INLINE_TEMPLATE const T QHash<Key, T>::operator[](const Key &akey) const
-{
- return value(akey);
-}
+ inline void detach() { if (!d || d->ref.isShared()) d = Data::detached(d); }
+ inline bool isDetached() const noexcept { return d && !d->ref.isShared(); }
+ bool isSharedWith(const QMultiHash &other) const noexcept { return d == other.d; }
-template <class Key, class T>
-Q_INLINE_TEMPLATE T &QHash<Key, T>::operator[](const Key &akey)
-{
- detach();
+ void clear() noexcept(std::is_nothrow_destructible<Node>::value)
+ {
+ if (d && !d->ref.deref())
+ delete d;
+ d = nullptr;
+ m_size = 0;
+ }
- uint h;
- Node **node = findNode(akey, &h);
- if (*node == e) {
- if (d->willGrow())
- node = findNode(akey, h);
- return createNode(h, akey, T(), node)->value;
+ qsizetype remove(const Key &key)
+ {
+ if (isEmpty()) // prevents detaching shared null
+ return 0;
+ detach();
+
+ auto it = d->find(key);
+ if (it.isUnused())
+ return 0;
+ qsizetype n = Node::freeChain(it.node());
+ m_size -= n;
+ Q_ASSERT(m_size >= 0);
+ d->erase(it);
+ return n;
}
- return (*node)->value;
-}
+ T take(const Key &key)
+ {
+ if (isEmpty()) // prevents detaching shared null
+ return T();
+ detach();
-template <class Key, class T>
-Q_INLINE_TEMPLATE typename QHash<Key, T>::iterator QHash<Key, T>::insert(const Key &akey,
- const T &avalue)
-{
- detach();
+ auto it = d->find(key);
+ if (it.isUnused())
+ return T();
+ Q_ASSERT(it.node()->value.size());
+ Chain *e = it.node()->value;
+ Q_ASSERT(e);
+ if (!e->next)
+ d->erase(it);
+ else
+ it.node()->value = e->next;
+ --m_size;
+ Q_ASSERT(m_size >= 0);
+ T t = std::move(e->value);
+ delete e;
+ return t;
+ }
- uint h;
- Node **node = findNode(akey, &h);
- if (*node == e) {
- if (d->willGrow())
- node = findNode(akey, h);
- return iterator(createNode(h, akey, avalue, node));
+ bool contains(const Key &key) const noexcept
+ {
+ if (!d)
+ return false;
+ return d->findNode(key) != nullptr;
}
- if (!std::is_same<T, QHashDummyValue>::value)
- (*node)->value = avalue;
- return iterator(*node);
-}
+ Key key(const T &value, const Key &defaultKey = Key()) const noexcept
+ {
+ if (d) {
+ auto i = d->begin();
+ while (i != d->end()) {
+ Chain *e = i.node()->value;
+ if (e->contains(value))
+ return i.node()->key;
+ ++i;
+ }
+ }
-template <class Key, class T>
-Q_INLINE_TEMPLATE void QHash<Key, T>::insert(const QHash &hash)
-{
- if (d == hash.d)
- return;
-
- detach();
-
- QHashData::Node *i = hash.d->firstNode();
- QHashData::Node *end = reinterpret_cast<QHashData::Node *>(hash.e);
- while (i != end) {
- Node *n = concrete(i);
- Node **node = findNode(n->key, n->h);
- if (*node == e) {
- if (d->willGrow())
- node = findNode(n->key, n->h);
- createNode(n->h, n->key, n->value, node);
- } else {
- if (!std::is_same<T, QHashDummyValue>::value)
- (*node)->value = n->value;
+ return defaultKey;
+ }
+ T value(const Key &key, const T &defaultValue = T()) const noexcept
+ {
+ if (d) {
+ Node *n = d->findNode(key);
+ if (n) {
+ Q_ASSERT(n->value);
+ return n->value->value;
+ }
}
- i = QHashData::nextNode(i);
+ return defaultValue;
}
-}
-template <class Key, class T>
-Q_OUTOFLINE_TEMPLATE int QHash<Key, T>::remove(const Key &akey)
-{
- if (isEmpty()) // prevents detaching shared null
- return 0;
- detach();
-
- int oldSize = d->size;
- Node **node = findNode(akey);
- if (*node != e) {
- bool deleteNext = true;
- do {
- Node *next = (*node)->next;
- deleteNext = (next != e && next->key == (*node)->key);
- deleteNode(*node);
- *node = next;
- --d->size;
- } while (deleteNext);
- d->hasShrunk();
- }
- return oldSize - d->size;
-}
+ T &operator[](const Key &key)
+ {
+ detach();
+ Node *n = d->findAndInsertNode(key);
+ Q_ASSERT(n);
+ return n->value->value;
+ }
-template <class Key, class T>
-Q_OUTOFLINE_TEMPLATE T QHash<Key, T>::take(const Key &akey)
-{
- if (isEmpty()) // prevents detaching shared null
- return T();
- detach();
-
- Node **node = findNode(akey);
- if (*node != e) {
- T t = std::move((*node)->value);
- Node *next = (*node)->next;
- deleteNode(*node);
- *node = next;
- --d->size;
- d->hasShrunk();
- return t;
+ const T operator[](const Key &key) const noexcept
+ {
+ return value(key);
}
- return T();
-}
-template <class Key, class T>
-Q_OUTOFLINE_TEMPLATE typename QHash<Key, T>::iterator QHash<Key, T>::erase(const_iterator it)
-{
- Q_ASSERT_X(isValidIterator(it), "QHash::erase", "The specified iterator argument 'it' is invalid");
-
- if (it == const_iterator(e))
- return iterator(it.i);
-
- if (d->ref.isShared()) {
- // save 'it' across the detach:
- int bucketNum = (it.i->h % d->numBuckets);
- const_iterator bucketIterator(*(d->buckets + bucketNum));
- int stepsFromBucketStartToIte = 0;
- while (bucketIterator != it) {
- ++stepsFromBucketStartToIte;
- ++bucketIterator;
- }
- detach();
- it = const_iterator(*(d->buckets + bucketNum));
- while (stepsFromBucketStartToIte > 0) {
- --stepsFromBucketStartToIte;
- ++it;
+ QVector<Key> uniqueKeys() const
+ {
+ QVector<Key> res;
+ if (d) {
+ auto i = d->begin();
+ while (i != d->end()) {
+ res.append(i.node()->key);
+ ++i;
+ }
}
+ return res;
}
- iterator ret(it.i);
- ++ret;
+ QVector<Key> keys() const
+ {
+ return QVector<Key>(keyBegin(), keyEnd());
+ }
+ QVector<Key> keys(const T &value) const
+ {
+ QVector<Key> res;
+ const_iterator i = begin();
+ while (i != end()) {
+ if (i.value()->contains(value))
+ res.append(i.key());
+ ++i;
+ }
+ return res;
+ }
+ QVector<T> values() const
+ {
+ return QVector<T>(begin(), end());
+ }
+ QVector<T> values(const Key &key) const
+ {
+ QVector<T> values;
+ if (d) {
+ Node *n = d->findNode(key);
+ if (n) {
+ Chain *e = n->value;
+ while (e) {
+ values.append(e->value);
+ e = e->next;
+ }
+ }
+ }
+ return values;
+ }
- Node *node = concrete(it.i);
- Node **node_ptr = reinterpret_cast<Node **>(&d->buckets[node->h % d->numBuckets]);
- while (*node_ptr != node)
- node_ptr = &(*node_ptr)->next;
- *node_ptr = node->next;
- deleteNode(node);
- --d->size;
- return ret;
-}
+ class const_iterator;
-template <class Key, class T>
-Q_INLINE_TEMPLATE void QHash<Key, T>::reserve(int asize)
-{
- detach();
- d->rehash(-qMax(asize, 1));
-}
+ class iterator
+ {
+ using piter = typename QHashPrivate::iterator<Node>;
+ friend class const_iterator;
+ friend class QMultiHash<Key, T>;
+ piter i;
+ Chain **e = nullptr;
+ explicit inline iterator(piter it, Chain **entry = nullptr) noexcept : i(it), e(entry)
+ {
+ if (!it.atEnd() && !e) {
+ e = &it.node()->value;
+ Q_ASSERT(e && *e);
+ }
+ }
-template <class Key, class T>
-Q_INLINE_TEMPLATE typename QHash<Key, T>::const_iterator QHash<Key, T>::find(const Key &akey) const
-{
- return const_iterator(*findNode(akey));
-}
+ public:
+ typedef std::forward_iterator_tag iterator_category;
+ typedef qptrdiff difference_type;
+ typedef T value_type;
+ typedef T *pointer;
+ typedef T &reference;
-template <class Key, class T>
-Q_INLINE_TEMPLATE typename QHash<Key, T>::const_iterator QHash<Key, T>::constFind(const Key &akey) const
-{
- return const_iterator(*findNode(akey));
-}
+ constexpr iterator() noexcept = default;
+
+ inline const Key &key() const noexcept { return i.node()->key; }
+ inline T &value() const noexcept { return (*e)->value; }
+ inline T &operator*() const noexcept { return (*e)->value; }
+ inline T *operator->() const noexcept { return &(*e)->value; }
+ inline bool operator==(const iterator &o) const noexcept { return e == o.e; }
+ inline bool operator!=(const iterator &o) const noexcept { return e != o.e; }
+
+ inline iterator &operator++() noexcept {
+ Q_ASSERT(e && *e);
+ e = &(*e)->next;
+ Q_ASSERT(e);
+ if (!*e) {
+ ++i;
+ e = i.atEnd() ? nullptr : &i.node()->value;
+ }
+ return *this;
+ }
+ inline iterator operator++(int) noexcept {
+ iterator r = *this;
+ ++(*this);
+ return r;
+ }
-template <class Key, class T>
-Q_INLINE_TEMPLATE typename QHash<Key, T>::iterator QHash<Key, T>::find(const Key &akey)
-{
- detach();
- return iterator(*findNode(akey));
-}
+ inline bool operator==(const const_iterator &o) const noexcept { return e == o.e; }
+ inline bool operator!=(const const_iterator &o) const noexcept { return e != o.e; }
+ };
+ friend class iterator;
-template <class Key, class T>
-Q_INLINE_TEMPLATE bool QHash<Key, T>::contains(const Key &akey) const
-{
- return *findNode(akey) != e;
-}
+ class const_iterator
+ {
+ using piter = typename QHashPrivate::iterator<Node>;
+ friend class iterator;
+ friend class QMultiHash<Key, T>;
+ piter i;
+ Chain **e = nullptr;
+ explicit inline const_iterator(piter it, Chain **entry = nullptr) noexcept : i(it), e(entry)
+ {
+ if (!it.atEnd() && !e) {
+ e = &it.node()->value;
+ Q_ASSERT(e && *e);
+ }
+ }
-template <class Key, class T>
-Q_OUTOFLINE_TEMPLATE typename QHash<Key, T>::Node **QHash<Key, T>::findNode(const Key &akey, uint h) const
-{
- Node **node;
+ public:
+ typedef std::forward_iterator_tag iterator_category;
+ typedef qptrdiff difference_type;
+ typedef T value_type;
+ typedef const T *pointer;
+ typedef const T &reference;
- if (d->numBuckets) {
- node = reinterpret_cast<Node **>(&d->buckets[h % d->numBuckets]);
- Q_ASSERT(*node == e || (*node)->next);
- while (*node != e && !(*node)->same_key(h, akey))
- node = &(*node)->next;
- } else {
- node = const_cast<Node **>(reinterpret_cast<const Node * const *>(&e));
- }
- return node;
-}
+ constexpr const_iterator() noexcept = default;
+ inline const_iterator(const iterator &o) noexcept : i(o.i), e(o.e) { }
+
+ inline const Key &key() const noexcept { return i.node()->key; }
+ inline T &value() const noexcept { return (*e)->value; }
+ inline T &operator*() const noexcept { return (*e)->value; }
+ inline T *operator->() const noexcept { return &(*e)->value; }
+ inline bool operator==(const const_iterator &o) const noexcept { return e == o.e; }
+ inline bool operator!=(const const_iterator &o) const noexcept { return e != o.e; }
+
+ inline const_iterator &operator++() noexcept {
+ Q_ASSERT(e && *e);
+ e = &(*e)->next;
+ Q_ASSERT(e);
+ if (!*e) {
+ ++i;
+ e = i.atEnd() ? nullptr : &i.node()->value;
+ }
+ return *this;
+ }
+ inline const_iterator operator++(int) noexcept
+ {
+ const_iterator r = *this;
+ ++(*this);
+ return r;
+ }
+ };
+ friend class const_iterator;
-template <class Key, class T>
-Q_OUTOFLINE_TEMPLATE typename QHash<Key, T>::Node **QHash<Key, T>::findNode(const Key &akey,
- uint *ahp) const
-{
- uint h = 0;
+ class key_iterator
+ {
+ const_iterator i;
- if (d->numBuckets || ahp) {
- h = qHash(akey, d->seed);
- if (ahp)
- *ahp = h;
- }
- return findNode(akey, h);
-}
+ public:
+ typedef typename const_iterator::iterator_category iterator_category;
+ typedef qptrdiff difference_type;
+ typedef Key value_type;
+ typedef const Key *pointer;
+ typedef const Key &reference;
-template <class Key, class T>
-Q_OUTOFLINE_TEMPLATE bool QHash<Key, T>::operator==(const QHash &other) const
-{
- if (d == other.d)
- return true;
- if (size() != other.size())
- return false;
+ key_iterator() noexcept = default;
+ explicit key_iterator(const_iterator o) noexcept : i(o) { }
- const_iterator it = begin();
+ const Key &operator*() const noexcept { return i.key(); }
+ const Key *operator->() const noexcept { return &i.key(); }
+ bool operator==(key_iterator o) const noexcept { return i == o.i; }
+ bool operator!=(key_iterator o) const noexcept { return i != o.i; }
- while (it != end()) {
- // Build two equal ranges for i.key(); one for *this and one for other.
- // For *this we can avoid a lookup via equal_range, as we know the beginning of the range.
- auto thisEqualRangeStart = it;
- const Key &thisEqualRangeKey = it.key();
- size_type n = 0;
- do {
- ++it;
- ++n;
- } while (it != end() && it.key() == thisEqualRangeKey);
+ inline key_iterator &operator++() noexcept { ++i; return *this; }
+ inline key_iterator operator++(int) noexcept { return key_iterator(i++);}
+ const_iterator base() const noexcept { return i; }
+ };
- const auto otherEqualRange = other.equal_range(thisEqualRangeKey);
+ typedef QKeyValueIterator<const Key&, const T&, const_iterator> const_key_value_iterator;
+ typedef QKeyValueIterator<const Key&, T&, iterator> key_value_iterator;
- if (n != std::distance(otherEqualRange.first, otherEqualRange.second))
- return false;
+ // STL style
+ inline iterator begin() { detach(); return iterator(d->begin()); }
+ inline const_iterator begin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
+ inline const_iterator cbegin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
+ inline const_iterator constBegin() const noexcept { return d ? const_iterator(d->begin()): const_iterator(); }
+ inline iterator end() noexcept { return iterator(); }
+ inline const_iterator end() const noexcept { return const_iterator(); }
+ inline const_iterator cend() const noexcept { return const_iterator(); }
+ inline const_iterator constEnd() const noexcept { return const_iterator(); }
+ inline key_iterator keyBegin() const noexcept { return key_iterator(begin()); }
+ inline key_iterator keyEnd() const noexcept { return key_iterator(end()); }
+ inline key_value_iterator keyValueBegin() noexcept { return key_value_iterator(begin()); }
+ inline key_value_iterator keyValueEnd() noexcept { return key_value_iterator(end()); }
+ inline const_key_value_iterator keyValueBegin() const noexcept { return const_key_value_iterator(begin()); }
+ inline const_key_value_iterator constKeyValueBegin() const noexcept { return const_key_value_iterator(begin()); }
+ inline const_key_value_iterator keyValueEnd() const noexcept { return const_key_value_iterator(end()); }
+ inline const_key_value_iterator constKeyValueEnd() const noexcept { return const_key_value_iterator(end()); }
+
+ iterator detach(const_iterator it)
+ {
+ auto i = it.i;
+ Chain **e = it.e;
+ if (d->ref.isShared()) {
+ // need to store iterator position before detaching
+ qsizetype n = 0;
+ Chain *entry = i.node()->value;
+ while (entry != *it.e) {
+ ++n;
+ entry = entry->next;
+ }
+ Q_ASSERT(entry);
+ detach_helper();
- // Keys in the ranges are equal by construction; this checks only the values.
- if (!qt_is_permutation(thisEqualRangeStart, it, otherEqualRange.first, otherEqualRange.second))
- return false;
+ i = d->detachedIterator(i);
+ e = &i.node()->value;
+ while (n) {
+ e = &(*e)->next;
+ --n;
+ }
+ Q_ASSERT(e && *e);
+ }
+ return iterator(i, e);
}
- return true;
-}
-
-template <class Key, class T>
-QPair<typename QHash<Key, T>::iterator, typename QHash<Key, T>::iterator> QHash<Key, T>::equal_range(const Key &akey)
-{
- detach();
- auto pair = qAsConst(*this).equal_range(akey);
- return qMakePair(iterator(pair.first.i), iterator(pair.second.i));
-}
-
-template <class Key, class T>
-QPair<typename QHash<Key, T>::const_iterator, typename QHash<Key, T>::const_iterator> QHash<Key, T>::equal_range(const Key &akey) const noexcept
-{
- Node *node = *findNode(akey);
- const_iterator firstIt = const_iterator(node);
-
- if (node != e) {
- // equal keys must hash to the same value and so they all
- // end up in the same bucket. So we can use node->next,
- // which only works within a bucket, instead of (out-of-line)
- // QHashData::nextNode()
- while (node->next != e && node->next->key == akey)
- node = node->next;
-
- // 'node' may be the last node in the bucket. To produce the end iterator, we'd
- // need to enter the next bucket in this case, so we need to use
- // QHashData::nextNode() here, which, unlike node->next above, can move between
- // buckets.
- node = concrete(QHashData::nextNode(reinterpret_cast<QHashData::Node *>(node)));
+ iterator erase(const_iterator it)
+ {
+ Q_ASSERT(d);
+ iterator i = detach(it);
+ Chain *e = *i.e;
+ Chain *next = e->next;
+ *i.e = next;
+ delete e;
+ if (!next) {
+ if (i.e == &i.i.node()->value) {
+ // last remaining entry, erase
+ i = iterator(d->erase(i.i));
+ } else {
+ i = iterator(++it.i);
+ }
+ }
+ --m_size;
+ Q_ASSERT(m_size >= 0);
+ return i;
}
- return qMakePair(firstIt, const_iterator(node));
-}
-
-template <class Key, class T>
-class QMultiHash : public QHash<Key, T>
-{
-public:
- QMultiHash() noexcept {}
- inline QMultiHash(std::initializer_list<std::pair<Key,T> > list)
+ // more Qt
+ typedef iterator Iterator;
+ typedef const_iterator ConstIterator;
+ inline qsizetype count() const noexcept { return size(); }
+ iterator find(const Key &key)
{
- this->reserve(int(list.size()));
- for (typename std::initializer_list<std::pair<Key,T> >::const_iterator it = list.begin(); it != list.end(); ++it)
- insert(it->first, it->second);
+ if (isEmpty())
+ return end();
+ detach();
+ auto it = d->find(key);
+ if (it.isUnused())
+ it = d->end();
+ return iterator(it);
}
-#ifdef Q_QDOC
- template <typename InputIterator>
- QMultiHash(InputIterator f, InputIterator l);
-#else
- template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasKeyAndValue<InputIterator> = true>
- QMultiHash(InputIterator f, InputIterator l)
+ const_iterator find(const Key &key) const noexcept
{
- QtPrivate::reserveIfForwardIterator(this, f, l);
- for (; f != l; ++f)
- insert(f.key(), f.value());
+ return constFind(key);
}
-
- template <typename InputIterator, QtPrivate::IfAssociativeIteratorHasFirstAndSecond<InputIterator> = true>
- QMultiHash(InputIterator f, InputIterator l)
+ const_iterator constFind(const Key &key) const noexcept
{
- QtPrivate::reserveIfForwardIterator(this, f, l);
- for (; f != l; ++f)
- insert(f->first, f->second);
+ if (isEmpty())
+ return end();
+ auto it = d->find(key);
+ if (it.isUnused())
+ it = d->end();
+ return const_iterator(it);
+ }
+ iterator insert(const Key &key, const T &value)
+ {
+ detach();
+ auto it = d->insertMulti(key, value);
+ ++m_size;
+ return iterator(it);
}
-#endif
- // compiler-generated copy/move ctors/assignment operators are fine!
- // compiler-generated destructor is fine!
-
- QMultiHash(const QHash<Key, T> &other) : QHash<Key, T>(other) {}
- QMultiHash(QHash<Key, T> &&other) noexcept : QHash<Key, T>(std::move(other)) {}
- void swap(QMultiHash &other) noexcept { QHash<Key, T>::swap(other); } // prevent QMultiHash<->QHash swaps
- inline typename QHash<Key, T>::iterator replace(const Key &key, const T &value)
- { return QHash<Key, T>::insert(key, value); }
+ float load_factor() const noexcept { return d ? d->loadFactor() : 0; }
+ static float max_load_factor() noexcept { return 0.5; }
+ size_t bucket_count() const noexcept { return d ? d->numBuckets : 0; }
+ static size_t max_bucket_count() noexcept { return QHashPrivate::GrowthPolicy::maxNumBuckets(); }
- typename QHash<Key, T>::iterator insert(const Key &key, const T &value);
+ inline bool empty() const noexcept { return isEmpty(); }
- inline QMultiHash &unite(const QMultiHash &other);
+ inline typename QMultiHash<Key, T>::iterator replace(const Key &key, const T &value)
+ {
+ detach();
+ qsizetype s = d->size;
+ auto it = d->insert(key, value);
+ m_size += d->size - s;
+ return iterator(it);
+ }
inline QMultiHash &operator+=(const QMultiHash &other)
- { return unite(other); }
+ { this->unite(other); return *this; }
inline QMultiHash operator+(const QMultiHash &other) const
{ QMultiHash result = *this; result += other; return result; }
- using QHash<Key, T>::contains;
- using QHash<Key, T>::remove;
- using QHash<Key, T>::count;
- using QHash<Key, T>::find;
- using QHash<Key, T>::constFind;
- using QHash<Key, T>::values;
- using QHash<Key, T>::findNode;
- using QHash<Key, T>::createNode;
- using QHash<Key, T>::concrete;
- using QHash<Key, T>::detach;
+ bool contains(const Key &key, const T &value) const noexcept
+ {
+ if (isEmpty())
+ return false;
+ auto n = d->findNode(key);
+ if (n == nullptr)
+ return false;
+ return n->value->contains(value);
+ }
- using typename QHash<Key, T>::Node;
- using typename QHash<Key, T>::iterator;
- using typename QHash<Key, T>::const_iterator;
+ qsizetype remove(const Key &key, const T &value)
+ {
+ if (isEmpty()) // prevents detaching shared null
+ return false;
+ detach();
- bool contains(const Key &key, const T &value) const;
+ auto it = d->find(key);
+ if (it.isUnused())
+ return 0;
+ qsizetype n = 0;
+ Chain **e = &it.node()->value;
+ while (*e) {
+ Chain *entry = *e;
+ if (entry->value == value) {
+ *e = entry->next;
+ delete entry;
+ ++n;
+ } else {
+ e = &entry->next;
+ }
+ }
+ if (!it.node()->value)
+ d->erase(it);
+ m_size -= n;
+ Q_ASSERT(m_size >= 0);
+ return n;
+ }
- int remove(const Key &key, const T &value);
+ qsizetype count(const Key &key) const noexcept
+ {
+ auto it = d->find(key);
+ if (it.isUnused())
+ return 0;
+ qsizetype n = 0;
+ Chain *e = it.node()->value;
+ while (e) {
+ ++n;
+ e = e->next;
+ }
- int count(const Key &key, const T &value) const;
+ return n;
+ }
- QList<Key> uniqueKeys() const;
+ qsizetype count(const Key &key, const T &value) const noexcept
+ {
+ auto it = d->find(key);
+ if (it.isUnused())
+ return 0;
+ qsizetype n = 0;
+ Chain *e = it.node()->value;
+ while (e) {
+ if (e->value == value)
+ ++n;
+ e = e->next;
+ }
- QList<T> values(const Key &akey) const;
+ return n;
+ }
- typename QHash<Key, T>::iterator find(const Key &key, const T &value) {
- typename QHash<Key, T>::iterator i(find(key));
- typename QHash<Key, T>::iterator end(this->end());
- while (i != end && i.key() == key) {
- if (i.value() == value)
- return i;
- ++i;
- }
- return end;
+ iterator find(const Key &key, const T &value)
+ {
+ detach();
+ auto it = constFind(key, value);
+ return iterator(it.i, it.e);
}
- typename QHash<Key, T>::const_iterator find(const Key &key, const T &value) const {
- typename QHash<Key, T>::const_iterator i(constFind(key));
- typename QHash<Key, T>::const_iterator end(QHash<Key, T>::constEnd());
+ const_iterator find(const Key &key, const T &value) const noexcept
+ {
+ return constFind(key, value);
+ }
+ const_iterator constFind(const Key &key, const T &value) const noexcept
+ {
+ const_iterator i(constFind(key));
+ const_iterator end(constEnd());
while (i != end && i.key() == key) {
if (i.value() == value)
return i;
@@ -1092,229 +1795,108 @@ public:
}
return end;
}
- typename QHash<Key, T>::const_iterator constFind(const Key &key, const T &value) const
- { return find(key, value); }
-private:
- T &operator[](const Key &key);
- const T operator[](const Key &key) const;
-};
-
-template <class Key, class T>
-Q_INLINE_TEMPLATE typename QHash<Key, T>::iterator QMultiHash<Key, T>::insert(const Key &akey, const T &avalue)
-{
- detach();
- this->d->willGrow();
-
- uint h;
- Node **nextNode = findNode(akey, &h);
- return iterator(createNode(h, akey, avalue, nextNode));
-}
-template <class Key, class T>
-Q_INLINE_TEMPLATE QMultiHash<Key, T> &QMultiHash<Key, T>::unite(const QMultiHash &other)
-{
- if (this->d == &QHashData::shared_null) {
- *this = other;
- } else {
-#if QT_DEPRECATED_SINCE(5, 15)
- QMultiHash copy(other);
- const_iterator it = copy.constEnd();
- while (it != copy.constBegin()) {
- it.i = QHashData::previousNode(it.i);
- insert(it.key(), it.value());
- }
-#else
- const QMultiHash copy(other);
- const_iterator it = copy.cbegin();
- const const_iterator end = copy.cend();
- while (it != end) {
- const auto rangeStart = it++;
- while (it != end && rangeStart.key() == it.key())
- ++it;
- const qint64 last = std::distance(rangeStart, it) - 1;
- for (qint64 i = last; i >= 0; --i) {
- auto next = std::next(rangeStart, i);
- insert(next.key(), next.value());
- }
+ QMultiHash &unite(const QMultiHash &other)
+ {
+ if (isEmpty()) {
+ *this = other;
+ } else if (other.isEmpty()) {
+ ;
+ } else {
+ QMultiHash copy(other);
+ detach();
+ for (auto cit = copy.cbegin(); cit != copy.cend(); ++cit)
+ insert(cit.key(), *cit);
}
-#endif
+ return *this;
}
- return *this;
-}
-
-
-template <class Key, class T>
-Q_INLINE_TEMPLATE bool QMultiHash<Key, T>::contains(const Key &key, const T &value) const
-{
- return constFind(key, value) != QHash<Key, T>::constEnd();
-}
-template <class Key, class T>
-Q_INLINE_TEMPLATE int QMultiHash<Key, T>::remove(const Key &key, const T &value)
-{
- int n = 0;
- typename QHash<Key, T>::iterator i(find(key));
- typename QHash<Key, T>::iterator end(QHash<Key, T>::end());
- while (i != end && i.key() == key) {
- if (i.value() == value) {
- i = this->erase(i);
- ++n;
- } else {
- ++i;
- }
+ QPair<iterator, iterator> equal_range(const Key &key)
+ {
+ detach();
+ auto pair = qAsConst(*this).equal_range(key);
+ return qMakePair(iterator(pair.first.i), iterator(pair.second.i));
}
- return n;
-}
-template <class Key, class T>
-Q_INLINE_TEMPLATE int QMultiHash<Key, T>::count(const Key &key, const T &value) const
-{
- int n = 0;
- typename QHash<Key, T>::const_iterator i(constFind(key));
- typename QHash<Key, T>::const_iterator end(QHash<Key, T>::constEnd());
- while (i != end && i.key() == key) {
- if (i.value() == value)
- ++n;
- ++i;
+ QPair<const_iterator, const_iterator> equal_range(const Key &key) const noexcept
+ {
+ auto it = d->find(key);
+ if (it.isUnused())
+ return qMakePair(end(), end());
+ auto end = it;
+ ++end;
+ return qMakePair(const_iterator(it), const_iterator(end));
}
- return n;
-}
-template <class Key, class T>
-Q_OUTOFLINE_TEMPLATE QList<Key> QMultiHash<Key, T>::uniqueKeys() const
-{
- QList<Key> res;
- res.reserve(QHash<Key, T>::size()); // May be too much, but assume short lifetime
- typename QHash<Key, T>::const_iterator i = QHash<Key, T>::begin();
- if (i != QHash<Key, T>::end()) {
- for (;;) {
- const Key &aKey = i.key();
- res.append(aKey);
- do {
- if (++i == QHash<Key, T>::end())
- goto break_out_of_outer_loop;
- } while (aKey == i.key());
+private:
+ void detach_helper()
+ {
+ if (!d) {
+ d = new Data;
+ return;
}
+ Data *dd = new Data(*d);
+ if (!d->ref.deref())
+ delete d;
+ d = dd;
}
-break_out_of_outer_loop:
- return res;
-}
-
-#if QT_DEPRECATED_SINCE(5, 15)
-
-template <class Key, class T>
-Q_OUTOFLINE_TEMPLATE typename QHash<Key, T>::iterator QHash<Key, T>::insertMulti(const Key &key, const T &value) {
- return static_cast<QMultiHash<Key, T> *>(this)->insert(key, value);
-}
-
-template <class Key, class T>
-Q_OUTOFLINE_TEMPLATE QHash<Key, T> &QHash<Key, T>::unite(const QHash &other) {
- return static_cast<QMultiHash<Key, T> *>(this)->unite(other);
-}
-
-template <class Key, class T>
-Q_OUTOFLINE_TEMPLATE QList<T> QHash<Key, T>::values(const Key &akey) const
-{
- return static_cast<const QMultiHash<Key, T> *>(this)->values(akey);
-}
-
-template <class Key, class T>
-Q_OUTOFLINE_TEMPLATE QList<Key> QHash<Key, T>::uniqueKeys() const
-{
- return static_cast<const QMultiHash<Key, T> *>(this)->uniqueKeys();
-}
-#endif
-
-template <class Key, class T>
-Q_OUTOFLINE_TEMPLATE QList<T> QMultiHash<Key, T>::values(const Key &akey) const
-{
- QList<T> res;
- Node *node = *findNode(akey);
- if (node != this->e) {
- do {
- res.append(node->value);
- } while ((node = node->next) != this->e && node->key == akey);
- }
- return res;
-}
+};
#if !defined(QT_NO_JAVA_STYLE_ITERATORS)
-template <class Key, class T>
+template<class Key, class T>
class QHashIterator
{
typedef typename QHash<Key, T>::const_iterator const_iterator;
typedef const_iterator Item;
QHash<Key, T> c;
const_iterator i, n;
- inline bool item_exists() const { return n != c.constEnd(); }
+ inline bool item_exists() const noexcept { return n != c.constEnd(); }
public:
- inline QHashIterator(const QHash<Key, T> &container)
+ inline QHashIterator(const QHash<Key, T> &container) noexcept
: c(container), i(c.constBegin()), n(c.constEnd())
- {
- }
- inline QHashIterator &operator=(const QHash<Key, T> &container)
+ { }
+ inline QHashIterator &operator=(const QHash<Key, T> &container) noexcept
{
c = container;
i = c.constBegin();
n = c.constEnd();
return *this;
}
- inline void toFront()
+ inline void toFront() noexcept
{
i = c.constBegin();
n = c.constEnd();
}
- inline void toBack()
+ inline void toBack() noexcept
{
i = c.constEnd();
n = c.constEnd();
}
- inline bool hasNext() const { return i != c.constEnd(); }
- inline Item next()
+ inline bool hasNext() const noexcept { return i != c.constEnd(); }
+ inline Item next() noexcept
{
n = i++;
return n;
}
- inline Item peekNext() const { return i; }
- inline const T &value() const
+ inline Item peekNext() const noexcept { return i; }
+ inline const T &value() const noexcept
{
Q_ASSERT(item_exists());
return *n;
}
- inline const Key &key() const
+ inline const Key &key() const noexcept
{
Q_ASSERT(item_exists());
return n.key();
}
- inline bool findNext(const T &t)
+ inline bool findNext(const T &t) noexcept
{
while ((n = i) != c.constEnd())
if (*i++ == t)
return true;
return false;
}
-#if QT_DEPRECATED_SINCE(5, 15)
- inline QT_DEPRECATED_VERSION_5_15 bool hasPrevious() const { return i != c.constBegin(); }
- inline QT_DEPRECATED_VERSION_5_15 Item previous()
- {
- n = --i;
- return n;
- }
- inline QT_DEPRECATED_VERSION_5_15 Item peekPrevious() const
- {
- const_iterator p = i;
- return --p;
- }
- inline bool QT_DEPRECATED_VERSION_5_15 findPrevious(const T &t)
- {
- while (i != c.constBegin())
- if (*(n = --i) == t)
- return true;
- n = c.constEnd();
- return false;
- }
-#endif
};
template<class Key, class T>
@@ -1325,10 +1907,11 @@ class QMutableHashIterator
typedef iterator Item;
QHash<Key, T> *c;
iterator i, n;
- inline bool item_exists() const { return const_iterator(n) != c->constEnd(); }
+ inline bool item_exists() const noexcept { return const_iterator(n) != c->constEnd(); }
public:
- inline QMutableHashIterator(QHash<Key, T> &container) : c(&container)
+ inline QMutableHashIterator(QHash<Key, T> &container)
+ : c(&container)
{
i = c->begin();
n = c->end();
@@ -1345,18 +1928,18 @@ public:
i = c->begin();
n = c->end();
}
- inline void toBack()
+ inline void toBack() noexcept
{
i = c->end();
n = c->end();
}
- inline bool hasNext() const { return const_iterator(i) != c->constEnd(); }
- inline Item next()
+ inline bool hasNext() const noexcept { return const_iterator(i) != c->constEnd(); }
+ inline Item next() noexcept
{
n = i++;
return n;
}
- inline Item peekNext() const { return i; }
+ inline Item peekNext() const noexcept { return i; }
inline void remove()
{
if (const_iterator(n) != c->constEnd()) {
@@ -1369,49 +1952,28 @@ public:
if (const_iterator(n) != c->constEnd())
*n = t;
}
- inline T &value()
+ inline T &value() noexcept
{
Q_ASSERT(item_exists());
return *n;
}
- inline const T &value() const
+ inline const T &value() const noexcept
{
Q_ASSERT(item_exists());
return *n;
}
- inline const Key &key() const
+ inline const Key &key() const noexcept
{
Q_ASSERT(item_exists());
return n.key();
}
- inline bool findNext(const T &t)
+ inline bool findNext(const T &t) noexcept
{
while (const_iterator(n = i) != c->constEnd())
if (*i++ == t)
return true;
return false;
}
-#if QT_DEPRECATED_SINCE(5, 15)
- inline QT_DEPRECATED_VERSION_5_15 bool hasPrevious() const { return const_iterator(i) != c->constBegin(); }
- inline QT_DEPRECATED_VERSION_5_15 Item previous()
- {
- n = --i;
- return n;
- }
- inline QT_DEPRECATED_VERSION_5_15 Item peekPrevious() const
- {
- iterator p = i;
- return --p;
- }
- inline QT_DEPRECATED_VERSION_5_15 bool findPrevious(const T &t)
- {
- while (const_iterator(i) != c->constBegin())
- if (*(n = --i) == t)
- return true;
- n = c->end();
- return false;
- }
-#endif
};
#endif // !QT_NO_JAVA_STYLE_ITERATORS
@@ -1438,8 +2000,4 @@ inline uint qHash(const QMultiHash<Key, T> &key, uint seed = 0)
QT_END_NAMESPACE
-#if defined(Q_CC_MSVC)
-#pragma warning( pop )
-#endif
-
#endif // QHASH_H