// 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 #define QHASHFUNCTIONS_H #include #include #include // for std::accumulate #include // for std::hash #include // For std::pair #ifdef __cpp_concepts # include #endif #if 0 #pragma qt_class(QHashFunctions) #endif #if defined(Q_CC_MSVC) #pragma warning( push ) #pragma warning( disable : 4311 ) // disable pointer truncation warning #pragma warning( disable : 4127 ) // conditional expression is constant #endif QT_BEGIN_NAMESPACE class QBitArray; #if QT_DEPRECATED_SINCE(6,6) QT_DEPRECATED_VERSION_X_6_6("Use QHashSeed instead") Q_CORE_EXPORT int qGlobalQHashSeed(); QT_DEPRECATED_VERSION_X_6_6("Use QHashSeed instead") Q_CORE_EXPORT void qSetGlobalQHashSeed(int newSeed); #endif struct QHashSeed { constexpr QHashSeed(size_t d = 0) : data(d) {} constexpr operator size_t() const noexcept { return data; } static Q_CORE_EXPORT QHashSeed globalSeed() noexcept; static Q_CORE_EXPORT void setDeterministicGlobalSeed(); static Q_CORE_EXPORT void resetRandomGlobalSeed(); private: size_t data; }; // Whether, ∀ t of type T && ∀ seed, qHash(Key(t), seed) == qHash(t, seed) template struct QHashHeterogeneousSearch : std::false_type {}; // Specializations template <> struct QHashHeterogeneousSearch : std::true_type {}; template <> struct QHashHeterogeneousSearch : std::true_type {}; template <> struct QHashHeterogeneousSearch : std::true_type {}; template <> struct QHashHeterogeneousSearch : std::true_type {}; #ifndef Q_PROCESSOR_ARM template <> struct QHashHeterogeneousSearch : std::true_type {}; template <> struct QHashHeterogeneousSearch : std::true_type {}; template <> struct QHashHeterogeneousSearch : std::true_type {}; template <> struct QHashHeterogeneousSearch : std::true_type {}; #endif namespace QHashPrivate { Q_DECL_CONST_FUNCTION constexpr size_t hash(size_t key, size_t seed) noexcept { key ^= seed; if constexpr (sizeof(size_t) == 4) { key ^= key >> 16; key *= UINT32_C(0x45d9f3b); key ^= key >> 16; key *= UINT32_C(0x45d9f3b); key ^= key >> 16; return key; } else { quint64 key64 = key; key64 ^= key64 >> 32; key64 *= UINT64_C(0xd6e8feb86659fd93); key64 ^= key64 >> 32; key64 *= UINT64_C(0xd6e8feb86659fd93); key64 ^= key64 >> 32; return size_t(key64); } } template static constexpr bool noexceptPairHash(); } Q_CORE_EXPORT Q_DECL_PURE_FUNCTION size_t qHashBits(const void *p, size_t size, size_t seed = 0) noexcept; // implementation below qHashMulti template inline size_t qHash(const std::pair &key, size_t seed = 0) noexcept(QHashPrivate::noexceptPairHash()); // C++ builtin types Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(char key, size_t seed = 0) noexcept { return QHashPrivate::hash(size_t(key), seed); } Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(uchar key, size_t seed = 0) noexcept { return QHashPrivate::hash(size_t(key), seed); } Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(signed char key, size_t seed = 0) noexcept { return QHashPrivate::hash(size_t(key), seed); } Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(ushort key, size_t seed = 0) noexcept { return QHashPrivate::hash(size_t(key), seed); } Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(short key, size_t seed = 0) noexcept { return QHashPrivate::hash(size_t(key), seed); } Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(uint key, size_t seed = 0) noexcept { return QHashPrivate::hash(size_t(key), seed); } Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(int key, size_t seed = 0) noexcept { return QHashPrivate::hash(size_t(key), seed); } Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(ulong key, size_t seed = 0) noexcept { return QHashPrivate::hash(size_t(key), seed); } Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(long key, size_t seed = 0) noexcept { return QHashPrivate::hash(size_t(key), seed); } Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(quint64 key, size_t seed = 0) noexcept { if constexpr (sizeof(quint64) > sizeof(size_t)) 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 { 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 { return qHash(quint64(key + (key >> 64)), seed); } constexpr size_t qHash(qint128 key, size_t seed = 0) noexcept { // 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 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 key += 0.0f; uint k; memcpy(&k, &key, sizeof(float)); return QHashPrivate::hash(k, seed); } Q_CORE_EXPORT Q_DECL_CONST_FUNCTION size_t qHash(double key, size_t seed = 0) noexcept; Q_CORE_EXPORT Q_DECL_CONST_FUNCTION size_t qHash(long double key, size_t seed = 0) noexcept; Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(wchar_t key, size_t seed = 0) noexcept { return QHashPrivate::hash(size_t(key), seed); } Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(char16_t key, size_t seed = 0) noexcept { return QHashPrivate::hash(size_t(key), seed); } Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(char32_t key, size_t seed = 0) noexcept { return QHashPrivate::hash(size_t(key), seed); } #ifdef __cpp_char8_t Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(char8_t key, size_t seed = 0) noexcept { return QHashPrivate::hash(size_t(key), seed); } #endif template inline size_t qHash(const T *key, size_t seed = 0) noexcept { return qHash(reinterpret_cast(key), seed); } Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(std::nullptr_t, size_t seed = 0) noexcept { return seed; } template , bool> = true> Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(Enum e, size_t seed = 0) noexcept { return QHashPrivate::hash(qToUnderlying(e), seed); } // (some) Qt types Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(const QChar key, size_t seed = 0) noexcept { return qHash(key.unicode(), seed); } #if QT_CORE_REMOVED_SINCE(6, 4) Q_CORE_EXPORT Q_DECL_PURE_FUNCTION size_t qHash(const QByteArray &key, size_t seed = 0) noexcept; Q_CORE_EXPORT Q_DECL_PURE_FUNCTION size_t qHash(const QByteArrayView &key, size_t seed = 0) noexcept; #else Q_CORE_EXPORT Q_DECL_PURE_FUNCTION size_t qHash(QByteArrayView key, size_t seed = 0) noexcept; inline Q_DECL_PURE_FUNCTION size_t qHash(const QByteArray &key, size_t seed = 0 QT6_DECL_NEW_OVERLOAD_TAIL) noexcept { return qHash(qToByteArrayViewIgnoringNull(key), seed); } #endif Q_CORE_EXPORT Q_DECL_PURE_FUNCTION size_t qHash(QStringView key, size_t seed = 0) noexcept; inline Q_DECL_PURE_FUNCTION size_t qHash(const QString &key, size_t seed = 0) noexcept { return qHash(QStringView{key}, seed); } #ifndef QT_BOOTSTRAPPED Q_CORE_EXPORT Q_DECL_PURE_FUNCTION size_t qHash(const QBitArray &key, size_t seed = 0) noexcept; #endif Q_CORE_EXPORT Q_DECL_PURE_FUNCTION size_t qHash(QLatin1StringView key, size_t seed = 0) noexcept; Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(QKeyCombination key, size_t seed = 0) noexcept { return qHash(key.toCombined(), seed); } Q_CORE_EXPORT Q_DECL_PURE_FUNCTION uint qt_hash(QStringView key, uint chained = 0) noexcept; template Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(QFlags flags, size_t seed = 0) noexcept { return qHash(flags.toInt(), seed); } // ### Qt 7: remove this "catch-all" overload logic, and require users // to provide the two-argument version of qHash. #if (QT_VERSION < QT_VERSION_CHECK(7, 0, 0)) // Beware of moving this code from here. It needs to see all the // declarations of qHash overloads for C++ fundamental types *before* // its own declaration. namespace QHashPrivate { template constexpr inline bool HasQHashSingleArgOverload = false; template constexpr inline bool HasQHashSingleArgOverload())), size_t> >> = true; } template && !std::is_enum_v, bool> = true> size_t qHash(const T &t, size_t seed) noexcept(noexcept(qHash(t))) { return qHash(t) ^ seed; } #endif // < Qt 7 namespace QHashPrivate { #ifdef __cpp_concepts template concept HeterogeneouslySearchableWithHelper = // if Key and T are not the same (member already exists) !std::is_same_v // but are comparable amongst each other && std::equality_comparable_with // and supports heteregenous hashing && QHashHeterogeneousSearch::value; template concept HeterogeneouslySearchableWith = HeterogeneouslySearchableWithHelper, q20::remove_cvref_t>; #else template constexpr bool HeterogeneouslySearchableWith = false; #endif } template bool qHashEquals(const T &a, const T &b) { return a == b; } template std::enable_if_t, bool> qHashEquals(const T1 &a, const T2 &b) { return a == b; } namespace QtPrivate { struct QHashCombine { typedef size_t result_type; template constexpr result_type operator()(size_t seed, const T &t) const noexcept(noexcept(qHash(t))) // combiner taken from N3876 / boost::hash_combine { return seed ^ (qHash(t) + 0x9e3779b9 + (seed << 6) + (seed >> 2)) ; } }; struct QHashCombineCommutative { // QHashCombine is a good hash combiner, but is not commutative, // ie. it depends on the order of the input elements. That is // usually what we want: {0,1,3} should hash differently than // {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 constexpr result_type operator()(size_t seed, const T &t) const noexcept(noexcept(qHash(t))) { return seed + qHash(t); } // don't use xor! }; template using QHashMultiReturnType = decltype( std::declval< std::enable_if_t<(sizeof...(T) > 0)> >(), (qHash(std::declval()), ...), size_t{} ); // workaround for a MSVC ICE, // https://developercommunity.visualstudio.com/content/problem/996540/internal-compiler-error-on-msvc-1924-when-doing-sf.html template inline constexpr bool QNothrowHashableHelper_v = noexcept(qHash(std::declval())); template struct QNothrowHashable : std::false_type {}; template struct QNothrowHashable>> : std::true_type {}; template constexpr inline bool QNothrowHashable_v = QNothrowHashable::value; } // namespace QtPrivate template constexpr #ifdef Q_QDOC size_t #else QtPrivate::QHashMultiReturnType #endif qHashMulti(size_t seed, const T &... args) noexcept(std::conjunction_v...>) { QtPrivate::QHashCombine hash; return ((seed = hash(seed, args)), ...), seed; } template constexpr #ifdef Q_QDOC size_t #else QtPrivate::QHashMultiReturnType #endif qHashMultiCommutative(size_t seed, const T &... args) noexcept(std::conjunction_v...>) { QtPrivate::QHashCombineCommutative hash; return ((seed = hash(seed, args)), ...), seed; } template 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()); } template 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()); } namespace QHashPrivate { template static constexpr bool noexceptPairHash() { size_t seed = 0; return noexcept(qHash(std::declval(), seed)) && noexcept(qHash(std::declval(), seed)); } } // QHashPrivate template inline size_t qHash(const std::pair &key, size_t seed) noexcept(QHashPrivate::noexceptPairHash()) { return qHashMulti(seed, key.first, key.second); } #define QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH(Class, Arguments) \ QT_BEGIN_INCLUDE_NAMESPACE \ namespace std { \ template <> \ struct hash< QT_PREPEND_NAMESPACE(Class) > { \ using argument_type = QT_PREPEND_NAMESPACE(Class); \ using result_type = size_t; \ size_t operator()(Arguments s) const \ noexcept(QT_PREPEND_NAMESPACE( \ QtPrivate::QNothrowHashable_v)) \ { \ /* this seeds qHash with the result of */ \ /* std::hash applied to an int, to reap */ \ /* any protection against predictable hash */ \ /* values the std implementation may provide */ \ using QT_PREPEND_NAMESPACE(qHash); \ return qHash(s, qHash(std::hash{}(0))); \ } \ }; \ } \ QT_END_INCLUDE_NAMESPACE \ /*end*/ #define QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH_BY_CREF(Class) \ QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH(Class, const argument_type &) #define QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH_BY_VALUE(Class) \ QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH(Class, argument_type) QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH_BY_CREF(QString) QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH_BY_VALUE(QStringView) QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH_BY_VALUE(QLatin1StringView) QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH_BY_VALUE(QByteArrayView) QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH_BY_CREF(QByteArray) #ifndef QT_BOOTSTRAPPED QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH_BY_CREF(QBitArray) #endif QT_END_NAMESPACE #if defined(Q_CC_MSVC) #pragma warning( pop ) #endif #endif // QHASHFUNCTIONS_H