mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-01 10:52:26 +00:00
7fda6dde73
Replaces all instances of ankerl's unordered map/set with boost's `unordered_flat_*` classes. This uses std::hash since boost::hash is actually a lot slower. Also adds an abstraction layer in `Common` so future changes are quicker and easier. Other implementation details: - ankerl provided hash specializations for tuple and pair, so those were ported here - std::erase_if doesn't work on boost, so just used the ADL'd erase_if This should be about equal or superior performance as unordered_dense for everything except iteration. Signed-off-by: crueter <crueter@eden-emu.dev> - [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions). - [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md) - [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability. ------------------- Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4326 Reviewed-by: Lizzie and Samuel <lizzie@eden-emu.dev> Reviewed-by: MaranBr <maranbr@eden-emu.dev>
131 lines
3.2 KiB
C++
131 lines
3.2 KiB
C++
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
// SPDX-FileCopyrightText: 2005-2014 Daniel James
|
|
// SPDX-FileCopyrightText: 2016 Austin Appleby
|
|
// SPDX-License-Identifier: BSL-1.0
|
|
|
|
#pragma once
|
|
|
|
#include <array>
|
|
#include <climits>
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <limits>
|
|
#include <tuple>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace Common {
|
|
|
|
namespace detail {
|
|
|
|
template <typename T>
|
|
requires std::is_unsigned_v<T>
|
|
inline std::size_t HashValue(T val) {
|
|
const unsigned int size_t_bits = std::numeric_limits<std::size_t>::digits;
|
|
const unsigned int length =
|
|
(std::numeric_limits<T>::digits - 1) / static_cast<unsigned int>(size_t_bits);
|
|
|
|
std::size_t seed = 0;
|
|
|
|
for (unsigned int i = length * size_t_bits; i > 0; i -= size_t_bits) {
|
|
seed ^= static_cast<size_t>(val >> i) + (seed << 6) + (seed >> 2);
|
|
}
|
|
|
|
seed ^= static_cast<size_t>(val) + (seed << 6) + (seed >> 2);
|
|
|
|
return seed;
|
|
}
|
|
|
|
template <size_t Bits>
|
|
struct HashCombineImpl {
|
|
template <typename T>
|
|
static inline T fn(T seed, T value) {
|
|
seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
|
return seed;
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct HashCombineImpl<64> {
|
|
static inline std::uint64_t fn(std::uint64_t h, std::uint64_t k) {
|
|
const std::uint64_t m = (std::uint64_t(0xc6a4a793) << 32) + 0x5bd1e995;
|
|
const int r = 47;
|
|
|
|
k *= m;
|
|
k ^= k >> r;
|
|
k *= m;
|
|
|
|
h ^= k;
|
|
h *= m;
|
|
|
|
// Completely arbitrary number, to prevent 0's
|
|
// from hashing to 0.
|
|
h += 0xe6546b64;
|
|
|
|
return h;
|
|
}
|
|
};
|
|
|
|
} // namespace detail
|
|
|
|
template <typename T>
|
|
requires std::is_unsigned_v<T>
|
|
inline void HashCombine(std::size_t& seed, const T& v) {
|
|
seed = detail::HashCombineImpl<sizeof(std::size_t) * CHAR_BIT>::fn(seed, detail::HashValue(v));
|
|
}
|
|
|
|
template <typename T>
|
|
requires (!std::is_unsigned_v<T>)
|
|
inline void HashCombine(std::size_t& seed, const T& v) {
|
|
seed = detail::HashCombineImpl<sizeof(std::size_t) * CHAR_BIT>::fn(seed, std::hash<T>{}(v));
|
|
}
|
|
|
|
template <typename It>
|
|
inline std::size_t HashRange(It first, It last) {
|
|
std::size_t seed = 0;
|
|
|
|
for (; first != last; ++first) {
|
|
HashCombine<typename std::iterator_traits<It>::value_type>(seed, *first);
|
|
}
|
|
|
|
return seed;
|
|
}
|
|
|
|
template <typename T, size_t Size>
|
|
std::size_t HashValue(const std::array<T, Size>& v) {
|
|
return HashRange(v.cbegin(), v.cend());
|
|
}
|
|
|
|
template <typename T, typename Allocator>
|
|
std::size_t HashValue(const std::vector<T, Allocator>& v) {
|
|
return HashRange(v.cbegin(), v.cend());
|
|
}
|
|
|
|
} // namespace Common
|
|
|
|
namespace std {
|
|
|
|
template <typename... Args>
|
|
struct hash<std::tuple<Args...>> {
|
|
std::size_t operator()(const std::tuple<Args...>& t) const noexcept {
|
|
std::size_t seed = 0;
|
|
std::apply([&seed](const Args&... args) { (Common::HashCombine(seed, args), ...); }, t);
|
|
return seed;
|
|
}
|
|
};
|
|
|
|
template <class A, class B>
|
|
struct hash<std::pair<A, B>> {
|
|
std::size_t operator()(const std::pair<A, B>& p) const noexcept {
|
|
std::size_t seed = 0;
|
|
Common::HashCombine(seed, p.first);
|
|
Common::HashCombine(seed, p.second);
|
|
return seed;
|
|
}
|
|
};
|
|
|
|
} // namespace std
|