24#ifndef PXR_TSL_ROBIN_GROWTH_POLICY_H
25#define PXR_TSL_ROBIN_GROWTH_POLICY_H
43#define PXR_TSL_RH_VERSION_MAJOR 1
46#define PXR_TSL_RH_VERSION_MINOR 3
49#define PXR_TSL_RH_VERSION_PATCH 0
52#define pxr_tsl_rh_assert(expr) assert(expr)
54#define pxr_tsl_rh_assert(expr) (static_cast<void>(0))
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)
66#define PXR_TSL_RH_NO_EXCEPTIONS
69#define PXR_TSL_RH_THROW_OR_TERMINATE(ex, msg) \
71 std::cerr << msg << std::endl; \
75#define PXR_TSL_RH_THROW_OR_TERMINATE(ex, msg) std::terminate()
79#if defined(__GNUC__) || defined(__clang__)
80#define PXR_TSL_RH_LIKELY(exp) (__builtin_expect(!!(exp), true))
82#define PXR_TSL_RH_LIKELY(exp) (exp)
85#define PXR_TSL_RH_UNUSED(x) static_cast<void>(x)
87PXR_NAMESPACE_OPEN_SCOPE
99template <std::
size_t GrowthFactor>
112 PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
113 "The hash table exceeds its maximum size.");
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;
130 return hash & m_mask;
138 PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
139 "The hash table exceeds its maximum size.");
142 return (m_mask + 1) * GrowthFactor;
150 return (std::numeric_limits<std::size_t>::max() / 2) + 1;
158 void clear() noexcept { m_mask = 0; }
161 static std::size_t round_up_to_power_of_two(std::size_t value) {
162 if (is_power_of_two(value)) {
171 for (std::size_t i = 1; i <
sizeof(std::size_t) * CHAR_BIT; i *= 2) {
178 static constexpr bool is_power_of_two(std::size_t value) {
179 return value != 0 && (value & (value - 1)) == 0;
183 static_assert(is_power_of_two(GrowthFactor) && GrowthFactor >= 2,
184 "GrowthFactor must be a power of two >= 2.");
194template <
class GrowthFactor = std::ratio<3, 2>>
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.");
203 if (min_bucket_count_in_out > 0) {
204 m_mod = min_bucket_count_in_out;
210 std::size_t bucket_for_hash(std::size_t hash)
const noexcept {
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.");
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.");
227 if (next_bucket_count >
double(max_bucket_count())) {
228 return max_bucket_count();
230 return std::size_t(next_bucket_count);
234 std::size_t max_bucket_count()
const {
return MAX_BUCKET_COUNT; }
236 void clear()
noexcept { m_mod = 1; }
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));
245 static_assert(REHASH_SIZE_MULTIPLICATION_FACTOR >= 1.1,
246 "Growth factor should be >= 1.1.");
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
258#define PXR_TSL_RH_NB_PRIMES 23
261static constexpr const std::array<std::size_t, PXR_TSL_RH_NB_PRIMES> PRIMES = {{
285#if SIZE_MAX >= ULONG_MAX
304#if SIZE_MAX >= ULLONG_MAX
319template <
unsigned int IPrime>
320static constexpr std::size_t mod(std::size_t hash) {
321 return hash % PRIMES[IPrime];
327static constexpr const std::array<std::size_t (*)(std::size_t),
328 PXR_TSL_RH_NB_PRIMES>
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>,
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>,
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.");
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;
389 min_bucket_count_in_out = 0;
393 std::size_t bucket_for_hash(std::size_t hash)
const noexcept {
394 return detail::MOD_PRIME[m_iprime](hash);
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.");
403 return detail::PRIMES[m_iprime + 1];
406 std::size_t max_bucket_count()
const {
return detail::PRIMES.back(); }
408 void clear()
noexcept { m_iprime = 0; }
411 unsigned int m_iprime;
413 static_assert(std::numeric_limits<
decltype(m_iprime)>::max() >=
414 detail::PRIMES.size(),
415 "The type of m_iprime is not big enough.");
421PXR_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.