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// A change of the major version indicates an API and/or ABI break (change of
42// in-memory layout of the data structure)
43#define PXR_TSL_RH_VERSION_MAJOR 1
44// A change of the minor version indicates the addition of a feature without
45// impact on the API/ABI
46#define PXR_TSL_RH_VERSION_MINOR 3
47// A change of the patch version indicates a bugfix without additional
48// functionality
49#define PXR_TSL_RH_VERSION_PATCH 0
50
51#ifdef PXR_TSL_DEBUG
52#define pxr_tsl_rh_assert(expr) assert(expr)
53#else
54#define pxr_tsl_rh_assert(expr) (static_cast<void>(0))
55#endif
56
61#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
62 (defined(_MSC_VER) && defined(_CPPUNWIND))) && \
63 !defined(PXR_TSL_NO_EXCEPTIONS)
64#define PXR_TSL_RH_THROW_OR_TERMINATE(ex, msg) throw ex(msg)
65#else
66#define PXR_TSL_RH_NO_EXCEPTIONS
67#ifdef PXR_TSL_DEBUG
68#include <iostream>
69#define PXR_TSL_RH_THROW_OR_TERMINATE(ex, msg) \
70 do { \
71 std::cerr << msg << std::endl; \
72 std::terminate(); \
73 } while (0)
74#else
75#define PXR_TSL_RH_THROW_OR_TERMINATE(ex, msg) std::terminate()
76#endif
77#endif
78
79#if defined(__GNUC__) || defined(__clang__)
80#define PXR_TSL_RH_LIKELY(exp) (__builtin_expect(!!(exp), true))
81#else
82#define PXR_TSL_RH_LIKELY(exp) (exp)
83#endif
84
85#define PXR_TSL_RH_UNUSED(x) static_cast<void>(x)
86
87PXR_NAMESPACE_OPEN_SCOPE
88
89namespace pxr_tsl {
90namespace rh {
91
99template <std::size_t GrowthFactor>
101 public:
110 explicit power_of_two_growth_policy(std::size_t& min_bucket_count_in_out) {
111 if (min_bucket_count_in_out > max_bucket_count()) {
112 PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
113 "The hash table exceeds its maximum size.");
114 }
115
116 if (min_bucket_count_in_out > 0) {
117 min_bucket_count_in_out =
118 round_up_to_power_of_two(min_bucket_count_in_out);
119 m_mask = min_bucket_count_in_out - 1;
120 } else {
121 m_mask = 0;
122 }
123 }
124
129 std::size_t bucket_for_hash(std::size_t hash) const noexcept {
130 return hash & m_mask;
131 }
132
136 std::size_t next_bucket_count() const {
137 if ((m_mask + 1) > max_bucket_count() / GrowthFactor) {
138 PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
139 "The hash table exceeds its maximum size.");
140 }
141
142 return (m_mask + 1) * GrowthFactor;
143 }
144
148 std::size_t max_bucket_count() const {
149 // Largest power of two.
150 return (std::numeric_limits<std::size_t>::max() / 2) + 1;
151 }
152
158 void clear() noexcept { m_mask = 0; }
159
160 private:
161 static std::size_t round_up_to_power_of_two(std::size_t value) {
162 if (is_power_of_two(value)) {
163 return value;
164 }
165
166 if (value == 0) {
167 return 1;
168 }
169
170 --value;
171 for (std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) {
172 value |= value >> i;
173 }
174
175 return value + 1;
176 }
177
178 static constexpr bool is_power_of_two(std::size_t value) {
179 return value != 0 && (value & (value - 1)) == 0;
180 }
181
182 protected:
183 static_assert(is_power_of_two(GrowthFactor) && GrowthFactor >= 2,
184 "GrowthFactor must be a power of two >= 2.");
185
186 std::size_t m_mask;
187};
188
194template <class GrowthFactor = std::ratio<3, 2>>
196 public:
197 explicit mod_growth_policy(std::size_t& min_bucket_count_in_out) {
198 if (min_bucket_count_in_out > max_bucket_count()) {
199 PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
200 "The hash table exceeds its maximum size.");
201 }
202
203 if (min_bucket_count_in_out > 0) {
204 m_mod = min_bucket_count_in_out;
205 } else {
206 m_mod = 1;
207 }
208 }
209
210 std::size_t bucket_for_hash(std::size_t hash) const noexcept {
211 return hash % m_mod;
212 }
213
214 std::size_t next_bucket_count() const {
215 if (m_mod == max_bucket_count()) {
216 PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
217 "The hash table exceeds its maximum size.");
218 }
219
220 const double next_bucket_count =
221 std::ceil(double(m_mod) * REHASH_SIZE_MULTIPLICATION_FACTOR);
222 if (!std::isnormal(next_bucket_count)) {
223 PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
224 "The hash table exceeds its maximum size.");
225 }
226
227 if (next_bucket_count > double(max_bucket_count())) {
228 return max_bucket_count();
229 } else {
230 return std::size_t(next_bucket_count);
231 }
232 }
233
234 std::size_t max_bucket_count() const { return MAX_BUCKET_COUNT; }
235
236 void clear() noexcept { m_mod = 1; }
237
238 private:
239 static constexpr double REHASH_SIZE_MULTIPLICATION_FACTOR =
240 1.0 * GrowthFactor::num / GrowthFactor::den;
241 static const std::size_t MAX_BUCKET_COUNT =
242 std::size_t(double(std::numeric_limits<std::size_t>::max() /
243 REHASH_SIZE_MULTIPLICATION_FACTOR));
244
245 static_assert(REHASH_SIZE_MULTIPLICATION_FACTOR >= 1.1,
246 "Growth factor should be >= 1.1.");
247
248 std::size_t m_mod;
249};
250
251namespace detail {
252
253#if SIZE_MAX >= ULLONG_MAX
254#define PXR_TSL_RH_NB_PRIMES 51
255#elif SIZE_MAX >= ULONG_MAX
256#define PXR_TSL_RH_NB_PRIMES 40
257#else
258#define PXR_TSL_RH_NB_PRIMES 23
259#endif
260
261static constexpr const std::array<std::size_t, PXR_TSL_RH_NB_PRIMES> PRIMES = {{
262 1u,
263 5u,
264 17u,
265 29u,
266 37u,
267 53u,
268 67u,
269 79u,
270 97u,
271 131u,
272 193u,
273 257u,
274 389u,
275 521u,
276 769u,
277 1031u,
278 1543u,
279 2053u,
280 3079u,
281 6151u,
282 12289u,
283 24593u,
284 49157u,
285#if SIZE_MAX >= ULONG_MAX
286 98317ul,
287 196613ul,
288 393241ul,
289 786433ul,
290 1572869ul,
291 3145739ul,
292 6291469ul,
293 12582917ul,
294 25165843ul,
295 50331653ul,
296 100663319ul,
297 201326611ul,
298 402653189ul,
299 805306457ul,
300 1610612741ul,
301 3221225473ul,
302 4294967291ul,
303#endif
304#if SIZE_MAX >= ULLONG_MAX
305 6442450939ull,
306 12884901893ull,
307 25769803751ull,
308 51539607551ull,
309 103079215111ull,
310 206158430209ull,
311 412316860441ull,
312 824633720831ull,
313 1649267441651ull,
314 3298534883309ull,
315 6597069766657ull,
316#endif
317}};
318
319template <unsigned int IPrime>
320static constexpr std::size_t mod(std::size_t hash) {
321 return hash % PRIMES[IPrime];
322}
323
324// MOD_PRIME[iprime](hash) returns hash % PRIMES[iprime]. This table allows for
325// faster modulo as the compiler can optimize the modulo code better with a
326// constant known at the compilation.
327static constexpr const std::array<std::size_t (*)(std::size_t),
328 PXR_TSL_RH_NB_PRIMES>
329 MOD_PRIME = {{
330 &mod<0>, &mod<1>, &mod<2>, &mod<3>, &mod<4>, &mod<5>,
331 &mod<6>, &mod<7>, &mod<8>, &mod<9>, &mod<10>, &mod<11>,
332 &mod<12>, &mod<13>, &mod<14>, &mod<15>, &mod<16>, &mod<17>,
333 &mod<18>, &mod<19>, &mod<20>, &mod<21>, &mod<22>,
334#if SIZE_MAX >= ULONG_MAX
335 &mod<23>, &mod<24>, &mod<25>, &mod<26>, &mod<27>, &mod<28>,
336 &mod<29>, &mod<30>, &mod<31>, &mod<32>, &mod<33>, &mod<34>,
337 &mod<35>, &mod<36>, &mod<37>, &mod<38>, &mod<39>,
338#endif
339#if SIZE_MAX >= ULLONG_MAX
340 &mod<40>, &mod<41>, &mod<42>, &mod<43>, &mod<44>, &mod<45>,
341 &mod<46>, &mod<47>, &mod<48>, &mod<49>, &mod<50>,
342#endif
343 }};
344
345} // namespace detail
346
375 public:
376 explicit prime_growth_policy(std::size_t& min_bucket_count_in_out) {
377 auto it_prime = std::lower_bound(
378 detail::PRIMES.begin(), detail::PRIMES.end(), min_bucket_count_in_out);
379 if (it_prime == detail::PRIMES.end()) {
380 PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
381 "The hash table exceeds its maximum size.");
382 }
383
384 m_iprime = static_cast<unsigned int>(
385 std::distance(detail::PRIMES.begin(), it_prime));
386 if (min_bucket_count_in_out > 0) {
387 min_bucket_count_in_out = *it_prime;
388 } else {
389 min_bucket_count_in_out = 0;
390 }
391 }
392
393 std::size_t bucket_for_hash(std::size_t hash) const noexcept {
394 return detail::MOD_PRIME[m_iprime](hash);
395 }
396
397 std::size_t next_bucket_count() const {
398 if (m_iprime + 1 >= detail::PRIMES.size()) {
399 PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
400 "The hash table exceeds its maximum size.");
401 }
402
403 return detail::PRIMES[m_iprime + 1];
404 }
405
406 std::size_t max_bucket_count() const { return detail::PRIMES.back(); }
407
408 void clear() noexcept { m_iprime = 0; }
409
410 private:
411 unsigned int m_iprime;
412
413 static_assert(std::numeric_limits<decltype(m_iprime)>::max() >=
414 detail::PRIMES.size(),
415 "The type of m_iprime is not big enough.");
416};
417
418} // namespace rh
419} // namespace pxr_tsl
420
421PXR_NAMESPACE_CLOSE_SCOPE
422
423#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.