hash_append.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. #ifndef BOOST_HASH2_HASH_APPEND_HPP_INCLUDED
  2. #define BOOST_HASH2_HASH_APPEND_HPP_INCLUDED
  3. // Copyright 2017, 2018, 2023, 2024 Peter Dimov.
  4. // Distributed under the Boost Software License, Version 1.0.
  5. // https://www.boost.org/LICENSE_1_0.txt
  6. //
  7. // Based on
  8. //
  9. // Types Don't Know #
  10. // Howard E. Hinnant, Vinnie Falco, John Bytheway
  11. // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3980.html
  12. #include <boost/hash2/hash_append_fwd.hpp>
  13. #include <boost/hash2/is_contiguously_hashable.hpp>
  14. #include <boost/hash2/has_constant_size.hpp>
  15. #include <boost/hash2/get_integral_result.hpp>
  16. #include <boost/hash2/flavor.hpp>
  17. #include <boost/hash2/detail/is_constant_evaluated.hpp>
  18. #include <boost/hash2/detail/bit_cast.hpp>
  19. #include <boost/hash2/detail/write.hpp>
  20. #include <boost/hash2/detail/has_tag_invoke.hpp>
  21. #include <boost/container_hash/is_range.hpp>
  22. #include <boost/container_hash/is_contiguous_range.hpp>
  23. #include <boost/container_hash/is_unordered_range.hpp>
  24. #include <boost/container_hash/is_tuple_like.hpp>
  25. #include <boost/container_hash/is_described_class.hpp>
  26. #include <boost/describe/bases.hpp>
  27. #include <boost/describe/members.hpp>
  28. #include <boost/mp11/algorithm.hpp>
  29. #include <boost/mp11/integer_sequence.hpp>
  30. #include <cstdint>
  31. #include <type_traits>
  32. #include <iterator>
  33. namespace boost
  34. {
  35. template<class T, std::size_t N> class array;
  36. namespace hash2
  37. {
  38. // hash_append_range
  39. namespace detail
  40. {
  41. template<class Hash, class Flavor, class It> BOOST_CXX14_CONSTEXPR void hash_append_range_( Hash& h, Flavor const& f, It first, It last )
  42. {
  43. for( ; first != last; ++first )
  44. {
  45. typename std::iterator_traits<It>::value_type const& v = *first;
  46. hash2::hash_append( h, f, v );
  47. }
  48. }
  49. template<class Hash, class Flavor> BOOST_CXX14_CONSTEXPR void hash_append_range_( Hash& h, Flavor const& /*f*/, unsigned char* first, unsigned char* last )
  50. {
  51. h.update( first, last - first );
  52. }
  53. template<class Hash, class Flavor> BOOST_CXX14_CONSTEXPR void hash_append_range_( Hash& h, Flavor const& /*f*/, unsigned char const* first, unsigned char const* last )
  54. {
  55. h.update( first, last - first );
  56. }
  57. #if defined(BOOST_NO_CXX14_CONSTEXPR)
  58. template<class Hash, class Flavor, class T>
  59. typename std::enable_if<
  60. is_contiguously_hashable<T, Flavor::byte_order>::value, void >::type
  61. hash_append_range_( Hash& h, Flavor const& /*f*/, T* first, T* last )
  62. {
  63. h.update( first, (last - first) * sizeof(T) );
  64. }
  65. #else
  66. template<class Hash, class Flavor, class T>
  67. BOOST_CXX14_CONSTEXPR
  68. typename std::enable_if<
  69. is_contiguously_hashable<T, Flavor::byte_order>::value, void >::type
  70. hash_append_range_( Hash& h, Flavor const& f, T* first, T* last )
  71. {
  72. if( !detail::is_constant_evaluated() )
  73. {
  74. h.update( first, (last - first) * sizeof(T) );
  75. }
  76. else
  77. {
  78. for( ; first != last; ++first )
  79. {
  80. hash2::hash_append( h, f, *first );
  81. }
  82. }
  83. }
  84. #endif
  85. } // namespace detail
  86. template<class Hash, class Flavor = default_flavor, class It> BOOST_CXX14_CONSTEXPR void hash_append_range( Hash& h, Flavor const& f, It first, It last )
  87. {
  88. detail::hash_append_range_( h, f, first, last );
  89. }
  90. // hash_append_size
  91. template<class Hash, class Flavor = default_flavor, class T> BOOST_CXX14_CONSTEXPR void hash_append_size( Hash& h, Flavor const& f, T const& v )
  92. {
  93. hash2::hash_append( h, f, static_cast<typename Flavor::size_type>( v ) );
  94. }
  95. // hash_append_range_and_size
  96. namespace detail
  97. {
  98. template<class Hash, class Flavor, class It> void BOOST_CXX14_CONSTEXPR hash_append_range_and_size_( Hash& h, Flavor const& f, It first, It last, std::input_iterator_tag )
  99. {
  100. typename std::iterator_traits<It>::difference_type m = 0;
  101. for( ; first != last; ++first, ++m )
  102. {
  103. hash2::hash_append( h, f, *first );
  104. }
  105. hash2::hash_append_size( h, f, m );
  106. }
  107. template<class Hash, class Flavor, class It> BOOST_CXX14_CONSTEXPR void hash_append_range_and_size_( Hash& h, Flavor const& f, It first, It last, std::random_access_iterator_tag )
  108. {
  109. hash2::hash_append_range( h, f, first, last );
  110. hash2::hash_append_size( h, f, last - first );
  111. }
  112. } // namespace detail
  113. template<class Hash, class Flavor = default_flavor, class It> BOOST_CXX14_CONSTEXPR void hash_append_range_and_size( Hash& h, Flavor const& f, It first, It last )
  114. {
  115. detail::hash_append_range_and_size_( h, f, first, last, typename std::iterator_traits<It>::iterator_category() );
  116. }
  117. // hash_append_unordered_range
  118. template<class Hash, class Flavor = default_flavor, class It> BOOST_CXX14_CONSTEXPR void hash_append_unordered_range( Hash& h, Flavor const& f, It first, It last )
  119. {
  120. typename std::iterator_traits<It>::difference_type m = 0;
  121. std::uint64_t w = 0;
  122. for( ; first != last; ++first, ++m )
  123. {
  124. Hash h2( h );
  125. hash2::hash_append( h2, f, *first );
  126. w += hash2::get_integral_result<std::uint64_t>( h2 );
  127. }
  128. hash2::hash_append( h, f, w );
  129. hash2::hash_append_size( h, f, m );
  130. }
  131. // do_hash_append
  132. namespace detail
  133. {
  134. // integral types
  135. template<class Hash, class Flavor, class T>
  136. BOOST_CXX14_CONSTEXPR
  137. typename std::enable_if< std::is_integral<T>::value, void >::type
  138. do_hash_append( Hash& h, Flavor const& /*f*/, T const& v )
  139. {
  140. constexpr auto N = sizeof(T);
  141. unsigned char tmp[ N ] = {};
  142. detail::write( v, Flavor::byte_order, tmp );
  143. h.update( tmp, N );
  144. }
  145. // enum types
  146. template<class Hash, class Flavor, class T>
  147. BOOST_CXX14_CONSTEXPR
  148. typename std::enable_if< std::is_enum<T>::value, void >::type
  149. do_hash_append( Hash& h, Flavor const& f, T const& v )
  150. {
  151. hash2::hash_append( h, f, static_cast<typename std::underlying_type<T>::type>( v ) );
  152. }
  153. // pointer types
  154. // never constexpr
  155. template<class Hash, class Flavor, class T>
  156. typename std::enable_if< std::is_pointer<T>::value, void >::type
  157. do_hash_append( Hash& h, Flavor const& f, T const& v )
  158. {
  159. hash2::hash_append( h, f, reinterpret_cast<std::uintptr_t>( v ) );
  160. }
  161. // floating point
  162. template<class Hash, class Flavor, class T>
  163. BOOST_CXX14_CONSTEXPR
  164. typename std::enable_if< std::is_floating_point<T>::value && sizeof(T) == 4, void >::type
  165. do_hash_append( Hash& h, Flavor const& f, T const& v )
  166. {
  167. hash2::hash_append( h, f, detail::bit_cast<std::uint32_t>( v + 0 ) );
  168. }
  169. template<class Hash, class Flavor, class T>
  170. BOOST_CXX14_CONSTEXPR
  171. typename std::enable_if< std::is_floating_point<T>::value && sizeof(T) == 8, void >::type
  172. do_hash_append( Hash& h, Flavor const& f, T const& v )
  173. {
  174. hash2::hash_append( h, f, detail::bit_cast<std::uint64_t>( v + 0 ) );
  175. }
  176. // std::nullptr_t
  177. // not constexpr for consistency with T*
  178. template<class Hash, class Flavor, class T>
  179. typename std::enable_if< std::is_same<T, std::nullptr_t>::value, void >::type
  180. do_hash_append( Hash& h, Flavor const& f, T const& v )
  181. {
  182. hash2::hash_append( h, f, static_cast<void*>( v ) );
  183. }
  184. // C arrays
  185. template<class Hash, class Flavor, class T, std::size_t N> BOOST_CXX14_CONSTEXPR void do_hash_append( Hash& h, Flavor const& f, T const (&v)[ N ] )
  186. {
  187. hash2::hash_append_range( h, f, v + 0, v + N );
  188. }
  189. // contiguous containers and ranges, w/ size
  190. template<class Hash, class Flavor, class T>
  191. BOOST_CXX14_CONSTEXPR
  192. typename std::enable_if< container_hash::is_contiguous_range<T>::value && !has_constant_size<T>::value, void >::type
  193. do_hash_append( Hash& h, Flavor const& f, T const& v )
  194. {
  195. hash2::hash_append_range( h, f, v.data(), v.data() + v.size() );
  196. hash2::hash_append_size( h, f, v.size() );
  197. }
  198. // containers and ranges, w/ size
  199. template<class Hash, class Flavor, class T>
  200. BOOST_CXX14_CONSTEXPR
  201. typename std::enable_if< container_hash::is_range<T>::value && !has_constant_size<T>::value && !container_hash::is_contiguous_range<T>::value && !container_hash::is_unordered_range<T>::value, void >::type
  202. do_hash_append( Hash& h, Flavor const& f, T const& v )
  203. {
  204. hash2::hash_append_range_and_size( h, f, v.begin(), v.end() );
  205. }
  206. #if defined(BOOST_MSVC)
  207. # pragma warning(push)
  208. # pragma warning(disable: 4702) // unreachable code
  209. #endif
  210. // constant size contiguous containers and ranges (std::array, boost::array, hash2::digest)
  211. template<class Hash, class Flavor, class T>
  212. BOOST_CXX14_CONSTEXPR
  213. typename std::enable_if< container_hash::is_contiguous_range<T>::value && has_constant_size<T>::value, void >::type
  214. do_hash_append( Hash& h, Flavor const& f, T const& v )
  215. {
  216. if( v.size() == 0 )
  217. {
  218. // A hash_append call must always result in a call to Hash::update
  219. hash2::hash_append( h, f, '\x00' );
  220. }
  221. else
  222. {
  223. // std::array<>::data() is only constexpr in C++17; boost::array<>::operator[] isn't constexpr
  224. hash2::hash_append_range( h, f, &v.front(), &v.front() + v.size() );
  225. }
  226. }
  227. // constant size non-contiguous containers and ranges
  228. template<class Hash, class Flavor, class T>
  229. BOOST_CXX14_CONSTEXPR
  230. typename std::enable_if< container_hash::is_range<T>::value && has_constant_size<T>::value && !container_hash::is_contiguous_range<T>::value, void >::type
  231. do_hash_append( Hash& h, Flavor const& f, T const& v )
  232. {
  233. if( v.begin() == v.end() )
  234. {
  235. // A hash_append call must always result in a call to Hash::update
  236. hash2::hash_append( h, f, '\x00' );
  237. }
  238. else
  239. {
  240. hash2::hash_append_range( h, f, v.begin(), v.end() );
  241. }
  242. }
  243. #if defined(BOOST_MSVC)
  244. # pragma warning(pop)
  245. #endif
  246. // unordered containers (is_unordered_range implies is_range)
  247. template<class Hash, class Flavor, class T>
  248. BOOST_CXX14_CONSTEXPR
  249. typename std::enable_if< container_hash::is_unordered_range<T>::value, void >::type
  250. do_hash_append( Hash& h, Flavor const& f, T const& v )
  251. {
  252. hash2::hash_append_unordered_range( h, f, v.begin(), v.end() );
  253. }
  254. // tuple-likes
  255. template<class Hash, class Flavor, class T, std::size_t... J> BOOST_CXX14_CONSTEXPR void hash_append_tuple( Hash& h, Flavor const& f, T const& v, mp11::integer_sequence<std::size_t, J...> )
  256. {
  257. using std::get;
  258. int a[] = { ((void)hash2::hash_append( h, f, get<J>(v) ), 0)... };
  259. (void)a;
  260. }
  261. template<class Hash, class Flavor, class T> BOOST_CXX14_CONSTEXPR void hash_append_tuple( Hash& h, Flavor const& f, T const& /*v*/, mp11::integer_sequence<std::size_t> )
  262. {
  263. // A hash_append call must always result in a call to Hash::update
  264. hash2::hash_append( h, f, '\x00' );
  265. }
  266. template<class Hash, class Flavor, class T>
  267. BOOST_CXX14_CONSTEXPR
  268. typename std::enable_if< !container_hash::is_range<T>::value && container_hash::is_tuple_like<T>::value, void >::type
  269. do_hash_append( Hash& h, Flavor const& f, T const& v )
  270. {
  271. using Seq = mp11::make_index_sequence<std::tuple_size<T>::value>;
  272. detail::hash_append_tuple( h, f, v, Seq() );
  273. }
  274. // described classes
  275. #if defined(BOOST_DESCRIBE_CXX14)
  276. #if defined(_MSC_VER) && _MSC_VER == 1900
  277. # pragma warning(push)
  278. # pragma warning(disable: 4100) // unreferenced formal parameter
  279. #endif
  280. template<class Hash, class Flavor, class T>
  281. BOOST_CXX14_CONSTEXPR
  282. typename std::enable_if< container_hash::is_described_class<T>::value, void >::type
  283. do_hash_append( Hash& h, Flavor const& f, T const& v )
  284. {
  285. static_assert( !std::is_union<T>::value, "Described unions are not supported" );
  286. std::size_t r = 0;
  287. using Bd = describe::describe_bases<T, describe::mod_any_access>;
  288. mp11::mp_for_each<Bd>([&](auto D){
  289. using B = typename decltype(D)::type;
  290. hash2::hash_append( h, f, (B const&)v );
  291. ++r;
  292. });
  293. using Md = describe::describe_members<T, describe::mod_any_access>;
  294. mp11::mp_for_each<Md>([&](auto D){
  295. hash2::hash_append( h, f, v.*D.pointer );
  296. ++r;
  297. });
  298. // A hash_append call must always result in a call to Hash::update
  299. if( r == 0 )
  300. {
  301. hash2::hash_append( h, f, '\x00' );
  302. }
  303. }
  304. #if defined(_MSC_VER) && _MSC_VER == 1900
  305. # pragma warning(pop)
  306. #endif
  307. #endif // defined(BOOST_DESCRIBE_CXX14)
  308. // classes with tag_invoke
  309. } // namespace detail
  310. struct hash_append_tag
  311. {
  312. };
  313. struct hash_append_provider
  314. {
  315. template<class Hash, class Flavor, class T>
  316. static BOOST_CXX14_CONSTEXPR void hash_append( Hash& h, Flavor const& f, T const& v )
  317. {
  318. hash2::hash_append( h, f, v );
  319. }
  320. template<class Hash, class Flavor, class It>
  321. static BOOST_CXX14_CONSTEXPR void hash_append_range( Hash& h, Flavor const& f, It first, It last )
  322. {
  323. hash2::hash_append_range( h, f, first, last );
  324. }
  325. template<class Hash, class Flavor, class T>
  326. static BOOST_CXX14_CONSTEXPR void hash_append_size( Hash& h, Flavor const& f, T const& v )
  327. {
  328. hash2::hash_append_size( h, f, v );
  329. }
  330. template<class Hash, class Flavor, class It>
  331. static BOOST_CXX14_CONSTEXPR void hash_append_range_and_size( Hash& h, Flavor const& f, It first, It last )
  332. {
  333. hash2::hash_append_range_and_size( h, f, first, last );
  334. }
  335. template<class Hash, class Flavor, class It>
  336. static BOOST_CXX14_CONSTEXPR void hash_append_unordered_range( Hash& h, Flavor const& f, It first, It last )
  337. {
  338. hash2::hash_append_unordered_range( h, f, first, last );
  339. }
  340. };
  341. namespace detail
  342. {
  343. template<class Hash, class Flavor, class T>
  344. BOOST_CXX14_CONSTEXPR
  345. typename std::enable_if< detail::has_tag_invoke<T>::value, void >::type
  346. do_hash_append( Hash& h, Flavor const& f, T const& v )
  347. {
  348. tag_invoke( hash_append_tag(), hash_append_provider(), h, f, &v );
  349. }
  350. } // namespace detail
  351. // hash_append
  352. template<class Hash, class Flavor = default_flavor, class T>
  353. BOOST_CXX14_CONSTEXPR void hash_append( Hash& h, Flavor const& f, T const& v )
  354. {
  355. if( !detail::is_constant_evaluated() && is_contiguously_hashable<T, Flavor::byte_order>::value )
  356. {
  357. h.update( &v, sizeof(T) );
  358. }
  359. else
  360. {
  361. detail::do_hash_append( h, f, v );
  362. }
  363. }
  364. } // namespace hash2
  365. } // namespace boost
  366. #endif // #ifndef BOOST_HASH2_HASH_APPEND_HPP_INCLUDED