Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-17 08:52:04

0001 // Copyright (c) 2018-2025 Jean-Louis Leroy
0002 // Distributed under the Boost Software License, Version 1.0.
0003 // See accompanying file LICENSE_1_0.txt
0004 // or copy at http://www.boost.org/LICENSE_1_0.txt)
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) // unreachable code
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 } // namespace detail
0037 
0038 namespace policies {
0039 
0040 //! Hash type ids using a fast, perfect hash function.
0041 //!
0042 //! `fast_perfect_hash` implements the @ref type_hash policy using a hash
0043 //! function in the form `H(x)=(M*x)>>S`. It attempts to determine values for
0044 //! `M` and `S` that do not result in collisions for the set of registered
0045 //! type_ids. This may fail for certain sets of inputs, although it is very
0046 //! likely to succeed for addresses of `std::type_info` objects.
0047 //!
0048 //! There is no guarantee that every value in the codomain of the function
0049 //! corresponds to a value in the domain, or even that the codomain is a dense
0050 //! range of integers. In other words, a lot of space may be wasted in presence
0051 //! of large sets of type_ids.
0052 struct fast_perfect_hash : type_hash {
0053 
0054     //! Cannot find hash factors
0055     struct search_error : openmethod_error {
0056         //! Number of attempts to find hash factors
0057         std::size_t attempts;
0058         //! Number of buckets used in the last attempt
0059         std::size_t buckets;
0060 
0061         //! Write a short description to an output stream
0062         //! @param os The output stream
0063         //! @tparam Registry The registry
0064         //! @tparam Stream A @ref LightweightOutputStream
0065         template<class Registry, class Stream>
0066         auto write(Stream& os) const -> void;
0067     };
0068 
0069     using errors = std::variant<search_error>;
0070 
0071     //! A TypeHashFn metafunction.
0072     //!
0073     //! @tparam Registry The registry containing this policy
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         //! Find the hash factors
0090         //!
0091         //! Attempts to find suitable values for the multiplication factor `M`
0092         //! and the shift amount `S` to that do not result in collisions for the
0093         //! specified input values.
0094         //!
0095         //! If no suitable values are found, calls the error handler with
0096         //! a @ref hash_error object then calls `abort`.
0097         //!
0098         //! @tparam Context An @ref InitializeContext.
0099         //! @param ctx A Context object.
0100         //! @return A pair containing the minimum and maximum hash values.
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         //! Hash a type id
0116         //!
0117         //! Hash a type id.
0118         //!
0119         //! If `Registry` contains the @ref runtime_checks policy, checks that
0120         //! the type id is valid, i.e. if it was present in the set passed to
0121         //! @ref initialize. Its absence indicates that a class involved in a
0122         //! method definition, method overrider, or method call was not
0123         //! registered. In this case, signal a @ref missing_class using
0124         //! the registry's @ref error_handler if present; then calls `abort`.
0125         //!
0126         //! @param type The type_id to hash
0127         //! @return The hash value
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         //! Releases the memory allocated by `initialize`.
0141         //!
0142         //! @tparam Options... Zero or more option types, deduced from the function
0143         //! arguments.
0144         //! @param options Zero or more option objects.
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 } // namespace policies
0271 } // namespace boost::openmethod
0272 
0273 #endif