robin_growth_policy.h
1 
24 #ifndef PXR_TSL_ROBIN_GROWTH_POLICY_H
25 #define PXR_TSL_ROBIN_GROWTH_POLICY_H
26 
27 #include <algorithm>
28 #include <array>
29 #include <climits>
30 #include <cmath>
31 #include <cstddef>
32 #include <cstdint>
33 #include <iterator>
34 #include <limits>
35 #include <ratio>
36 #include <stdexcept>
37 
38 // Pixar modification, modify namespace for isolation.
39 #include "pxr/pxr.h"
40 
41 #ifdef PXR_TSL_DEBUG
42 #define pxr_tsl_rh_assert(expr) assert(expr)
43 #else
44 #define pxr_tsl_rh_assert(expr) (static_cast<void>(0))
45 #endif
46 
51 #if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
52  (defined(_MSC_VER) && defined(_CPPUNWIND))) && \
53  !defined(PXR_TSL_NO_EXCEPTIONS)
54 #define PXR_TSL_RH_THROW_OR_TERMINATE(ex, msg) throw ex(msg)
55 #else
56 #define PXR_TSL_RH_NO_EXCEPTIONS
57 #ifdef NDEBUG
58 #define PXR_TSL_RH_THROW_OR_TERMINATE(ex, msg) std::terminate()
59 #else
60 #include <iostream>
61 #define PXR_TSL_RH_THROW_OR_TERMINATE(ex, msg) \
62  do { \
63  std::cerr << msg << std::endl; \
64  std::terminate(); \
65  } while (0)
66 #endif
67 #endif
68 
69 #if defined(__GNUC__) || defined(__clang__)
70 #define PXR_TSL_RH_LIKELY(exp) (__builtin_expect(!!(exp), true))
71 #else
72 #define PXR_TSL_RH_LIKELY(exp) (exp)
73 #endif
74 
75 #define PXR_TSL_RH_UNUSED(x) static_cast<void>(x)
76 
77 PXR_NAMESPACE_OPEN_SCOPE
78 
79 namespace pxr_tsl {
80 namespace rh {
81 
89 template <std::size_t GrowthFactor>
91  public:
100  explicit power_of_two_growth_policy(std::size_t& min_bucket_count_in_out) {
101  if (min_bucket_count_in_out > max_bucket_count()) {
102  PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
103  "The hash table exceeds its maximum size.");
104  }
105 
106  if (min_bucket_count_in_out > 0) {
107  min_bucket_count_in_out =
108  round_up_to_power_of_two(min_bucket_count_in_out);
109  m_mask = min_bucket_count_in_out - 1;
110  } else {
111  m_mask = 0;
112  }
113  }
114 
119  std::size_t bucket_for_hash(std::size_t hash) const noexcept {
120  return hash & m_mask;
121  }
122 
126  std::size_t next_bucket_count() const {
127  if ((m_mask + 1) > max_bucket_count() / GrowthFactor) {
128  PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
129  "The hash table exceeds its maximum size.");
130  }
131 
132  return (m_mask + 1) * GrowthFactor;
133  }
134 
138  std::size_t max_bucket_count() const {
139  // Largest power of two.
140  return (std::numeric_limits<std::size_t>::max() / 2) + 1;
141  }
142 
148  void clear() noexcept { m_mask = 0; }
149 
150  private:
151  static std::size_t round_up_to_power_of_two(std::size_t value) {
152  if (is_power_of_two(value)) {
153  return value;
154  }
155 
156  if (value == 0) {
157  return 1;
158  }
159 
160  --value;
161  for (std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) {
162  value |= value >> i;
163  }
164 
165  return value + 1;
166  }
167 
168  static constexpr bool is_power_of_two(std::size_t value) {
169  return value != 0 && (value & (value - 1)) == 0;
170  }
171 
172  protected:
173  static_assert(is_power_of_two(GrowthFactor) && GrowthFactor >= 2,
174  "GrowthFactor must be a power of two >= 2.");
175 
176  std::size_t m_mask;
177 };
178 
184 template <class GrowthFactor = std::ratio<3, 2>>
186  public:
187  explicit mod_growth_policy(std::size_t& min_bucket_count_in_out) {
188  if (min_bucket_count_in_out > max_bucket_count()) {
189  PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
190  "The hash table exceeds its maximum size.");
191  }
192 
193  if (min_bucket_count_in_out > 0) {
194  m_mod = min_bucket_count_in_out;
195  } else {
196  m_mod = 1;
197  }
198  }
199 
200  std::size_t bucket_for_hash(std::size_t hash) const noexcept {
201  return hash % m_mod;
202  }
203 
204  std::size_t next_bucket_count() const {
205  if (m_mod == max_bucket_count()) {
206  PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
207  "The hash table exceeds its maximum size.");
208  }
209 
210  const double next_bucket_count =
211  std::ceil(double(m_mod) * REHASH_SIZE_MULTIPLICATION_FACTOR);
212  if (!std::isnormal(next_bucket_count)) {
213  PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
214  "The hash table exceeds its maximum size.");
215  }
216 
217  if (next_bucket_count > double(max_bucket_count())) {
218  return max_bucket_count();
219  } else {
220  return std::size_t(next_bucket_count);
221  }
222  }
223 
224  std::size_t max_bucket_count() const { return MAX_BUCKET_COUNT; }
225 
226  void clear() noexcept { m_mod = 1; }
227 
228  private:
229  static constexpr double REHASH_SIZE_MULTIPLICATION_FACTOR =
230  1.0 * GrowthFactor::num / GrowthFactor::den;
231  static const std::size_t MAX_BUCKET_COUNT =
232  std::size_t(double(std::numeric_limits<std::size_t>::max() /
233  REHASH_SIZE_MULTIPLICATION_FACTOR));
234 
235  static_assert(REHASH_SIZE_MULTIPLICATION_FACTOR >= 1.1,
236  "Growth factor should be >= 1.1.");
237 
238  std::size_t m_mod;
239 };
240 
241 namespace detail {
242 
243 #if SIZE_MAX >= ULLONG_MAX
244 #define PXR_TSL_RH_NB_PRIMES 51
245 #elif SIZE_MAX >= ULONG_MAX
246 #define PXR_TSL_RH_NB_PRIMES 40
247 #else
248 #define PXR_TSL_RH_NB_PRIMES 23
249 #endif
250 
251 static constexpr const std::array<std::size_t, PXR_TSL_RH_NB_PRIMES> PRIMES = {{
252  1u,
253  5u,
254  17u,
255  29u,
256  37u,
257  53u,
258  67u,
259  79u,
260  97u,
261  131u,
262  193u,
263  257u,
264  389u,
265  521u,
266  769u,
267  1031u,
268  1543u,
269  2053u,
270  3079u,
271  6151u,
272  12289u,
273  24593u,
274  49157u,
275 #if SIZE_MAX >= ULONG_MAX
276  98317ul,
277  196613ul,
278  393241ul,
279  786433ul,
280  1572869ul,
281  3145739ul,
282  6291469ul,
283  12582917ul,
284  25165843ul,
285  50331653ul,
286  100663319ul,
287  201326611ul,
288  402653189ul,
289  805306457ul,
290  1610612741ul,
291  3221225473ul,
292  4294967291ul,
293 #endif
294 #if SIZE_MAX >= ULLONG_MAX
295  6442450939ull,
296  12884901893ull,
297  25769803751ull,
298  51539607551ull,
299  103079215111ull,
300  206158430209ull,
301  412316860441ull,
302  824633720831ull,
303  1649267441651ull,
304  3298534883309ull,
305  6597069766657ull,
306 #endif
307 }};
308 
309 template <unsigned int IPrime>
310 static constexpr std::size_t mod(std::size_t hash) {
311  return hash % PRIMES[IPrime];
312 }
313 
314 // MOD_PRIME[iprime](hash) returns hash % PRIMES[iprime]. This table allows for
315 // faster modulo as the compiler can optimize the modulo code better with a
316 // constant known at the compilation.
317 static constexpr const std::array<std::size_t (*)(std::size_t),
318  PXR_TSL_RH_NB_PRIMES>
319  MOD_PRIME = {{
320  &mod<0>, &mod<1>, &mod<2>, &mod<3>, &mod<4>, &mod<5>,
321  &mod<6>, &mod<7>, &mod<8>, &mod<9>, &mod<10>, &mod<11>,
322  &mod<12>, &mod<13>, &mod<14>, &mod<15>, &mod<16>, &mod<17>,
323  &mod<18>, &mod<19>, &mod<20>, &mod<21>, &mod<22>,
324 #if SIZE_MAX >= ULONG_MAX
325  &mod<23>, &mod<24>, &mod<25>, &mod<26>, &mod<27>, &mod<28>,
326  &mod<29>, &mod<30>, &mod<31>, &mod<32>, &mod<33>, &mod<34>,
327  &mod<35>, &mod<36>, &mod<37>, &mod<38>, &mod<39>,
328 #endif
329 #if SIZE_MAX >= ULLONG_MAX
330  &mod<40>, &mod<41>, &mod<42>, &mod<43>, &mod<44>, &mod<45>,
331  &mod<46>, &mod<47>, &mod<48>, &mod<49>, &mod<50>,
332 #endif
333  }};
334 
335 } // namespace detail
336 
365  public:
366  explicit prime_growth_policy(std::size_t& min_bucket_count_in_out) {
367  auto it_prime = std::lower_bound(
368  detail::PRIMES.begin(), detail::PRIMES.end(), min_bucket_count_in_out);
369  if (it_prime == detail::PRIMES.end()) {
370  PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
371  "The hash table exceeds its maximum size.");
372  }
373 
374  m_iprime = static_cast<unsigned int>(
375  std::distance(detail::PRIMES.begin(), it_prime));
376  if (min_bucket_count_in_out > 0) {
377  min_bucket_count_in_out = *it_prime;
378  } else {
379  min_bucket_count_in_out = 0;
380  }
381  }
382 
383  std::size_t bucket_for_hash(std::size_t hash) const noexcept {
384  return detail::MOD_PRIME[m_iprime](hash);
385  }
386 
387  std::size_t next_bucket_count() const {
388  if (m_iprime + 1 >= detail::PRIMES.size()) {
389  PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
390  "The hash table exceeds its maximum size.");
391  }
392 
393  return detail::PRIMES[m_iprime + 1];
394  }
395 
396  std::size_t max_bucket_count() const { return detail::PRIMES.back(); }
397 
398  void clear() noexcept { m_iprime = 0; }
399 
400  private:
401  unsigned int m_iprime;
402 
403  static_assert(std::numeric_limits<decltype(m_iprime)>::max() >=
404  detail::PRIMES.size(),
405  "The type of m_iprime is not big enough.");
406 };
407 
408 } // namespace rh
409 } // namespace pxr_tsl
410 
411 PXR_NAMESPACE_CLOSE_SCOPE
412 
413 #endif
std::size_t bucket_for_hash(std::size_t hash) const noexcept
Return the bucket [0, bucket_count()) to which the hash belongs.
Grow the hash table by a factor of GrowthFactor keeping the bucket count to a power of two.
power_of_two_growth_policy(std::size_t &min_bucket_count_in_out)
Called on the hash table creation and on rehash.
Grow the hash table by using prime numbers as bucket count.
Grow the hash table by GrowthFactor::num / GrowthFactor::den and use a modulo to map a hash to a buck...
void clear() noexcept
Reset the growth policy as if it was created with a bucket count of 0.
std::size_t max_bucket_count() const
Return the maximum number of buckets supported by the policy.
std::size_t next_bucket_count() const
Return the number of buckets that should be used on next growth.
MIT License.