File indexing completed on 2026-08-17 08:52:04
0001
0002
0003
0004
0005
0006 #ifndef BOOST_OPENMETHOD_POLICY_FAST_PERFECT_HASH_HPP
0007 #define BOOST_OPENMETHOD_POLICY_FAST_PERFECT_HASH_HPP
0008
0009 #include <boost/openmethod/preamble.hpp>
0010
0011 #include <limits>
0012 #include <random>
0013 #ifdef _MSC_VER
0014 #pragma warning(push)
0015 #pragma warning(disable : 4702)
0016 #endif
0017
0018 namespace boost::openmethod {
0019
0020 namespace detail {
0021
0022 #if defined(UINTPTR_MAX)
0023 using uintptr = std::uintptr_t;
0024 constexpr uintptr uintptr_max = UINTPTR_MAX;
0025 #else
0026 static_assert(
0027 sizeof(std::size_t) == sizeof(void*),
0028 "This implementation requires that size_t and void* have the same size.");
0029 using uintptr = std::size_t;
0030 constexpr uintptr uintptr_max = (std::numeric_limits<std::size_t>::max)();
0031 #endif
0032
0033 template<class Registry>
0034 std::vector<type_id> fast_perfect_hash_control;
0035
0036 }
0037
0038 namespace policies {
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052 struct fast_perfect_hash : type_hash {
0053
0054
0055 struct search_error : openmethod_error {
0056
0057 std::size_t attempts;
0058
0059 std::size_t buckets;
0060
0061
0062
0063
0064
0065 template<class Registry, class Stream>
0066 auto write(Stream& os) const -> void;
0067 };
0068
0069 using errors = std::variant<search_error>;
0070
0071
0072
0073
0074 template<class Registry>
0075 class fn {
0076 static std::size_t mult;
0077 static std::size_t shift;
0078 static std::size_t min_value;
0079 static std::size_t max_value;
0080
0081 static void check(std::size_t index, type_id type);
0082
0083 template<class InitializeContext, class... Options>
0084 static void initialize(
0085 const InitializeContext& ctx, std::vector<type_id>& buckets,
0086 const std::tuple<Options...>& options);
0087
0088 public:
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101 template<class Context, class... Options>
0102 static auto
0103 initialize(const Context& ctx, const std::tuple<Options...>& options) {
0104 if constexpr (Registry::has_runtime_checks) {
0105 initialize(
0106 ctx, detail::fast_perfect_hash_control<Registry>, options);
0107 } else {
0108 std::vector<type_id> buckets;
0109 initialize(ctx, buckets, options);
0110 }
0111
0112 return std::pair{min_value, max_value};
0113 }
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123
0124
0125
0126
0127
0128 BOOST_FORCEINLINE
0129 static auto hash(type_id type) -> std::size_t {
0130 auto index =
0131 (mult * reinterpret_cast<detail::uintptr>(type)) >> shift;
0132
0133 if constexpr (Registry::has_runtime_checks) {
0134 check(index, type);
0135 }
0136
0137 return index;
0138 }
0139
0140
0141
0142
0143
0144
0145 template<class... Options>
0146 static auto finalize(const std::tuple<Options...>&) -> void {
0147 detail::fast_perfect_hash_control<Registry>.clear();
0148 }
0149 };
0150 };
0151
0152 template<class Registry>
0153 std::size_t fast_perfect_hash::fn<Registry>::mult;
0154
0155 template<class Registry>
0156 std::size_t fast_perfect_hash::fn<Registry>::shift;
0157
0158 template<class Registry>
0159 std::size_t fast_perfect_hash::fn<Registry>::min_value;
0160
0161 template<class Registry>
0162 std::size_t fast_perfect_hash::fn<Registry>::max_value;
0163
0164 template<class Registry>
0165 template<class InitializeContext, class... Options>
0166 void fast_perfect_hash::fn<Registry>::initialize(
0167 const InitializeContext& ctx, std::vector<type_id>& buckets,
0168 const std::tuple<Options...>& options) {
0169 (void)options;
0170
0171 const auto N = std::distance(ctx.classes_begin(), ctx.classes_end());
0172
0173 if constexpr (mp11::mp_contains<mp11::mp_list<Options...>, trace>::value) {
0174 Registry::output::os << "Finding hash factor for " << N << " types\n";
0175 }
0176
0177 std::default_random_engine rnd(13081963);
0178 std::size_t total_attempts = 0;
0179 std::size_t M = 1;
0180
0181 for (auto size = N * 5 / 4; size >>= 1;) {
0182 ++M;
0183 }
0184
0185 std::uniform_int_distribution<std::size_t> uniform_dist;
0186
0187 for (std::size_t pass = 0; pass < 4; ++pass, ++M) {
0188 shift = 8 * sizeof(type_id) - M;
0189 auto hash_size = 1 << M;
0190 min_value = (std::numeric_limits<std::size_t>::max)();
0191 max_value = (std::numeric_limits<std::size_t>::min)();
0192
0193 if constexpr (InitializeContext::template has_option<trace>) {
0194 ctx.tr << " trying with M = " << M << ", " << hash_size
0195 << " buckets\n";
0196 }
0197
0198 std::size_t attempts = 0;
0199 buckets.resize(hash_size);
0200
0201 while (attempts < 100000) {
0202 std::fill(
0203 buckets.begin(), buckets.end(), type_id(detail::uintptr_max));
0204 ++attempts;
0205 ++total_attempts;
0206 mult = uniform_dist(rnd) | 1;
0207
0208 for (auto iter = ctx.classes_begin(); iter != ctx.classes_end();
0209 ++iter) {
0210 for (auto type_iter = iter->type_id_begin();
0211 type_iter != iter->type_id_end(); ++type_iter) {
0212 auto type = *type_iter;
0213 auto index = (detail::uintptr(type) * mult) >> shift;
0214 min_value = (std::min)(min_value, index);
0215 max_value = (std::max)(max_value, index);
0216
0217 if (detail::uintptr(buckets[index]) !=
0218 detail::uintptr_max) {
0219 goto collision;
0220 }
0221
0222 buckets[index] = type;
0223 }
0224 }
0225
0226 if constexpr (InitializeContext::template has_option<trace>) {
0227 ctx.tr << " found " << mult << " after " << total_attempts
0228 << " attempts; span = [" << min_value << ", "
0229 << max_value << "]\n";
0230 }
0231
0232 return;
0233
0234 collision: {}
0235 }
0236 }
0237
0238 search_error error;
0239 error.attempts = total_attempts;
0240 error.buckets = std::size_t(1) << M;
0241
0242 if constexpr (Registry::has_error_handler) {
0243 Registry::error_handler::error(error);
0244 }
0245
0246 abort();
0247 }
0248
0249 template<class Registry>
0250 void fast_perfect_hash::fn<Registry>::check(std::size_t index, type_id type) {
0251 if (index < min_value || index > max_value ||
0252 detail::fast_perfect_hash_control<Registry>[index] != type) {
0253
0254 if constexpr (Registry::has_error_handler) {
0255 missing_class error;
0256 error.type = type;
0257 Registry::error_handler::error(error);
0258 }
0259
0260 abort();
0261 }
0262 }
0263
0264 template<class Registry, class Stream>
0265 auto fast_perfect_hash::search_error::write(Stream& os) const -> void {
0266 os << "could not find hash factors after " << attempts << "s using "
0267 << buckets << " buckets\n";
0268 }
0269
0270 }
0271 }
0272
0273 #endif