hash_range.hpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. // Copyright 2022 Peter Dimov
  2. // Distributed under the Boost Software License, Version 1.0.
  3. // https://www.boost.org/LICENSE_1_0.txt
  4. #ifndef BOOST_HASH_DETAIL_HASH_RANGE_HPP
  5. #define BOOST_HASH_DETAIL_HASH_RANGE_HPP
  6. #include <boost/container_hash/hash_fwd.hpp>
  7. #include <boost/container_hash/detail/mulx.hpp>
  8. #include <type_traits>
  9. #include <cstdint>
  10. #include <iterator>
  11. #include <limits>
  12. #include <cstddef>
  13. #include <climits>
  14. #include <cstring>
  15. namespace boost
  16. {
  17. namespace hash_detail
  18. {
  19. template<class T> struct is_char_type: public std::false_type {};
  20. #if CHAR_BIT == 8
  21. template<> struct is_char_type<char>: public std::true_type {};
  22. template<> struct is_char_type<signed char>: public std::true_type {};
  23. template<> struct is_char_type<unsigned char>: public std::true_type {};
  24. #if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
  25. template<> struct is_char_type<char8_t>: public std::true_type {};
  26. #endif
  27. #if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L
  28. template<> struct is_char_type<std::byte>: public std::true_type {};
  29. #endif
  30. #endif
  31. // generic version
  32. template<class It>
  33. inline typename std::enable_if<
  34. !is_char_type<typename std::iterator_traits<It>::value_type>::value,
  35. std::size_t >::type
  36. hash_range( std::size_t seed, It first, It last )
  37. {
  38. for( ; first != last; ++first )
  39. {
  40. hash_combine<typename std::iterator_traits<It>::value_type>( seed, *first );
  41. }
  42. return seed;
  43. }
  44. // specialized char[] version, 32 bit
  45. template<class It> inline std::uint32_t read32le( It p )
  46. {
  47. // clang 5+, gcc 5+ figure out this pattern and use a single mov on x86
  48. // gcc on s390x and power BE even knows how to use load-reverse
  49. std::uint32_t w =
  50. static_cast<std::uint32_t>( static_cast<unsigned char>( p[0] ) ) |
  51. static_cast<std::uint32_t>( static_cast<unsigned char>( p[1] ) ) << 8 |
  52. static_cast<std::uint32_t>( static_cast<unsigned char>( p[2] ) ) << 16 |
  53. static_cast<std::uint32_t>( static_cast<unsigned char>( p[3] ) ) << 24;
  54. return w;
  55. }
  56. #if defined(_MSC_VER) && !defined(__clang__)
  57. template<class T> inline std::uint32_t read32le( T* p )
  58. {
  59. std::uint32_t w;
  60. std::memcpy( &w, p, 4 );
  61. return w;
  62. }
  63. #endif
  64. inline std::uint64_t mul32( std::uint32_t x, std::uint32_t y )
  65. {
  66. return static_cast<std::uint64_t>( x ) * y;
  67. }
  68. template<class It>
  69. inline typename std::enable_if<
  70. is_char_type<typename std::iterator_traits<It>::value_type>::value &&
  71. std::is_same<typename std::iterator_traits<It>::iterator_category, std::random_access_iterator_tag>::value &&
  72. std::numeric_limits<std::size_t>::digits <= 32,
  73. std::size_t>::type
  74. hash_range( std::size_t seed, It first, It last )
  75. {
  76. It p = first;
  77. std::size_t n = static_cast<std::size_t>( last - first );
  78. std::uint32_t const q = 0x9e3779b9U;
  79. std::uint32_t const k = 0xe35e67b1U; // q * q
  80. std::uint64_t h = mul32( static_cast<std::uint32_t>( seed ) + q, k );
  81. std::uint32_t w = static_cast<std::uint32_t>( h & 0xFFFFFFFF );
  82. h ^= n;
  83. while( n >= 4 )
  84. {
  85. std::uint32_t v1 = read32le( p );
  86. w += q;
  87. h ^= mul32( v1 + w, k );
  88. p += 4;
  89. n -= 4;
  90. }
  91. {
  92. std::uint32_t v1 = 0;
  93. if( n >= 1 )
  94. {
  95. std::size_t const x1 = ( n - 1 ) & 2; // 1: 0, 2: 0, 3: 2
  96. std::size_t const x2 = n >> 1; // 1: 0, 2: 1, 3: 1
  97. v1 =
  98. static_cast<std::uint32_t>( static_cast<unsigned char>( p[ static_cast<std::ptrdiff_t>( x1 ) ] ) ) << x1 * 8 |
  99. static_cast<std::uint32_t>( static_cast<unsigned char>( p[ static_cast<std::ptrdiff_t>( x2 ) ] ) ) << x2 * 8 |
  100. static_cast<std::uint32_t>( static_cast<unsigned char>( p[ 0 ] ) );
  101. }
  102. w += q;
  103. h ^= mul32( v1 + w, k );
  104. }
  105. w += q;
  106. h ^= mul32( static_cast<std::uint32_t>( h & 0xFFFFFFFF ) + w, static_cast<std::uint32_t>( h >> 32 ) + w + k );
  107. return static_cast<std::uint32_t>( h & 0xFFFFFFFF ) ^ static_cast<std::uint32_t>( h >> 32 );
  108. }
  109. template<class It>
  110. inline typename std::enable_if<
  111. is_char_type<typename std::iterator_traits<It>::value_type>::value &&
  112. !std::is_same<typename std::iterator_traits<It>::iterator_category, std::random_access_iterator_tag>::value &&
  113. std::numeric_limits<std::size_t>::digits <= 32,
  114. std::size_t>::type
  115. hash_range( std::size_t seed, It first, It last )
  116. {
  117. std::size_t n = 0;
  118. std::uint32_t const q = 0x9e3779b9U;
  119. std::uint32_t const k = 0xe35e67b1U; // q * q
  120. std::uint64_t h = mul32( static_cast<std::uint32_t>( seed ) + q, k );
  121. std::uint32_t w = static_cast<std::uint32_t>( h & 0xFFFFFFFF );
  122. std::uint32_t v1 = 0;
  123. for( ;; )
  124. {
  125. v1 = 0;
  126. if( first == last )
  127. {
  128. break;
  129. }
  130. v1 |= static_cast<std::uint32_t>( static_cast<unsigned char>( *first ) );
  131. ++first;
  132. ++n;
  133. if( first == last )
  134. {
  135. break;
  136. }
  137. v1 |= static_cast<std::uint32_t>( static_cast<unsigned char>( *first ) ) << 8;
  138. ++first;
  139. ++n;
  140. if( first == last )
  141. {
  142. break;
  143. }
  144. v1 |= static_cast<std::uint32_t>( static_cast<unsigned char>( *first ) ) << 16;
  145. ++first;
  146. ++n;
  147. if( first == last )
  148. {
  149. break;
  150. }
  151. v1 |= static_cast<std::uint32_t>( static_cast<unsigned char>( *first ) ) << 24;
  152. ++first;
  153. ++n;
  154. w += q;
  155. h ^= mul32( v1 + w, k );
  156. }
  157. h ^= n;
  158. w += q;
  159. h ^= mul32( v1 + w, k );
  160. w += q;
  161. h ^= mul32( static_cast<std::uint32_t>( h & 0xFFFFFFFF ) + w, static_cast<std::uint32_t>( h >> 32 ) + w + k );
  162. return static_cast<std::uint32_t>( h & 0xFFFFFFFF ) ^ static_cast<std::uint32_t>( h >> 32 );
  163. }
  164. // specialized char[] version, 64 bit
  165. template<class It> inline std::uint64_t read64le( It p )
  166. {
  167. std::uint64_t w =
  168. static_cast<std::uint64_t>( static_cast<unsigned char>( p[0] ) ) |
  169. static_cast<std::uint64_t>( static_cast<unsigned char>( p[1] ) ) << 8 |
  170. static_cast<std::uint64_t>( static_cast<unsigned char>( p[2] ) ) << 16 |
  171. static_cast<std::uint64_t>( static_cast<unsigned char>( p[3] ) ) << 24 |
  172. static_cast<std::uint64_t>( static_cast<unsigned char>( p[4] ) ) << 32 |
  173. static_cast<std::uint64_t>( static_cast<unsigned char>( p[5] ) ) << 40 |
  174. static_cast<std::uint64_t>( static_cast<unsigned char>( p[6] ) ) << 48 |
  175. static_cast<std::uint64_t>( static_cast<unsigned char>( p[7] ) ) << 56;
  176. return w;
  177. }
  178. #if defined(_MSC_VER) && !defined(__clang__)
  179. template<class T> inline std::uint64_t read64le( T* p )
  180. {
  181. std::uint64_t w;
  182. std::memcpy( &w, p, 8 );
  183. return w;
  184. }
  185. #endif
  186. template<class It>
  187. inline typename std::enable_if<
  188. is_char_type<typename std::iterator_traits<It>::value_type>::value &&
  189. std::is_same<typename std::iterator_traits<It>::iterator_category, std::random_access_iterator_tag>::value &&
  190. (std::numeric_limits<std::size_t>::digits > 32),
  191. std::size_t>::type
  192. hash_range( std::size_t seed, It first, It last )
  193. {
  194. It p = first;
  195. std::size_t n = static_cast<std::size_t>( last - first );
  196. std::uint64_t const q = 0x9e3779b97f4a7c15;
  197. std::uint64_t const k = 0xdf442d22ce4859b9; // q * q
  198. std::uint64_t w = mulx( seed + q, k );
  199. std::uint64_t h = w ^ n;
  200. while( n >= 8 )
  201. {
  202. std::uint64_t v1 = read64le( p );
  203. w += q;
  204. h ^= mulx( v1 + w, k );
  205. p += 8;
  206. n -= 8;
  207. }
  208. {
  209. std::uint64_t v1 = 0;
  210. if( n >= 4 )
  211. {
  212. v1 = static_cast<std::uint64_t>( read32le( p + static_cast<std::ptrdiff_t>( n - 4 ) ) ) << ( n - 4 ) * 8 | read32le( p );
  213. }
  214. else if( n >= 1 )
  215. {
  216. std::size_t const x1 = ( n - 1 ) & 2; // 1: 0, 2: 0, 3: 2
  217. std::size_t const x2 = n >> 1; // 1: 0, 2: 1, 3: 1
  218. v1 =
  219. static_cast<std::uint64_t>( static_cast<unsigned char>( p[ static_cast<std::ptrdiff_t>( x1 ) ] ) ) << x1 * 8 |
  220. static_cast<std::uint64_t>( static_cast<unsigned char>( p[ static_cast<std::ptrdiff_t>( x2 ) ] ) ) << x2 * 8 |
  221. static_cast<std::uint64_t>( static_cast<unsigned char>( p[ 0 ] ) );
  222. }
  223. w += q;
  224. h ^= mulx( v1 + w, k );
  225. }
  226. return mulx( h + w, k );
  227. }
  228. template<class It>
  229. inline typename std::enable_if<
  230. is_char_type<typename std::iterator_traits<It>::value_type>::value &&
  231. !std::is_same<typename std::iterator_traits<It>::iterator_category, std::random_access_iterator_tag>::value &&
  232. (std::numeric_limits<std::size_t>::digits > 32),
  233. std::size_t>::type
  234. hash_range( std::size_t seed, It first, It last )
  235. {
  236. std::size_t n = 0;
  237. std::uint64_t const q = 0x9e3779b97f4a7c15;
  238. std::uint64_t const k = 0xdf442d22ce4859b9; // q * q
  239. std::uint64_t w = mulx( seed + q, k );
  240. std::uint64_t h = w;
  241. std::uint64_t v1 = 0;
  242. for( ;; )
  243. {
  244. v1 = 0;
  245. if( first == last )
  246. {
  247. break;
  248. }
  249. v1 |= static_cast<std::uint64_t>( static_cast<unsigned char>( *first ) );
  250. ++first;
  251. ++n;
  252. if( first == last )
  253. {
  254. break;
  255. }
  256. v1 |= static_cast<std::uint64_t>( static_cast<unsigned char>( *first ) ) << 8;
  257. ++first;
  258. ++n;
  259. if( first == last )
  260. {
  261. break;
  262. }
  263. v1 |= static_cast<std::uint64_t>( static_cast<unsigned char>( *first ) ) << 16;
  264. ++first;
  265. ++n;
  266. if( first == last )
  267. {
  268. break;
  269. }
  270. v1 |= static_cast<std::uint64_t>( static_cast<unsigned char>( *first ) ) << 24;
  271. ++first;
  272. ++n;
  273. if( first == last )
  274. {
  275. break;
  276. }
  277. v1 |= static_cast<std::uint64_t>( static_cast<unsigned char>( *first ) ) << 32;
  278. ++first;
  279. ++n;
  280. if( first == last )
  281. {
  282. break;
  283. }
  284. v1 |= static_cast<std::uint64_t>( static_cast<unsigned char>( *first ) ) << 40;
  285. ++first;
  286. ++n;
  287. if( first == last )
  288. {
  289. break;
  290. }
  291. v1 |= static_cast<std::uint64_t>( static_cast<unsigned char>( *first ) ) << 48;
  292. ++first;
  293. ++n;
  294. if( first == last )
  295. {
  296. break;
  297. }
  298. v1 |= static_cast<std::uint64_t>( static_cast<unsigned char>( *first ) ) << 56;
  299. ++first;
  300. ++n;
  301. w += q;
  302. h ^= mulx( v1 + w, k );
  303. }
  304. h ^= n;
  305. w += q;
  306. h ^= mulx( v1 + w, k );
  307. return mulx( h + w, k );
  308. }
  309. } // namespace hash_detail
  310. } // namespace boost
  311. #endif // #ifndef BOOST_HASH_DETAIL_HASH_RANGE_HPP