From 40ec854466812aac8b21077c5ad07604b2dc2aca Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Fri, 16 Sep 2022 18:57:21 +0300 Subject: [PATCH] Add hash_mix, change hash_combine to use it --- .../boost/container_hash/detail/hash_mix.hpp | 113 ++++++++++++++++++ include/boost/container_hash/hash.hpp | 89 +------------- 2 files changed, 117 insertions(+), 85 deletions(-) create mode 100644 include/boost/container_hash/detail/hash_mix.hpp diff --git a/include/boost/container_hash/detail/hash_mix.hpp b/include/boost/container_hash/detail/hash_mix.hpp new file mode 100644 index 0000000..6556c15 --- /dev/null +++ b/include/boost/container_hash/detail/hash_mix.hpp @@ -0,0 +1,113 @@ +// Copyright 2022 Peter Dimov +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_HASH_DETAIL_HASH_MIX_HPP +#define BOOST_HASH_DETAIL_HASH_MIX_HPP + +#include +#include +#include + +namespace boost +{ +namespace hash_detail +{ + +template struct hash_mix_impl; + +// hash_mix for 64 bit size_t +// +// The general "xmxmx" form of state of the art 64 bit mixers originates +// from Murmur3 by Austin Appleby, which uses the following function as +// its "final mix": +// +// k ^= k >> 33; +// k *= 0xff51afd7ed558ccd; +// k ^= k >> 33; +// k *= 0xc4ceb9fe1a85ec53; +// k ^= k >> 33; +// +// (https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp) +// +// It has subsequently been improved multiple times by different authors +// by changing the constants. The most well known improvement is the +// so-called "variant 13" function by David Stafford: +// +// k ^= k >> 30; +// k *= 0xbf58476d1ce4e5b9; +// k ^= k >> 27; +// k *= 0x94d049bb133111eb; +// k ^= k >> 31; +// +// (https://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html) +// +// This mixing function is used in the splitmix64 RNG: +// http://xorshift.di.unimi.it/splitmix64.c +// +// We use Jon Maiga's implementation from +// http://jonkagstrom.com/mx3/mx3_rev2.html +// +// x ^= x >> 32; +// x *= 0xe9846af9b1a615d; +// x ^= x >> 32; +// x *= 0xe9846af9b1a615d; +// x ^= x >> 28; +// +// An equally good alternative is Pelle Evensen's Moremur: +// +// x ^= x >> 27; +// x *= 0x3C79AC492BA7B653; +// x ^= x >> 33; +// x *= 0x1C69B3F74AC4AE35; +// x ^= x >> 27; +// +// (https://mostlymangling.blogspot.com/2019/12/stronger-better-morer-moremur-better.html) + +template<> struct hash_mix_impl<64> +{ + inline static boost::uint64_t fn( boost::uint64_t x ) + { + boost::uint64_t const m = (boost::uint64_t(0xe9846af9) << 32) + 0xb1a615d; + + x ^= x >> 32; + x *= m; + x ^= x >> 32; + x *= m; + x ^= x >> 28; + + return x; + } +}; + +// hash_mix for 32 bit size_t +// +// We use the "best xmxmx" implementation from +// https://github.com/skeeto/hash-prospector/issues/19 + +template<> struct hash_mix_impl<32> +{ + inline static boost::uint32_t fn( boost::uint32_t x ) + { + boost::uint32_t const m1 = 0x21f0aaad; + boost::uint32_t const m2 = 0x735a2d97; + + x ^= x >> 16; + x *= m1; + x ^= x >> 15; + x *= m2; + x ^= x >> 15; + + return x; + } +}; + +inline std::size_t hash_mix( std::size_t v ) +{ + return hash_mix_impl::fn( v ); +} + +} // namespace hash_detail +} // namespace boost + +#endif // #ifndef BOOST_HASH_DETAIL_HASH_MIX_HPP diff --git a/include/boost/container_hash/hash.hpp b/include/boost/container_hash/hash.hpp index 991a505..c2e0cbe 100644 --- a/include/boost/container_hash/hash.hpp +++ b/include/boost/container_hash/hash.hpp @@ -1,17 +1,11 @@ // Copyright 2005-2014 Daniel James. -// Copyright 2021 Peter Dimov. +// Copyright 2021, 2022 Peter Dimov. // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt // Based on Peter Dimov's proposal // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1756.pdf // issue 6.18. -// -// This also contains public domain code from MurmurHash. From the -// MurmurHash header: - -// MurmurHash3 was written by Austin Appleby, and is placed in the public -// domain. The author hereby disclaims copyright to this source code. #ifndef BOOST_FUNCTIONAL_HASH_HASH_HPP #define BOOST_FUNCTIONAL_HASH_HASH_HPP @@ -21,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -58,12 +53,6 @@ #include #endif -#if defined(_MSC_VER) -# define BOOST_FUNCTIONAL_HASH_ROTL32(x, r) _rotl(x,r) -#else -# define BOOST_FUNCTIONAL_HASH_ROTL32(x, r) (x << r) | (x >> (32 - r)) -#endif - namespace boost { @@ -558,82 +547,12 @@ namespace boost // boost::hash_combine // - namespace hash_detail - { - - template struct hash_combine_impl - { - template - inline static SizeT fn(SizeT seed, SizeT value) - { - seed ^= value + 0x9e3779b9 + (seed<<6) + (seed>>2); - return seed; - } - }; - - template<> struct hash_combine_impl<32> - { - inline static boost::uint32_t fn(boost::uint32_t h1, boost::uint32_t k1) - { - const boost::uint32_t c1 = 0xcc9e2d51; - const boost::uint32_t c2 = 0x1b873593; - - k1 *= c1; - k1 = BOOST_FUNCTIONAL_HASH_ROTL32(k1,15); - k1 *= c2; - - h1 ^= k1; - h1 = BOOST_FUNCTIONAL_HASH_ROTL32(h1,13); - h1 = h1*5+0xe6546b64; - - return h1; - } - }; - - template<> struct hash_combine_impl<64> - { - inline static boost::uint64_t fn(boost::uint64_t h, boost::uint64_t k) - { - const boost::uint64_t m = (boost::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; - } - }; - } - -#if defined(BOOST_MSVC) -#pragma warning(push) -#if BOOST_MSVC <= 1400 -#pragma warning(disable:4267) // 'argument' : conversion from 'size_t' to - // 'unsigned int', possible loss of data - // A misguided attempt to detect 64-bit - // incompatability. -#endif -#endif - template - inline void hash_combine(std::size_t& seed, T const& v) + inline void hash_combine( std::size_t& seed, T const& v ) { - boost::hash hasher; - seed = boost::hash_detail::hash_combine_impl::fn(seed, hasher(v)); + seed = boost::hash_detail::hash_mix( seed + 0x9e3779b9 + boost::hash()( v ) ); } -#if defined(BOOST_MSVC) -#pragma warning(pop) -#endif - // // boost::hash_range //