/**************************************************************************** ** ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this ** file. Please review the following information to ensure the GNU Lesser ** General Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU General ** Public License version 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of this ** file. Please review the following information to ensure the GNU General ** Public License version 3.0 requirements will be met: ** http://www.gnu.org/copyleft/gpl.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QHASH_H #define QHASH_H #include #include #include #include #include QT_BEGIN_HEADER QT_BEGIN_NAMESPACE QT_MODULE(Core) class QBitArray; class QByteArray; class QString; class QStringRef; inline uint qHash(char key) { return uint(key); } inline uint qHash(uchar key) { return uint(key); } inline uint qHash(signed char key) { return uint(key); } inline uint qHash(ushort key) { return uint(key); } inline uint qHash(short key) { return uint(key); } inline uint qHash(uint key) { return key; } inline uint qHash(int key) { return uint(key); } inline uint qHash(ulong key) { if (sizeof(ulong) > sizeof(uint)) { return uint(((key >> (8 * sizeof(uint) - 1)) ^ key) & (~0U)); } else { return uint(key & (~0U)); } } inline uint qHash(long key) { return qHash(ulong(key)); } inline uint qHash(quint64 key) { if (sizeof(quint64) > sizeof(uint)) { return uint(((key >> (8 * sizeof(uint) - 1)) ^ key) & (~0U)); } else { return uint(key & (~0U)); } } inline uint qHash(qint64 key) { return qHash(quint64(key)); } inline uint qHash(QChar key) { return qHash(key.unicode()); } Q_CORE_EXPORT uint qHash(const QByteArray &key); Q_CORE_EXPORT uint qHash(const QString &key); Q_CORE_EXPORT uint qHash(const QStringRef &key); Q_CORE_EXPORT uint qHash(const QBitArray &key); #if defined(Q_CC_MSVC) #pragma warning( push ) #pragma warning( disable : 4311 ) // disable pointer truncation warning #endif template inline uint qHash(const T *key) { return qHash(reinterpret_cast(key)); } #if defined(Q_CC_MSVC) #pragma warning( pop ) #endif template inline uint qHash(const QPair &key) { uint h1 = qHash(key.first); uint h2 = qHash(key.second); return ((h1 << 16) | (h1 >> 16)) ^ h2; } struct Q_CORE_EXPORT QHashData { struct Node { Node *next; uint h; }; Node *fakeNext; Node **buckets; QBasicAtomicInt ref; int size; int nodeSize; short userNumBits; short numBits; int numBuckets; uint sharable : 1; uint strictAlignment : 1; uint reserved : 30; void *allocateNode(); // ### Qt5 remove me void *allocateNode(int nodeAlign); void freeNode(void *node); QHashData *detach_helper(void (*node_duplicate)(Node *, void *), int nodeSize); // ### Qt5 remove me QHashData *detach_helper2(void (*node_duplicate)(Node *, void *), void (*node_delete)(Node *), int nodeSize, int nodeAlign); void mightGrow(); bool willGrow(); void hasShrunk(); void rehash(int hint); void free_helper(void (*node_delete)(Node *)); void destroyAndFree(); // ### Qt5 remove me Node *firstNode(); #ifdef QT_QHASH_DEBUG void dump(); void checkSanity(); #endif static Node *nextNode(Node *node); static Node *previousNode(Node *node); static QHashData shared_null; }; inline void QHashData::mightGrow() // ### Qt 5: eliminate { if (size >= numBuckets) rehash(numBits + 1); } inline bool QHashData::willGrow() { if (size >= numBuckets) { rehash(numBits + 1); return true; } else { return false; } } inline void QHashData::hasShrunk() { 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. } } } inline QHashData::Node *QHashData::firstNode() { Node *e = reinterpret_cast(this); Node **bucket = buckets; int n = numBuckets; while (n--) { if (*bucket != e) return *bucket; ++bucket; } return e; } struct QHashDummyValue { }; inline bool operator==(const QHashDummyValue & /* v1 */, const QHashDummyValue & /* v2 */) { return true; } Q_DECLARE_TYPEINFO(QHashDummyValue, Q_MOVABLE_TYPE | Q_DUMMY_TYPE); template struct QHashDummyNode { QHashDummyNode *next; uint h; Key key; inline QHashDummyNode(const Key &key0) : key(key0) {} }; template struct QHashNode { QHashNode *next; uint h; Key key; T value; inline QHashNode(const Key &key0) : key(key0) {} // ### remove in 5.0 inline QHashNode(const Key &key0, const T &value0) : key(key0), value(value0) {} inline bool same_key(uint h0, const Key &key0) { return h0 == h && key0 == key; } }; #define Q_HASH_DECLARE_INT_NODES(key_type) \ template \ struct QHashDummyNode { \ QHashDummyNode *next; \ union { uint h; key_type key; }; \ \ inline QHashDummyNode(key_type /* key0 */) {} \ }; \ \ template \ struct QHashNode { \ 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) { 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 template class QHash { typedef QHashDummyNode DummyNode; typedef QHashNode Node; union { QHashData *d; QHashNode *e; }; static inline Node *concrete(QHashData::Node *node) { return reinterpret_cast(node); } #ifdef Q_ALIGNOF static inline int alignOfNode() { return qMax(sizeof(void*), Q_ALIGNOF(Node)); } static inline int alignOfDummyNode() { return qMax(sizeof(void*), Q_ALIGNOF(DummyNode)); } #else static inline int alignOfNode() { return 0; } static inline int alignOfDummyNode() { return 0; } #endif public: inline QHash() : d(&QHashData::shared_null) { d->ref.ref(); } inline QHash(const QHash &other) : d(other.d) { d->ref.ref(); if (!d->sharable) detach(); } inline ~QHash() { if (!d->ref.deref()) freeData(d); } QHash &operator=(const QHash &other); #ifdef Q_COMPILER_RVALUE_REFS inline QHash &operator=(QHash &&other) { qSwap(d, other.d); return *this; } #endif inline void swap(QHash &other) { qSwap(d, other.d); } bool operator==(const QHash &other) const; inline bool operator!=(const QHash &other) const { return !(*this == other); } inline int size() const { return d->size; } inline bool isEmpty() const { return d->size == 0; } inline int capacity() const { return d->numBuckets; } void reserve(int size); inline void squeeze() { reserve(1); } inline void detach() { if (d->ref != 1) detach_helper(); } inline bool isDetached() const { return d->ref == 1; } inline void setSharable(bool sharable) { if (!sharable) detach(); d->sharable = sharable; } inline bool isSharedWith(const QHash &other) const { return d == other.d; } void clear(); int remove(const Key &key); T take(const Key &key); bool contains(const Key &key) const; const Key key(const T &value) const; const Key key(const T &value, const Key &defaultKey) const; const T value(const Key &key) const; const T value(const Key &key, const T &defaultValue) const; T &operator[](const Key &key); const T operator[](const Key &key) const; QList uniqueKeys() const; QList keys() const; QList keys(const T &value) const; QList values() const; QList values(const Key &key) const; int count(const Key &key) const; class const_iterator; class iterator { friend class const_iterator; QHashData::Node *i; public: typedef std::bidirectional_iterator_tag iterator_category; typedef qptrdiff difference_type; typedef T value_type; typedef T *pointer; typedef T &reference; // ### Qt 5: get rid of 'operator Node *' inline operator Node *() const { return concrete(i); } inline iterator() : i(0) { } explicit inline iterator(void *node) : i(reinterpret_cast(node)) { } 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 iterator &operator++() { i = QHashData::nextNode(i); return *this; } inline iterator operator++(int) { iterator r = *this; i = QHashData::nextNode(i); return r; } inline iterator &operator--() { i = QHashData::previousNode(i); return *this; } inline iterator operator--(int) { iterator r = *this; i = QHashData::previousNode(i); return r; } inline iterator operator+(int j) const { iterator r = *this; if (j > 0) while (j--) ++r; else while (j++) --r; return r; } inline iterator operator-(int j) const { return operator+(-j); } inline iterator &operator+=(int j) { return *this = *this + j; } inline iterator &operator-=(int j) { return *this = *this - j; } // ### Qt 5: not sure this is necessary anymore #ifdef QT_STRICT_ITERATORS private: #else public: #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; } private: // ### Qt 5: remove inline operator bool() const { return false; } }; friend class iterator; class const_iterator { friend class iterator; QHashData::Node *i; public: typedef std::bidirectional_iterator_tag iterator_category; typedef qptrdiff difference_type; typedef T value_type; typedef const T *pointer; typedef const T &reference; // ### Qt 5: get rid of 'operator Node *' inline operator Node *() const { return concrete(i); } inline const_iterator() : i(0) { } explicit inline const_iterator(void *node) : i(reinterpret_cast(node)) { } #ifdef QT_STRICT_ITERATORS explicit inline const_iterator(const iterator &o) #else inline const_iterator(const iterator &o) #endif { 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; } 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 const_iterator &operator++() { i = QHashData::nextNode(i); return *this; } inline const_iterator operator++(int) { const_iterator r = *this; i = QHashData::nextNode(i); return r; } inline const_iterator &operator--() { i = QHashData::previousNode(i); return *this; } inline const_iterator operator--(int) { const_iterator r = *this; i = QHashData::previousNode(i); return r; } inline const_iterator operator+(int j) const { const_iterator r = *this; if (j > 0) while (j--) ++r; else while (j++) --r; return r; } inline const_iterator operator-(int j) const { return operator+(-j); } inline const_iterator &operator+=(int j) { return *this = *this + j; } inline const_iterator &operator-=(int j) { return *this = *this - j; } // ### Qt 5: not sure this is necessary anymore #ifdef QT_STRICT_ITERATORS private: inline bool operator==(const iterator &o) const { return operator==(const_iterator(o)); } inline bool operator!=(const iterator &o) const { return operator!=(const_iterator(o)); } #endif private: // ### Qt 5: remove inline operator bool() const { return false; } }; friend class const_iterator; // STL style inline iterator begin() { detach(); return iterator(d->firstNode()); } inline const_iterator begin() 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 constEnd() const { return const_iterator(e); } iterator erase(iterator it); // 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); iterator insertMulti(const Key &key, const T &value); QHash &unite(const QHash &other); // STL compatibility typedef T mapped_type; typedef Key key_type; typedef qptrdiff difference_type; typedef int size_type; inline bool empty() const { return isEmpty(); } #ifdef QT_QHASH_DEBUG inline void dump() const { d->dump(); } inline void checkSanity() const { d->checkSanity(); } #endif private: void detach_helper(); void freeData(QHashData *d); Node **findNode(const Key &key, uint *hp = 0) 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); }; template Q_INLINE_TEMPLATE void QHash::deleteNode(Node *node) { deleteNode2(reinterpret_cast(node)); d->freeNode(node); } template Q_INLINE_TEMPLATE void QHash::deleteNode2(QHashData::Node *node) { #ifdef Q_CC_BOR concrete(node)->~QHashNode(); #else concrete(node)->~Node(); #endif } template Q_INLINE_TEMPLATE void QHash::duplicateNode(QHashData::Node *node, void *newNode) { Node *concreteNode = concrete(node); if (QTypeInfo::isDummy) { (void) new (newNode) DummyNode(concreteNode->key); } else { (void) new (newNode) Node(concreteNode->key, concreteNode->value); } } template Q_INLINE_TEMPLATE typename QHash::Node * QHash::createNode(uint ah, const Key &akey, const T &avalue, Node **anextNode) { Node *node; if (QTypeInfo::isDummy) { node = reinterpret_cast(new (d->allocateNode(alignOfDummyNode())) DummyNode(akey)); } else { node = new (d->allocateNode(alignOfNode())) Node(akey, avalue); } node->h = ah; node->next = *anextNode; *anextNode = node; ++d->size; return node; } template Q_INLINE_TEMPLATE QHash &QHash::unite(const QHash &other) { QHash copy(other); const_iterator it = copy.constEnd(); while (it != copy.constBegin()) { --it; insertMulti(it.key(), it.value()); } return *this; } template Q_OUTOFLINE_TEMPLATE void QHash::freeData(QHashData *x) { x->free_helper(deleteNode2); } template Q_INLINE_TEMPLATE void QHash::clear() { *this = QHash(); } template Q_OUTOFLINE_TEMPLATE void QHash::detach_helper() { QHashData *x = d->detach_helper2(duplicateNode, deleteNode2, QTypeInfo::isDummy ? sizeof(DummyNode) : sizeof(Node), QTypeInfo::isDummy ? alignOfDummyNode() : alignOfNode()); if (!d->ref.deref()) freeData(d); d = x; } template Q_INLINE_TEMPLATE QHash &QHash::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(); } return *this; } template Q_INLINE_TEMPLATE const T QHash::value(const Key &akey) const { Node *node; if (d->size == 0 || (node = *findNode(akey)) == e) { return T(); } else { return node->value; } } template Q_INLINE_TEMPLATE const T QHash::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 Q_OUTOFLINE_TEMPLATE QList QHash::uniqueKeys() const { QList res; res.reserve(size()); // May be too much, but assume short lifetime const_iterator i = begin(); if (i != end()) { for (;;) { const Key &aKey = i.key(); res.append(aKey); do { if (++i == end()) goto break_out_of_outer_loop; } while (aKey == i.key()); } } break_out_of_outer_loop: return res; } template Q_OUTOFLINE_TEMPLATE QList QHash::keys() const { QList res; res.reserve(size()); const_iterator i = begin(); while (i != end()) { res.append(i.key()); ++i; } return res; } template Q_OUTOFLINE_TEMPLATE QList QHash::keys(const T &avalue) const { QList res; const_iterator i = begin(); while (i != end()) { if (i.value() == avalue) res.append(i.key()); ++i; } return res; } template Q_OUTOFLINE_TEMPLATE const Key QHash::key(const T &avalue) const { return key(avalue, Key()); } template Q_OUTOFLINE_TEMPLATE const Key QHash::key(const T &avalue, const Key &defaultValue) const { const_iterator i = begin(); while (i != end()) { if (i.value() == avalue) return i.key(); ++i; } return defaultValue; } template Q_OUTOFLINE_TEMPLATE QList QHash::values() const { QList res; res.reserve(size()); const_iterator i = begin(); while (i != end()) { res.append(i.value()); ++i; } return res; } template Q_OUTOFLINE_TEMPLATE QList QHash::values(const Key &akey) const { QList res; Node *node = *findNode(akey); if (node != e) { do { res.append(node->value); } while ((node = node->next) != e && node->key == akey); } return res; } template Q_OUTOFLINE_TEMPLATE int QHash::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; } template Q_INLINE_TEMPLATE const T QHash::operator[](const Key &akey) const { return value(akey); } template Q_INLINE_TEMPLATE T &QHash::operator[](const Key &akey) { detach(); uint h; Node **node = findNode(akey, &h); if (*node == e) { if (d->willGrow()) node = findNode(akey, &h); return createNode(h, akey, T(), node)->value; } return (*node)->value; } template Q_INLINE_TEMPLATE typename QHash::iterator QHash::insert(const Key &akey, const T &avalue) { detach(); uint h; Node **node = findNode(akey, &h); if (*node == e) { if (d->willGrow()) node = findNode(akey, &h); return iterator(createNode(h, akey, avalue, node)); } if (!QTypeInfo::isDummy) (*node)->value = avalue; return iterator(*node); } template Q_INLINE_TEMPLATE typename QHash::iterator QHash::insertMulti(const Key &akey, const T &avalue) { detach(); d->willGrow(); uint h; Node **nextNode = findNode(akey, &h); return iterator(createNode(h, akey, avalue, nextNode)); } template Q_OUTOFLINE_TEMPLATE int QHash::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; } template Q_OUTOFLINE_TEMPLATE T QHash::take(const Key &akey) { if (isEmpty()) // prevents detaching shared null return T(); detach(); Node **node = findNode(akey); if (*node != e) { T t = (*node)->value; Node *next = (*node)->next; deleteNode(*node); *node = next; --d->size; d->hasShrunk(); return t; } return T(); } template Q_OUTOFLINE_TEMPLATE typename QHash::iterator QHash::erase(iterator it) { if (it == iterator(e)) return it; iterator ret = it; ++ret; Node *node = it; Node **node_ptr = reinterpret_cast(&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; } template Q_INLINE_TEMPLATE void QHash::reserve(int asize) { detach(); d->rehash(-qMax(asize, 1)); } template Q_INLINE_TEMPLATE typename QHash::const_iterator QHash::find(const Key &akey) const { return const_iterator(*findNode(akey)); } template Q_INLINE_TEMPLATE typename QHash::const_iterator QHash::constFind(const Key &akey) const { return const_iterator(*findNode(akey)); } template Q_INLINE_TEMPLATE typename QHash::iterator QHash::find(const Key &akey) { detach(); return iterator(*findNode(akey)); } template Q_INLINE_TEMPLATE bool QHash::contains(const Key &akey) const { return *findNode(akey) != e; } template Q_OUTOFLINE_TEMPLATE typename QHash::Node **QHash::findNode(const Key &akey, uint *ahp) const { Node **node; uint h = qHash(akey); if (d->numBuckets) { node = reinterpret_cast(&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(reinterpret_cast(&e)); } if (ahp) *ahp = h; return node; } template Q_OUTOFLINE_TEMPLATE bool QHash::operator==(const QHash &other) const { if (size() != other.size()) return false; if (d == other.d) return true; const_iterator it = begin(); while (it != end()) { const Key &akey = it.key(); const_iterator it2 = other.find(akey); do { if (it2 == other.end() || !(it2.key() == akey)) return false; if (!QTypeInfo::isDummy && !(it.value() == it2.value())) return false; ++it; ++it2; } while (it != end() && it.key() == akey); } return true; } template class QMultiHash : public QHash { public: QMultiHash() {} QMultiHash(const QHash &other) : QHash(other) {} inline void swap(QMultiHash &other) { QHash::swap(other); } // prevent QMultiHash<->QHash swaps inline typename QHash::iterator replace(const Key &key, const T &value) { return QHash::insert(key, value); } inline typename QHash::iterator insert(const Key &key, const T &value) { return QHash::insertMulti(key, value); } inline QMultiHash &operator+=(const QMultiHash &other) { this->unite(other); return *this; } inline QMultiHash operator+(const QMultiHash &other) const { QMultiHash result = *this; result += other; return result; } #if !defined(Q_NO_USING_KEYWORD) && !defined(Q_CC_RVCT) // RVCT compiler doesn't handle using-keyword right when used functions are overloaded in child class using QHash::contains; using QHash::remove; using QHash::count; using QHash::find; using QHash::constFind; #else inline bool contains(const Key &key) const { return QHash::contains(key); } inline int remove(const Key &key) { return QHash::remove(key); } inline int count(const Key &key) const { return QHash::count(key); } inline int count() const { return QHash::count(); } inline typename QHash::iterator find(const Key &key) { return QHash::find(key); } inline typename QHash::const_iterator find(const Key &key) const { return QHash::find(key); } inline typename QHash::const_iterator constFind(const Key &key) const { return QHash::constFind(key); } #endif bool contains(const Key &key, const T &value) const; int remove(const Key &key, const T &value); int count(const Key &key, const T &value) const; typename QHash::iterator find(const Key &key, const T &value) { typename QHash::iterator i(find(key)); typename QHash::iterator end(this->end()); while (i != end && i.key() == key) { if (i.value() == value) return i; ++i; } return end; } typename QHash::const_iterator find(const Key &key, const T &value) const { typename QHash::const_iterator i(constFind(key)); typename QHash::const_iterator end(QHash::constEnd()); while (i != end && i.key() == key) { if (i.value() == value) return i; ++i; } return end; } typename QHash::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 Q_INLINE_TEMPLATE bool QMultiHash::contains(const Key &key, const T &value) const { return constFind(key, value) != QHash::constEnd(); } template Q_INLINE_TEMPLATE int QMultiHash::remove(const Key &key, const T &value) { int n = 0; typename QHash::iterator i(find(key)); typename QHash::iterator end(QHash::end()); while (i != end && i.key() == key) { if (i.value() == value) { i = this->erase(i); ++n; } else { ++i; } } return n; } template Q_INLINE_TEMPLATE int QMultiHash::count(const Key &key, const T &value) const { int n = 0; typename QHash::const_iterator i(constFind(key)); typename QHash::const_iterator end(QHash::constEnd()); while (i != end && i.key() == key) { if (i.value() == value) ++n; ++i; } return n; } Q_DECLARE_ASSOCIATIVE_ITERATOR(Hash) Q_DECLARE_MUTABLE_ASSOCIATIVE_ITERATOR(Hash) QT_END_NAMESPACE QT_END_HEADER #endif // QHASH_H