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
This commit is contained in:
Shauren
2026-01-08 00:41:16 +01:00
parent 44839b1a4c
commit 38886d5f01
9 changed files with 160 additions and 68 deletions
+1 -1
View File
@@ -46,7 +46,7 @@ std::unordered_map<uint32, Minutes, std::identity> InitTimezoneHashDb()
std::chrono::sys_info sysInfo = zone.get_info(dummmy);
Minutes offsetMinutes = std::chrono::duration_cast<Minutes>(sysInfo.offset);
std::string offsetStr = Trinity::ToString(offsetMinutes.count());
hashToOffset.emplace(Trinity::HashFnv1a(offsetStr), offsetMinutes);
hashToOffset.emplace(Trinity::HashFnv1a<uint32>::GetHash(offsetStr), offsetMinutes);
}
#else
+128 -18
View File
@@ -15,46 +15,156 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TrinityCore_Hash_h__
#define TrinityCore_Hash_h__
#ifndef TRINITYCORE_HASH_H
#define TRINITYCORE_HASH_H
#include <functional>
#include <string_view>
#include <span>
#include <utility>
namespace Trinity
{
template<typename T>
template <typename T>
inline void hash_combine(std::size_t& seed, T const& val)
{
seed ^= std::hash<T>()(val) + 0x9E3779B9 + (seed << 6) + (seed >> 2);
// Taken from boost::hash_combine
seed = seed + 0x9E3779B9 + std::hash<T>()(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 <std::size_t>
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 <typename T>
concept HashablePrimitive = std::is_arithmetic_v<T> || std::is_enum_v<T> || std::is_pointer_v<T>;
template <typename T = std::size_t>
struct HashFnv1a
{
using Constants = HashFnv1aConstants<sizeof(T)>;
T Value = Constants::Basis;
template <HashablePrimitive V, std::size_t Extent>
inline constexpr void UpdateData(std::span<V, Extent> data) noexcept
{
hash ^= c;
hash *= 0x1000193u;
T hash = Value;
if (std::is_constant_evaluated())
{
static_assert(std::is_integral_v<V> || std::is_enum_v<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<T>(c) >> (i * 8)) & 0xFF;
hash *= Constants::Prime;
}
}
}
else
{
std::byte const* c = reinterpret_cast<std::byte const*>(data.data());
std::byte const* end = c + data.size_bytes();
while (c != end)
{
hash ^= static_cast<T>(*c);
hash *= Constants::Prime;
++c;
}
}
Value = hash;
}
return hash;
}
template <HashablePrimitive V>
inline constexpr void UpdateData(V data) noexcept
{
this->UpdateData(std::span<V, 1>(&data, 1));
}
template <typename V>
inline constexpr void UpdateData(V const& data) noexcept requires requires { std::span(data); }
{
this->UpdateData(std::span(data));
}
template <HashablePrimitive V, std::size_t Extent>
inline static constexpr std::size_t GetHash(std::span<V, Extent> data) noexcept
{
HashFnv1a hash;
hash.UpdateData(data);
return hash.Value;
}
template <HashablePrimitive V>
inline static constexpr std::size_t GetHash(V data) noexcept
{
return HashFnv1a::GetHash(std::span<V, 1>(&data, 1));
}
template <typename V>
inline static constexpr std::size_t GetHash(V const& data) noexcept requires requires { std::span(data); }
{
return HashFnv1a::GetHash(std::span(data));
}
};
template <typename T, typename Hash = std::hash<T>>
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<class K, class V>
template <typename K, typename V>
struct std::hash<std::pair<K, V>>
{
public:
size_t operator()(std::pair<K, V> const& p) const
std::size_t operator()(std::pair<K, V> const& p) const
{
size_t hashVal = 0;
Trinity::hash_combine(hashVal, p.first);
std::size_t hashVal = std::hash<K>()(p.first);
Trinity::hash_combine(hashVal, p.second);
return hashVal;
}
};
#endif // TrinityCore_Hash_h__
#endif // TRINITYCORE_HASH_H
+2 -2
View File
@@ -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<char>::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;
@@ -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<uint32>()(key.ItemId));
Trinity::hash_combine(hashVal, std::hash<uint16>()(key.ItemLevel));
Trinity::hash_combine(hashVal, std::hash<uint16>()(key.BattlePetSpeciesId));
Trinity::hash_combine(hashVal, std::hash<uint16>()(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)
+5 -5
View File
@@ -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
@@ -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<uint8, 16> ObjectGuid::GetRawValue() const
{
std::array<uint8, 16> raw;
memcpy(raw.data(), _data.data(), BytesSize);
return raw;
}
void ObjectGuid::SetRawValue(std::span<uint8 const> rawBytes)
{
ASSERT(rawBytes.size() == BytesSize, SZFMTD " == " SZFMTD, rawBytes.size(), BytesSize);
+5 -5
View File
@@ -20,6 +20,7 @@
#include "Define.h"
#include "EnumFlag.h"
#include "Hash.h"
#include "StringFormatFwd.h"
#include "advstd.h"
#include <array>
@@ -322,7 +323,7 @@ class TC_GAME_API ObjectGuid
constexpr ObjectGuid() = default;
uint64 GetRawValue(std::size_t i) const { return _data[i]; }
std::array<uint8, 16> GetRawValue() const;
std::span<uint8 const, 16> GetRawValue() const { return std::span<uint8 const, 16>(reinterpret_cast<uint8 const*>(_data.data()), BytesSize); }
void SetRawValue(std::span<uint8 const> 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 <HighGuid type, std::enable_if_t<ObjectGuidTraits<type>::Format::value == ObjectGuidFormatType::Null, int32> = 0> static constexpr ObjectGuid Create() { return ObjectGuidFactory::CreateNull(); }
template <HighGuid type, std::enable_if_t<ObjectGuidTraits<type>::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<ObjectGuid>;
TC_GAME_API ByteBuffer& operator<<(ByteBuffer& buf, ObjectGuid const& guid);
TC_GAME_API ByteBuffer& operator>>(ByteBuffer& buf, ObjectGuid& guid);
template<>
template <>
struct std::hash<ObjectGuid>
{
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());
}
};
@@ -34,10 +34,10 @@ struct std::hash<AreaTriggerId>
{
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;
}
};
@@ -63,20 +63,18 @@ struct std::hash<TileCacheKey>
{
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