24#ifndef PXR_TSL_ROBIN_GROWTH_POLICY_H
25#define PXR_TSL_ROBIN_GROWTH_POLICY_H
42#define pxr_tsl_rh_assert(expr) assert(expr)
44#define pxr_tsl_rh_assert(expr) (static_cast<void>(0))
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)
56#define PXR_TSL_RH_NO_EXCEPTIONS
58#define PXR_TSL_RH_THROW_OR_TERMINATE(ex, msg) std::terminate()
61#define PXR_TSL_RH_THROW_OR_TERMINATE(ex, msg) \
63 std::cerr << msg << std::endl; \
69#if defined(__GNUC__) || defined(__clang__)
70#define PXR_TSL_RH_LIKELY(exp) (__builtin_expect(!!(exp), true))
72#define PXR_TSL_RH_LIKELY(exp) (exp)
75#define PXR_TSL_RH_UNUSED(x) static_cast<void>(x)
77PXR_NAMESPACE_OPEN_SCOPE
89template <std::
size_t GrowthFactor>
102 PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
103 "The hash table exceeds its maximum size.");
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;
120 return hash & m_mask;
128 PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
129 "The hash table exceeds its maximum size.");
132 return (m_mask + 1) * GrowthFactor;
140 return (std::numeric_limits<std::size_t>::max() / 2) + 1;
148 void clear() noexcept { m_mask = 0; }
151 static std::size_t round_up_to_power_of_two(std::size_t value) {
152 if (is_power_of_two(value)) {
161 for (std::size_t i = 1; i <
sizeof(std::size_t) * CHAR_BIT; i *= 2) {
168 static constexpr bool is_power_of_two(std::size_t value) {
169 return value != 0 && (value & (value - 1)) == 0;
173 static_assert(is_power_of_two(GrowthFactor) && GrowthFactor >= 2,
174 "GrowthFactor must be a power of two >= 2.");
184template <
class GrowthFactor = std::ratio<3, 2>>
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.");
193 if (min_bucket_count_in_out > 0) {
194 m_mod = min_bucket_count_in_out;
200 std::size_t bucket_for_hash(std::size_t hash)
const noexcept {
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.");
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.");
217 if (next_bucket_count >
double(max_bucket_count())) {
218 return max_bucket_count();
220 return std::size_t(next_bucket_count);
224 std::size_t max_bucket_count()
const {
return MAX_BUCKET_COUNT; }
226 void clear()
noexcept { m_mod = 1; }
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));
235 static_assert(REHASH_SIZE_MULTIPLICATION_FACTOR >= 1.1,
236 "Growth factor should be >= 1.1.");
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
248#define PXR_TSL_RH_NB_PRIMES 23
251static constexpr const std::array<std::size_t, PXR_TSL_RH_NB_PRIMES> PRIMES = {{
275#if SIZE_MAX >= ULONG_MAX
294#if SIZE_MAX >= ULLONG_MAX
309template <
unsigned int IPrime>
310static constexpr std::size_t mod(std::size_t hash) {
311 return hash % PRIMES[IPrime];
317static constexpr const std::array<std::size_t (*)(std::size_t),
318 PXR_TSL_RH_NB_PRIMES>
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>,
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>,
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.");
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;
379 min_bucket_count_in_out = 0;
383 std::size_t bucket_for_hash(std::size_t hash)
const noexcept {
384 return detail::MOD_PRIME[m_iprime](hash);
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.");
393 return detail::PRIMES[m_iprime + 1];
396 std::size_t max_bucket_count()
const {
return detail::PRIMES.back(); }
398 void clear()
noexcept { m_iprime = 0; }
401 unsigned int m_iprime;
403 static_assert(std::numeric_limits<
decltype(m_iprime)>::max() >=
404 detail::PRIMES.size(),
405 "The type of m_iprime is not big enough.");
411PXR_NAMESPACE_CLOSE_SCOPE
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.