-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement new hashing protocol now that Boost.ContainerHash requires …
…C++11: - Hashes trivially scalar types - For the non scalar types calls unqualified hash_value activating ADL (boost::hash protocol) - If ADL does not find a viable candidate, a boost::hash direct call is performed via a forward declaration, so that additional candidates are looked up if the user has included boost/container_hash/hash.hpp. This maintains a high level of compatibility with boost::hash but making Boost.Intrusive independent from Boost.ContainerHash, so that different C++ standard levels can be used and users using different hash functions don't need direct and indirect dependencies brought by Boost.ContainerHash.
- Loading branch information
1 parent
8b4297b
commit 6464d0e
Showing
9 changed files
with
820 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
################################################################################ | ||
# This .gitignore file was automatically created by Microsoft(R) Visual Studio. | ||
################################################################################ | ||
|
||
/proj/vc14.1ide |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,277 @@ | ||
///////////////////////////////////////////////////////////////////////////// | ||
// | ||
// Copyright 2005-2014 Daniel James. | ||
// Copyright 2021, 2022 Peter Dimov. | ||
// Copyright 2024 Ion Gaztañaga. | ||
// 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. | ||
// | ||
// The original C++11 implementation was done by Peter Dimov | ||
// The C++03 porting was done by Ion Gaztanaga. | ||
// | ||
// The goal of this header is to avoid Intrusive's hard dependency on ContainerHash, | ||
// which adds additional dependencies and the minimum supported C++ standard can | ||
// differ between both libraries. However, a compatibility protocol is used so that | ||
// users compatible with ContainerHash are also compatible with Intrusive: | ||
// | ||
// - If users define `hash_value` (as required by boost::hash) for their classes | ||
// are automatically compatible with Intrusive unordered containers. | ||
// | ||
// - If users include boost/container_hash/hash.hpp in their headers, Intrusive | ||
// unordered containers will take advantage of boost::hash compatibility hash functions | ||
// (such as hashing functions for range-compatible types, standard containers, etc.) | ||
// | ||
// See http://www.boost.org/libs/intrusive for documentation. | ||
// | ||
///////////////////////////////////////////////////////////////////////////// | ||
|
||
#ifndef BOOST_INTRUSIVE_HASH_HASH_HPP | ||
#define BOOST_INTRUSIVE_HASH_HASH_HPP | ||
|
||
#include <boost/intrusive/detail/config_begin.hpp> | ||
#include <boost/intrusive/detail/workaround.hpp> | ||
#include <boost/intrusive/detail/hash_integral.hpp> | ||
#include <boost/intrusive/detail/hash_mix.hpp> | ||
#include <boost/intrusive/detail/hash_combine.hpp> | ||
#include <boost/cstdint.hpp> | ||
#include <climits> | ||
#include <cstring> | ||
#include <cfloat> | ||
#include <boost/intrusive/detail/mpl.hpp> | ||
|
||
namespace boost { | ||
|
||
template<class T> | ||
struct hash; | ||
|
||
} //namespace boost | ||
|
||
//Fallback function to call boost::hash if scalar type and ADL fail. | ||
//The user must include boost/container_hash/hash.hpp when to make this call work, | ||
//this allows boost::intrusive to be compatible with boost::hash without | ||
//a mandatory physical (header inclusion) dependency | ||
namespace boost_intrusive_adl | ||
{ | ||
template<class T> | ||
inline std::size_t hash_value(const T& v) | ||
{ | ||
return boost::hash<T>()(v); | ||
} | ||
} | ||
|
||
namespace boost { | ||
namespace intrusive { | ||
namespace detail { | ||
|
||
//ADL-based lookup hash call | ||
template <class T> | ||
inline typename detail::disable_if_c<detail::is_scalar<T>::value, std::size_t>::type | ||
hash_value_dispatch(const T& v) | ||
{ | ||
//Try ADL lookup, if it fails, boost_intrusive_adl::hash_value will retry with boost::hash | ||
using boost_intrusive_adl::hash_value; | ||
return hash_value(v); | ||
} | ||
|
||
template <typename T> | ||
typename enable_if_c<is_enum<T>::value, std::size_t>::type | ||
hash_value( T v ) | ||
{ | ||
return static_cast<std::size_t>( v ); | ||
} | ||
|
||
//////////////////////////////////////////////////////////// | ||
// | ||
// floating point types | ||
// | ||
//////////////////////////////////////////////////////////// | ||
|
||
template<class T, std::size_t Bits = sizeof(T) * CHAR_BIT> | ||
struct hash_float_impl; | ||
|
||
// float | ||
template<class T> struct hash_float_impl<T, 32> | ||
{ | ||
static std::size_t fn( T v ) | ||
{ | ||
boost::uint32_t w; | ||
std::memcpy( &w, &v, sizeof( v ) ); | ||
|
||
return w; | ||
} | ||
}; | ||
|
||
// double | ||
template<class T> struct hash_float_impl<T, 64> | ||
{ | ||
static std::size_t fn( T v ) | ||
{ | ||
boost::uint64_t w; | ||
std::memcpy( &w, &v, sizeof( v ) ); | ||
|
||
return hash_value( w ); | ||
} | ||
}; | ||
|
||
// 80 bit long double in 12 bytes | ||
template<class T> struct hash_float_impl<T, 96> | ||
{ | ||
static std::size_t fn( T v ) | ||
{ | ||
boost::uint64_t w[ 2 ] = {}; | ||
std::memcpy( &w, &v, 80 / CHAR_BIT ); | ||
|
||
std::size_t seed = 0; | ||
|
||
seed = hash_value( w[0] ) + (hash_mix)( seed ); | ||
seed = hash_value( w[1] ) + (hash_mix)( seed ); | ||
|
||
return seed; | ||
} | ||
}; | ||
|
||
#if (LDBL_MAX_10_EXP == 4932) | ||
|
||
// 80 bit long double in 16 bytes | ||
template<class T> struct hash_float_impl<T, 128> | ||
{ | ||
static std::size_t fn( T v ) | ||
{ | ||
boost::uint64_t w[ 2 ] = {}; | ||
std::memcpy( &w, &v, 80 / CHAR_BIT ); | ||
|
||
std::size_t seed = 0; | ||
|
||
seed = hash_value( w[0] ) + (hash_mix)( seed ); | ||
seed = hash_value( w[1] ) + (hash_mix)( seed ); | ||
|
||
return seed; | ||
} | ||
}; | ||
|
||
#elif (LDBL_MAX_10_EXP > 4932) | ||
// 128 bit long double | ||
template<class T> struct hash_float_impl<T, 128> | ||
{ | ||
static std::size_t fn( T v ) | ||
{ | ||
boost::uint64_t w[ 2 ]; | ||
std::memcpy( &w, &v, sizeof( v ) ); | ||
|
||
std::size_t seed = 0; | ||
|
||
#if defined(__FLOAT_WORD_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__ | ||
|
||
seed = hash_value( w[1] ) + (hash_mix)( seed ); | ||
seed = hash_value( w[0] ) + (hash_mix)( seed ); | ||
|
||
#else | ||
|
||
seed = hash_value( w[0] ) + (hash_mix)( seed ); | ||
seed = hash_value( w[1] ) + (hash_mix)( seed ); | ||
|
||
#endif | ||
return seed; | ||
} | ||
}; | ||
#endif //#if (LDBL_MAX_10_EXP == 4932) | ||
|
||
template <typename T> | ||
typename enable_if_c<is_floating_point<T>::value, std::size_t>::type | ||
hash_value( T v ) | ||
{ | ||
return boost::intrusive::detail::hash_float_impl<T>::fn( v + 0 ); | ||
} | ||
|
||
//////////////////////////////////////////////////////////// | ||
// | ||
// pointer types | ||
// | ||
//////////////////////////////////////////////////////////// | ||
// `x + (x >> 3)` adjustment by Alberto Barbati and Dave Harris. | ||
template <class T> std::size_t hash_value( T* const& v ) | ||
{ | ||
std::size_t x = reinterpret_cast<std::size_t>( v ); | ||
return hash_value( x + (x >> 3) ); | ||
} | ||
|
||
//////////////////////////////////////////////////////////// | ||
// | ||
// std::nullptr_t | ||
// | ||
//////////////////////////////////////////////////////////// | ||
#if !defined(BOOST_NO_CXX11_NULLPTR) | ||
template <typename T> | ||
typename enable_if_c<is_same<T, std::nullptr_t>::value, std::size_t>::type | ||
hash_value( T const &) | ||
{ | ||
return (hash_value)( static_cast<void*>( nullptr ) ); | ||
} | ||
#endif | ||
|
||
//////////////////////////////////////////////////////////// | ||
// | ||
// Array types | ||
// | ||
//////////////////////////////////////////////////////////// | ||
|
||
//Forward declaration or internal hash functor, for array iteration | ||
template<class T> | ||
struct internal_hash_functor; | ||
|
||
template<class T, std::size_t N> | ||
inline std::size_t hash_value_dispatch( T const (&x)[ N ] ) | ||
{ | ||
std::size_t seed = 0; | ||
for(std::size_t i = 0; i != N; ++i){ | ||
hash_combine_size_t(seed, internal_hash_functor<T>()(x[i])); | ||
} | ||
return seed; | ||
} | ||
|
||
template<class T, std::size_t N> | ||
inline std::size_t hash_value_dispatch( T (&x)[ N ] ) | ||
{ | ||
std::size_t seed = 0; | ||
for (std::size_t i = 0; i != N; ++i) { | ||
hash_combine_size_t(seed, internal_hash_functor<T>()(x[i])); | ||
} | ||
return seed; | ||
} | ||
|
||
//////////////////////////////////////////////////////////// | ||
// | ||
// Scalar types, calls proper overload | ||
// | ||
//////////////////////////////////////////////////////////// | ||
template <class T> | ||
inline typename detail::enable_if_c<detail::is_scalar<T>::value, std::size_t>::type | ||
hash_value_dispatch(const T &v) | ||
{ | ||
return boost::intrusive::detail::hash_value(v); | ||
} | ||
|
||
//Internal "anonymous" hash functor, first selects between "built-in" scalar/array types | ||
//and ADL-based lookup | ||
|
||
template<class T> | ||
struct internal_hash_functor | ||
{ | ||
inline std::size_t operator()(T const& val) const | ||
{ | ||
return ::boost::intrusive::detail::hash_value_dispatch(val); | ||
} | ||
}; | ||
|
||
} // namespace detail { | ||
} // namespace intrusive { | ||
} // namespace boost | ||
|
||
#include <boost/intrusive/detail/config_end.hpp> | ||
|
||
#endif // #ifndef BOOST_INTRUSIVE_HASH_HASH_HPP | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.