prime_fmod.hpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. // Copyright (C) 2022 Joaquin M Lopez Munoz.
  2. // Copyright (C) 2022-2023 Christian Mazakas
  3. //
  4. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  5. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. #ifndef BOOST_UNORDERED_DETAIL_PRIME_FMOD_HPP
  7. #define BOOST_UNORDERED_DETAIL_PRIME_FMOD_HPP
  8. #include <boost/unordered/detail/narrow_cast.hpp>
  9. #include <boost/config.hpp>
  10. #include <boost/cstdint.hpp>
  11. #include <climits>
  12. #include <cstddef>
  13. #if defined(SIZE_MAX)
  14. #if ((((SIZE_MAX >> 16) >> 16) >> 16) >> 15) != 0
  15. #define BOOST_UNORDERED_FCA_HAS_64B_SIZE_T
  16. #endif
  17. #elif defined(UINTPTR_MAX) /* used as proxy for std::size_t */
  18. #if ((((UINTPTR_MAX >> 16) >> 16) >> 16) >> 15) != 0
  19. #define BOOST_UNORDERED_FCA_HAS_64B_SIZE_T
  20. #endif
  21. #endif
  22. #if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) && defined(_MSC_VER)
  23. #include <intrin.h>
  24. #endif
  25. namespace boost {
  26. namespace unordered {
  27. namespace detail {
  28. template <class = void> struct prime_fmod_size
  29. {
  30. constexpr static std::size_t const sizes[] = {13ul, 29ul, 53ul, 97ul,
  31. 193ul, 389ul, 769ul, 1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
  32. 49157ul, 98317ul, 196613ul, 393241ul, 786433ul, 1572869ul, 3145739ul,
  33. 6291469ul, 12582917ul, 25165843ul, 50331653ul, 100663319ul,
  34. 201326611ul, 402653189ul, 805306457ul, 1610612741ul, 3221225473ul,
  35. #if !defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
  36. 4294967291ul
  37. #else
  38. 6442450939ull, 12884901893ull, 25769803751ull, 51539607551ull,
  39. 103079215111ull, 206158430209ull, 412316860441ull, 824633720831ull,
  40. 1649267441651ull
  41. #endif
  42. };
  43. constexpr static std::size_t const sizes_len =
  44. sizeof(sizes) / sizeof(sizes[0]);
  45. #if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
  46. constexpr static boost::uint64_t const inv_sizes32[] = {
  47. 1418980313362273202ull, 636094623231363849ull, 348051774975651918ull,
  48. 190172619316593316ull, 95578984837873325ull, 47420935922132524ull,
  49. 23987963684927896ull, 11955116055547344ull, 5991147799191151ull,
  50. 2998982941588287ull, 1501077717772769ull, 750081082979285ull,
  51. 375261795343686ull, 187625172388393ull, 93822606204624ull,
  52. 46909513691883ull, 23456218233098ull, 11728086747027ull,
  53. 5864041509391ull, 2932024948977ull, 1466014921160ull, 733007198436ull,
  54. 366503839517ull, 183251896093ull, 91625960335ull, 45812983922ull,
  55. 22906489714ull, 11453246088ull, 5726623060ull};
  56. constexpr static std::size_t const inv_sizes32_len =
  57. sizeof(inv_sizes32) / sizeof(inv_sizes32[0]);
  58. #endif /* defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) */
  59. template <std::size_t SizeIndex, std::size_t Size = sizes[SizeIndex]>
  60. static std::size_t position(std::size_t hash)
  61. {
  62. return hash % Size;
  63. }
  64. constexpr static std::size_t (*positions[])(std::size_t) = {
  65. #if !defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
  66. position<0, sizes[0]>,
  67. position<1, sizes[1]>,
  68. position<2, sizes[2]>,
  69. position<3, sizes[3]>,
  70. position<4, sizes[4]>,
  71. position<5, sizes[5]>,
  72. position<6, sizes[6]>,
  73. position<7, sizes[7]>,
  74. position<8, sizes[8]>,
  75. position<9, sizes[9]>,
  76. position<10, sizes[10]>,
  77. position<11, sizes[11]>,
  78. position<12, sizes[12]>,
  79. position<13, sizes[13]>,
  80. position<14, sizes[14]>,
  81. position<15, sizes[15]>,
  82. position<16, sizes[16]>,
  83. position<17, sizes[17]>,
  84. position<18, sizes[18]>,
  85. position<19, sizes[19]>,
  86. position<20, sizes[20]>,
  87. position<21, sizes[21]>,
  88. position<22, sizes[22]>,
  89. position<23, sizes[23]>,
  90. position<24, sizes[24]>,
  91. position<25, sizes[25]>,
  92. position<26, sizes[26]>,
  93. position<27, sizes[27]>,
  94. position<28, sizes[28]>,
  95. position<29, sizes[29]>,
  96. #else
  97. position<29, sizes[29]>,
  98. position<30, sizes[30]>,
  99. position<31, sizes[31]>,
  100. position<32, sizes[32]>,
  101. position<33, sizes[33]>,
  102. position<34, sizes[34]>,
  103. position<35, sizes[35]>,
  104. position<36, sizes[36]>,
  105. position<37, sizes[37]>,
  106. #endif
  107. };
  108. static inline std::size_t size_index(std::size_t n)
  109. {
  110. std::size_t i = 0;
  111. for (; i < (sizes_len - 1); ++i) {
  112. if (sizes[i] >= n) {
  113. break;
  114. }
  115. }
  116. return i;
  117. }
  118. static inline std::size_t size(std::size_t size_index)
  119. {
  120. return sizes[size_index];
  121. }
  122. #if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
  123. // We emulate the techniques taken from:
  124. // Faster Remainder by Direct Computation: Applications to Compilers and
  125. // Software Libraries
  126. // https://arxiv.org/abs/1902.01961
  127. //
  128. // In essence, use fancy math to directly calculate the remainder (aka
  129. // modulo) exploiting how compilers transform division
  130. //
  131. static inline boost::uint64_t get_remainder(
  132. boost::uint64_t fractional, boost::uint32_t d)
  133. {
  134. #if defined(_MSC_VER)
  135. // use MSVC intrinsics when available to avoid promotion to 128 bits
  136. return __umulh(fractional, d);
  137. #elif defined(BOOST_HAS_INT128)
  138. return static_cast<boost::uint64_t>(
  139. ((boost::uint128_type)fractional * d) >> 64);
  140. #else
  141. // portable implementation in the absence of boost::uint128_type on 64
  142. // bits, which happens at least in GCC 4.5 and prior
  143. boost::uint64_t r1 = (fractional & UINT32_MAX) * d;
  144. boost::uint64_t r2 = (fractional >> 32) * d;
  145. r2 += r1 >> 32;
  146. return r2 >> 32;
  147. #endif /* defined(_MSC_VER) */
  148. }
  149. static inline boost::uint32_t fast_modulo(
  150. boost::uint32_t a, boost::uint64_t M, boost::uint32_t d)
  151. {
  152. boost::uint64_t fractional = M * a;
  153. return (boost::uint32_t)(get_remainder(fractional, d));
  154. }
  155. #endif /* defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) */
  156. static inline std::size_t position(
  157. std::size_t hash, std::size_t size_index)
  158. {
  159. #if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
  160. std::size_t sizes_under_32bit = inv_sizes32_len;
  161. if (BOOST_LIKELY(size_index < sizes_under_32bit)) {
  162. return fast_modulo(narrow_cast<boost::uint32_t>(hash) +
  163. narrow_cast<boost::uint32_t>(hash >> 32),
  164. inv_sizes32[size_index], boost::uint32_t(sizes[size_index]));
  165. } else {
  166. return positions[size_index - sizes_under_32bit](hash);
  167. }
  168. #else
  169. return positions[size_index](hash);
  170. #endif /* defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) */
  171. }
  172. }; // prime_fmod_size
  173. #if defined(BOOST_NO_CXX17_INLINE_VARIABLES)
  174. // https://en.cppreference.com/w/cpp/language/static#Constant_static_members
  175. // If a const non-inline (since C++17) static data member or a constexpr
  176. // static data member (since C++11)(until C++17) is odr-used, a definition
  177. // at namespace scope is still required, but it cannot have an
  178. // initializer.
  179. template <class T> constexpr std::size_t prime_fmod_size<T>::sizes[];
  180. #if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
  181. template <class T>
  182. constexpr boost::uint64_t prime_fmod_size<T>::inv_sizes32[];
  183. #endif
  184. template <class T>
  185. constexpr std::size_t (*prime_fmod_size<T>::positions[])(std::size_t);
  186. #endif
  187. } // namespace detail
  188. } // namespace unordered
  189. } // namespace boost
  190. #endif // BOOST_UNORDERED_DETAIL_PRIME_FMOD_HPP