29#ifndef ANKERL_UNORDERED_DENSE_H
30#define ANKERL_UNORDERED_DENSE_H
33#define ANKERL_UNORDERED_DENSE_VERSION_MAJOR 4
34#define ANKERL_UNORDERED_DENSE_VERSION_MINOR \
36#define ANKERL_UNORDERED_DENSE_VERSION_PATCH 0
41#define ANKERL_UNORDERED_DENSE_VERSION_CONCAT1(major, minor, patch) v##major##_##minor##_##patch
43#define ANKERL_UNORDERED_DENSE_VERSION_CONCAT(major, minor, patch) \
44 ANKERL_UNORDERED_DENSE_VERSION_CONCAT1(major, minor, patch)
45#define ANKERL_UNORDERED_DENSE_NAMESPACE \
46 ANKERL_UNORDERED_DENSE_VERSION_CONCAT(ANKERL_UNORDERED_DENSE_VERSION_MAJOR, \
47 ANKERL_UNORDERED_DENSE_VERSION_MINOR, \
48 ANKERL_UNORDERED_DENSE_VERSION_PATCH)
50#if defined(_MSVC_LANG)
51#define ANKERL_UNORDERED_DENSE_CPP_VERSION _MSVC_LANG
53#define ANKERL_UNORDERED_DENSE_CPP_VERSION __cplusplus
58#define ANKERL_UNORDERED_DENSE_PACK(decl) decl __attribute__((__packed__))
59#elif defined(_MSC_VER)
61#define ANKERL_UNORDERED_DENSE_PACK(decl) __pragma(pack(push, 1)) decl __pragma(pack(pop))
65#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
66#define ANKERL_UNORDERED_DENSE_HAS_EXCEPTIONS() 1
68#define ANKERL_UNORDERED_DENSE_HAS_EXCEPTIONS() 0
71#define ANKERL_UNORDERED_DENSE_NOINLINE __declspec(noinline)
73#define ANKERL_UNORDERED_DENSE_NOINLINE __attribute__((noinline))
77#if !defined(ANKERL_UNORDERED_DENSE_EXPORT)
78#define ANKERL_UNORDERED_DENSE_EXPORT
81#if ANKERL_UNORDERED_DENSE_CPP_VERSION < 201703L
82#error ankerl::unordered_dense requires C++17 or higher
88#include <initializer_list>
100#if ANKERL_UNORDERED_DENSE_HAS_EXCEPTIONS() == 0
104#if defined(__has_include) && !defined(ANKERL_UNORDERED_DENSE_DISABLE_PMR)
105#if __has_include(<memory_resource>)
106#define ANKERL_UNORDERED_DENSE_PMR std::pmr
107#include <memory_resource>
108#elif __has_include(<experimental/memory_resource>)
109#define ANKERL_UNORDERED_DENSE_PMR std::experimental::pmr
110#include <experimental/memory_resource>
114#if defined(_MSC_VER) && defined(_M_X64)
116#pragma intrinsic(_umul128)
119#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__)
120#define ANKERL_UNORDERED_DENSE_LIKELY(x) __builtin_expect(x, 1)
121#define ANKERL_UNORDERED_DENSE_UNLIKELY(x) __builtin_expect(x, 0)
123#define ANKERL_UNORDERED_DENSE_LIKELY(x) (x)
124#define ANKERL_UNORDERED_DENSE_UNLIKELY(x) (x)
127namespace ankerl::unordered_dense {
132#if ANKERL_UNORDERED_DENSE_HAS_EXCEPTIONS()
138 throw std::out_of_range(
"ankerl::unordered_dense::map::at(): key not found");
142 throw std::overflow_error(
"ankerl::unordered_dense: reached max bucket size, cannot increase size");
146 throw std::out_of_range(
"ankerl::unordered_dense::map::replace(): too many elements");
151[[noreturn]]
inline void on_error_key_not_found()
155[[noreturn]]
inline void on_error_bucket_overflow()
159[[noreturn]]
inline void on_error_too_many_elements()
173namespace detail::wyhash {
175inline void mum(uint64_t*
a, uint64_t*
b)
177#if defined(__SIZEOF_INT128__)
180 *
a =
static_cast<uint64_t
>(r);
181 *
b =
static_cast<uint64_t
>(r >> 64U);
182#elif defined(_MSC_VER) && defined(_M_X64)
183 *
a = _umul128(*
a, *
b,
b);
185 uint64_t ha = *
a >> 32U;
186 uint64_t hb = *
b >> 32U;
187 uint64_t la =
static_cast<uint32_t
>(*a);
188 uint64_t lb =
static_cast<uint32_t
>(*b);
191 uint64_t rh = ha * hb;
192 uint64_t rm0 = ha * lb;
193 uint64_t rm1 = hb * la;
194 uint64_t rl = la * lb;
195 uint64_t t = rl + (rm0 << 32U);
196 auto c =
static_cast<uint64_t
>(t < rl);
197 lo = t + (rm1 << 32U);
198 c +=
static_cast<uint64_t
>(lo < t);
199 hi = rh + (rm0 >> 32U) + (rm1 >> 32U) + c;
206[[nodiscard]]
inline auto mix(uint64_t
a, uint64_t
b) -> uint64_t
213[[nodiscard]]
inline auto r8(
const uint8_t* p) -> uint64_t
220[[nodiscard]]
inline auto r4(
const uint8_t* p) -> uint64_t
228[[nodiscard]]
inline auto r3(
const uint8_t* p,
size_t k) -> uint64_t
230 return (
static_cast<uint64_t
>(p[0]) << 16U) | (
static_cast<uint64_t
>(p[k >> 1U]) << 8U) | p[k - 1];
233[[maybe_unused]] [[nodiscard]]
inline auto hash(
void const*
key,
size_t len) -> uint64_t
235 static constexpr auto secret = std::array{ UINT64_C(0xa0761d6478bd642f),
236 UINT64_C(0xe7037ed1a0b428db),
237 UINT64_C(0x8ebc6af09c88c6e3),
238 UINT64_C(0x589965cc75374cc3) };
240 auto const* p =
static_cast<uint8_t const*
>(
key);
241 uint64_t seed = secret[0];
244 if (ANKERL_UNORDERED_DENSE_LIKELY(
len <= 16)) {
245 if (ANKERL_UNORDERED_DENSE_LIKELY(
len >= 4)) {
246 a = (r4(p) << 32U) | r4(p + ((
len >> 3U) << 2U));
247 b = (r4(p +
len - 4) << 32U) | r4(p +
len - 4 - ((
len >> 3U) << 2U));
248 }
else if (ANKERL_UNORDERED_DENSE_LIKELY(
len > 0)) {
257 if (ANKERL_UNORDERED_DENSE_UNLIKELY(i > 48)) {
258 uint64_t see1 = seed;
259 uint64_t see2 = seed;
261 seed = mix(r8(p) ^ secret[1], r8(p + 8) ^ seed);
262 see1 = mix(r8(p + 16) ^ secret[2], r8(p + 24) ^ see1);
263 see2 = mix(r8(p + 32) ^ secret[3], r8(p + 40) ^ see2);
266 }
while (ANKERL_UNORDERED_DENSE_LIKELY(i > 48));
269 while (ANKERL_UNORDERED_DENSE_UNLIKELY(i > 16)) {
270 seed = mix(r8(p) ^ secret[1], r8(p + 8) ^ seed);
278 return mix(secret[1] ^
len, mix(
a ^ secret[1],
b ^ seed));
281[[nodiscard]]
inline auto hash(uint64_t x) -> uint64_t
283 return detail::wyhash::mix(x, UINT64_C(0x9E3779B97F4A7C15));
289 auto operator()(T
const& obj)
const
292 return std::hash<T>{}(obj);
296template <
typename T>
struct hash<T, typename
std::
hash<T>::is_avalanching> {
297 using is_avalanching = void;
298 auto operator()(T
const& obj)
const
301 return std::hash<T>{}(obj);
305template <
typename CharT>
struct hash<
std::basic_string<CharT>> {
306 using is_avalanching = void;
309 return detail::wyhash::hash(str.data(),
sizeof(CharT) * str.size());
313template <
typename CharT>
struct hash<
std::basic_string_view<CharT>> {
314 using is_avalanching = void;
317 return detail::wyhash::hash(sv.data(),
sizeof(CharT) * sv.size());
321template <
class T>
struct hash<T*> {
322 using is_avalanching = void;
323 auto operator()(T* ptr)
const noexcept -> uint64_t
326 return detail::wyhash::hash(
reinterpret_cast<uintptr_t
>(ptr));
330template <
class T>
struct hash<
std::unique_ptr<T>> {
331 using is_avalanching = void;
335 return detail::wyhash::hash(
reinterpret_cast<uintptr_t
>(ptr.get()));
339template <
class T>
struct hash<
std::shared_ptr<T>> {
340 using is_avalanching = void;
344 return detail::wyhash::hash(
reinterpret_cast<uintptr_t
>(ptr.get()));
348template <
typename Enum>
struct hash<Enum, typename
std::enable_if<std::is_enum<Enum>::value>::type> {
349 using is_avalanching = void;
350 auto operator()(Enum e)
const noexcept -> uint64_t
353 return detail::wyhash::hash(
static_cast<underlying
>(e));
357template <
typename... Args>
struct tuple_hash_helper {
360 template <
typename Arg> [[nodiscard]]
constexpr static auto to64(Arg
const& arg) -> uint64_t
363 return static_cast<uint64_t
>(arg);
365 return hash<Arg>{}(arg);
369 [[nodiscard]]
static auto mix64(uint64_t state, uint64_t v) -> uint64_t
371 return detail::wyhash::mix(state + v, uint64_t{ 0x9ddfea08eb382d69 });
386template <
typename... Args>
struct hash<
std::tuple<Args...>> : tuple_hash_helper<Args...> {
387 using is_avalanching = void;
394template <
typename A,
typename B>
struct hash<
std::pair<A, B>> : tuple_hash_helper<A, B> {
395 using is_avalanching = void;
403#define ANKERL_UNORDERED_DENSE_HASH_STATICCAST(T) \
404 template <> struct hash<T> { \
405 using is_avalanching = void; \
406 auto operator()(T const& obj) const noexcept -> uint64_t \
408 return detail::wyhash::hash(static_cast<uint64_t>(obj)); \
412#if defined(__GNUC__) && !defined(__clang__)
413#pragma GCC diagnostic push
414#pragma GCC diagnostic ignored "-Wuseless-cast"
417ANKERL_UNORDERED_DENSE_HASH_STATICCAST(
bool);
418ANKERL_UNORDERED_DENSE_HASH_STATICCAST(
char);
419ANKERL_UNORDERED_DENSE_HASH_STATICCAST(
signed char);
420ANKERL_UNORDERED_DENSE_HASH_STATICCAST(
unsigned char);
421#if ANKERL_UNORDERED_DENSE_CPP_VERSION >= 202002L && defined(__cpp_char8_t)
422ANKERL_UNORDERED_DENSE_HASH_STATICCAST(
char8_t);
424ANKERL_UNORDERED_DENSE_HASH_STATICCAST(
char16_t);
425ANKERL_UNORDERED_DENSE_HASH_STATICCAST(
char32_t);
426ANKERL_UNORDERED_DENSE_HASH_STATICCAST(
wchar_t);
427ANKERL_UNORDERED_DENSE_HASH_STATICCAST(
short);
428ANKERL_UNORDERED_DENSE_HASH_STATICCAST(
unsigned short);
429ANKERL_UNORDERED_DENSE_HASH_STATICCAST(
int);
430ANKERL_UNORDERED_DENSE_HASH_STATICCAST(
unsigned int);
431ANKERL_UNORDERED_DENSE_HASH_STATICCAST(
long);
432ANKERL_UNORDERED_DENSE_HASH_STATICCAST(
long long);
433ANKERL_UNORDERED_DENSE_HASH_STATICCAST(
unsigned long);
434ANKERL_UNORDERED_DENSE_HASH_STATICCAST(
unsigned long long);
436#if defined(__GNUC__) && !defined(__clang__)
437#pragma GCC diagnostic pop
442namespace bucket_type {
445 static constexpr uint32_t dist_inc = 1U << 8U;
446 static constexpr uint32_t fingerprint_mask = dist_inc - 1;
448 uint32_t m_dist_and_fingerprint;
449 uint32_t m_value_idx;
452ANKERL_UNORDERED_DENSE_PACK(
struct big {
453 static constexpr uint32_t dist_inc = 1U << 8U;
454 static constexpr uint32_t fingerprint_mask = dist_inc - 1;
456 uint32_t m_dist_and_fingerprint;
465struct default_container_t {};
467template <
class Default,
class AlwaysVoid,
template <
class...>
class Op,
class... Args>
struct detector {
468 using value_t = std::false_type;
469 using type = Default;
472template <
class Default,
template <
class...>
class Op,
class... Args>
473struct detector<Default,
std::void_t<Op<Args...>>, Op, Args...> {
474 using value_t = std::true_type;
475 using type = Op<Args...>;
478template <
template <
class...>
class Op,
class... Args>
479using is_detected =
typename detail::detector<detail::nonesuch, void, Op, Args...>::value_t;
481template <
template <
class...>
class Op,
class... Args>
constexpr bool is_detected_v = is_detected<Op, Args...>
::value;
483template <
typename T>
using detect_avalanching =
typename T::is_avalanching;
485template <
typename T>
using detect_is_transparent =
typename T::is_transparent;
487template <
typename T>
using detect_iterator =
typename T::iterator;
489template <
typename T>
using detect_reserve =
decltype(
std::declval<T&>().reserve(
size_t{}));
496template <
typename Hash,
typename KeyEqual>
497constexpr bool is_transparent_v = is_detected_v<detect_is_transparent, Hash> && is_detected_v<detect_is_transparent, KeyEqual>;
500template <
typename From,
typename To1,
typename To2>
503template <
typename T>
constexpr bool has_reserve = is_detected_v<detect_reserve, T>;
506template <
class T>
struct base_table_type_map {
507 using mapped_type = T;
511struct base_table_type_set {};
520template <
typename T,
typename Allocator = std::allocator<T>,
size_t MaxSegmentSizeBytes = 4096>
521class segmented_vector {
522 template <
bool IsConst>
class iter_t;
525 using allocator_type = Allocator;
526 using pointer =
typename std::allocator_traits<allocator_type>::pointer;
527 using const_pointer =
typename std::allocator_traits<allocator_type>::const_pointer;
528 using difference_type =
typename std::allocator_traits<allocator_type>::difference_type;
529 using value_type = T;
531 using reference = T&;
532 using const_reference = T
const&;
533 using iterator = iter_t<false>;
534 using const_iterator = iter_t<true>;
537 using vec_alloc =
typename std::allocator_traits<Allocator>::template rebind_alloc<pointer>;
542 static constexpr auto num_bits_closest(
size_t max_val,
size_t s) ->
size_t
544 auto f =
size_t{ 0 };
545 while (s << (f + 1) <= max_val) {
551 using self_t = segmented_vector<T, Allocator, MaxSegmentSizeBytes>;
552 static constexpr auto num_bits = num_bits_closest(MaxSegmentSizeBytes,
sizeof(T));
553 static constexpr auto num_elements_in_block = 1U << num_bits;
554 static constexpr auto mask = num_elements_in_block - 1U;
559 template <
bool IsConst>
class iter_t {
565 template <
bool B>
friend class iter_t;
568 using difference_type = segmented_vector::difference_type;
569 using value_type = T;
575 iter_t()
noexcept =
default;
577 template <bool OtherIsConst, typename = typename std::enable_if<IsConst && !OtherIsConst>::type>
579 constexpr iter_t(iter_t<OtherIsConst>
const& other) noexcept
580 : m_data(other.m_data)
584 constexpr iter_t(ptr_t
data,
size_t idx) noexcept
589 template <bool OtherIsConst, typename = typename std::enable_if<IsConst && !OtherIsConst>::type>
590 constexpr auto operator=(iter_t<OtherIsConst>
const& other)
noexcept -> iter_t&
592 m_data = other.m_data;
597 constexpr auto operator++()
noexcept -> iter_t&
603 constexpr auto operator++(
int)
noexcept -> iter_t
610 constexpr auto operator+(difference_type diff)
noexcept -> iter_t
612 return { m_data,
static_cast<size_t>(
static_cast<difference_type
>(m_idx) + diff) };
615 template <
bool OtherIsConst>
616 constexpr auto operator-(iter_t<OtherIsConst>
const& other)
noexcept -> difference_type
618 return static_cast<difference_type
>(m_idx) -
static_cast<difference_type
>(other.m_idx);
621 constexpr auto operator*()
const noexcept -> reference {
return m_data[m_idx >> num_bits][m_idx & mask]; }
623 constexpr auto operator->()
const noexcept -> pointer {
return &m_data[m_idx >> num_bits][m_idx & mask]; }
625 template <
bool O>
constexpr auto operator==(iter_t<O>
const& o)
const noexcept ->
bool
627 return m_idx == o.m_idx;
630 template <
bool O>
constexpr auto operator!=(iter_t<O>
const& o)
const noexcept ->
bool {
return !(*
this == o); }
634 void increase_capacity()
636 auto ba = Allocator(m_blocks.get_allocator());
637 pointer block = std::allocator_traits<Allocator>::allocate(ba, num_elements_in_block);
638 m_blocks.push_back(block);
642 void append_everything_from(segmented_vector&& other)
644 reserve(size() + other.size());
645 for (
auto&& o : other) {
651 void append_everything_from(segmented_vector
const& other)
653 reserve(size() + other.size());
654 for (
auto const& o : other) {
661 auto ba = Allocator(m_blocks.get_allocator());
662 for (
auto ptr : m_blocks) {
663 std::allocator_traits<Allocator>::deallocate(ba, ptr, num_elements_in_block);
667 [[nodiscard]]
static constexpr auto calc_num_blocks_for_capacity(
size_t capacity)
669 return (capacity + num_elements_in_block - 1U) / num_elements_in_block;
673 segmented_vector() =
default;
676 segmented_vector(Allocator alloc)
677 : m_blocks(vec_alloc(alloc))
680 segmented_vector(segmented_vector&& other, Allocator alloc)
681 : segmented_vector(alloc)
686 segmented_vector(segmented_vector
const& other, Allocator alloc)
687 : m_blocks(vec_alloc(alloc))
689 append_everything_from(other);
692 segmented_vector(segmented_vector&& other) noexcept
693 : segmented_vector(
std::move(other), get_allocator())
696 segmented_vector(segmented_vector
const& other) { append_everything_from(other); }
698 auto operator=(segmented_vector
const& other) -> segmented_vector&
700 if (
this == &other) {
704 append_everything_from(other);
708 auto operator=(segmented_vector&& other)
noexcept -> segmented_vector&
712 if (other.get_allocator() == get_allocator()) {
718 append_everything_from(
std::move(other));
729 [[nodiscard]]
constexpr auto size()
const ->
size_t {
return m_size; }
731 [[nodiscard]]
constexpr auto capacity()
const ->
size_t {
return m_blocks.size() * num_elements_in_block; }
734 [[nodiscard]]
constexpr auto operator[](
size_t i)
const noexcept -> T
const&
736 return m_blocks[i >> num_bits][i & mask];
739 [[nodiscard]]
constexpr auto operator[](
size_t i)
noexcept -> T& {
return m_blocks[i >> num_bits][i & mask]; }
741 [[nodiscard]]
constexpr auto begin() -> iterator {
return { m_blocks.data(), 0U }; }
742 [[nodiscard]]
constexpr auto begin()
const -> const_iterator {
return { m_blocks.data(), 0U }; }
743 [[nodiscard]]
constexpr auto cbegin()
const -> const_iterator {
return { m_blocks.data(), 0U }; }
745 [[nodiscard]]
constexpr auto end() -> iterator {
return { m_blocks.data(), m_size }; }
746 [[nodiscard]]
constexpr auto end()
const -> const_iterator {
return { m_blocks.data(), m_size }; }
747 [[nodiscard]]
constexpr auto cend()
const -> const_iterator {
return { m_blocks.data(), m_size }; }
749 [[nodiscard]]
constexpr auto back() -> reference {
return operator[](m_size - 1); }
750 [[nodiscard]]
constexpr auto back()
const -> const_reference {
return operator[](m_size - 1); }
758 [[nodiscard]]
auto empty()
const {
return 0 == m_size; }
760 void reserve(
size_t new_capacity)
762 m_blocks.reserve(calc_num_blocks_for_capacity(new_capacity));
763 while (new_capacity > capacity()) {
768 [[nodiscard]]
auto get_allocator()
const -> allocator_type {
return allocator_type{ m_blocks.get_allocator() }; }
770 template <
class... Args>
auto emplace_back(Args&&... args) -> reference
772 if (m_size == capacity()) {
775 auto* ptr =
static_cast<void*
>(&
operator[](m_size));
784 for (
size_t i = 0, s = size(); i < s; ++i) {
793 auto ba = Allocator(m_blocks.get_allocator());
794 auto num_blocks_required = calc_num_blocks_for_capacity(m_size);
795 while (m_blocks.size() > num_blocks_required) {
796 std::allocator_traits<Allocator>::deallocate(ba, m_blocks.back(), num_elements_in_block);
799 m_blocks.shrink_to_fit();
810 class AllocatorOrContainer,
812 class BucketContainer,
814class table :
public std::conditional_t<is_map_v<T>, base_table_type_map<T>, base_table_type_set> {
817 segmented_vector<underlying_value_type, AllocatorOrContainer>,
822 AllocatorOrContainer,
823 underlying_container_type>;
827 typename std::allocator_traits<typename value_container_type::allocator_type>::template rebind_alloc<Bucket>;
828 using default_bucket_container_type =
832 default_bucket_container_type,
835 static constexpr uint8_t initial_shifts = 64 - 2;
836 static constexpr float default_max_load_factor = 0.8F;
839 using key_type = Key;
840 using value_type =
typename value_container_type::value_type;
841 using size_type =
typename value_container_type::size_type;
842 using difference_type =
typename value_container_type::difference_type;
844 using key_equal = KeyEqual;
845 using allocator_type =
typename value_container_type::allocator_type;
846 using reference =
typename value_container_type::reference;
847 using const_reference =
typename value_container_type::const_reference;
848 using pointer =
typename value_container_type::pointer;
849 using const_pointer =
typename value_container_type::const_pointer;
850 using const_iterator =
typename value_container_type::const_iterator;
852 using bucket_type = Bucket;
855 using value_idx_type =
decltype(Bucket::m_value_idx);
856 using dist_and_fingerprint_type =
decltype(Bucket::m_dist_and_fingerprint);
861 value_container_type m_values{};
862 bucket_container_type m_buckets{};
863 size_t m_max_bucket_capacity = 0;
864 float m_max_load_factor = default_max_load_factor;
867 uint8_t m_shifts = initial_shifts;
869 [[nodiscard]]
auto next(value_idx_type bucket_idx)
const -> value_idx_type
871 return ANKERL_UNORDERED_DENSE_UNLIKELY(bucket_idx + 1U == bucket_count())
873 :
static_cast<value_idx_type
>(bucket_idx + 1U);
877 [[nodiscard]]
static constexpr auto at(bucket_container_type& bucket,
size_t offset) -> Bucket&
882 [[nodiscard]]
static constexpr auto at(
const bucket_container_type& bucket,
size_t offset) ->
const Bucket&
888 [[nodiscard]]
static constexpr auto dist_inc(dist_and_fingerprint_type x) -> dist_and_fingerprint_type
890 return static_cast<dist_and_fingerprint_type
>(x + Bucket::dist_inc);
893 [[nodiscard]]
static constexpr auto dist_dec(dist_and_fingerprint_type x) -> dist_and_fingerprint_type
895 return static_cast<dist_and_fingerprint_type
>(x - Bucket::dist_inc);
899 template <
typename K> [[nodiscard]]
constexpr auto mixed_hash(K
const&
key)
const -> uint64_t
901 if constexpr (is_detected_v<detect_avalanching, Hash>) {
903 if constexpr (
sizeof(
decltype(m_hash(
key))) <
sizeof(uint64_t)) {
905 return m_hash(
key) * UINT64_C(0x9ddfea08eb382d69);
912 return wyhash::hash(m_hash(
key));
916 [[nodiscard]]
constexpr auto dist_and_fingerprint_from_hash(uint64_t
hash)
const -> dist_and_fingerprint_type
918 return Bucket::dist_inc | (
static_cast<dist_and_fingerprint_type
>(
hash) & Bucket::fingerprint_mask);
921 [[nodiscard]]
constexpr auto bucket_idx_from_hash(uint64_t
hash)
const -> value_idx_type
923 return static_cast<value_idx_type
>(
hash >> m_shifts);
926 [[nodiscard]]
static constexpr auto get_key(value_type
const& vt) -> key_type
const&
928 if constexpr (is_map_v<T>) {
935 template <
typename K> [[nodiscard]]
auto next_while_less(K
const&
key)
const -> Bucket
938 auto dist_and_fingerprint = dist_and_fingerprint_from_hash(
hash);
939 auto bucket_idx = bucket_idx_from_hash(
hash);
941 while (dist_and_fingerprint < at(m_buckets, bucket_idx).m_dist_and_fingerprint) {
942 dist_and_fingerprint = dist_inc(dist_and_fingerprint);
943 bucket_idx = next(bucket_idx);
945 return { dist_and_fingerprint, bucket_idx };
948 void place_and_shift_up(Bucket bucket, value_idx_type place)
950 while (0 != at(m_buckets, place).m_dist_and_fingerprint) {
952 bucket.m_dist_and_fingerprint = dist_inc(bucket.m_dist_and_fingerprint);
955 at(m_buckets, place) = bucket;
958 [[nodiscard]]
static constexpr auto calc_num_buckets(uint8_t shifts) ->
size_t
960 return (std::min)(max_bucket_count(),
size_t{ 1 } << (64U - shifts));
963 [[nodiscard]]
constexpr auto calc_shifts_for_size(
size_t s)
const -> uint8_t
965 auto shifts = initial_shifts;
967 static_cast<size_t>(
static_cast<float>(calc_num_buckets(shifts)) * max_load_factor()) < s) {
974 void copy_buckets(table
const& other)
979 allocate_buckets_from_shift();
982 m_shifts = other.m_shifts;
983 allocate_buckets_from_shift();
985 for (
auto i = 0UL; i < bucket_count(); ++i) {
986 at(m_buckets, i) = at(other.m_buckets, i);
989 std::memcpy(m_buckets.data(), other.m_buckets.data(),
sizeof(Bucket) * bucket_count());
997 [[nodiscard]]
auto is_full()
const ->
bool {
return size() > m_max_bucket_capacity; }
999 void deallocate_buckets()
1002 m_buckets.shrink_to_fit();
1003 m_max_bucket_capacity = 0;
1006 void allocate_buckets_from_shift()
1008 auto num_buckets = calc_num_buckets(m_shifts);
1010 if constexpr (has_reserve<bucket_container_type>) {
1011 m_buckets.reserve(num_buckets);
1013 for (
size_t i = m_buckets.size(); i < num_buckets; ++i) {
1014 m_buckets.emplace_back();
1017 m_buckets.resize(num_buckets);
1019 if (num_buckets == max_bucket_count()) {
1021 m_max_bucket_capacity = max_bucket_count();
1023 m_max_bucket_capacity =
static_cast<value_idx_type
>(
static_cast<float>(num_buckets) * max_load_factor());
1027 void clear_buckets()
1030 for (
auto&& e : m_buckets) {
1034 std::memset(m_buckets.data(), 0,
sizeof(Bucket) * bucket_count());
1038 void clear_and_fill_buckets_from_values()
1041 for (value_idx_type value_idx = 0, end_idx =
static_cast<value_idx_type
>(m_values.size()); value_idx < end_idx;
1043 auto const&
key = get_key(m_values[value_idx]);
1044 auto [dist_and_fingerprint, bucket] = next_while_less(
key);
1047 place_and_shift_up({ dist_and_fingerprint, value_idx }, bucket);
1051 void increase_size()
1053 if (m_max_bucket_capacity == max_bucket_count()) {
1055 m_values.pop_back();
1056 on_error_bucket_overflow();
1060 deallocate_buckets();
1062 allocate_buckets_from_shift();
1063 clear_and_fill_buckets_from_values();
1066 template <
typename Op>
void do_erase(value_idx_type bucket_idx, Op handle_erased_value)
1068 auto const value_idx_to_remove = at(m_buckets, bucket_idx).m_value_idx;
1071 auto next_bucket_idx = next(bucket_idx);
1072 while (at(m_buckets, next_bucket_idx).m_dist_and_fingerprint >= Bucket::dist_inc * 2) {
1073 at(m_buckets, bucket_idx) = { dist_dec(at(m_buckets, next_bucket_idx).m_dist_and_fingerprint),
1074 at(m_buckets, next_bucket_idx).m_value_idx };
1075 bucket_idx =
std::exchange(next_bucket_idx, next(next_bucket_idx));
1077 at(m_buckets, bucket_idx) = {};
1078 handle_erased_value(
std::move(m_values[value_idx_to_remove]));
1081 if (value_idx_to_remove != m_values.size() - 1) {
1083 auto& val = m_values[value_idx_to_remove];
1088 auto mh = mixed_hash(get_key(val));
1089 bucket_idx = bucket_idx_from_hash(mh);
1091 auto const values_idx_back =
static_cast<value_idx_type
>(m_values.size() - 1);
1092 while (values_idx_back != at(m_buckets, bucket_idx).m_value_idx) {
1093 bucket_idx = next(bucket_idx);
1095 at(m_buckets, bucket_idx).m_value_idx = value_idx_to_remove;
1097 m_values.pop_back();
1100 template <
typename K,
typename Op>
auto do_erase_key(K&&
key, Op handle_erased_value) ->
size_t
1106 auto [dist_and_fingerprint, bucket_idx] = next_while_less(
key);
1108 while (dist_and_fingerprint == at(m_buckets, bucket_idx).m_dist_and_fingerprint &&
1109 !m_equal(
key, get_key(m_values[at(m_buckets, bucket_idx).m_value_idx]))) {
1110 dist_and_fingerprint = dist_inc(dist_and_fingerprint);
1111 bucket_idx = next(bucket_idx);
1114 if (dist_and_fingerprint != at(m_buckets, bucket_idx).m_dist_and_fingerprint) {
1117 do_erase(bucket_idx, handle_erased_value);
1124 if (!it_isinserted.second) {
1127 return it_isinserted;
1130 template <
typename... Args>
1131 auto do_place_element(dist_and_fingerprint_type dist_and_fingerprint, value_idx_type bucket_idx, Args&&... args)
1138 auto value_idx =
static_cast<value_idx_type
>(m_values.size() - 1);
1139 if (ANKERL_UNORDERED_DENSE_UNLIKELY(is_full())) {
1142 place_and_shift_up({ dist_and_fingerprint, value_idx }, bucket_idx);
1146 return { begin() +
static_cast<difference_type
>(value_idx),
true };
1152 auto dist_and_fingerprint = dist_and_fingerprint_from_hash(
hash);
1153 auto bucket_idx = bucket_idx_from_hash(
hash);
1156 auto* bucket = &at(m_buckets, bucket_idx);
1157 if (dist_and_fingerprint == bucket->m_dist_and_fingerprint) {
1158 if (m_equal(
key, get_key(m_values[bucket->m_value_idx]))) {
1159 return { begin() +
static_cast<difference_type
>(bucket->m_value_idx),
false };
1161 }
else if (dist_and_fingerprint > bucket->m_dist_and_fingerprint) {
1162 return do_place_element(dist_and_fingerprint,
1168 dist_and_fingerprint = dist_inc(dist_and_fingerprint);
1169 bucket_idx = next(bucket_idx);
1173 template <
typename K>
auto do_find(K
const&
key) -> iterator
1175 if (ANKERL_UNORDERED_DENSE_UNLIKELY(empty())) {
1179 auto mh = mixed_hash(
key);
1180 auto dist_and_fingerprint = dist_and_fingerprint_from_hash(mh);
1181 auto bucket_idx = bucket_idx_from_hash(mh);
1182 auto* bucket = &at(m_buckets, bucket_idx);
1185 if (dist_and_fingerprint == bucket->m_dist_and_fingerprint &&
1186 m_equal(
key, get_key(m_values[bucket->m_value_idx]))) {
1187 return begin() +
static_cast<difference_type
>(bucket->m_value_idx);
1189 dist_and_fingerprint = dist_inc(dist_and_fingerprint);
1190 bucket_idx = next(bucket_idx);
1191 bucket = &at(m_buckets, bucket_idx);
1193 if (dist_and_fingerprint == bucket->m_dist_and_fingerprint &&
1194 m_equal(
key, get_key(m_values[bucket->m_value_idx]))) {
1195 return begin() +
static_cast<difference_type
>(bucket->m_value_idx);
1197 dist_and_fingerprint = dist_inc(dist_and_fingerprint);
1198 bucket_idx = next(bucket_idx);
1199 bucket = &at(m_buckets, bucket_idx);
1202 if (dist_and_fingerprint == bucket->m_dist_and_fingerprint) {
1203 if (m_equal(
key, get_key(m_values[bucket->m_value_idx]))) {
1204 return begin() +
static_cast<difference_type
>(bucket->m_value_idx);
1206 }
else if (dist_and_fingerprint > bucket->m_dist_and_fingerprint) {
1209 dist_and_fingerprint = dist_inc(dist_and_fingerprint);
1210 bucket_idx = next(bucket_idx);
1211 bucket = &at(m_buckets, bucket_idx);
1215 template <
typename K>
auto do_find(K
const&
key)
const -> const_iterator
1217 return const_cast<table*
>(
this)->do_find(
key);
1220 template <
typename K,
typename Q = T, std::enable_if_t<is_map_v<Q>,
bool> = true>
auto do_at(K
const&
key) -> Q&
1222 if (
auto it = find(
key); ANKERL_UNORDERED_DENSE_LIKELY(end() != it)) {
1225 on_error_key_not_found();
1228 template <
typename K,
typename Q = T, std::enable_if_t<is_map_v<Q>,
bool> = true>
1229 auto do_at(K
const&
key)
const -> Q
const&
1231 return const_cast<table*
>(
this)->at(
key);
1235 explicit table(
size_t bucket_count,
1236 Hash
const&
hash = Hash(),
1237 KeyEqual
const& equal = KeyEqual(),
1238 allocator_type
const& alloc_or_container = allocator_type())
1239 : m_values(alloc_or_container)
1240 , m_buckets(alloc_or_container)
1244 if (0 != bucket_count) {
1245 reserve(bucket_count);
1247 allocate_buckets_from_shift();
1256 table(
size_t bucket_count, allocator_type
const& alloc)
1257 : table(bucket_count, Hash(), KeyEqual(), alloc)
1260 table(
size_t bucket_count, Hash
const&
hash, allocator_type
const& alloc)
1261 : table(bucket_count,
hash, KeyEqual(), alloc)
1264 explicit table(allocator_type
const& alloc)
1265 : table(0, Hash(), KeyEqual(), alloc)
1268 template <
class InputIt>
1269 table(InputIt first,
1271 size_type bucket_count = 0,
1272 Hash
const&
hash = Hash(),
1273 KeyEqual
const& equal = KeyEqual(),
1274 allocator_type
const& alloc = allocator_type())
1275 : table(bucket_count,
hash, equal, alloc)
1277 insert(first, last);
1280 template <
class InputIt>
1281 table(InputIt first, InputIt last, size_type bucket_count, allocator_type
const& alloc)
1282 : table(first, last, bucket_count, Hash(), KeyEqual(), alloc)
1285 template <
class InputIt>
1286 table(InputIt first, InputIt last, size_type bucket_count, Hash
const&
hash, allocator_type
const& alloc)
1287 : table(first, last, bucket_count,
hash, KeyEqual(), alloc)
1290 table(table
const& other)
1291 : table(other, other.m_values.get_allocator())
1294 table(table
const& other, allocator_type
const& alloc)
1295 : m_values(other.m_values, alloc)
1296 , m_max_load_factor(other.m_max_load_factor)
1297 , m_hash(other.m_hash)
1298 , m_equal(other.m_equal)
1300 copy_buckets(other);
1303 table(table&& other) noexcept
1304 : table(
std::move(other), other.m_values.get_allocator())
1307 table(table&& other, allocator_type
const& alloc) noexcept
1314 size_t bucket_count = 0,
1315 Hash
const&
hash = Hash(),
1316 KeyEqual
const& equal = KeyEqual(),
1317 allocator_type
const& alloc = allocator_type())
1318 : table(bucket_count,
hash, equal, alloc)
1324 : table(ilist, bucket_count, Hash(), KeyEqual(), alloc)
1328 : table(
init, bucket_count,
hash, KeyEqual(), alloc)
1333 auto operator=(table
const& other) -> table&
1335 if (&other !=
this) {
1336 deallocate_buckets();
1337 m_values = other.m_values;
1338 m_max_load_factor = other.m_max_load_factor;
1339 m_hash = other.m_hash;
1340 m_equal = other.m_equal;
1341 m_shifts = initial_shifts;
1342 copy_buckets(other);
1351 if (&other !=
this) {
1352 deallocate_buckets();
1354 other.m_values.clear();
1357 if (get_allocator() == other.get_allocator()) {
1359 other.m_buckets.clear();
1360 m_max_bucket_capacity =
std::exchange(other.m_max_bucket_capacity, 0);
1362 m_max_load_factor =
std::exchange(other.m_max_load_factor, default_max_load_factor);
1365 other.allocate_buckets_from_shift();
1366 other.clear_buckets();
1370 m_max_load_factor = other.m_max_load_factor;
1373 copy_buckets(other);
1375 other.clear_buckets();
1376 m_hash = other.m_hash;
1377 m_equal = other.m_equal;
1391 auto get_allocator()
const noexcept -> allocator_type {
return m_values.get_allocator(); }
1395 auto begin()
noexcept -> iterator {
return m_values.begin(); }
1397 auto begin()
const noexcept -> const_iterator {
return m_values.begin(); }
1399 auto cbegin()
const noexcept -> const_iterator {
return m_values.cbegin(); }
1401 auto end()
noexcept -> iterator {
return m_values.end(); }
1403 auto cend()
const noexcept -> const_iterator {
return m_values.cend(); }
1405 auto end()
const noexcept -> const_iterator {
return m_values.end(); }
1409 [[nodiscard]]
auto empty()
const noexcept ->
bool {
return m_values.empty(); }
1411 [[nodiscard]]
auto size()
const noexcept ->
size_t {
return m_values.size(); }
1413 [[nodiscard]]
static constexpr auto max_size()
noexcept ->
size_t
1415 if constexpr ((std::numeric_limits<value_idx_type>::max)() == (std::numeric_limits<size_t>::max)()) {
1416 return size_t{ 1 } << (
sizeof(value_idx_type) * 8 - 1);
1418 return size_t{ 1 } << (
sizeof(value_idx_type) * 8);
1434 template <
class P, std::enable_if_t<std::is_constructible_v<value_type, P&&>,
bool> = true>
1440 auto insert(const_iterator , value_type
const&
value) -> iterator {
return insert(
value).first; }
1442 auto insert(const_iterator , value_type&&
value) -> iterator {
return insert(
std::move(
value)).first; }
1444 template <
class P, std::enable_if_t<std::is_constructible_v<value_type, P&&>,
bool> = true>
1445 auto insert(const_iterator , P&&
value) -> iterator
1450 template <
class InputIt>
void insert(InputIt first, InputIt last)
1452 while (first != last) {
1462 auto extract() && -> value_container_type {
return std::move(m_values); }
1466 auto replace(value_container_type&& container)
1468 if (ANKERL_UNORDERED_DENSE_UNLIKELY(container.size() > max_size())) {
1469 on_error_too_many_elements();
1471 auto shifts = calc_shifts_for_size(container.size());
1472 if (0 == bucket_count() || shifts < m_shifts || container.get_allocator() != m_values.get_allocator()) {
1474 deallocate_buckets();
1475 allocate_buckets_from_shift();
1482 auto value_idx = value_idx_type{};
1485 while (value_idx !=
static_cast<value_idx_type
>(m_values.size())) {
1486 auto const&
key = get_key(m_values[value_idx]);
1489 auto dist_and_fingerprint = dist_and_fingerprint_from_hash(
hash);
1490 auto bucket_idx = bucket_idx_from_hash(
hash);
1492 bool key_found =
false;
1494 auto const& bucket = at(m_buckets, bucket_idx);
1495 if (dist_and_fingerprint > bucket.m_dist_and_fingerprint) {
1498 if (dist_and_fingerprint == bucket.m_dist_and_fingerprint &&
1499 m_equal(
key, get_key(m_values[bucket.m_value_idx]))) {
1503 dist_and_fingerprint = dist_inc(dist_and_fingerprint);
1504 bucket_idx = next(bucket_idx);
1508 if (value_idx !=
static_cast<value_idx_type
>(m_values.size() - 1)) {
1509 m_values[value_idx] =
std::move(m_values.back());
1511 m_values.pop_back();
1513 place_and_shift_up({ dist_and_fingerprint, value_idx }, bucket_idx);
1519 template <
class M,
typename Q = T, std::enable_if_t<is_map_v<Q>,
bool> = true>
1525 template <
class M,
typename Q = T, std::enable_if_t<is_map_v<Q>,
bool> = true>
1531 template <
typename K,
1535 typename KE = KeyEqual,
1542 template <
class M,
typename Q = T, std::enable_if_t<is_map_v<Q>,
bool> = true>
1543 auto insert_or_assign(const_iterator , Key
const&
key, M&& mapped) -> iterator
1548 template <
class M,
typename Q = T, std::enable_if_t<is_map_v<Q>,
bool> = true>
1549 auto insert_or_assign(const_iterator , Key&&
key, M&& mapped) -> iterator
1554 template <
typename K,
1558 typename KE = KeyEqual,
1560 auto insert_or_assign(const_iterator , K&&
key, M&& mapped) -> iterator
1569 typename KE = KeyEqual,
1574 auto dist_and_fingerprint = dist_and_fingerprint_from_hash(
hash);
1575 auto bucket_idx = bucket_idx_from_hash(
hash);
1577 while (dist_and_fingerprint <= at(m_buckets, bucket_idx).m_dist_and_fingerprint) {
1578 if (dist_and_fingerprint == at(m_buckets, bucket_idx).m_dist_and_fingerprint &&
1579 m_equal(
key, m_values[at(m_buckets, bucket_idx).m_value_idx])) {
1581 return { begin() +
static_cast<difference_type
>(at(m_buckets, bucket_idx).m_value_idx),
false };
1583 dist_and_fingerprint = dist_inc(dist_and_fingerprint);
1584 bucket_idx = next(bucket_idx);
1597 auto dist_and_fingerprint = dist_and_fingerprint_from_hash(
hash);
1598 auto bucket_idx = bucket_idx_from_hash(
hash);
1600 while (dist_and_fingerprint <= at(m_buckets, bucket_idx).m_dist_and_fingerprint) {
1601 if (dist_and_fingerprint == at(m_buckets, bucket_idx).m_dist_and_fingerprint &&
1602 m_equal(
key, get_key(m_values[at(m_buckets, bucket_idx).m_value_idx]))) {
1603 m_values.pop_back();
1604 return { begin() +
static_cast<difference_type
>(at(m_buckets, bucket_idx).m_value_idx),
false };
1606 dist_and_fingerprint = dist_inc(dist_and_fingerprint);
1607 bucket_idx = next(bucket_idx);
1611 auto value_idx =
static_cast<value_idx_type
>(m_values.size() - 1);
1612 if (ANKERL_UNORDERED_DENSE_UNLIKELY(is_full())) {
1617 place_and_shift_up({ dist_and_fingerprint, value_idx }, bucket_idx);
1619 return { begin() +
static_cast<difference_type
>(value_idx),
true };
1622 template <
class... Args>
auto emplace_hint(const_iterator , Args&&... args) -> iterator
1640 auto try_emplace(const_iterator , Key
const&
key, Args&&... args) -> iterator
1646 auto try_emplace(const_iterator , Key&&
key, Args&&... args) -> iterator
1651 template <
typename K,
1655 typename KE = KeyEqual,
1657 is_neither_convertible_v<K&&, iterator, const_iterator>,
1664 template <
typename K,
1668 typename KE = KeyEqual,
1670 is_neither_convertible_v<K&&, iterator, const_iterator>,
1672 auto try_emplace(const_iterator , K&&
key, Args&&... args) -> iterator
1677 auto erase(iterator it) -> iterator
1679 auto hash = mixed_hash(get_key(*it));
1680 auto bucket_idx = bucket_idx_from_hash(
hash);
1682 auto const value_idx_to_remove =
static_cast<value_idx_type
>(it - cbegin());
1683 while (at(m_buckets, bucket_idx).m_value_idx != value_idx_to_remove) {
1684 bucket_idx = next(bucket_idx);
1687 do_erase(bucket_idx, [](value_type&& ) {});
1688 return begin() +
static_cast<difference_type
>(value_idx_to_remove);
1691 auto extract(iterator it) -> value_type
1693 auto hash = mixed_hash(get_key(*it));
1694 auto bucket_idx = bucket_idx_from_hash(
hash);
1696 auto const value_idx_to_remove =
static_cast<value_idx_type
>(it - cbegin());
1697 while (at(m_buckets, bucket_idx).m_value_idx != value_idx_to_remove) {
1698 bucket_idx = next(bucket_idx);
1702 do_erase(bucket_idx, [&tmp](value_type&& val) { tmp =
std::move(val); });
1706 template <
typename Q = T, std::enable_if_t<is_map_v<Q>,
bool> = true>
auto erase(const_iterator it) -> iterator
1708 return erase(begin() + (it - cbegin()));
1711 template <
typename Q = T, std::enable_if_t<is_map_v<Q>,
bool> = true>
auto extract(const_iterator it) -> value_type
1713 return extract(begin() + (it - cbegin()));
1716 auto erase(const_iterator first, const_iterator last) -> iterator
1718 auto const idx_first = first - cbegin();
1719 auto const idx_last = last - cbegin();
1724 auto const mid = idx_first + (std::min)(first_to_last, last_to_end);
1725 auto idx = idx_first;
1726 while (idx != mid) {
1727 erase(begin() + idx);
1733 while (idx != mid) {
1735 erase(begin() + idx);
1738 return begin() + idx_first;
1741 auto erase(Key
const&
key) ->
size_t
1743 return do_erase_key(
key, [](value_type&& ) {});
1749 do_erase_key(
key, [&tmp](value_type&& val) { tmp =
std::move(val); });
1753 template <
class K,
class H = Hash,
class KE = KeyEqual, std::enable_if_t<is_transparent_v<H, KE>,
bool> = true>
1754 auto erase(K&&
key) ->
size_t
1759 template <
class K,
class H = Hash,
class KE = KeyEqual, std::enable_if_t<is_transparent_v<H, KE>,
bool> = true>
1777 template <
typename Q = T, std::enable_if_t<is_map_v<Q>,
bool> = true>
auto at(key_type
const&
key) -> Q&
1782 template <
typename K,
1785 typename KE = KeyEqual,
1787 auto at(K
const&
key) -> Q&
1792 template <
typename Q = T, std::enable_if_t<is_map_v<Q>,
bool> = true>
auto at(key_type
const&
key)
const -> Q
const&
1797 template <
typename K,
1800 typename KE = KeyEqual,
1802 auto at(K
const&
key)
const -> Q
const&
1807 template <
typename Q = T, std::enable_if_t<is_map_v<Q>,
bool> = true>
auto operator[](Key
const&
key) -> Q&
1809 return try_emplace(
key).first->second;
1812 template <
typename Q = T, std::enable_if_t<is_map_v<Q>,
bool> = true>
auto operator[](Key&&
key) -> Q&
1817 template <
typename K,
1820 typename KE = KeyEqual,
1827 auto count(Key
const&
key)
const ->
size_t {
return find(
key) == end() ? 0 : 1; }
1829 template <
class K,
class H = Hash,
class KE = KeyEqual, std::enable_if_t<is_transparent_v<H, KE>,
bool> = true>
1830 auto count(K
const&
key)
const ->
size_t
1832 return find(
key) == end() ? 0 : 1;
1835 auto find(Key
const&
key) -> iterator {
return do_find(
key); }
1837 auto find(Key
const&
key)
const -> const_iterator {
return do_find(
key); }
1839 template <
class K,
class H = Hash,
class KE = KeyEqual, std::enable_if_t<is_transparent_v<H, KE>,
bool> = true>
1840 auto find(K
const&
key) -> iterator
1842 return do_find(
key);
1845 template <
class K,
class H = Hash,
class KE = KeyEqual, std::enable_if_t<is_transparent_v<H, KE>,
bool> = true>
1846 auto find(K
const&
key)
const -> const_iterator
1848 return do_find(
key);
1851 auto contains(Key
const&
key)
const ->
bool {
return find(
key) != end(); }
1853 template <
class K,
class H = Hash,
class KE = KeyEqual, std::enable_if_t<is_transparent_v<H, KE>,
bool> = true>
1854 auto contains(K
const&
key)
const ->
bool
1856 return find(
key) != end();
1861 auto it = do_find(
key);
1862 return { it, it == end() ? end() : it + 1 };
1867 auto it = do_find(
key);
1868 return { it, it == end() ? end() : it + 1 };
1871 template <
class K,
class H = Hash,
class KE = KeyEqual, std::enable_if_t<is_transparent_v<H, KE>,
bool> = true>
1874 auto it = do_find(
key);
1875 return { it, it == end() ? end() : it + 1 };
1878 template <
class K,
class H = Hash,
class KE = KeyEqual, std::enable_if_t<is_transparent_v<H, KE>,
bool> = true>
1881 auto it = do_find(
key);
1882 return { it, it == end() ? end() : it + 1 };
1887 auto bucket_count()
const noexcept ->
size_t
1889 return m_buckets.size();
1892 static constexpr auto max_bucket_count()
noexcept ->
size_t
1899 [[nodiscard]]
auto load_factor()
const ->
float
1901 return bucket_count() ?
static_cast<float>(size()) /
static_cast<float>(bucket_count()) : 0.0F;
1904 [[nodiscard]]
auto max_load_factor()
const ->
float {
return m_max_load_factor; }
1906 void max_load_factor(
float ml)
1908 m_max_load_factor = ml;
1909 if (bucket_count() != max_bucket_count()) {
1910 m_max_bucket_capacity =
static_cast<value_idx_type
>(
static_cast<float>(bucket_count()) * max_load_factor());
1914 void rehash(
size_t count)
1916 count = (std::min)(count, max_size());
1917 auto shifts = calc_shifts_for_size((
std::max)(count, size()));
1918 if (shifts != m_shifts) {
1920 deallocate_buckets();
1921 m_values.shrink_to_fit();
1922 allocate_buckets_from_shift();
1923 clear_and_fill_buckets_from_values();
1927 void reserve(
size_t capa)
1929 capa = (std::min)(capa, max_size());
1930 if constexpr (has_reserve<value_container_type>) {
1932 m_values.reserve(capa);
1934 auto shifts = calc_shifts_for_size((
std::max)(capa, size()));
1935 if (0 == bucket_count() || shifts < m_shifts) {
1937 deallocate_buckets();
1938 allocate_buckets_from_shift();
1939 clear_and_fill_buckets_from_values();
1945 auto hash_function()
const -> hasher {
return m_hash; }
1947 auto key_eq()
const -> key_equal {
return m_equal; }
1950 [[nodiscard]]
auto values()
const noexcept -> value_container_type
const& {
return m_values; }
1954 friend auto operator==(table
const&
a, table
const&
b) ->
bool
1959 if (
a.size() !=
b.size()) {
1962 for (
auto const& b_entry :
b) {
1963 auto it =
a.find(get_key(b_entry));
1964 if constexpr (is_map_v<T>) {
1966 if (
a.end() == it || !(b_entry.second == it->second)) {
1971 if (
a.end() == it) {
1979 friend auto operator!=(table
const&
a, table
const&
b) ->
bool {
return !(
a ==
b); }
1986 class Hash = hash<Key>,
1989 class Bucket = bucket_type::standard,
1990 class BucketContainer = detail::default_container_t>
1991using map = detail::table<Key, T, Hash, KeyEqual, AllocatorOrContainer, Bucket, BucketContainer, false>;
1995 class Hash = hash<Key>,
1998 class Bucket = bucket_type::standard,
1999 class BucketContainer = detail::default_container_t>
2000using segmented_map = detail::table<Key, T, Hash, KeyEqual, AllocatorOrContainer, Bucket, BucketContainer, true>;
2003 class Hash = hash<Key>,
2006 class Bucket = bucket_type::standard,
2007 class BucketContainer = detail::default_container_t>
2008using set = detail::table<Key, void, Hash, KeyEqual, AllocatorOrContainer, Bucket, BucketContainer, false>;
2011 class Hash = hash<Key>,
2014 class Bucket = bucket_type::standard,
2015 class BucketContainer = detail::default_container_t>
2016using segmented_set = detail::table<Key, void, Hash, KeyEqual, AllocatorOrContainer, Bucket, BucketContainer, true>;
2018#if defined(ANKERL_UNORDERED_DENSE_PMR)
2024 class Hash = hash<Key>,
2026 class Bucket = bucket_type::standard>
2027using map = detail::table<Key,
2031 ANKERL_UNORDERED_DENSE_PMR::polymorphic_allocator<std::pair<Key, T>>,
2033 detail::default_container_t,
2038 class Hash = hash<Key>,
2040 class Bucket = bucket_type::standard>
2041using segmented_map = detail::table<Key,
2045 ANKERL_UNORDERED_DENSE_PMR::polymorphic_allocator<std::pair<Key, T>>,
2047 detail::default_container_t,
2051 class Hash = hash<Key>,
2053 class Bucket = bucket_type::standard>
2054using set = detail::table<Key,
2058 ANKERL_UNORDERED_DENSE_PMR::polymorphic_allocator<Key>,
2060 detail::default_container_t,
2064 class Hash = hash<Key>,
2066 class Bucket = bucket_type::standard>
2067using segmented_set = detail::table<Key,
2071 ANKERL_UNORDERED_DENSE_PMR::polymorphic_allocator<Key>,
2073 detail::default_container_t,
2102auto erase_if(ankerl::unordered_dense::detail::
2106 using map_t = ankerl::unordered_dense::detail::
2107 table<Key, T, Hash, KeyEqual, AllocatorOrContainer, Bucket, BucketContainer, IsSegmented>;
#define ANKERL_UNORDERED_DENSE_NAMESPACE
#define ANKERL_UNORDERED_DENSE_NOINLINE
#define ANKERL_UNORDERED_DENSE_EXPORT
const std::vector< FF > data
FF const & operator[](size_t idx) const
Retruns the element in beta_products at place #idx.
void hash(State &state) noexcept
std::vector< uint8_t > Key
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept