Loading...
Searching...
No Matches
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
77PXR_NAMESPACE_OPEN_SCOPE
78
79namespace pxr_tsl {
80namespace rh {
81
89template <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
184template <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
241namespace 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
251static 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
309template <unsigned int IPrime>
310static 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.
317static 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
411PXR_NAMESPACE_CLOSE_SCOPE
412
413#endif
Grow the hash table by GrowthFactor::num / GrowthFactor::den and use a modulo to map a hash to a buck...
Grow the hash table by a factor of GrowthFactor keeping the bucket count to a power of two.
void clear() noexcept
Reset the growth policy as if it was created with a bucket count of 0.
std::size_t next_bucket_count() const
Return the number of buckets that should be used on next growth.
std::size_t max_bucket_count() const
Return the maximum number of buckets supported by the policy.
power_of_two_growth_policy(std::size_t &min_bucket_count_in_out)
Called on the hash table creation and on rehash.
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 using prime numbers as bucket count.
MIT License.