/* Copyright (c) 2017, 2017, Oracle and/or its affiliates. The code in this file is copied from Boost 1.63.0 boost/functional/hash/hash.hpp, which contains the following copyright notice: 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) 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. */ /** @file include/my_hash_combine.h A convenient way to combine two hash values. It was decided to copy (parts of) the boost::hash_combine() implementation instead of using it directly (by including ) because of the following reasons, raised by Steinar and Tor: Pros: - It solves a real problem (how to hash std::pair). - Few dependencies (just type_traits and enable_if). - Seems like a reasonable implementation. Cons: - It's more Boost. - Doesn't seem to be accepted into C++17, so it's something we'd have to drag around for a long time without an easy migration path off it. - It solves the problem in a suboptimal way; combining values after hash-finalization is going to both hash worse and slower than before it. The real way requires an interface change to how std::hash works (exposing more internal hasher state). I know people have been working on this, but evidently it didn't reach C++17 either. - Uses boost::hash instead of std::hash. This is probably the biggest killer for me. - Can easily be implemented by ourselves by lifting the core parts of the Boost implementation. (It's about 20 lines.) I could go either way, but my immediate thought is probably that we should copy the Boost implementation into some header, and then prefix it with a warning saying that you shouldn't use this if you need optimal performance or hash distribution. Steinar */ #ifndef MY_HASH_COMBINE_INCLUDED #define MY_HASH_COMBINE_INCLUDED #if defined(_MSC_VER) #define MY_FUNCTIONAL_HASH_ROTL32(x, r) _rotl(x, r) #else #define MY_FUNCTIONAL_HASH_ROTL32(x, r) (x << r) | (x >> (32 - r)) #endif /* _MSC_VER */ template inline void my_hash_combine(SizeT &seed, SizeT value) { seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2); } inline void my_hash_combine(std::uint32_t &h1, std::uint32_t k1) { const uint32_t c1 = 0xcc9e2d51; const uint32_t c2 = 0x1b873593; k1 *= c1; k1 = MY_FUNCTIONAL_HASH_ROTL32(k1, 15); k1 *= c2; h1 ^= k1; h1 = MY_FUNCTIONAL_HASH_ROTL32(h1, 13); h1 = h1 * 5 + 0xe6546b64; } inline void my_hash_combine(std::uint64_t &h, std::uint64_t k) { const std::uint64_t m = 0xc6a4a7935bd1e995ull; 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; } #endif /* MY_HASH_COMBINE_INCLUDED */