summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThiago Macieira <[email protected]>2025-05-21 15:21:11 -0700
committerThiago Macieira <[email protected]>2025-07-04 14:24:00 -0300
commit423278c6ee18a86320d253d8f873347c12f882f5 (patch)
tree45f2b66a0d708adde271556e462acfe85eb12e7d
parent4b4e77a0869ce00c96acfc209cd827b39ffac7b0 (diff)
QUrl: use QHashCombine so we use the faster & stronger QString hashing
We were only using the seed for hashing the port number, which is the weakest of all the uses of the seed, resulting in a weak hash for the QUrl itself. This commit changes it so QString components of the URL are themselves also hashed using the seed. As a side benefit, the aeshash() implementation in qhash.cpp where supported (ARM and x86) is faster than the siphash() one because it's vectorized. We're retaining the qHash(QUrl(), seed) == hashing of -1, but it's not qHash(-1, seed) because QHashCombine adds a constant. Pick-to: 6.10 Change-Id: If6466d054fd5a4f05205fffdfbc7b655eae5aefb Reviewed-by: David Faure <[email protected]>
-rw-r--r--src/corelib/io/qurl.cpp32
-rw-r--r--tests/auto/corelib/io/qurl/tst_qurl.cpp4
2 files changed, 26 insertions, 10 deletions
diff --git a/src/corelib/io/qurl.cpp b/src/corelib/io/qurl.cpp
index 565ce04c6ce..a4fbf69da8b 100644
--- a/src/corelib/io/qurl.cpp
+++ b/src/corelib/io/qurl.cpp
@@ -3660,17 +3660,29 @@ QList<QUrl> QUrl::fromStringList(const QStringList &urls, ParsingMode mode)
*/
size_t qHash(const QUrl &url, size_t seed) noexcept
{
+ QtPrivate::QHashCombineWithSeed hasher(seed);
+
+ // non-commutative, we must hash the port first
if (!url.d)
- return qHash(-1, seed); // the hash of an unset port (-1)
-
- return qHash(url.d->scheme) ^
- qHash(url.d->userName) ^
- qHash(url.d->password) ^
- qHash(url.d->host) ^
- qHash(url.d->port, seed) ^
- qHash(url.d->path) ^
- qHash(url.d->query) ^
- qHash(url.d->fragment);
+ return hasher(0, -1);
+ size_t state = hasher(0, url.d->port);
+
+ if (url.d->hasScheme())
+ state = hasher(state, url.d->scheme);
+ if (url.d->hasUserInfo()) {
+ // see presentSections(), appendUserName(), etc.
+ state = hasher(state, url.d->userName);
+ state = hasher(state, url.d->password);
+ }
+ if (url.d->hasHost() || url.d->isLocalFile()) // for XDG compatibility
+ state = hasher(state, url.d->host);
+ if (url.d->hasPath())
+ state = hasher(state, url.d->path);
+ if (url.d->hasQuery())
+ state = hasher(state, url.d->query);
+ if (url.d->hasFragment())
+ state = hasher(state, url.d->fragment);
+ return state;
}
static QUrl adjustFtpPath(QUrl url)
diff --git a/tests/auto/corelib/io/qurl/tst_qurl.cpp b/tests/auto/corelib/io/qurl/tst_qurl.cpp
index 282c7f2e360..71389abc976 100644
--- a/tests/auto/corelib/io/qurl/tst_qurl.cpp
+++ b/tests/auto/corelib/io/qurl/tst_qurl.cpp
@@ -513,6 +513,7 @@ void tst_QUrl::comparison2()
QCOMPARE(url1.toString(), url2.toString());
QCOMPARE(url1, url2);
QCOMPARE(qHash(url1), qHash(url2));
+ QCOMPARE(qHash(url1, 1), qHash(url2, 1));
} else if (ordering < 0) {
QCOMPARE_LT(url1.toString(), url2.toString());
QCOMPARE_NE(url1, url2);
@@ -1387,12 +1388,14 @@ void tst_QUrl::toString_constructed()
QUrl parsed(asString);
QCOMPARE(url, parsed);
QCOMPARE(qHash(url), qHash(parsed));
+ QCOMPARE(qHash(url, 1), qHash(parsed, 1));
}
// clear it and ensure no memory of the previous state remains
url.setUrl(QString());
QCOMPARE(url, QUrl());
QCOMPARE(qHash(url), qHash(QUrl()));
+ QCOMPARE(qHash(url, 1), qHash(QUrl(), 1));
}
void tst_QUrl::toDisplayString_PreferLocalFile_data()
@@ -4265,6 +4268,7 @@ void tst_QUrl::setComponents()
QUrl recreated(toString);
QCOMPARE(copy, recreated);
QCOMPARE(qHash(copy), qHash(recreated));
+ QCOMPARE(qHash(copy, 1), qHash(recreated, 1));
} else {
QVERIFY(copy.toString().isEmpty());
}