From 1d7950c9467ba4db75ac065bd54ffe08e4a29710 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Tue, 15 Aug 2023 13:25:33 +0200 Subject: [PATCH] Add missing qHash(qu/int128) overloads If we add the typedefs, we also need to add the qHash() overloads. See code comments for the explanation of how it works. I chose to use an addition to merge the upper and lower parts because of the comment in QHashCombineCommutative's operator(), introduced by commit 91b44afdcba3f4cb2ddc7a80c0cff723b408b447. Found in 6.6 API-review, but didn't make the cut and then was forgotten. Drive-by fix long line nearby. [ChangeLog][QtCore] Added qHash() overloads for quint128 and qint128. Fixes: QTBUG-116054 Task-number: QTBUG-116080 Change-Id: If484aed08ba476e0eace800b719f435203100f3e Reviewed-by: Ivan Solovev Reviewed-by: Fabian Kosmale Reviewed-by: Allan Sandfeld Jensen --- src/corelib/tools/qhash.cpp | 20 +++++++++++++++ src/corelib/tools/qhashfunctions.h | 22 +++++++++++++++- .../qhashfunctions/tst_qhashfunctions.cpp | 25 +++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index 56de45fd32..6157c206ef 100644 --- a/src/corelib/tools/qhash.cpp +++ b/src/corelib/tools/qhash.cpp @@ -1379,6 +1379,26 @@ uint qt_hash(QStringView key, uint chained) noexcept Returns the hash value for the \a key, using \a seed to seed the calculation. */ +/*! \fn size_t qHash(quint128 key, size_t seed = 0) + \relates QHash + \since 6.8 + + Returns the hash value for the \a key, using \a seed to seed the calculation. + + \note This function is only available on platforms that support a native + 128-bit integer type. +*/ + +/*! \fn size_t qHash(qint128 key, size_t seed = 0) + \relates QHash + \since 6.8 + + Returns the hash value for the \a key, using \a seed to seed the calculation. + + \note This function is only available on platforms that support a native + 128-bit integer type. + */ + /*! \fn size_t qHash(char8_t key, size_t seed = 0) \relates QHash \since 6.0 diff --git a/src/corelib/tools/qhashfunctions.h b/src/corelib/tools/qhashfunctions.h index 3d44c6ac2f..43a7b4b599 100644 --- a/src/corelib/tools/qhashfunctions.h +++ b/src/corelib/tools/qhashfunctions.h @@ -1,5 +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 +// Copyright (C) 2024 Intel Corporation. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QHASHFUNCTIONS_H @@ -102,7 +103,26 @@ Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(quint64 key, size_t seed = 0 key ^= (key >> 32); 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); } +Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(qint64 key, size_t seed = 0) noexcept +{ return qHash(quint64(key), seed); } +#if QT_SUPPORTS_INT128 +constexpr size_t qHash(quint128 key, size_t seed = 0) noexcept +{ + return qHash(quint64(key + (key >> 64)), seed); +} +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 + quint64 high = quint64(quint128(key) >> 64); + quint64 low = quint64(quint128(key)); + quint64 signmask = qint64(high) >> 63; // all zeroes or all ones + low += signmask ^ high; + return qHash(low, seed); +} +#endif // QT_SUPPORTS_INT128 Q_DECL_CONST_FUNCTION inline size_t qHash(float key, size_t seed = 0) noexcept { // ensure -0 gets mapped to 0 diff --git a/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp b/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp index 424e869d98..aebc0422ad 100644 --- a/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp +++ b/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp @@ -35,6 +35,7 @@ private Q_SLOTS: void unsignedIntegerConsistency(); void signedIntegerConsistency_data(); void signedIntegerConsistency(); + void extendedIntegerConsistency(); void floatingPointConsistency_data(); void floatingPointConsistency(); void stringConsistency_data(); @@ -151,6 +152,11 @@ static void unsignedIntegerConsistency(quint64 value, size_t seed) if (v32 == value) QCOMPARE(hu64, hu32); +#if QT_SUPPORTS_INT128 + const auto hu128 = qHash(quint128(value), seed); + QCOMPARE(hu128, hu64); +#endif + // there are a few more unsigned types: #ifdef __cpp_char8_t const auto hc8 = qHash(char8_t(value), seed); @@ -193,6 +199,11 @@ void tst_QHashFunctions::signedIntegerConsistency() QCOMPARE(hs64, hs32); } +#if QT_SUPPORTS_INT128 + const auto hs128 = qHash(qint128(value), seed); + QCOMPARE(hs128, hs64); +#endif + if (value > 0) { quint64 u64 = quint64(value); const auto hu64 = qHash(u64, seed); @@ -202,6 +213,20 @@ void tst_QHashFunctions::signedIntegerConsistency() } } +void tst_QHashFunctions::extendedIntegerConsistency() +{ +#ifdef QT_SUPPORTS_INT128 + // We only need to check qint128 and quint128 consistency here. + qint128 v65bit = Q_INT128_C(0x1'abea'06b7'dcf5'106a); + qint128 v127bit = Q_INT128_C(0x387c'ac7a'22a0'5242'9ee9'bcaa'6a53'13af); + + QCOMPARE(qHash(quint128(v65bit), seed), qHash(v65bit, seed)); + QCOMPARE(qHash(quint128(v127bit), seed), qHash(v127bit, seed)); +#else + QSKIP("This platform does not support extended integer types."); +#endif +} + void tst_QHashFunctions::floatingPointConsistency_data() { QTest::addColumn("value");