hash.hpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. // Copyright 2005-2014 Daniel James.
  2. // Copyright 2021, 2022, 2025 Peter Dimov.
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // https://www.boost.org/LICENSE_1_0.txt
  5. // Based on Peter Dimov's proposal
  6. // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1756.pdf
  7. // issue 6.18.
  8. #ifndef BOOST_FUNCTIONAL_HASH_HASH_HPP
  9. #define BOOST_FUNCTIONAL_HASH_HASH_HPP
  10. #include <boost/container_hash/hash_fwd.hpp>
  11. #include <boost/container_hash/hash_is_avalanching.hpp>
  12. #include <boost/container_hash/is_range.hpp>
  13. #include <boost/container_hash/is_contiguous_range.hpp>
  14. #include <boost/container_hash/is_unordered_range.hpp>
  15. #include <boost/container_hash/is_described_class.hpp>
  16. #include <boost/container_hash/detail/hash_integral.hpp>
  17. #include <boost/container_hash/detail/hash_tuple_like.hpp>
  18. #include <boost/container_hash/detail/hash_mix.hpp>
  19. #include <boost/container_hash/detail/hash_range.hpp>
  20. #include <boost/describe/bases.hpp>
  21. #include <boost/describe/members.hpp>
  22. #include <type_traits>
  23. #include <cstdint>
  24. #if defined(BOOST_DESCRIBE_CXX14)
  25. # include <boost/mp11/algorithm.hpp>
  26. #endif
  27. #include <string>
  28. #include <iterator>
  29. #include <complex>
  30. #include <utility>
  31. #include <limits>
  32. #include <climits>
  33. #include <cstring>
  34. #if !defined(BOOST_NO_CXX11_SMART_PTR)
  35. # include <memory>
  36. #endif
  37. #if !defined(BOOST_NO_CXX11_HDR_TYPEINDEX)
  38. #include <typeindex>
  39. #endif
  40. #if !defined(BOOST_NO_CXX11_HDR_SYSTEM_ERROR)
  41. #include <system_error>
  42. #endif
  43. #if !defined(BOOST_NO_CXX17_HDR_OPTIONAL)
  44. #include <optional>
  45. #endif
  46. #if !defined(BOOST_NO_CXX17_HDR_VARIANT)
  47. #include <variant>
  48. #endif
  49. #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
  50. # include <string_view>
  51. #endif
  52. namespace boost
  53. {
  54. //
  55. // boost::hash_value
  56. //
  57. // integral types
  58. // in detail/hash_integral.hpp
  59. // enumeration types
  60. template <typename T>
  61. typename std::enable_if<std::is_enum<T>::value, std::size_t>::type
  62. hash_value( T v )
  63. {
  64. // This should in principle return the equivalent of
  65. //
  66. // boost::hash_value( to_underlying(v) );
  67. //
  68. // However, the C++03 implementation of underlying_type,
  69. //
  70. // conditional<is_signed<T>, make_signed<T>, make_unsigned<T>>::type::type
  71. //
  72. // generates a legitimate -Wconversion warning in is_signed,
  73. // because -1 is not a valid enum value when all the enumerators
  74. // are nonnegative.
  75. //
  76. // So the legacy implementation will have to do for now.
  77. return static_cast<std::size_t>( v );
  78. }
  79. // floating point types
  80. namespace hash_detail
  81. {
  82. template<class T,
  83. std::size_t Bits = sizeof(T) * CHAR_BIT,
  84. int Digits = std::numeric_limits<T>::digits>
  85. struct hash_float_impl;
  86. // float
  87. template<class T, int Digits> struct hash_float_impl<T, 32, Digits>
  88. {
  89. static std::size_t fn( T v )
  90. {
  91. std::uint32_t w;
  92. std::memcpy( &w, &v, sizeof( v ) );
  93. return w;
  94. }
  95. };
  96. // double
  97. template<class T, int Digits> struct hash_float_impl<T, 64, Digits>
  98. {
  99. static std::size_t fn( T v )
  100. {
  101. std::uint64_t w;
  102. std::memcpy( &w, &v, sizeof( v ) );
  103. return hash_value( w );
  104. }
  105. };
  106. // 80 bit long double in 12 bytes
  107. template<class T> struct hash_float_impl<T, 96, 64>
  108. {
  109. static std::size_t fn( T v )
  110. {
  111. std::uint64_t w[ 2 ] = {};
  112. std::memcpy( &w, &v, 80 / CHAR_BIT );
  113. std::size_t seed = 0;
  114. seed = hash_value( w[0] ) + hash_detail::hash_mix( seed );
  115. seed = hash_value( w[1] ) + hash_detail::hash_mix( seed );
  116. return seed;
  117. }
  118. };
  119. // 80 bit long double in 16 bytes
  120. template<class T> struct hash_float_impl<T, 128, 64>
  121. {
  122. static std::size_t fn( T v )
  123. {
  124. std::uint64_t w[ 2 ] = {};
  125. std::memcpy( &w, &v, 80 / CHAR_BIT );
  126. std::size_t seed = 0;
  127. seed = hash_value( w[0] ) + hash_detail::hash_mix( seed );
  128. seed = hash_value( w[1] ) + hash_detail::hash_mix( seed );
  129. return seed;
  130. }
  131. };
  132. // 128 bit long double
  133. template<class T, int Digits> struct hash_float_impl<T, 128, Digits>
  134. {
  135. static std::size_t fn( T v )
  136. {
  137. std::uint64_t w[ 2 ];
  138. std::memcpy( &w, &v, sizeof( v ) );
  139. std::size_t seed = 0;
  140. #if defined(__FLOAT_WORD_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__
  141. seed = hash_value( w[1] ) + hash_detail::hash_mix( seed );
  142. seed = hash_value( w[0] ) + hash_detail::hash_mix( seed );
  143. #else
  144. seed = hash_value( w[0] ) + hash_detail::hash_mix( seed );
  145. seed = hash_value( w[1] ) + hash_detail::hash_mix( seed );
  146. #endif
  147. return seed;
  148. }
  149. };
  150. } // namespace hash_detail
  151. template <typename T>
  152. typename std::enable_if<std::is_floating_point<T>::value, std::size_t>::type
  153. hash_value( T v )
  154. {
  155. return boost::hash_detail::hash_float_impl<T>::fn( v + 0 );
  156. }
  157. // pointer types
  158. // `x + (x >> 3)` adjustment by Alberto Barbati and Dave Harris.
  159. template <class T> std::size_t hash_value( T* const& v )
  160. {
  161. std::uintptr_t x = reinterpret_cast<std::uintptr_t>( v );
  162. return boost::hash_value( x + (x >> 3) );
  163. }
  164. // array types
  165. template<class T, std::size_t N>
  166. inline std::size_t hash_value( T const (&x)[ N ] )
  167. {
  168. return boost::hash_range( x, x + N );
  169. }
  170. template<class T, std::size_t N>
  171. inline std::size_t hash_value( T (&x)[ N ] )
  172. {
  173. return boost::hash_range( x, x + N );
  174. }
  175. // complex
  176. template <class T>
  177. std::size_t hash_value( std::complex<T> const& v )
  178. {
  179. std::size_t re = boost::hash<T>()( v.real() );
  180. std::size_t im = boost::hash<T>()( v.imag() );
  181. return re + hash_detail::hash_mix( im );
  182. }
  183. // pair
  184. template <class A, class B>
  185. std::size_t hash_value( std::pair<A, B> const& v )
  186. {
  187. std::size_t seed = 0;
  188. boost::hash_combine( seed, v.first );
  189. boost::hash_combine( seed, v.second );
  190. return seed;
  191. }
  192. // ranges (list, set, deque...)
  193. template <typename T>
  194. typename std::enable_if<container_hash::is_range<T>::value && !container_hash::is_contiguous_range<T>::value && !container_hash::is_unordered_range<T>::value, std::size_t>::type
  195. hash_value( T const& v )
  196. {
  197. return boost::hash_range( v.begin(), v.end() );
  198. }
  199. // contiguous ranges (string, vector, array)
  200. template <typename T>
  201. typename std::enable_if<container_hash::is_contiguous_range<T>::value, std::size_t>::type
  202. hash_value( T const& v )
  203. {
  204. return boost::hash_range( v.data(), v.data() + v.size() );
  205. }
  206. // unordered ranges (unordered_set, unordered_map)
  207. template <typename T>
  208. typename std::enable_if<container_hash::is_unordered_range<T>::value, std::size_t>::type
  209. hash_value( T const& v )
  210. {
  211. return boost::hash_unordered_range( v.begin(), v.end() );
  212. }
  213. #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && ( \
  214. ( defined(_MSVC_STL_VERSION) && _MSVC_STL_VERSION < 142 ) || \
  215. ( !defined(_MSVC_STL_VERSION) && defined(_CPPLIB_VER) && _CPPLIB_VER >= 520 ) )
  216. // resolve ambiguity with unconstrained stdext::hash_value in <xhash> :-/
  217. template<template<class...> class L, class... T>
  218. typename std::enable_if<container_hash::is_range<L<T...>>::value && !container_hash::is_contiguous_range<L<T...>>::value && !container_hash::is_unordered_range<L<T...>>::value, std::size_t>::type
  219. hash_value( L<T...> const& v )
  220. {
  221. return boost::hash_range( v.begin(), v.end() );
  222. }
  223. // contiguous ranges (string, vector, array)
  224. template<template<class...> class L, class... T>
  225. typename std::enable_if<container_hash::is_contiguous_range<L<T...>>::value, std::size_t>::type
  226. hash_value( L<T...> const& v )
  227. {
  228. return boost::hash_range( v.data(), v.data() + v.size() );
  229. }
  230. template<template<class, std::size_t> class L, class T, std::size_t N>
  231. typename std::enable_if<container_hash::is_contiguous_range<L<T, N>>::value, std::size_t>::type
  232. hash_value( L<T, N> const& v )
  233. {
  234. return boost::hash_range( v.data(), v.data() + v.size() );
  235. }
  236. // unordered ranges (unordered_set, unordered_map)
  237. template<template<class...> class L, class... T>
  238. typename std::enable_if<container_hash::is_unordered_range<L<T...>>::value, std::size_t>::type
  239. hash_value( L<T...> const& v )
  240. {
  241. return boost::hash_unordered_range( v.begin(), v.end() );
  242. }
  243. #endif
  244. // described classes
  245. #if defined(BOOST_DESCRIBE_CXX14)
  246. #if defined(_MSC_VER) && _MSC_VER == 1900
  247. # pragma warning(push)
  248. # pragma warning(disable: 4100) // unreferenced formal parameter
  249. #endif
  250. template <typename T>
  251. typename std::enable_if<container_hash::is_described_class<T>::value, std::size_t>::type
  252. hash_value( T const& v )
  253. {
  254. static_assert( !std::is_union<T>::value, "described unions are not supported" );
  255. std::size_t r = 0;
  256. using Bd = describe::describe_bases<T, describe::mod_any_access>;
  257. mp11::mp_for_each<Bd>([&](auto D){
  258. using B = typename decltype(D)::type;
  259. boost::hash_combine( r, (B const&)v );
  260. });
  261. using Md = describe::describe_members<T, describe::mod_any_access>;
  262. mp11::mp_for_each<Md>([&](auto D){
  263. boost::hash_combine( r, v.*D.pointer );
  264. });
  265. return r;
  266. }
  267. #if defined(_MSC_VER) && _MSC_VER == 1900
  268. # pragma warning(pop)
  269. #endif
  270. #endif
  271. // std::unique_ptr, std::shared_ptr
  272. #if !defined(BOOST_NO_CXX11_SMART_PTR)
  273. template <typename T>
  274. std::size_t hash_value( std::shared_ptr<T> const& x )
  275. {
  276. return boost::hash_value( x.get() );
  277. }
  278. template <typename T, typename Deleter>
  279. std::size_t hash_value( std::unique_ptr<T, Deleter> const& x )
  280. {
  281. return boost::hash_value( x.get() );
  282. }
  283. #endif
  284. // std::type_index
  285. #if !defined(BOOST_NO_CXX11_HDR_TYPEINDEX)
  286. inline std::size_t hash_value( std::type_index const& v )
  287. {
  288. return v.hash_code();
  289. }
  290. #endif
  291. // std::error_code, std::error_condition
  292. #if !defined(BOOST_NO_CXX11_HDR_SYSTEM_ERROR)
  293. inline std::size_t hash_value( std::error_code const& v )
  294. {
  295. std::size_t seed = 0;
  296. boost::hash_combine( seed, v.value() );
  297. boost::hash_combine( seed, &v.category() );
  298. return seed;
  299. }
  300. inline std::size_t hash_value( std::error_condition const& v )
  301. {
  302. std::size_t seed = 0;
  303. boost::hash_combine( seed, v.value() );
  304. boost::hash_combine( seed, &v.category() );
  305. return seed;
  306. }
  307. #endif
  308. // std::nullptr_t
  309. #if !defined(BOOST_NO_CXX11_NULLPTR)
  310. template <typename T>
  311. typename std::enable_if<std::is_same<T, std::nullptr_t>::value, std::size_t>::type
  312. hash_value( T const& /*v*/ )
  313. {
  314. return boost::hash_value( static_cast<void*>( nullptr ) );
  315. }
  316. #endif
  317. // std::optional
  318. #if !defined(BOOST_NO_CXX17_HDR_OPTIONAL)
  319. template <typename T>
  320. std::size_t hash_value( std::optional<T> const& v )
  321. {
  322. if( !v )
  323. {
  324. // Arbitrary value for empty optional.
  325. return 0x12345678;
  326. }
  327. else
  328. {
  329. return boost::hash<T>()(*v);
  330. }
  331. }
  332. #endif
  333. // std::variant
  334. #if !defined(BOOST_NO_CXX17_HDR_VARIANT)
  335. inline std::size_t hash_value( std::monostate )
  336. {
  337. return 0x87654321;
  338. }
  339. template <typename... Types>
  340. std::size_t hash_value( std::variant<Types...> const& v )
  341. {
  342. std::size_t seed = 0;
  343. hash_combine( seed, v.index() );
  344. std::visit( [&seed](auto&& x) { hash_combine(seed, x); }, v );
  345. return seed;
  346. }
  347. #endif
  348. //
  349. // boost::hash_combine
  350. //
  351. template <class T>
  352. inline void hash_combine( std::size_t& seed, T const& v )
  353. {
  354. seed = boost::hash_detail::hash_mix( seed + 0x9e3779b9 + boost::hash<T>()( v ) );
  355. }
  356. //
  357. // boost::hash_range
  358. //
  359. template <class It>
  360. inline void hash_range( std::size_t& seed, It first, It last )
  361. {
  362. seed = hash_detail::hash_range( seed, first, last );
  363. }
  364. template <class It>
  365. inline std::size_t hash_range( It first, It last )
  366. {
  367. std::size_t seed = 0;
  368. hash_range( seed, first, last );
  369. return seed;
  370. }
  371. //
  372. // boost::hash_unordered_range
  373. //
  374. template <class It>
  375. inline void hash_unordered_range( std::size_t& seed, It first, It last )
  376. {
  377. std::size_t r = 0;
  378. std::size_t const s2( seed );
  379. for( ; first != last; ++first )
  380. {
  381. std::size_t s3( s2 );
  382. hash_combine<typename std::iterator_traits<It>::value_type>( s3, *first );
  383. r += s3;
  384. }
  385. seed += r;
  386. }
  387. template <class It>
  388. inline std::size_t hash_unordered_range( It first, It last )
  389. {
  390. std::size_t seed = 0;
  391. hash_unordered_range( seed, first, last );
  392. return seed;
  393. }
  394. //
  395. // boost::hash
  396. //
  397. template <class T> struct hash
  398. {
  399. typedef T argument_type;
  400. typedef std::size_t result_type;
  401. std::size_t operator()( T const& val ) const
  402. {
  403. return hash_value( val );
  404. }
  405. };
  406. #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && ( \
  407. ( defined(_MSVC_STL_VERSION) && _MSVC_STL_VERSION < 142 ) || \
  408. ( !defined(_MSVC_STL_VERSION) && defined(_CPPLIB_VER) && _CPPLIB_VER >= 520 ) )
  409. // Dinkumware has stdext::hash_value for basic_string in <xhash> :-/
  410. template<class E, class T, class A> struct hash< std::basic_string<E, T, A> >
  411. {
  412. typedef std::basic_string<E, T, A> argument_type;
  413. typedef std::size_t result_type;
  414. std::size_t operator()( std::basic_string<E, T, A> const& val ) const
  415. {
  416. return boost::hash_value( val );
  417. }
  418. };
  419. #endif
  420. // hash_is_avalanching
  421. template<class Ch> struct hash_is_avalanching< boost::hash< std::basic_string<Ch> > >: std::is_integral<Ch> {};
  422. #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
  423. template<class Ch> struct hash_is_avalanching< boost::hash< std::basic_string_view<Ch> > >: std::is_integral<Ch> {};
  424. #endif
  425. } // namespace boost
  426. #endif // #ifndef BOOST_FUNCTIONAL_HASH_HASH_HPP