From 38886d5f013a1e0ccf7076bd0ca8f6f6d012f6b5 Mon Sep 17 00:00:00 2001 From: Shauren Date: Thu, 8 Jan 2026 00:41:16 +0100 Subject: [PATCH] Core/Misc: Hashing update * Update hash_combine with latest version from boost * Change most std::hash specializations to simply hash all of the input bytes instead of combining their hashes --- src/common/Time/Timezone.cpp | 2 +- src/common/Utilities/Hash.h | 146 +++++++++++++++--- src/server/database/Database/QueryResult.h | 4 +- .../game/AuctionHouse/AuctionHouseMgr.cpp | 12 +- src/server/game/Conditions/ConditionMgr.cpp | 10 +- .../game/Entities/Object/ObjectGuid.cpp | 16 -- src/server/game/Entities/Object/ObjectGuid.h | 10 +- .../game/Globals/AreaTriggerDataStore.cpp | 8 +- .../game/Maps/DynamicMMapTileBuilder.cpp | 20 ++- 9 files changed, 160 insertions(+), 68 deletions(-) diff --git a/src/common/Time/Timezone.cpp b/src/common/Time/Timezone.cpp index 8e7bde19c34..3dfb99d5f85 100644 --- a/src/common/Time/Timezone.cpp +++ b/src/common/Time/Timezone.cpp @@ -46,7 +46,7 @@ std::unordered_map InitTimezoneHashDb() std::chrono::sys_info sysInfo = zone.get_info(dummmy); Minutes offsetMinutes = std::chrono::duration_cast(sysInfo.offset); std::string offsetStr = Trinity::ToString(offsetMinutes.count()); - hashToOffset.emplace(Trinity::HashFnv1a(offsetStr), offsetMinutes); + hashToOffset.emplace(Trinity::HashFnv1a::GetHash(offsetStr), offsetMinutes); } #else diff --git a/src/common/Utilities/Hash.h b/src/common/Utilities/Hash.h index 32e79fb5a31..038906c4539 100644 --- a/src/common/Utilities/Hash.h +++ b/src/common/Utilities/Hash.h @@ -15,46 +15,156 @@ * with this program. If not, see . */ -#ifndef TrinityCore_Hash_h__ -#define TrinityCore_Hash_h__ +#ifndef TRINITYCORE_HASH_H +#define TRINITYCORE_HASH_H #include -#include +#include #include namespace Trinity { - template + template inline void hash_combine(std::size_t& seed, T const& val) { - seed ^= std::hash()(val) + 0x9E3779B9 + (seed << 6) + (seed >> 2); + // Taken from boost::hash_combine + + seed = seed + 0x9E3779B9 + std::hash()(val); + + if constexpr (sizeof(std::size_t) == 8) + { + constexpr std::size_t m = 0xE9846AF9B1A615D; + + seed ^= seed >> 32; + seed *= m; + seed ^= seed >> 32; + seed *= m; + seed ^= seed >> 28; + } + else + { + constexpr std::size_t m1 = 0x21F0AAAD; + constexpr std::size_t m2 = 0x735A2D97; + + seed ^= seed >> 16; + seed *= m1; + seed ^= seed >> 15; + seed *= m2; + seed ^= seed >> 15; + } } - inline constexpr std::uint32_t HashFnv1a(std::string_view data) + template + struct HashFnv1aConstants { }; + + template <> + struct HashFnv1aConstants<4> { - std::uint32_t hash = 0x811C9DC5u; - for (char c : data) + static constexpr std::uint32_t Basis = 0x811C9DC5u; + static constexpr std::uint32_t Prime = 0x01000193u; + }; + + template <> + struct HashFnv1aConstants<8> + { + static constexpr std::uint64_t Basis = 0xCBF29CE484222325ull; + static constexpr std::uint64_t Prime = 0x00000100000001B3ull; + }; + + template + concept HashablePrimitive = std::is_arithmetic_v || std::is_enum_v || std::is_pointer_v; + + template + struct HashFnv1a + { + using Constants = HashFnv1aConstants; + + T Value = Constants::Basis; + + template + inline constexpr void UpdateData(std::span data) noexcept { - hash ^= c; - hash *= 0x1000193u; + T hash = Value; + + if (std::is_constant_evaluated()) + { + static_assert(std::is_integral_v || std::is_enum_v, "Only integral types can be hashed at compile time"); + + for (V c : data) + { + for (std::size_t i = 0; i < sizeof(V); ++i) + { + hash ^= (static_cast(c) >> (i * 8)) & 0xFF; + hash *= Constants::Prime; + } + } + } + else + { + std::byte const* c = reinterpret_cast(data.data()); + std::byte const* end = c + data.size_bytes(); + while (c != end) + { + hash ^= static_cast(*c); + hash *= Constants::Prime; + ++c; + } + } + + Value = hash; } - return hash; - } + + template + inline constexpr void UpdateData(V data) noexcept + { + this->UpdateData(std::span(&data, 1)); + } + + template + inline constexpr void UpdateData(V const& data) noexcept requires requires { std::span(data); } + { + this->UpdateData(std::span(data)); + } + + template + inline static constexpr std::size_t GetHash(std::span data) noexcept + { + HashFnv1a hash; + hash.UpdateData(data); + return hash.Value; + } + + template + inline static constexpr std::size_t GetHash(V data) noexcept + { + return HashFnv1a::GetHash(std::span(&data, 1)); + } + + template + inline static constexpr std::size_t GetHash(V const& data) noexcept requires requires { std::span(data); } + { + return HashFnv1a::GetHash(std::span(data)); + } + }; + + template > + struct TransparentHash : Hash + { + using is_transparent = int; + }; } //! Hash implementation for std::pair to allow using pairs in unordered_set or as key for unordered_map //! Individual types used in pair must be hashable by std::hash -template +template struct std::hash> { -public: - size_t operator()(std::pair const& p) const + std::size_t operator()(std::pair const& p) const { - size_t hashVal = 0; - Trinity::hash_combine(hashVal, p.first); + std::size_t hashVal = std::hash()(p.first); Trinity::hash_combine(hashVal, p.second); return hashVal; } }; -#endif // TrinityCore_Hash_h__ +#endif // TRINITYCORE_HASH_H diff --git a/src/server/database/Database/QueryResult.h b/src/server/database/Database/QueryResult.h index 2e80574b29d..cab0d9c72b0 100644 --- a/src/server/database/Database/QueryResult.h +++ b/src/server/database/Database/QueryResult.h @@ -32,11 +32,11 @@ struct FieldLookupByAliasKey std::string_view Alias; // implicit constructor from string literal for users of the query result - consteval FieldLookupByAliasKey(char const* alias) : HashValue(HashFnv1a(alias)), Alias(alias) { } + consteval FieldLookupByAliasKey(char const* alias) : HashValue(HashFnv1a<>::GetHash(std::span(alias, std::char_traits::length(alias)))), Alias(alias) { } // runtime only constructor used internally to fill alias to index mapping struct RuntimeInitTag { } static inline constexpr RuntimeInit = { }; - FieldLookupByAliasKey(RuntimeInitTag, std::string_view alias) : HashValue(HashFnv1a(alias)), Alias(std::move(alias)) { } + FieldLookupByAliasKey(RuntimeInitTag, std::string_view alias) : HashValue(HashFnv1a<>::GetHash(alias)), Alias(std::move(alias)) { } friend bool operator==(FieldLookupByAliasKey const& left, FieldLookupByAliasKey const& right) = default; diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index 267886c51e5..584198799e1 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -56,12 +56,12 @@ AuctionsBucketKey::AuctionsBucketKey(WorldPackets::AuctionHouse::AuctionBucketKe std::size_t AuctionsBucketKey::Hash(AuctionsBucketKey const& key) { - std::size_t hashVal = 0; - Trinity::hash_combine(hashVal, std::hash()(key.ItemId)); - Trinity::hash_combine(hashVal, std::hash()(key.ItemLevel)); - Trinity::hash_combine(hashVal, std::hash()(key.BattlePetSpeciesId)); - Trinity::hash_combine(hashVal, std::hash()(key.SuffixItemNameDescriptionId)); - return hashVal; + Trinity::HashFnv1a<> hash; + hash.UpdateData(key.ItemId); + hash.UpdateData(key.ItemLevel); + hash.UpdateData(key.BattlePetSpeciesId); + hash.UpdateData(key.SuffixItemNameDescriptionId); + return hash.Value; } AuctionsBucketKey AuctionsBucketKey::ForItem(Item const* item) diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index 6745f9fa553..148d1d2dc42 100644 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -182,11 +182,11 @@ ConditionSourceInfo::ConditionSourceInfo(Map const* map) : std::size_t ConditionId::GetHash() const { - std::size_t hashVal = 0; - Trinity::hash_combine(hashVal, SourceGroup); - Trinity::hash_combine(hashVal, SourceEntry); - Trinity::hash_combine(hashVal, SourceId); - return hashVal; + Trinity::HashFnv1a<> hash; + hash.UpdateData(SourceGroup); + hash.UpdateData(SourceEntry); + hash.UpdateData(SourceId); + return hash.Value; } // Checks if object meets the condition diff --git a/src/server/game/Entities/Object/ObjectGuid.cpp b/src/server/game/Entities/Object/ObjectGuid.cpp index b5510dc9eb2..0b18e78a3a9 100644 --- a/src/server/game/Entities/Object/ObjectGuid.cpp +++ b/src/server/game/Entities/Object/ObjectGuid.cpp @@ -18,7 +18,6 @@ #include "ObjectGuid.h" #include "ByteBuffer.h" #include "Errors.h" -#include "Hash.h" #include "RealmList.h" #include "StringFormat.h" #include "Util.h" @@ -838,21 +837,6 @@ ObjectGuid ObjectGuid::FromString(std::string_view guidString) return Info.Parse(guidString); } -std::size_t ObjectGuid::GetHash() const -{ - std::size_t hashVal = 0; - Trinity::hash_combine(hashVal, _data[0]); - Trinity::hash_combine(hashVal, _data[1]); - return hashVal; -} - -std::array ObjectGuid::GetRawValue() const -{ - std::array raw; - memcpy(raw.data(), _data.data(), BytesSize); - return raw; -} - void ObjectGuid::SetRawValue(std::span rawBytes) { ASSERT(rawBytes.size() == BytesSize, SZFMTD " == " SZFMTD, rawBytes.size(), BytesSize); diff --git a/src/server/game/Entities/Object/ObjectGuid.h b/src/server/game/Entities/Object/ObjectGuid.h index b5f9b84e29a..437ed1e7c61 100644 --- a/src/server/game/Entities/Object/ObjectGuid.h +++ b/src/server/game/Entities/Object/ObjectGuid.h @@ -20,6 +20,7 @@ #include "Define.h" #include "EnumFlag.h" +#include "Hash.h" #include "StringFormatFwd.h" #include "advstd.h" #include @@ -322,7 +323,7 @@ class TC_GAME_API ObjectGuid constexpr ObjectGuid() = default; uint64 GetRawValue(std::size_t i) const { return _data[i]; } - std::array GetRawValue() const; + std::span GetRawValue() const { return std::span(reinterpret_cast(_data.data()), BytesSize); } void SetRawValue(std::span rawBytes); void SetRawValue(uint64 high, uint64 low) { _data[0] = low; _data[1] = high; } void Clear() { _data = { }; } @@ -396,7 +397,6 @@ class TC_GAME_API ObjectGuid std::string ToString() const; std::string ToHexString() const; static ObjectGuid FromString(std::string_view guidString); - std::size_t GetHash() const; template ::Format::value == ObjectGuidFormatType::Null, int32> = 0> static constexpr ObjectGuid Create() { return ObjectGuidFactory::CreateNull(); } template ::Format::value == ObjectGuidFormatType::Uniq, int32> = 0> static constexpr ObjectGuid Create(LowType id) { return ObjectGuidFactory::CreateUniq(id); } @@ -437,12 +437,12 @@ using GuidUnorderedSet = std::unordered_set; TC_GAME_API ByteBuffer& operator<<(ByteBuffer& buf, ObjectGuid const& guid); TC_GAME_API ByteBuffer& operator>>(ByteBuffer& buf, ObjectGuid& guid); -template<> +template <> struct std::hash { - size_t operator()(ObjectGuid const& key) const noexcept + std::size_t operator()(ObjectGuid const& key) const noexcept { - return key.GetHash(); + return Trinity::HashFnv1a<>::GetHash(key.GetRawValue()); } }; diff --git a/src/server/game/Globals/AreaTriggerDataStore.cpp b/src/server/game/Globals/AreaTriggerDataStore.cpp index 55a64148916..2f245336725 100644 --- a/src/server/game/Globals/AreaTriggerDataStore.cpp +++ b/src/server/game/Globals/AreaTriggerDataStore.cpp @@ -34,10 +34,10 @@ struct std::hash { std::size_t operator()(AreaTriggerId const& value) const noexcept { - size_t hashVal = 0; - Trinity::hash_combine(hashVal, value.Id); - Trinity::hash_combine(hashVal, value.IsCustom); - return hashVal; + Trinity::HashFnv1a<> hash; + hash.UpdateData(value.Id); + hash.UpdateData(value.IsCustom); + return hash.Value; } }; diff --git a/src/server/game/Maps/DynamicMMapTileBuilder.cpp b/src/server/game/Maps/DynamicMMapTileBuilder.cpp index 984dc4a7edc..47e10743ee2 100644 --- a/src/server/game/Maps/DynamicMMapTileBuilder.cpp +++ b/src/server/game/Maps/DynamicMMapTileBuilder.cpp @@ -63,20 +63,18 @@ struct std::hash { static std::size_t Compute(TileCacheKey const& key) noexcept { - size_t hashVal = 0; - Trinity::hash_combine(hashVal, key.TerrainMapId); - Trinity::hash_combine(hashVal, key.X); - Trinity::hash_combine(hashVal, key.Y); + Trinity::HashFnv1a<> hash; + hash.UpdateData(key.TerrainMapId); + hash.UpdateData(key.X); + hash.UpdateData(key.Y); for (TileCacheKeyObject const& object : key.Objects) { - Trinity::hash_combine(hashVal, object.DisplayId); - Trinity::hash_combine(hashVal, object.Scale); - Trinity::hash_combine(hashVal, object.Position[0]); - Trinity::hash_combine(hashVal, object.Position[1]); - Trinity::hash_combine(hashVal, object.Position[2]); - Trinity::hash_combine(hashVal, object.Rotation); + hash.UpdateData(object.DisplayId); + hash.UpdateData(object.Scale); + hash.UpdateData(object.Position); + hash.UpdateData(object.Rotation); } - return hashVal; + return hash.Value; } std::size_t operator()(TileCacheKey const& key) const noexcept