QHash: Fix erase() edge-case

When the element you want to erase is the last element AND the
next element (element 0), when rehashed, would be relocated to the last
element, this leads to the state below. Which is similar to a test in
tst_qhash for some seeds.

auto it = hash.begin + (hash.size - 1)
it = hash.erase(it)
it != hash.end

By forcing the iterator to increment if we were erasing the last element
we always end up with a pointer which is equal to hash.end

Befriend the tst_qhash class so we can set the seed to a known-bad one

Change-Id: Ie0b175003a2acb175ef5e3ab5a984e010f65d986
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
(cherry picked from commit ea7d87b5b59ded22b145ecbb4dd648ecfd1abbdd)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Mårten Nordheim 2021-06-16 11:40:04 +02:00 committed by Qt Cherry-pick Bot
parent 9f459dee1f
commit 66aa4c86f4
2 changed files with 23 additions and 1 deletions

View File

@ -51,6 +51,8 @@
#include <initializer_list>
#include <functional> // for std::hash
class tst_QHash; // for befriending
QT_BEGIN_NAMESPACE
struct QHashDummyValue
@ -680,7 +682,7 @@ struct Data
}
// return correct position of the next element
if (!spans[span].hasNode(index))
if (bucket == numBuckets - 1 || !spans[span].hasNode(index))
++it;
return it;
}
@ -740,6 +742,7 @@ class QHash
using Data = QHashPrivate::Data<Node>;
friend class QSet<Key>;
friend class QMultiHash<Key, T>;
friend tst_QHash;
Data *d = nullptr;

View File

@ -42,6 +42,7 @@ class tst_QHash : public QObject
private slots:
void insert1();
void erase();
void erase_edge_case();
void key();
void swap();
@ -532,6 +533,24 @@ void tst_QHash::erase()
QVERIFY(mit == h2.end());
}
/*
With a specific seed we could end up in a situation where, upon deleting the
last entry in a QHash, the returned iterator would not point to the end()
iterator.
*/
void tst_QHash::erase_edge_case()
{
QHash<int, int> h1;
h1.reserve(2);
h1.d->seed = 10230148258692185509ull;
h1.insert(3, 4);
h1.insert(5, 6);
QHash<int, int>::iterator it1 = h1.begin();
++it1;
it1 = h1.erase(it1);
QVERIFY(it1 == h1.end());
}
void tst_QHash::key()
{
{