Fix issue #158 - imperfect hash for tuple of small integers

This commit is contained in:
greg7mdp
2022-07-19 20:32:09 -04:00
parent 01ea8093e6
commit 1249057455
2 changed files with 62 additions and 13 deletions
+1
View File
@@ -145,6 +145,7 @@ if (PHMAP_BUILD_EXAMPLES)
target_include_directories(ex_serialize PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../cereal/include>)
add_executable(ex_hash_std examples/hash_std.cc phmap.natvis)
add_executable(ex_hash_value examples/hash_value.cc phmap.natvis)
add_executable(ex_hash examples/hash.cc phmap.natvis)
add_executable(ex_two_files examples/f1.cc examples/f2.cc phmap.natvis)
add_executable(ex_insert_bench examples/insert_bench.cc phmap.natvis)
add_executable(ex_knucleotide examples/knucleotide.cc phmap.natvis)
+61 -13
View File
@@ -286,6 +286,13 @@ struct Hash<double> : public phmap_unary_function<double, size_t>
#endif
#if defined(_MSC_VER)
# define PHMAP_HASH_ROTL32(x, r) _rotl(x,r)
#else
# define PHMAP_HASH_ROTL32(x, r) (x << r) | (x >> (32 - r))
#endif
template <class H, int sz> struct Combiner
{
H operator()(H seed, size_t value);
@@ -293,17 +300,57 @@ template <class H, int sz> struct Combiner
template <class H> struct Combiner<H, 4>
{
H operator()(H seed, size_t value)
H operator()(H h1, size_t k1)
{
return seed ^ (value + 0x9e3779b9 + (seed << 6) + (seed >> 2));
#if 1
// Copyright 2005-2014 Daniel James.
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
const uint32_t c1 = 0xcc9e2d51;
const uint32_t c2 = 0x1b873593;
k1 *= c1;
k1 = PHMAP_HASH_ROTL32(k1,15);
k1 *= c2;
h1 ^= k1;
h1 = PHMAP_HASH_ROTL32(h1,13);
h1 = h1*5+0xe6546b64;
return h1;
#else
return h1 ^ (k1 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2));
#endif
}
};
template <class H> struct Combiner<H, 8>
{
H operator()(H seed, size_t value)
H operator()(H h, size_t k)
{
return seed ^ (value + size_t(0xc6a4a7935bd1e995) + (seed << 6) + (seed >> 2));
#if 1
// Copyright 2005-2014 Daniel James.
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
const uint64_t m = (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;
#else
return h ^ (k + size_t(0xc6a4a7935bd1e995) + (h << 6) + (h >> 2));
#endif
}
};
@@ -347,21 +394,22 @@ struct Hash<std::pair<T1, T2>> {
template<class... T>
struct Hash<std::tuple<T...>> {
size_t operator()(std::tuple<T...> const& t) const noexcept {
return _hash_helper(t);
size_t seed = 0;
return _hash_helper(seed, t);
}
private:
template<size_t I = 0, class ...P>
typename std::enable_if<I == sizeof...(P), size_t>::type
_hash_helper(const std::tuple<P...> &) const noexcept { return 0; }
template<size_t I = 0, class TUP>
typename std::enable_if<I == std::tuple_size<TUP>::value, size_t>::type
_hash_helper(size_t seed, const TUP &) const noexcept { return seed; }
template<size_t I = 0, class ...P>
typename std::enable_if<I < sizeof...(P), size_t>::type
_hash_helper(const std::tuple<P...> &t) const noexcept {
template<size_t I = 0, class TUP>
typename std::enable_if<I < std::tuple_size<TUP>::value, size_t>::type
_hash_helper(size_t seed, const TUP &t) const noexcept {
const auto &el = std::get<I>(t);
using el_type = typename std::remove_cv<typename std::remove_reference<decltype(el)>::type>::type;
return Combiner<size_t, sizeof(size_t)>()(
phmap::Hash<el_type>()(el), _hash_helper<I + 1>(t));
seed = Combiner<size_t, sizeof(size_t)>()(seed, phmap::Hash<el_type>()(el));
return _hash_helper<I + 1>(seed, t);
}
};