diff options
author | Thiago Macieira <[email protected]> | 2025-03-13 08:30:06 -0700 |
---|---|---|
committer | Thiago Macieira <[email protected]> | 2025-05-09 16:54:13 -0700 |
commit | c05ae82efb33507959ae2082483fb9425ccca8a0 (patch) | |
tree | de0398da78fee891aaf5f9bbf2ce1299c3e5aeba | |
parent | b10c7b1680d57174f2b4d5a08387378550434ae9 (diff) |
qHashMulti: switch to passing the seed to the qHash() function in 7.0
Amends 707129fd5a7c6390fbdf4270119226df2a427fcd.
This is much better for our string types because qHashBits() has a
faster implementation when seed!=0. The algorithm is the same, except
that QHashCombine now separates the seed from the combined hash, not
merging the two unrelated concepts together.
We can't fix it in Qt 6.x because it changes the hashing algorithm of
existing types that used qHashMulti(), which will have been inlined
throughout user code.
The deprecated constructor is there only until the rest of Qt is fixed.
Since this is a private class, that won't stay for long.
Task-number: QTBUG-134683
Fixes: QTBUG-134690
Change-Id: I2167e154f083089d12a1fffd61c1ab8670731156
Reviewed-by: Edward Welbourne <[email protected]>
-rw-r--r-- | src/corelib/serialization/qjsonobject.cpp | 2 | ||||
-rw-r--r-- | src/corelib/tools/qhash.h | 4 | ||||
-rw-r--r-- | src/corelib/tools/qhashfunctions.h | 49 | ||||
-rw-r--r-- | src/corelib/tools/qmap.h | 2 | ||||
-rw-r--r-- | src/gui/painting/qtransform.cpp | 2 | ||||
-rw-r--r-- | src/gui/rhi/qrhi.cpp | 2 | ||||
-rw-r--r-- | src/gui/rhi/qrhi.h | 14 | ||||
-rw-r--r-- | src/gui/rhi/qrhid3d12_p.h | 2 | ||||
-rw-r--r-- | src/gui/rhi/qshader.cpp | 2 | ||||
-rw-r--r-- | src/gui/text/qfontdatabase_p.h | 2 | ||||
-rw-r--r-- | src/gui/vulkan/qvulkaninstance.h | 4 |
11 files changed, 52 insertions, 33 deletions
diff --git a/src/corelib/serialization/qjsonobject.cpp b/src/corelib/serialization/qjsonobject.cpp index 99f5507aa12..3e0857dc954 100644 --- a/src/corelib/serialization/qjsonobject.cpp +++ b/src/corelib/serialization/qjsonobject.cpp @@ -1445,7 +1445,7 @@ void QJsonObject::removeAt(qsizetype index) size_t qHash(const QJsonObject &object, size_t seed) { - QtPrivate::QHashCombine hash; + QtPrivate::QHashCombine hash(seed); for (auto it = object.begin(), end = object.end(); it != end; ++it) { const QString key = it.key(); const QJsonValue value = it.value(); diff --git a/src/corelib/tools/qhash.h b/src/corelib/tools/qhash.h index 24c014e2153..87713b93a77 100644 --- a/src/corelib/tools/qhash.h +++ b/src/corelib/tools/qhash.h @@ -2710,9 +2710,9 @@ template <class Key, class T> size_t qHash(const QHash<Key, T> &key, size_t seed = 0) noexcept(noexcept(qHash(std::declval<Key&>())) && noexcept(qHash(std::declval<T&>()))) { + const QtPrivate::QHashCombine combine(seed); size_t hash = 0; for (auto it = key.begin(), end = key.end(); it != end; ++it) { - QtPrivate::QHashCombine combine; size_t h = combine(seed, it.key()); // use + to keep the result independent of the ordering of the keys hash += combine(h, it.value()); @@ -2724,9 +2724,9 @@ template <class Key, class T> inline size_t qHash(const QMultiHash<Key, T> &key, size_t seed = 0) noexcept(noexcept(qHash(std::declval<Key&>())) && noexcept(qHash(std::declval<T&>()))) { + const QtPrivate::QHashCombine combine(seed); size_t hash = 0; for (auto it = key.begin(), end = key.end(); it != end; ++it) { - QtPrivate::QHashCombine combine; size_t h = combine(seed, it.key()); // use + to keep the result independent of the ordering of the keys hash += combine(h, it.value()); diff --git a/src/corelib/tools/qhashfunctions.h b/src/corelib/tools/qhashfunctions.h index c687cdaeb72..cbe217db11e 100644 --- a/src/corelib/tools/qhashfunctions.h +++ b/src/corelib/tools/qhashfunctions.h @@ -298,18 +298,40 @@ bool qHashEquals(const T1 &a, const T2 &b) } namespace QtPrivate { - -struct QHashCombine +template <typename Mixer> struct QHashCombiner : private Mixer { - typedef size_t result_type; + using result_type = typename Mixer::result_type ; + +#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) || defined(QT_BOOTSTRAPPED) + // Qt 6.x didn't use to pass the seed; bootstrap has no seed + static constexpr size_t seed = 0; + constexpr QHashCombiner(result_type) noexcept {} + Q_DECL_DEPRECATED_X("pass the seed argument") constexpr QHashCombiner() noexcept {} +#else + size_t seed; + constexpr QHashCombiner(result_type s) : seed(s) noexcept {} +#endif + template <typename T> - constexpr result_type operator()(size_t seed, const T &t) const + constexpr result_type operator()(result_type result, const T &t) const noexcept(noexcept(qHash(t, seed))) - // combiner taken from N3876 / boost::hash_combine - { return seed ^ (qHash(t, size_t(0)) + 0x9e3779b9 + (seed << 6) + (seed >> 2)); } + { + return Mixer::operator()(result, qHash(t, seed)); + } }; -struct QHashCombineCommutative +struct QHashCombineMixer +{ + typedef size_t result_type; + constexpr result_type operator()(result_type result, result_type hash) const noexcept + { + // combiner taken from N3876 / boost::hash_combine + return result ^ (hash + 0x9e3779b9 + (result << 6) + (result >> 2)); + } +}; +using QHashCombine = QHashCombiner<QHashCombineMixer>; + +struct QHashCombineCommutativeMixer : std::plus<size_t> { // QHashCombine is a good hash combiner, but is not commutative, // ie. it depends on the order of the input elements. That is @@ -317,11 +339,8 @@ struct QHashCombineCommutative // {1,3,0}. Except when it isn't (e.g. for QSet and // QHash). Therefore, provide a commutative combiner, too. typedef size_t result_type; - template <typename T> - constexpr result_type operator()(size_t seed, const T &t) const - noexcept(noexcept(qHash(t, seed))) - { return seed + qHash(t, size_t(0)); } // don't use xor! }; +using QHashCombineCommutative = QHashCombiner<QHashCombineCommutativeMixer>; template <typename... T> using QHashMultiReturnType = decltype( @@ -356,7 +375,7 @@ QtPrivate::QHashMultiReturnType<T...> qHashMulti(size_t seed, const T &... args) noexcept(std::conjunction_v<QtPrivate::QNothrowHashable<T>...>) { - QtPrivate::QHashCombine hash; + QtPrivate::QHashCombine hash(seed); return ((seed = hash(seed, args)), ...), seed; } @@ -370,7 +389,7 @@ QtPrivate::QHashMultiReturnType<T...> qHashMultiCommutative(size_t seed, const T &... args) noexcept(std::conjunction_v<QtPrivate::QNothrowHashable<T>...>) { - QtPrivate::QHashCombineCommutative hash; + QtPrivate::QHashCombineCommutative hash(seed); return ((seed = hash(seed, args)), ...), seed; } @@ -378,14 +397,14 @@ template <typename InputIterator> inline size_t qHashRange(InputIterator first, InputIterator last, size_t seed = 0) noexcept(noexcept(qHash(*first))) // assume iterator operations don't throw { - return std::accumulate(first, last, seed, QtPrivate::QHashCombine()); + return std::accumulate(first, last, seed, QtPrivate::QHashCombine(seed)); } template <typename InputIterator> inline size_t qHashRangeCommutative(InputIterator first, InputIterator last, size_t seed = 0) noexcept(noexcept(qHash(*first))) // assume iterator operations don't throw { - return std::accumulate(first, last, seed, QtPrivate::QHashCombineCommutative()); + return std::accumulate(first, last, seed, QtPrivate::QHashCombineCommutative(seed)); } namespace QHashPrivate { diff --git a/src/corelib/tools/qmap.h b/src/corelib/tools/qmap.h index 01e614aa28b..ac2f04b8cd8 100644 --- a/src/corelib/tools/qmap.h +++ b/src/corelib/tools/qmap.h @@ -810,7 +810,7 @@ private: return seed; // don't use qHashRange to avoid its compile-time overhead: return std::accumulate(key.d->m.begin(), key.d->m.end(), seed, - QtPrivate::QHashCombine{}); + QtPrivate::QHashCombine{seed}); } #endif // !Q_QDOC }; diff --git a/src/gui/painting/qtransform.cpp b/src/gui/painting/qtransform.cpp index 1ea63f40965..66f1cf2a985 100644 --- a/src/gui/painting/qtransform.cpp +++ b/src/gui/painting/qtransform.cpp @@ -784,7 +784,7 @@ bool QTransform::operator==(const QTransform &o) const */ size_t qHash(const QTransform &key, size_t seed) noexcept { - QtPrivate::QHashCombine hash; + QtPrivate::QHashCombine hash(seed); seed = hash(seed, key.m11()); seed = hash(seed, key.m12()); seed = hash(seed, key.m21()); diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index 42e7753e4cb..0628d07ce73 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -6543,7 +6543,7 @@ bool operator!=(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBind size_t qHash(const QRhiShaderResourceBinding &b, size_t seed) noexcept { const QRhiShaderResourceBinding::Data *d = QRhiImplementation::shaderResourceBindingData(b); - QtPrivate::QHashCombine hash; + QtPrivate::QHashCombine hash(seed); seed = hash(seed, d->binding); seed = hash(seed, d->stage); seed = hash(seed, d->type); diff --git a/src/gui/rhi/qrhi.h b/src/gui/rhi/qrhi.h index 5776554916a..66651dcf979 100644 --- a/src/gui/rhi/qrhi.h +++ b/src/gui/rhi/qrhi.h @@ -69,7 +69,7 @@ private: friend size_t qHash(const QRhiDepthStencilClearValue &v, size_t seed = 0) noexcept { - QtPrivate::QHashCombine hash; + QtPrivate::QHashCombine hash(seed); seed = hash(seed, v.m_d); seed = hash(seed, v.m_s); return seed; @@ -118,7 +118,7 @@ private: friend size_t qHash(const QRhiViewport &v, size_t seed = 0) noexcept { - QtPrivate::QHashCombine hash; + QtPrivate::QHashCombine hash(seed); seed = hash(seed, v.m_rect[0]); seed = hash(seed, v.m_rect[1]); seed = hash(seed, v.m_rect[2]); @@ -161,7 +161,7 @@ private: friend size_t qHash(const QRhiScissor &v, size_t seed = 0) noexcept { - QtPrivate::QHashCombine hash; + QtPrivate::QHashCombine hash(seed); seed = hash(seed, v.m_rect[0]); seed = hash(seed, v.m_rect[1]); seed = hash(seed, v.m_rect[2]); @@ -215,7 +215,7 @@ private: friend size_t qHash(const QRhiVertexInputBinding &v, size_t seed = 0) noexcept { - QtPrivate::QHashCombine hash; + QtPrivate::QHashCombine hash(seed); seed = hash(seed, v.m_stride); seed = hash(seed, v.m_classification); seed = hash(seed, v.m_instanceStepRate); @@ -303,7 +303,7 @@ private: friend size_t qHash(const QRhiVertexInputAttribute &v, size_t seed = 0) noexcept { - QtPrivate::QHashCombine hash; + QtPrivate::QHashCombine hash(seed); seed = hash(seed, v.m_binding); seed = hash(seed, v.m_location); seed = hash(seed, v.m_format); @@ -363,7 +363,7 @@ private: friend size_t qHash(const QRhiVertexInputLayout &v, size_t seed = 0) noexcept { - QtPrivate::QHashCombine hash; + QtPrivate::QHashCombine hash(seed); seed = hash(seed, v.m_bindings); seed = hash(seed, v.m_attributes); return seed; @@ -420,7 +420,7 @@ private: friend size_t qHash(const QRhiShaderStage &v, size_t seed = 0) noexcept { - QtPrivate::QHashCombine hash; + QtPrivate::QHashCombine hash(seed); seed = hash(seed, v.m_type); seed = hash(seed, v.m_shader); seed = hash(seed, v.m_shaderVariant); diff --git a/src/gui/rhi/qrhid3d12_p.h b/src/gui/rhi/qrhid3d12_p.h index 2381bb40d42..caa428c6cbc 100644 --- a/src/gui/rhi/qrhid3d12_p.h +++ b/src/gui/rhi/qrhid3d12_p.h @@ -507,7 +507,7 @@ struct Q_D3D12_SAMPLER_DESC friend size_t qHash(const Q_D3D12_SAMPLER_DESC &key, size_t seed = 0) noexcept { - QtPrivate::QHashCombine hash; + QtPrivate::QHashCombine hash(seed); seed = hash(seed, key.desc.Filter); seed = hash(seed, key.desc.AddressU); seed = hash(seed, key.desc.AddressV); diff --git a/src/gui/rhi/qshader.cpp b/src/gui/rhi/qshader.cpp index e1fe23c2a2f..5e967c62e40 100644 --- a/src/gui/rhi/qshader.cpp +++ b/src/gui/rhi/qshader.cpp @@ -812,7 +812,7 @@ bool operator==(const QShader &lhs, const QShader &rhs) noexcept size_t qHash(const QShader &s, size_t seed) noexcept { if (s.d) { - QtPrivate::QHashCombine hash; + QtPrivate::QHashCombine hash(seed); seed = hash(seed, s.stage()); if (!s.d->shaders.isEmpty()) { seed = hash(seed, s.d->shaders.firstKey()); diff --git a/src/gui/text/qfontdatabase_p.h b/src/gui/text/qfontdatabase_p.h index 58feb9457b9..cbfa02b2b7c 100644 --- a/src/gui/text/qfontdatabase_p.h +++ b/src/gui/text/qfontdatabase_p.h @@ -51,7 +51,7 @@ inline bool operator!=(const QtFontFallbacksCacheKey &lhs, const QtFontFallbacks inline size_t qHash(const QtFontFallbacksCacheKey &key, size_t seed = 0) noexcept { - QtPrivate::QHashCombine hash; + QtPrivate::QHashCombine hash(seed); seed = hash(seed, key.family); seed = hash(seed, int(key.style)); seed = hash(seed, int(key.styleHint)); diff --git a/src/gui/vulkan/qvulkaninstance.h b/src/gui/vulkan/qvulkaninstance.h index 819751025be..2728fc2e1b0 100644 --- a/src/gui/vulkan/qvulkaninstance.h +++ b/src/gui/vulkan/qvulkaninstance.h @@ -81,7 +81,7 @@ inline bool operator!=(const QVulkanLayer &lhs, const QVulkanLayer &rhs) noexcep inline size_t qHash(const QVulkanLayer &key, size_t seed = 0) noexcept { - QtPrivate::QHashCombine hash; + QtPrivate::QHashCombine hash(seed); seed = hash(seed, key.name); seed = hash(seed, key.version); seed = hash(seed, key.specVersion); @@ -104,7 +104,7 @@ inline bool operator!=(const QVulkanExtension &lhs, const QVulkanExtension &rhs) inline size_t qHash(const QVulkanExtension &key, size_t seed = 0) noexcept { - QtPrivate::QHashCombine hash; + QtPrivate::QHashCombine hash(seed); seed = hash(seed, key.name); seed = hash(seed, key.version); return seed; |