diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2024-01-26 16:08:35 -0800 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2024-02-12 17:01:33 -0800 |
commit | 0d15c000ad0dbdc5c04af0fd5b5799f2cd37d696 (patch) | |
tree | 06803673447714b2974d6712e7fe24b360e80c15 | |
parent | 1d7950c9467ba4db75ac065bd54ffe08e4a29710 (diff) |
qHash: backport the q(u)int128-to-quint64 reduction trick to 32-bit
I didn't do it in a template because the 32-bit code requires a
compatibility hack for Qt 6 and it's using an XOR of the high and low
parts.
Fixes: QTBUG-116080
Change-Id: I5dd50a1a7ca5424d9e7afffd17ae0ba5b9ff52f6
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
-rw-r--r-- | src/corelib/tools/qhashfunctions.h | 23 | ||||
-rw-r--r-- | tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp | 2 |
2 files changed, 19 insertions, 6 deletions
diff --git a/src/corelib/tools/qhashfunctions.h b/src/corelib/tools/qhashfunctions.h index 43a7b4b599..b7182ac6d4 100644 --- a/src/corelib/tools/qhashfunctions.h +++ b/src/corelib/tools/qhashfunctions.h @@ -104,7 +104,22 @@ Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(quint64 key, size_t seed = 0 return QHashPrivate::hash(size_t(key), seed); } Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(qint64 key, size_t seed = 0) noexcept -{ return qHash(quint64(key), seed); } +{ + if constexpr (sizeof(qint64) > sizeof(size_t)) { + // Avoid QTBUG-116080: we XOR the top half with its own sign bit: + // - if the qint64 is in range of qint32, then signmask ^ high == 0 + // (for Qt 7 only) + // - if the qint64 is in range of quint32, then signmask == 0 and we + // do the same as the quint64 overload above + quint32 high = quint32(quint64(key) >> 32); + quint32 low = quint32(quint64(key)); + quint32 signmask = qint32(high) >> 31; // all zeroes or all ones + signmask = QT_VERSION_MAJOR > 6 ? signmask : 0; + low ^= signmask ^ high; + return qHash(low, seed); + } + return qHash(quint64(key), seed); +} #if QT_SUPPORTS_INT128 constexpr size_t qHash(quint128 key, size_t seed = 0) noexcept { @@ -112,10 +127,8 @@ constexpr size_t qHash(quint128 key, size_t seed = 0) noexcept } constexpr size_t qHash(qint128 key, size_t seed = 0) noexcept { - // Avoid QTBUG-116080: we XOR the top half with its own sign bit: - // - if the qint128 is in range of qint64, then signmask ^ high == 0 - // - if the qint128 is in range of quint64, then signmask == 0 and we - // do the same as the quint128 overload above + // Avoid QTBUG-116080: same as above, but with double the sizes and without + // the need for compatibility quint64 high = quint64(quint128(key) >> 64); quint64 low = quint64(quint128(key)); quint64 signmask = qint64(high) >> 63; // all zeroes or all ones diff --git a/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp b/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp index aebc0422ad..b9a7d88056 100644 --- a/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp +++ b/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp @@ -195,7 +195,7 @@ void tst_QHashFunctions::signedIntegerConsistency() if (v32 == value) { // because of QTBUG-116080, this may not match, but we can't guarantee // it mismatches 100% of the time either - if constexpr (sizeof(size_t) > sizeof(int)) + if constexpr (sizeof(size_t) > sizeof(int) || QT_VERSION_MAJOR > 6) QCOMPARE(hs64, hs32); } |