///////////////////////// ankerl::unordered_dense::{map, set} ///////////////////////// // A fast & densely stored hashmap and hashset based on robin-hood backward shift deletion. // Version 4.0.2 // https://github.com/martinus/unordered_dense // // Licensed under the MIT License . // SPDX-License-Identifier: MIT // Copyright (c) 2022-2023 Martin Leitner-Ankerl // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #ifndef ANKERL_UNORDERED_DENSE_H #define ANKERL_UNORDERED_DENSE_H // see https://semver.org/spec/v2.0.0.html #define ANKERL_UNORDERED_DENSE_VERSION_MAJOR 4 // NOLINT(cppcoreguidelines-macro-usage) incompatible API changes #define ANKERL_UNORDERED_DENSE_VERSION_MINOR 0 // NOLINT(cppcoreguidelines-macro-usage) backwards compatible functionality #define ANKERL_UNORDERED_DENSE_VERSION_PATCH 2 // NOLINT(cppcoreguidelines-macro-usage) backwards compatible bug fixes // API versioning with inline namespace, see https://www.foonathan.net/2018/11/inline-namespaces/ // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define ANKERL_UNORDERED_DENSE_VERSION_CONCAT1(major, minor, patch) v##major##_##minor##_##patch // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define ANKERL_UNORDERED_DENSE_VERSION_CONCAT(major, minor, patch) ANKERL_UNORDERED_DENSE_VERSION_CONCAT1(major, minor, patch) #define ANKERL_UNORDERED_DENSE_NAMESPACE \ ANKERL_UNORDERED_DENSE_VERSION_CONCAT( \ ANKERL_UNORDERED_DENSE_VERSION_MAJOR, ANKERL_UNORDERED_DENSE_VERSION_MINOR, ANKERL_UNORDERED_DENSE_VERSION_PATCH) #if defined(_MSVC_LANG) # define ANKERL_UNORDERED_DENSE_CPP_VERSION _MSVC_LANG #else # define ANKERL_UNORDERED_DENSE_CPP_VERSION __cplusplus #endif #if defined(__GNUC__) // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) # define ANKERL_UNORDERED_DENSE_PACK(decl) decl __attribute__((__packed__)) #elif defined(_MSC_VER) // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) # define ANKERL_UNORDERED_DENSE_PACK(decl) __pragma(pack(push, 1)) decl __pragma(pack(pop)) #endif // exceptions #if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) # define ANKERL_UNORDERED_DENSE_HAS_EXCEPTIONS() 1 // NOLINT(cppcoreguidelines-macro-usage) #else # define ANKERL_UNORDERED_DENSE_HAS_EXCEPTIONS() 0 // NOLINT(cppcoreguidelines-macro-usage) #endif #ifdef _MSC_VER # define ANKERL_UNORDERED_DENSE_NOINLINE __declspec(noinline) #else # define ANKERL_UNORDERED_DENSE_NOINLINE __attribute__((noinline)) #endif #if ANKERL_UNORDERED_DENSE_CPP_VERSION < 201703L # error ankerl::unordered_dense requires C++17 or higher #else # include // for array # include // for uint64_t, uint32_t, uint8_t, UINT64_C # include // for size_t, memcpy, memset # include // for equal_to, hash # include // for initializer_list # include // for pair, distance # include // for numeric_limits # include // for allocator, allocator_traits, shared_ptr # include // for out_of_range # include // for basic_string # include // for basic_string_view, hash # include // for forward_as_tuple # include // for enable_if_t, declval, conditional_t, ena... # include // for forward, exchange, pair, as_const, piece... # include // for vector # if ANKERL_UNORDERED_DENSE_HAS_EXCEPTIONS() == 0 # include // for abort # endif # if defined(__has_include) # if __has_include() # define ANKERL_UNORDERED_DENSE_PMR std::pmr // NOLINT(cppcoreguidelines-macro-usage) # include // for polymorphic_allocator # elif __has_include() # define ANKERL_UNORDERED_DENSE_PMR std::experimental::pmr // NOLINT(cppcoreguidelines-macro-usage) # include // for polymorphic_allocator # endif # endif # if defined(_MSC_VER) && defined(_M_X64) # include # pragma intrinsic(_umul128) # endif # if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__) # define ANKERL_UNORDERED_DENSE_LIKELY(x) __builtin_expect(x, 1) // NOLINT(cppcoreguidelines-macro-usage) # define ANKERL_UNORDERED_DENSE_UNLIKELY(x) __builtin_expect(x, 0) // NOLINT(cppcoreguidelines-macro-usage) # else # define ANKERL_UNORDERED_DENSE_LIKELY(x) (x) // NOLINT(cppcoreguidelines-macro-usage) # define ANKERL_UNORDERED_DENSE_UNLIKELY(x) (x) // NOLINT(cppcoreguidelines-macro-usage) # endif namespace ankerl::unordered_dense { inline namespace ANKERL_UNORDERED_DENSE_NAMESPACE { namespace detail { # if ANKERL_UNORDERED_DENSE_HAS_EXCEPTIONS() // make sure this is not inlined as it is slow and dramatically enlarges code, thus making other // inlinings more difficult. Throws are also generally the slow path. [[noreturn]] inline ANKERL_UNORDERED_DENSE_NOINLINE void on_error_key_not_found() { throw std::out_of_range("ankerl::unordered_dense::map::at(): key not found"); } [[noreturn]] inline ANKERL_UNORDERED_DENSE_NOINLINE void on_error_bucket_overflow() { throw std::overflow_error("ankerl::unordered_dense: reached max bucket size, cannot increase size"); } [[noreturn]] inline ANKERL_UNORDERED_DENSE_NOINLINE void on_error_too_many_elements() { throw std::out_of_range("ankerl::unordered_dense::map::replace(): too many elements"); } # else [[noreturn]] inline void on_error_key_not_found() { abort(); } [[noreturn]] inline void on_error_bucket_overflow() { abort(); } [[noreturn]] inline void on_error_too_many_elements() { abort(); } # endif } // namespace detail // hash /////////////////////////////////////////////////////////////////////// // This is a stripped-down implementation of wyhash: https://github.com/wangyi-fudan/wyhash // No big-endian support (because different values on different machines don't matter), // hardcodes seed and the secret, reformattes the code, and clang-tidy fixes. namespace detail::wyhash { static inline void mum(uint64_t* a, uint64_t* b) { # if defined(__SIZEOF_INT128__) __uint128_t r = *a; r *= *b; *a = static_cast(r); *b = static_cast(r >> 64U); # elif defined(_MSC_VER) && defined(_M_X64) *a = _umul128(*a, *b, b); # else uint64_t ha = *a >> 32U; uint64_t hb = *b >> 32U; uint64_t la = static_cast(*a); uint64_t lb = static_cast(*b); uint64_t hi{}; uint64_t lo{}; uint64_t rh = ha * hb; uint64_t rm0 = ha * lb; uint64_t rm1 = hb * la; uint64_t rl = la * lb; uint64_t t = rl + (rm0 << 32U); auto c = static_cast(t < rl); lo = t + (rm1 << 32U); c += static_cast(lo < t); hi = rh + (rm0 >> 32U) + (rm1 >> 32U) + c; *a = lo; *b = hi; # endif } // multiply and xor mix function, aka MUM [[nodiscard]] static inline auto mix(uint64_t a, uint64_t b) -> uint64_t { mum(&a, &b); return a ^ b; } // read functions. WARNING: we don't care about endianness, so results are different on big endian! [[nodiscard]] static inline auto r8(const uint8_t* p) -> uint64_t { uint64_t v{}; std::memcpy(&v, p, 8U); return v; } [[nodiscard]] static inline auto r4(const uint8_t* p) -> uint64_t { uint32_t v{}; std::memcpy(&v, p, 4); return v; } // reads 1, 2, or 3 bytes [[nodiscard]] static inline auto r3(const uint8_t* p, size_t k) -> uint64_t { return (static_cast(p[0]) << 16U) | (static_cast(p[k >> 1U]) << 8U) | p[k - 1]; } [[maybe_unused]] [[nodiscard]] static inline auto hash(void const* key, size_t len) -> uint64_t { static constexpr auto secret = std::array{UINT64_C(0xa0761d6478bd642f), UINT64_C(0xe7037ed1a0b428db), UINT64_C(0x8ebc6af09c88c6e3), UINT64_C(0x589965cc75374cc3)}; auto const* p = static_cast(key); uint64_t seed = secret[0]; uint64_t a{}; uint64_t b{}; if (ANKERL_UNORDERED_DENSE_LIKELY(len <= 16)) { if (ANKERL_UNORDERED_DENSE_LIKELY(len >= 4)) { a = (r4(p) << 32U) | r4(p + ((len >> 3U) << 2U)); b = (r4(p + len - 4) << 32U) | r4(p + len - 4 - ((len >> 3U) << 2U)); } else if (ANKERL_UNORDERED_DENSE_LIKELY(len > 0)) { a = r3(p, len); b = 0; } else { a = 0; b = 0; } } else { size_t i = len; if (ANKERL_UNORDERED_DENSE_UNLIKELY(i > 48)) { uint64_t see1 = seed; uint64_t see2 = seed; do { seed = mix(r8(p) ^ secret[1], r8(p + 8) ^ seed); see1 = mix(r8(p + 16) ^ secret[2], r8(p + 24) ^ see1); see2 = mix(r8(p + 32) ^ secret[3], r8(p + 40) ^ see2); p += 48; i -= 48; } while (ANKERL_UNORDERED_DENSE_LIKELY(i > 48)); seed ^= see1 ^ see2; } while (ANKERL_UNORDERED_DENSE_UNLIKELY(i > 16)) { seed = mix(r8(p) ^ secret[1], r8(p + 8) ^ seed); i -= 16; p += 16; } a = r8(p + i - 16); b = r8(p + i - 8); } return mix(secret[1] ^ len, mix(a ^ secret[1], b ^ seed)); } [[nodiscard]] static inline auto hash(uint64_t x) -> uint64_t { return detail::wyhash::mix(x, UINT64_C(0x9E3779B97F4A7C15)); } } // namespace detail::wyhash template struct hash { auto operator()(T const& obj) const noexcept(noexcept(std::declval>().operator()(std::declval()))) -> uint64_t { return std::hash{}(obj); } }; template struct hash> { using is_avalanching = void; auto operator()(std::basic_string const& str) const noexcept -> uint64_t { return detail::wyhash::hash(str.data(), sizeof(CharT) * str.size()); } }; template struct hash> { using is_avalanching = void; auto operator()(std::basic_string_view const& sv) const noexcept -> uint64_t { return detail::wyhash::hash(sv.data(), sizeof(CharT) * sv.size()); } }; template struct hash { using is_avalanching = void; auto operator()(T* ptr) const noexcept -> uint64_t { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) return detail::wyhash::hash(reinterpret_cast(ptr)); } }; template struct hash> { using is_avalanching = void; auto operator()(std::unique_ptr const& ptr) const noexcept -> uint64_t { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) return detail::wyhash::hash(reinterpret_cast(ptr.get())); } }; template struct hash> { using is_avalanching = void; auto operator()(std::shared_ptr const& ptr) const noexcept -> uint64_t { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) return detail::wyhash::hash(reinterpret_cast(ptr.get())); } }; template struct hash::value>::type> { using is_avalanching = void; auto operator()(Enum e) const noexcept -> uint64_t { using underlying = typename std::underlying_type_t; return detail::wyhash::hash(static_cast(e)); } }; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) # define ANKERL_UNORDERED_DENSE_HASH_STATICCAST(T) \ template <> \ struct hash { \ using is_avalanching = void; \ auto operator()(T const& obj) const noexcept -> uint64_t { \ return detail::wyhash::hash(static_cast(obj)); \ } \ } # if defined(__GNUC__) && !defined(__clang__) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wuseless-cast" # endif // see https://en.cppreference.com/w/cpp/utility/hash ANKERL_UNORDERED_DENSE_HASH_STATICCAST(bool); ANKERL_UNORDERED_DENSE_HASH_STATICCAST(char); ANKERL_UNORDERED_DENSE_HASH_STATICCAST(signed char); ANKERL_UNORDERED_DENSE_HASH_STATICCAST(unsigned char); # if ANKERL_UNORDERED_DENSE_CPP_VERSION >= 202002L ANKERL_UNORDERED_DENSE_HASH_STATICCAST(char8_t); # endif ANKERL_UNORDERED_DENSE_HASH_STATICCAST(char16_t); ANKERL_UNORDERED_DENSE_HASH_STATICCAST(char32_t); ANKERL_UNORDERED_DENSE_HASH_STATICCAST(wchar_t); ANKERL_UNORDERED_DENSE_HASH_STATICCAST(short); ANKERL_UNORDERED_DENSE_HASH_STATICCAST(unsigned short); ANKERL_UNORDERED_DENSE_HASH_STATICCAST(int); ANKERL_UNORDERED_DENSE_HASH_STATICCAST(unsigned int); ANKERL_UNORDERED_DENSE_HASH_STATICCAST(long); ANKERL_UNORDERED_DENSE_HASH_STATICCAST(long long); ANKERL_UNORDERED_DENSE_HASH_STATICCAST(unsigned long); ANKERL_UNORDERED_DENSE_HASH_STATICCAST(unsigned long long); # if defined(__GNUC__) && !defined(__clang__) # pragma GCC diagnostic pop # endif // bucket_type ////////////////////////////////////////////////////////// namespace bucket_type { struct standard { static constexpr uint32_t dist_inc = 1U << 8U; // skip 1 byte fingerprint static constexpr uint32_t fingerprint_mask = dist_inc - 1; // mask for 1 byte of fingerprint uint32_t m_dist_and_fingerprint; // upper 3 byte: distance to original bucket. lower byte: fingerprint from hash uint32_t m_value_idx; // index into the m_values vector. }; ANKERL_UNORDERED_DENSE_PACK(struct big { static constexpr uint32_t dist_inc = 1U << 8U; // skip 1 byte fingerprint static constexpr uint32_t fingerprint_mask = dist_inc - 1; // mask for 1 byte of fingerprint uint32_t m_dist_and_fingerprint; // upper 3 byte: distance to original bucket. lower byte: fingerprint from hash size_t m_value_idx; // index into the m_values vector. }); } // namespace bucket_type namespace detail { struct nonesuch {}; template class Op, class... Args> struct detector { using value_t = std::false_type; using type = Default; }; template class Op, class... Args> struct detector>, Op, Args...> { using value_t = std::true_type; using type = Op; }; template