mirror of
https://github.com/boostorg/intrusive.git
synced 2025-05-11 05:07:46 +00:00
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.
This commit is contained in:
parent
8b4297b1db
commit
6464d0e083
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
################################################################################
|
||||||
|
# This .gitignore file was automatically created by Microsoft(R) Visual Studio.
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
/proj/vc14.1ide
|
@ -1304,7 +1304,11 @@ And they also can receive additional options:
|
|||||||
in containers. Default: `equal< std::equal_to<T> >`
|
in containers. Default: `equal< std::equal_to<T> >`
|
||||||
|
|
||||||
* [*`hash<class Hash>`]: Hash function to be used in the container.
|
* [*`hash<class Hash>`]: Hash function to be used in the container.
|
||||||
Default: `hash< boost::hash<T> >`
|
Default: internal hash functor that hashes scalar types, triggering unqualified call to
|
||||||
|
`hash_value`for non-scalar types (boost::hash<T> compatible protocol). If
|
||||||
|
the ADL call does not find a viable candidate a boost::hash<T>() call is tried
|
||||||
|
in case the user has included boost/container_hash/hash.hpp and a viable
|
||||||
|
candidate is found there. Fails otherwise.
|
||||||
|
|
||||||
* [*`bucket_traits<class BucketTraits>`]: A type that wraps the bucket vector to
|
* [*`bucket_traits<class BucketTraits>`]: A type that wraps the bucket vector to
|
||||||
be used by the unordered container. Default: a type initialized by the address
|
be used by the unordered container. Default: a type initialized by the address
|
||||||
|
277
include/boost/intrusive/detail/hash.hpp
Normal file
277
include/boost/intrusive/detail/hash.hpp
Normal file
@ -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
|
||||||
|
|
@ -1,3 +1,5 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
// Copyright 2005-2014 Daniel James.
|
// Copyright 2005-2014 Daniel James.
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
// 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)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
@ -13,9 +15,14 @@
|
|||||||
// domain. The author hereby disclaims copyright to this source code.
|
// domain. The author hereby disclaims copyright to this source code.
|
||||||
//
|
//
|
||||||
// Copyright 2021 Ion Gaztanaga
|
// Copyright 2021 Ion Gaztanaga
|
||||||
// Refactored the original boost/container_hash/hash.hpp to avoid
|
// Refactored the original Boost ContainerHash library to avoid
|
||||||
// any heavy std header dependencies to just combine two hash
|
// any heavy std header dependencies to just combine two hash
|
||||||
// values represented in a std::size_t type.
|
// values represented in a std::size_t type.
|
||||||
|
//
|
||||||
|
// See http://www.boost.org/libs/intrusive for documentation.
|
||||||
|
//
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
#ifndef BOOST_INTRUSIVE_DETAIL_HASH_COMBINE_HPP
|
#ifndef BOOST_INTRUSIVE_DETAIL_HASH_COMBINE_HPP
|
||||||
#define BOOST_INTRUSIVE_DETAIL_HASH_COMBINE_HPP
|
#define BOOST_INTRUSIVE_DETAIL_HASH_COMBINE_HPP
|
||||||
@ -29,62 +36,17 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <boost/cstdint.hpp>
|
#include <boost/cstdint.hpp>
|
||||||
|
#include "hash_mix.hpp"
|
||||||
#if defined(_MSC_VER)
|
|
||||||
# include <stdlib.h>
|
|
||||||
# define BOOST_INTRUSIVE_HASH_ROTL32(x, r) _rotl(x,r)
|
|
||||||
#else
|
|
||||||
# define BOOST_INTRUSIVE_HASH_ROTL32(x, r) (x << r) | (x >> (32 - r))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace boost {
|
namespace boost {
|
||||||
namespace intrusive {
|
namespace intrusive {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template <typename SizeT>
|
inline void hash_combine_size_t(std::size_t& seed, std::size_t value)
|
||||||
inline void hash_combine_size_t(SizeT& seed, SizeT value)
|
|
||||||
{
|
{
|
||||||
seed ^= value + 0x9e3779b9 + (seed<<6) + (seed>>2);
|
seed = boost::intrusive::detail::hash_mix(seed + 0x9e3779b9 + value);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void hash_combine_size_t(boost::uint32_t& h1, boost::uint32_t k1)
|
|
||||||
{
|
|
||||||
const uint32_t c1 = 0xcc9e2d51;
|
|
||||||
const uint32_t c2 = 0x1b873593;
|
|
||||||
|
|
||||||
k1 *= c1;
|
|
||||||
k1 = BOOST_INTRUSIVE_HASH_ROTL32(k1,15);
|
|
||||||
k1 *= c2;
|
|
||||||
|
|
||||||
h1 ^= k1;
|
|
||||||
h1 = BOOST_INTRUSIVE_HASH_ROTL32(h1,13);
|
|
||||||
h1 = h1*5+0xe6546b64;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Don't define 64-bit hash combine on platforms without 64 bit integers,
|
|
||||||
// and also not for 32-bit gcc as it warns about the 64-bit constant.
|
|
||||||
#if !defined(BOOST_NO_INT64_T) && \
|
|
||||||
!(defined(__GNUC__) && ULONG_MAX == 0xffffffff)
|
|
||||||
inline void hash_combine_size_t(boost::uint64_t& h, boost::uint64_t k)
|
|
||||||
{
|
|
||||||
const boost::uint64_t m = UINT64_C(0xc6a4a7935bd1e995);
|
|
||||||
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 // BOOST_NO_INT64_T
|
|
||||||
|
|
||||||
} //namespace detail {
|
} //namespace detail {
|
||||||
} //namespace intrusive {
|
} //namespace intrusive {
|
||||||
} //namespace boost {
|
} //namespace boost {
|
||||||
|
119
include/boost/intrusive/detail/hash_integral.hpp
Normal file
119
include/boost/intrusive/detail/hash_integral.hpp
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Copyright 2021-2023 Peter Dimov
|
||||||
|
// Copyright 2024 Ion Gaztanaga
|
||||||
|
// Distributed under the Boost Software License, Version 1.0.
|
||||||
|
// https://www.boost.org/LICENSE_1_0.txt
|
||||||
|
//
|
||||||
|
// The original C++11 implementation was done by Peter Dimov
|
||||||
|
// The C++03 porting was done by Ion Gaztanaga
|
||||||
|
//
|
||||||
|
// See http://www.boost.org/libs/intrusive for documentation.
|
||||||
|
//
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BOOST_INTRUSIVE_DETAIL_HASH_INTEGRAL_HPP
|
||||||
|
#define BOOST_INTRUSIVE_DETAIL_HASH_INTEGRAL_HPP
|
||||||
|
|
||||||
|
#include <boost/config.hpp>
|
||||||
|
#include "hash_mix.hpp"
|
||||||
|
#include <cstddef>
|
||||||
|
#include <climits>
|
||||||
|
#include <boost/intrusive/detail/mpl.hpp>
|
||||||
|
|
||||||
|
namespace boost {
|
||||||
|
namespace intrusive {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
|
||||||
|
template<class T,
|
||||||
|
bool bigger_than_size_t = (sizeof(T) > sizeof(std::size_t)),
|
||||||
|
bool is_unsigned = is_unsigned<T>::value,
|
||||||
|
std::size_t size_t_bits = sizeof(std::size_t) * CHAR_BIT,
|
||||||
|
std::size_t type_bits = sizeof(T) * CHAR_BIT>
|
||||||
|
struct hash_integral_impl;
|
||||||
|
|
||||||
|
template<class T, bool is_unsigned, std::size_t size_t_bits, std::size_t type_bits>
|
||||||
|
struct hash_integral_impl<T, false, is_unsigned, size_t_bits, type_bits>
|
||||||
|
{
|
||||||
|
static std::size_t fn( T v )
|
||||||
|
{
|
||||||
|
return static_cast<std::size_t>( v );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T, std::size_t size_t_bits, std::size_t type_bits>
|
||||||
|
struct hash_integral_impl<T, true, false, size_t_bits, type_bits>
|
||||||
|
{
|
||||||
|
static std::size_t fn( T v )
|
||||||
|
{
|
||||||
|
typedef typename make_unsigned<T>::type U;
|
||||||
|
|
||||||
|
if( v >= 0 )
|
||||||
|
{
|
||||||
|
return hash_integral_impl<U>::fn( static_cast<U>( v ) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ~hash_integral_impl<U>::fn( static_cast<U>( ~static_cast<U>( v ) ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct hash_integral_impl<T, true, true, 32, 64>
|
||||||
|
{
|
||||||
|
static std::size_t fn( T v )
|
||||||
|
{
|
||||||
|
std::size_t seed = 0;
|
||||||
|
|
||||||
|
seed = static_cast<std::size_t>( v >> 32 ) + (hash_mix)( seed );
|
||||||
|
seed = static_cast<std::size_t>( v & 0xFFFFFFFF ) + (hash_mix)( seed );
|
||||||
|
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct hash_integral_impl<T, true, true, 32, 128>
|
||||||
|
{
|
||||||
|
static std::size_t fn( T v )
|
||||||
|
{
|
||||||
|
std::size_t seed = 0;
|
||||||
|
|
||||||
|
seed = static_cast<std::size_t>( v >> 96 ) + (hash_mix)( seed );
|
||||||
|
seed = static_cast<std::size_t>( v >> 64 ) + (hash_mix)( seed );
|
||||||
|
seed = static_cast<std::size_t>( v >> 32 ) + (hash_mix)( seed );
|
||||||
|
seed = static_cast<std::size_t>( v ) + (hash_mix)( seed );
|
||||||
|
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct hash_integral_impl<T, true, true, 64, 128>
|
||||||
|
{
|
||||||
|
static std::size_t fn( T v )
|
||||||
|
{
|
||||||
|
std::size_t seed = 0;
|
||||||
|
|
||||||
|
seed = static_cast<std::size_t>( v >> 64 ) + (hash_mix)( seed );
|
||||||
|
seed = static_cast<std::size_t>( v ) + (hash_mix)( seed );
|
||||||
|
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename enable_if_c<is_integral<T>::value, std::size_t>::type
|
||||||
|
hash_value( T v )
|
||||||
|
{
|
||||||
|
return hash_integral_impl<T>::fn( v );
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace intrusive
|
||||||
|
} // namespace boost
|
||||||
|
|
||||||
|
#endif // #ifndef BOOST_INTRUSIVE_DETAIL_HASH_INTEGRAL_HPP
|
130
include/boost/intrusive/detail/hash_mix.hpp
Normal file
130
include/boost/intrusive/detail/hash_mix.hpp
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Copyright 2022 Peter Dimov
|
||||||
|
// Copyright 2024 Ion Gaztanaga
|
||||||
|
// Distributed under the Boost Software License, Version 1.0.
|
||||||
|
// https://www.boost.org/LICENSE_1_0.txt
|
||||||
|
//
|
||||||
|
// The original C++11 implementation was done by Peter Dimov
|
||||||
|
// The C++03 porting was done by Ion Gaztanaga
|
||||||
|
//
|
||||||
|
// Refactored the original Boost ContainerHash library to avoid
|
||||||
|
// any heavy std header dependencies to just mix a hash
|
||||||
|
// value represented in a std::size_t type.
|
||||||
|
//
|
||||||
|
// See http://www.boost.org/libs/intrusive for documentation.
|
||||||
|
//
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BOOST_INTRUSIVE_DETAIL_HASH_MIX_HPP
|
||||||
|
#define BOOST_INTRUSIVE_DETAIL_HASH_MIX_HPP
|
||||||
|
|
||||||
|
#include <boost/cstdint.hpp> //boost::uint64_t
|
||||||
|
#include <cstddef>
|
||||||
|
#include <climits>
|
||||||
|
|
||||||
|
namespace boost {
|
||||||
|
namespace intrusive {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
|
||||||
|
template<std::size_t Bits> 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 = 0xe9846af9b1a615d;
|
||||||
|
|
||||||
|
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<sizeof(std::size_t) * CHAR_BIT>::fn( v );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace intrusive
|
||||||
|
} // namespace boost
|
||||||
|
|
||||||
|
#endif // #ifndef BOOST_INTRUSIVE_DETAIL_HASH_MIX_HPP
|
@ -32,6 +32,8 @@
|
|||||||
#include <boost/intrusive/detail/config_begin.hpp>
|
#include <boost/intrusive/detail/config_begin.hpp>
|
||||||
#include <boost/intrusive/intrusive_fwd.hpp>
|
#include <boost/intrusive/intrusive_fwd.hpp>
|
||||||
|
|
||||||
|
#include <boost/move/detail/meta_utils_core.hpp>
|
||||||
|
|
||||||
//General intrusive utilities
|
//General intrusive utilities
|
||||||
#include <boost/intrusive/detail/hashtable_node.hpp>
|
#include <boost/intrusive/detail/hashtable_node.hpp>
|
||||||
#include <boost/intrusive/detail/transform_iterator.hpp>
|
#include <boost/intrusive/detail/transform_iterator.hpp>
|
||||||
@ -57,7 +59,6 @@
|
|||||||
#include <boost/intrusive/linear_slist_algorithms.hpp>
|
#include <boost/intrusive/linear_slist_algorithms.hpp>
|
||||||
|
|
||||||
//boost
|
//boost
|
||||||
#include <boost/container_hash/hash.hpp>
|
|
||||||
#include <boost/intrusive/detail/assert.hpp>
|
#include <boost/intrusive/detail/assert.hpp>
|
||||||
#include <boost/static_assert.hpp>
|
#include <boost/static_assert.hpp>
|
||||||
#include <boost/move/utility_core.hpp>
|
#include <boost/move/utility_core.hpp>
|
||||||
@ -69,6 +70,9 @@
|
|||||||
#include <cstddef> //std::size_t
|
#include <cstddef> //std::size_t
|
||||||
#include <boost/cstdint.hpp> //std::uint64_t
|
#include <boost/cstdint.hpp> //std::uint64_t
|
||||||
|
|
||||||
|
|
||||||
|
#include "detail/hash.hpp"
|
||||||
|
|
||||||
#if defined(BOOST_HAS_PRAGMA_ONCE)
|
#if defined(BOOST_HAS_PRAGMA_ONCE)
|
||||||
# pragma once
|
# pragma once
|
||||||
#endif
|
#endif
|
||||||
@ -79,8 +83,10 @@
|
|||||||
|
|
||||||
|
|
||||||
namespace boost {
|
namespace boost {
|
||||||
|
|
||||||
namespace intrusive {
|
namespace intrusive {
|
||||||
|
|
||||||
|
|
||||||
#if !defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED)
|
#if !defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED)
|
||||||
|
|
||||||
/// @cond
|
/// @cond
|
||||||
@ -1394,7 +1400,7 @@ struct get_hash
|
|||||||
template<class T>
|
template<class T>
|
||||||
struct get_hash<void, T>
|
struct get_hash<void, T>
|
||||||
{
|
{
|
||||||
typedef ::boost::hash<T> type;
|
typedef detail::internal_hash_functor<T> type;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class EqualTo, class>
|
template<class EqualTo, class>
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
PlatformToolSet=v143:VCToolArchitecture=Native64Bit:VCToolsVersion=14.35.32215:TargetPlatformVersion=10.0.19041.0:
|
||||||
|
Debug|x64|C:\Data\Libs\LocalGit\boost\libs\intrusive\proj\vc14.1ide\|
|
263
test/hash_functor_test.cpp
Normal file
263
test/hash_functor_test.cpp
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// (C) Copyright Ion Gaztanaga 2023-2024.
|
||||||
|
//
|
||||||
|
// 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)
|
||||||
|
//
|
||||||
|
// See http://www.boost.org/libs/intrusive for documentation.
|
||||||
|
//
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
#include <boost/intrusive/unordered_set.hpp>
|
||||||
|
|
||||||
|
#ifdef NDEBUG
|
||||||
|
#undef NDEBUG
|
||||||
|
#endif
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
using namespace boost::intrusive;
|
||||||
|
|
||||||
|
|
||||||
|
//Generic class that stores a key as "first" element
|
||||||
|
template <class Key>
|
||||||
|
struct first_key;
|
||||||
|
|
||||||
|
template<class Key>
|
||||||
|
class FirstKeyClass : public unordered_set_base_hook<>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Key first;
|
||||||
|
|
||||||
|
explicit FirstKeyClass() : first() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct equal_type
|
||||||
|
: boost::intrusive::value_equal<T>
|
||||||
|
{};
|
||||||
|
|
||||||
|
template<class T, std::size_t N>
|
||||||
|
struct equal_type<T[N]>
|
||||||
|
{
|
||||||
|
bool operator() (T const (&a)[N], T const (&b)[N])
|
||||||
|
{
|
||||||
|
for (std::size_t i = 0; i != N; ++i) {
|
||||||
|
if (a[i] != b[i])
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//key_of_value argument when isnerting FirstKeyClass objects in unordered map
|
||||||
|
template <class Key>
|
||||||
|
struct first_key
|
||||||
|
{
|
||||||
|
typedef Key type;
|
||||||
|
|
||||||
|
const type& operator()(const FirstKeyClass<Key>& v) const
|
||||||
|
{
|
||||||
|
return v.first;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//Function that instantiates an unordered intrusive container that stores FirstKeyClass objects
|
||||||
|
template<class Key>
|
||||||
|
void instantiate_first_key_unordered()
|
||||||
|
{
|
||||||
|
typedef unordered_set< FirstKeyClass<Key>, key_of_value<first_key<Key> >, equal<equal_type<Key> > > UnorderedMap;
|
||||||
|
typedef typename UnorderedMap::bucket_type bucket_type;
|
||||||
|
typedef typename UnorderedMap::bucket_traits bucket_traits;
|
||||||
|
typedef typename UnorderedMap::value_type value_type;
|
||||||
|
typedef typename UnorderedMap::key_of_value key_of_value;
|
||||||
|
|
||||||
|
const std::size_t bucket_len = 2;
|
||||||
|
bucket_type buckets[bucket_len];
|
||||||
|
|
||||||
|
UnorderedMap u(bucket_traits(buckets, bucket_len));
|
||||||
|
value_type v;
|
||||||
|
assert(u.find(key_of_value()(v)) == u.end());
|
||||||
|
u.insert(v);
|
||||||
|
value_type v2;
|
||||||
|
assert(u.find(v2.first) != u.end());
|
||||||
|
u.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Function that instantiates typical values in an unordered intrusive container
|
||||||
|
template<class ValueType>
|
||||||
|
void instantiate_value_unordered()
|
||||||
|
{
|
||||||
|
typedef unordered_set< ValueType > Unordered;
|
||||||
|
typedef typename Unordered::bucket_type bucket_type;
|
||||||
|
typedef typename Unordered::bucket_traits bucket_traits;
|
||||||
|
typedef typename Unordered::value_type value_type;
|
||||||
|
|
||||||
|
const std::size_t bucket_len = 2;
|
||||||
|
bucket_type buckets[bucket_len];
|
||||||
|
|
||||||
|
Unordered u(bucket_traits(buckets, bucket_len));
|
||||||
|
value_type v;
|
||||||
|
assert(u.find(v) == u.end());
|
||||||
|
u.insert(v);
|
||||||
|
value_type v2;
|
||||||
|
assert(u.find(v2) != u.end());
|
||||||
|
u.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MyEnum
|
||||||
|
{
|
||||||
|
MyZero,
|
||||||
|
MyOne,
|
||||||
|
MyTwo
|
||||||
|
};
|
||||||
|
|
||||||
|
//Function that tests all scalar types
|
||||||
|
void test_first_key_scalar_unordered()
|
||||||
|
{
|
||||||
|
//characters
|
||||||
|
instantiate_first_key_unordered<char>();
|
||||||
|
|
||||||
|
#ifndef BOOST_NO_INTRINSIC_WCHAR_T
|
||||||
|
instantiate_first_key_unordered<wchar_t>();
|
||||||
|
#endif
|
||||||
|
#ifndef BOOST_NO_CXX11_CHAR16_T
|
||||||
|
instantiate_first_key_unordered<char16_t>();
|
||||||
|
#endif
|
||||||
|
#ifndef BOOST_NO_CXX11_CHAR32_T
|
||||||
|
instantiate_first_key_unordered<char32_t>();
|
||||||
|
#endif
|
||||||
|
#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
|
||||||
|
instantiate_first_key_unordered<char8_t>();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//integers
|
||||||
|
instantiate_first_key_unordered<signed char>();
|
||||||
|
instantiate_first_key_unordered<unsigned char>();
|
||||||
|
instantiate_first_key_unordered<signed short>();
|
||||||
|
instantiate_first_key_unordered<unsigned short>();
|
||||||
|
instantiate_first_key_unordered<signed int>();
|
||||||
|
instantiate_first_key_unordered<unsigned int>();
|
||||||
|
instantiate_first_key_unordered<signed long>();
|
||||||
|
instantiate_first_key_unordered<unsigned long>();
|
||||||
|
#ifdef BOOST_HAS_LONG_LONG
|
||||||
|
instantiate_first_key_unordered< ::boost::long_long_type>();
|
||||||
|
instantiate_first_key_unordered< ::boost::ulong_long_type>();
|
||||||
|
#endif
|
||||||
|
#ifdef BOOST_HAS_INT128
|
||||||
|
instantiate_first_key_unordered< ::boost::int128_type>();
|
||||||
|
instantiate_first_key_unordered< ::boost::uint128_type>();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//floating
|
||||||
|
instantiate_first_key_unordered<float>();
|
||||||
|
instantiate_first_key_unordered<double>();
|
||||||
|
instantiate_first_key_unordered<long double>();
|
||||||
|
|
||||||
|
//Array
|
||||||
|
instantiate_first_key_unordered<int[2]>();
|
||||||
|
instantiate_first_key_unordered<const unsigned[3]>();
|
||||||
|
|
||||||
|
//Pointer
|
||||||
|
instantiate_first_key_unordered<void*>();
|
||||||
|
instantiate_first_key_unordered<const void*>();
|
||||||
|
|
||||||
|
//std::nullptr_t
|
||||||
|
#if !defined(BOOST_NO_CXX11_NULLPTR)
|
||||||
|
instantiate_first_key_unordered<std::nullptr_t>();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//Enum
|
||||||
|
instantiate_first_key_unordered<MyEnum>();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace some_ns {
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class HashValueDefined : public unordered_set_base_hook<>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
T val;
|
||||||
|
|
||||||
|
public:
|
||||||
|
HashValueDefined() : val() {}
|
||||||
|
|
||||||
|
friend bool operator==(const HashValueDefined& a, const HashValueDefined& b)
|
||||||
|
{
|
||||||
|
return a.val == b.val;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend std::size_t hash_value(const HashValueDefined& a)
|
||||||
|
{
|
||||||
|
return static_cast<std::size_t>(a.val);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace some_ns {
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class HashValueDefined2 : public unordered_set_base_hook<>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
T val;
|
||||||
|
|
||||||
|
public:
|
||||||
|
HashValueDefined2() : val() {}
|
||||||
|
|
||||||
|
friend bool operator==(const HashValueDefined2& a, const HashValueDefined2& b)
|
||||||
|
{
|
||||||
|
return a.val == b.val;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend std::size_t hash_value(const HashValueDefined2& a)
|
||||||
|
{
|
||||||
|
return static_cast<std::size_t>(a.val);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void test_value_unordered()
|
||||||
|
{
|
||||||
|
instantiate_value_unordered<some_ns::HashValueDefined<int> >();
|
||||||
|
instantiate_value_unordered<some_ns::HashValueDefined<unsigned> >();
|
||||||
|
instantiate_value_unordered<some_ns::HashValueDefined<float> >();
|
||||||
|
instantiate_value_unordered<HashValueDefined2<int> >();
|
||||||
|
instantiate_value_unordered<HashValueDefined2<unsigned> >();
|
||||||
|
instantiate_value_unordered<HashValueDefined2<float> >();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if (BOOST_CXX_VERSION >= 201103L)
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
#include <boost/container_hash/hash.hpp>
|
||||||
|
|
||||||
|
template<class StdBase>
|
||||||
|
class DerivedFromType : public unordered_set_base_hook<>, public StdBase
|
||||||
|
{
|
||||||
|
friend bool operator==(const DerivedFromType& a, const DerivedFromType& b)
|
||||||
|
{
|
||||||
|
return static_cast<const StdBase&>(a) == static_cast<const StdBase&>(b);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void test_value_stdbase_unordered()
|
||||||
|
{
|
||||||
|
instantiate_first_key_unordered<DerivedFromType<std::string> >();
|
||||||
|
instantiate_first_key_unordered<DerivedFromType<std::list<int> > >();
|
||||||
|
instantiate_value_unordered<DerivedFromType<std::string> >();
|
||||||
|
instantiate_value_unordered<DerivedFromType<std::list<int> > >();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
test_first_key_scalar_unordered();
|
||||||
|
test_value_unordered();
|
||||||
|
#if (BOOST_CXX_VERSION >= 201103L)
|
||||||
|
test_value_stdbase_unordered();
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user