hash_primes.cpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. #include "hash_primes.h"
  2. #include "array_size.h"
  3. #include "algorithm.h"
  4. /// Order of fields: reciprocal, reciprocal shift, adjacent hint, divisor
  5. #if defined(_32_)
  6. static constexpr ::NPrivate::THashDivisor PRIME_DIVISORS_HOLDER[]{
  7. {0x00000000u, 0u, -1, 0xffffffffu}, // guard value, not a valid divisor
  8. {0x24924925u, 2u, 0, 7u},
  9. {0xe1e1e1e2u, 4u, 1, 17u},
  10. {0x1a7b9612u, 4u, 2, 29u},
  11. {0x3521cfb3u, 5u, 3, 53u},
  12. {0x51d07eafu, 6u, 4, 97u},
  13. {0x53909490u, 7u, 5, 193u},
  14. {0x50f22e12u, 8u, 6, 389u},
  15. {0x54e3b41au, 9u, 7, 769u},
  16. {0x53c8eaeeu, 10u, 8, 1543u},
  17. {0x548eacc6u, 11u, 9, 3079u},
  18. {0x54f1e41eu, 12u, 10, 6151u},
  19. {0x554e390au, 13u, 11, 12289u},
  20. {0x5518ee41u, 14u, 12, 24593u},
  21. {0x554c7203u, 15u, 13, 49157u},
  22. {0x5549c781u, 16u, 14, 98317u},
  23. {0x55531c76u, 17u, 15, 196613u},
  24. {0x554fc734u, 18u, 16, 393241u},
  25. {0x555538e4u, 19u, 17, 786433u},
  26. {0x55550e39u, 20u, 18, 1572869u},
  27. {0x5555071du, 21u, 19, 3145739u},
  28. {0x5555271du, 22u, 20, 6291469u},
  29. {0x55554c72u, 23u, 21, 12582917u},
  30. {0x55554472u, 24u, 22, 25165843u},
  31. {0x5555531du, 25u, 23, 50331653u},
  32. {0x55555039u, 26u, 24, 100663319u},
  33. {0x55555339u, 27u, 25, 201326611u},
  34. {0x5555550fu, 28u, 26, 402653189u},
  35. {0x555552ddu, 29u, 27, 805306457u},
  36. {0x55555544u, 30u, 28, 1610612741u},
  37. {0x55555554u, 31u, 29, 3221225473u},
  38. {0x00000006u, 31u, 30, 4294967291u},
  39. };
  40. #else
  41. static constexpr ::NPrivate::THashDivisor PRIME_DIVISORS_HOLDER[]{
  42. {0x0000000000000000ul, 0u, -1, 0xffffffffu}, // guard value, not a valid divisor
  43. {0x2492492492492493ul, 2u, 0, 7u},
  44. {0xe1e1e1e1e1e1e1e2ul, 4u, 1, 17u},
  45. {0x1a7b9611a7b9611bul, 4u, 2, 29u},
  46. {0x3521cfb2b78c1353ul, 5u, 3, 53u},
  47. {0x51d07eae2f8151d1ul, 6u, 4, 97u},
  48. {0x5390948f40feac70ul, 7u, 5, 193u},
  49. {0x50f22e111c4c56dful, 8u, 6, 389u},
  50. {0x54e3b4194ce65de1ul, 9u, 7, 769u},
  51. {0x53c8eaedea6e7f17ul, 10u, 8, 1543u},
  52. {0x548eacc5e1e6e3fcul, 11u, 9, 3079u},
  53. {0x54f1e41d7767d70cul, 12u, 10, 6151u},
  54. {0x554e39097a781d80ul, 13u, 11, 12289u},
  55. {0x5518ee4079ea6929ul, 14u, 12, 24593u},
  56. {0x554c72025d459231ul, 15u, 13, 49157u},
  57. {0x5549c78094504ff3ul, 16u, 14, 98317u},
  58. {0x55531c757b3c329cul, 17u, 15, 196613u},
  59. {0x554fc7339753b424ul, 18u, 16, 393241u},
  60. {0x555538e39097b3f4ul, 19u, 17, 786433u},
  61. {0x55550e38f25ecd82ul, 20u, 18, 1572869u},
  62. {0x5555071c83b421d2ul, 21u, 19, 3145739u},
  63. {0x5555271c78097a6aul, 22u, 20, 6291469u},
  64. {0x55554c71c757b425ul, 23u, 21, 12582917u},
  65. {0x55554471c7f25ec7ul, 24u, 22, 25165843u},
  66. {0x5555531c71cad098ul, 25u, 23, 50331653u},
  67. {0x55555038e3a1d098ul, 26u, 24, 100663319u},
  68. {0x55555338e3919098ul, 27u, 25, 201326611u},
  69. {0x5555550e38e39d0aul, 28u, 26, 402653189u},
  70. {0x555552dc71cbb1eeul, 29u, 27, 805306457u},
  71. {0x555555438e38e47cul, 30u, 28, 1610612741u},
  72. {0x555555538e38e391ul, 31u, 29, 3221225473u},
  73. {0x000000050000001aul, 31u, 30, 4294967291u},
  74. };
  75. #endif
  76. static constexpr const ::NPrivate::THashDivisor* PRIME_DIVISORS = &PRIME_DIVISORS_HOLDER[1]; ///< Address of the first valid divisor
  77. static constexpr size_t PRIME_DIVISORS_SIZE = Y_ARRAY_SIZE(PRIME_DIVISORS_HOLDER) - 1; ///< Number of valid divisors without the guarding value
  78. unsigned long HashBucketCount(unsigned long elementCount) {
  79. return HashBucketCountExt(elementCount)();
  80. }
  81. static inline ::NPrivate::THashDivisor HashBucketBoundedSearch(unsigned long elementCount) {
  82. const auto begin = PRIME_DIVISORS;
  83. const auto end = PRIME_DIVISORS + PRIME_DIVISORS_SIZE - 1; // adjust range so the last element will be returned if elementCount is bigger than all PRIME_DIVISORS
  84. return *LowerBoundBy(begin, end, elementCount, std::mem_fn(&::NPrivate::THashDivisor::Divisor));
  85. }
  86. Y_CONST_FUNCTION
  87. ::NPrivate::THashDivisor HashBucketCountExt(unsigned long elementCount) {
  88. if (elementCount <= PRIME_DIVISORS[0]()) {
  89. return PRIME_DIVISORS[0];
  90. }
  91. return HashBucketBoundedSearch(elementCount);
  92. }
  93. Y_CONST_FUNCTION
  94. ::NPrivate::THashDivisor HashBucketCountExt(unsigned long elementCount, int hint) {
  95. if (Y_LIKELY(static_cast<size_t>(hint) < PRIME_DIVISORS_SIZE)) {
  96. const int index = hint;
  97. const ::NPrivate::THashDivisor* cnd = PRIME_DIVISORS + index;
  98. if (Y_LIKELY(elementCount <= cnd->Divisor)) {
  99. const ::NPrivate::THashDivisor* prev = cnd - 1;
  100. static_assert(~PRIME_DIVISORS[-1].Divisor == 0, "Invalid guard");
  101. /*
  102. If index == 0 then PRIME_DIVISORS[0] should be returned.
  103. Otherwise `cnd` is correct value iff (prev->Divisor < elementCount).
  104. Ergo hint is correct if (index == 0 || prev->Divisor < elementCount);
  105. But we can set guard's value to -1 and check both conditions at once.
  106. */
  107. if (Y_LIKELY(prev->Divisor + 1u <= elementCount)) {
  108. return *cnd;
  109. }
  110. }
  111. }
  112. return HashBucketBoundedSearch(elementCount);
  113. }