24 #ifndef TSL_ROBIN_HASH_H
25 #define TSL_ROBIN_HASH_H
39 #include <type_traits>
47 namespace detail_robin_hash {
54 template <
typename T,
typename =
void>
59 typename
make_void<typename T::is_transparent>::type>
65 template <std::
size_t GrowthFactor>
71 const T&
clamp(
const T& v,
const T& lo,
const T& hi) {
72 return std::min(hi, std::max(lo, v));
75 template <
typename T,
typename U>
77 const char* error_message =
"numeric_cast() failed.") {
78 T ret =
static_cast<T
>(value);
79 if (
static_cast<U
>(ret) != value) {
83 const bool is_same_signedness =
84 (std::is_unsigned<T>::value && std::is_unsigned<U>::value) ||
85 (std::is_signed<T>::value && std::is_signed<U>::value);
86 if (!is_same_signedness && (ret < T{}) != (value < U{})) {
95 template <
class T,
class Deserializer>
99 #if defined(_MSC_VER) && _MSC_VER < 1910
102 return deserializer.Deserializer::template operator()<T>();
112 static_assert(std::numeric_limits<slz_size_type>::max() >=
113 std::numeric_limits<std::size_t>::max(),
114 "slz_size_type must be >= std::size_t");
122 template <
bool StoreHash>
169 template <
typename ValueType,
bool StoreHash>
192 std::is_nothrow_copy_constructible<value_type>::value)
196 if (!other.empty()) {
197 ::new (
static_cast<void*
>(std::addressof(
m_value)))
210 std::is_nothrow_move_constructible<value_type>::value)
214 if (!other.empty()) {
215 ::new (
static_cast<void*
>(std::addressof(
m_value)))
223 std::is_nothrow_copy_constructible<value_type>::value) {
224 if (
this != &other) {
227 bucket_hash::operator=(other);
228 if (!other.empty()) {
229 ::new (
static_cast<void*
>(std::addressof(
m_value)))
257 #if defined(__cplusplus) && __cplusplus >= 201703L
258 return *std::launder(
267 #if defined(__cplusplus) && __cplusplus >= 201703L
268 return *std::launder(
283 template <
typename... Args>
286 Args&&... value_type_args) {
290 ::new (
static_cast<void*
>(std::addressof(
m_value)))
291 value_type(std::forward<Args>(value_type_args)...);
324 value().~value_type();
331 std::numeric_limits<distance_type>::max() - 1,
332 "DIST_FROM_IDEAL_BUCKET_LIMIT must be <= "
333 "std::numeric_limits<distance_type>::max() - 1.");
360 template <
class ValueType,
class KeySelect,
class ValueSelect,
class Hash,
361 class KeyEqual,
class Allocator,
bool StoreHash,
class GrowthPolicy>
362 class robin_hash :
private Hash,
private KeyEqual,
private GrowthPolicy {
364 template <
typename U>
366 typename std::integral_constant<bool, !std::is_same<U, void>::value>;
369 noexcept(std::declval<GrowthPolicy>().
bucket_for_hash(std::size_t(0))),
370 "GrowthPolicy::bucket_for_hash must be noexcept.");
371 static_assert(noexcept(std::declval<GrowthPolicy>().
clear()),
372 "GrowthPolicy::clear must be noexcept.");
375 template <
bool IsConst>
405 (!std::is_arithmetic<key_type>::value ||
406 !std::is_same<Hash, std::hash<key_type>>::value));
428 std::numeric_limits<truncated_hash_type>::max();
455 template <
bool IsConst>
456 class robin_iterator {
476 template <
bool TIsConst = IsConst,
477 typename std::enable_if<TIsConst>::type* =
nullptr>
487 return KeySelect()(
m_bucket->value());
490 template <
class U = ValueSelect,
491 typename std::enable_if<has_mapped_type<U>::value &&
492 IsConst>::type* =
nullptr>
493 const typename U::value_type&
value()
const {
497 template <
class U = ValueSelect,
498 typename std::enable_if<has_mapped_type<U>::value &&
499 !IsConst>::type* =
nullptr>
500 typename U::value_type&
value()
const {
531 return lhs.m_bucket == rhs.m_bucket;
536 return !(lhs == rhs);
544 #if defined(__cplusplus) && __cplusplus >= 201402L
546 const Allocator& alloc,
561 "The map exceeds its maximum bucket count.");
584 const Allocator& alloc,
598 "The map exceeds its maximum bucket count.");
630 std::is_nothrow_move_constructible<
631 Hash>::value&& std::is_nothrow_move_constructible<KeyEqual>::value&&
632 std::is_nothrow_move_constructible<GrowthPolicy>::value&&
633 std::is_nothrow_move_constructible<buckets_container_type>::value)
634 : Hash(
std::move(static_cast<Hash&>(other))),
635 KeyEqual(
std::move(static_cast<KeyEqual&>(other))),
636 GrowthPolicy(
std::move(static_cast<GrowthPolicy&>(other))),
647 other.clear_and_shrink();
651 if (&other !=
this) {
652 Hash::operator=(other);
653 KeyEqual::operator=(other);
654 GrowthPolicy::operator=(other);
675 other.clear_and_shrink();
740 template <
typename P>
742 return insert_impl(KeySelect()(value), std::forward<P>(value));
745 template <
typename P>
747 if (hint !=
cend() &&
752 return insert(std::forward<P>(value)).first;
755 template <
class InputIt>
756 void insert(InputIt first, InputIt last) {
758 std::forward_iterator_tag,
759 typename std::iterator_traits<InputIt>::iterator_category>::value) {
760 const auto nb_elements_insert = std::distance(first, last);
764 if (nb_elements_insert > 0 &&
765 nb_free_buckets <
size_type(nb_elements_insert)) {
770 for (; first != last; ++first) {
775 template <
class K,
class M>
777 auto it =
try_emplace(std::forward<K>(key), std::forward<M>(obj));
779 it.first.value() = std::forward<M>(obj);
785 template <
class K,
class M>
789 it.value() = std::forward<M>(obj);
797 template <
class... Args>
798 std::pair<iterator, bool>
emplace(Args&&... args) {
802 template <
class... Args>
807 template <
class K,
class... Args>
810 std::forward_as_tuple(std::forward<K>(key)),
811 std::forward_as_tuple(std::forward<Args>(args)...));
814 template <
class K,
class... Args>
820 return try_emplace(std::forward<K>(key), std::forward<Args>(args)...).first;
839 if (pos.m_bucket->empty()) {
855 for (
auto it = first_mutable.m_bucket; it != last_mutable.m_bucket; ++it) {
862 if (last_mutable ==
end()) {
871 std::size_t icloser_bucket =
872 static_cast<std::size_t
>(first_mutable.m_bucket -
m_buckets);
873 std::size_t ito_move_closer_value =
874 static_cast<std::size_t
>(last_mutable.m_bucket -
m_buckets);
877 const std::size_t ireturn_bucket =
878 ito_move_closer_value -
880 ito_move_closer_value - icloser_bucket,
882 m_buckets[ito_move_closer_value].dist_from_ideal_bucket()));
885 m_buckets[ito_move_closer_value].dist_from_ideal_bucket() > 0) {
887 ito_move_closer_value -
889 ito_move_closer_value - icloser_bucket,
891 m_buckets[ito_move_closer_value].dist_from_ideal_bucket()));
895 m_buckets[ito_move_closer_value].dist_from_ideal_bucket() -
896 (ito_move_closer_value - icloser_bucket));
898 new_distance,
m_buckets[ito_move_closer_value].truncated_hash(),
899 std::move(
m_buckets[ito_move_closer_value].value()));
903 ++ito_move_closer_value;
918 auto it =
find(key, hash);
930 swap(
static_cast<Hash&
>(*
this),
static_cast<Hash&
>(other));
931 swap(
static_cast<KeyEqual&
>(*
this),
static_cast<KeyEqual&
>(other));
932 swap(
static_cast<GrowthPolicy&
>(*
this),
static_cast<GrowthPolicy&
>(other));
947 template <
class K,
class U = ValueSelect,
948 typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
949 typename U::value_type&
at(
const K& key) {
953 template <
class K,
class U = ValueSelect,
954 typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
955 typename U::value_type&
at(
const K& key, std::size_t hash) {
956 return const_cast<typename U::value_type&
>(
960 template <
class K,
class U = ValueSelect,
961 typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
962 const typename U::value_type&
at(
const K& key)
const {
966 template <
class K,
class U = ValueSelect,
967 typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
968 const typename U::value_type&
at(
const K& key, std::size_t hash)
const {
969 auto it =
find(key, hash);
977 template <
class K,
class U = ValueSelect,
978 typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
980 return try_emplace(std::forward<K>(key)).first.value();
1024 return count(key, hash) != 0;
1033 std::pair<iterator, iterator>
equal_range(
const K& key, std::size_t hash) {
1035 return std::make_pair(it, (it ==
end()) ? it : std::next(it));
1039 std::pair<const_iterator, const_iterator>
equal_range(
const K& key)
const {
1045 const K& key, std::size_t hash)
const {
1047 return std::make_pair(it, (it ==
cend()) ? it : std::next(it));
1056 return std::min(GrowthPolicy::max_bucket_count(),
1088 count_ = std::max(count_,
1111 template <
class Serializer>
1116 template <
class Deserializer>
1124 return Hash::operator()(key);
1127 template <
class K1,
class K2>
1129 return KeyEqual::operator()(key1, key2);
1133 const std::size_t bucket = GrowthPolicy::bucket_for_hash(hash);
1140 template <
class U = GrowthPolicy,
1141 typename std::enable_if<is_power_of_two_policy<U>::value>::type* =
1146 return (index + 1) & this->m_mask;
1149 template <
class U = GrowthPolicy,
1150 typename std::enable_if<!is_power_of_two_policy<U>::value>::type* =
1170 while (dist_from_ideal_bucket <=
1171 m_buckets[ibucket].dist_from_ideal_bucket()) {
1174 m_buckets[ibucket].bucket_hash_equal(hash)) &&
1180 dist_from_ideal_bucket++;
1187 pos.m_bucket->clear();
1197 std::size_t previous_ibucket =
1198 static_cast<std::size_t
>(pos.m_bucket -
m_buckets);
1199 std::size_t ibucket =
next_bucket(previous_ibucket);
1201 while (
m_buckets[ibucket].dist_from_ideal_bucket() > 0) {
1207 new_distance,
m_buckets[ibucket].truncated_hash(),
1211 previous_ibucket = ibucket;
1217 template <
class K,
class... Args>
1219 Args&&... value_type_args) {
1220 const std::size_t hash =
hash_key(key);
1225 while (dist_from_ideal_bucket <=
1226 m_buckets[ibucket].dist_from_ideal_bucket()) {
1228 m_buckets[ibucket].bucket_hash_equal(hash)) &&
1234 dist_from_ideal_bucket++;
1239 dist_from_ideal_bucket = 0;
1241 while (dist_from_ideal_bucket <=
1242 m_buckets[ibucket].dist_from_ideal_bucket()) {
1244 dist_from_ideal_bucket++;
1251 std::forward<Args>(value_type_args)...);
1255 std::forward<Args>(value_type_args)...);
1266 template <
class... Args>
1269 value_type value(std::forward<Args>(value_type_args)...);
1290 m_buckets[ibucket].dist_from_ideal_bucket());
1294 dist_from_ideal_bucket++;
1297 if (dist_from_ideal_bucket >
1298 m_buckets[ibucket].dist_from_ideal_bucket()) {
1299 if (dist_from_ideal_bucket >
1313 dist_from_ideal_bucket++;
1321 robin_hash new_table(count_,
static_cast<Hash&
>(*
this),
1326 const bool use_stored_hash =
1329 if (bucket.empty()) {
1333 const std::size_t hash =
1334 use_stored_hash ? bucket.truncated_hash()
1335 : new_table.
hash_key(KeySelect()(bucket.value()));
1339 std::move(bucket.value()));
1343 new_table.
swap(*
this);
1347 GrowthPolicy::clear();
1361 if (dist_from_ideal_bucket >
1362 m_buckets[ibucket].dist_from_ideal_bucket()) {
1365 hash, std::move(value));
1373 dist_from_ideal_bucket++;
1387 curr_dist_from_ideal_bucket >
1408 template <
class Serializer>
1416 const std::int16_t hash_stored_for_bucket =
1433 if (bucket.empty()) {
1434 const std::int16_t empty_bucket =
1438 const std::int16_t dist_from_ideal_bucket =
1439 bucket.dist_from_ideal_bucket();
1442 const std::uint32_t truncated_hash = bucket.truncated_hash();
1450 template <
class Deserializer>
1460 "Can't deserialize the ordered_map/set. "
1461 "The protocol version header is invalid.");
1464 const bool hash_stored_for_bucket =
1465 deserialize_value<std::int16_t>(
deserializer) ? true :
false;
1466 if (hash_compatible &&
STORE_HASH != hash_stored_for_bucket) {
1469 "Can't deserialize a map with a different StoreHash "
1470 "than the one used during the serialization when "
1471 "hash compatibility is used");
1485 "Invalid min_load_factor. Check that the serializer "
1486 "and deserializer support floats correctly as they "
1487 "can be converted implicitly to ints.");
1494 "Invalid max_load_factor. Check that the serializer "
1495 "and deserializer support floats correctly as they "
1496 "can be converted implicitly to ints.");
1502 if (bucket_count_ds == 0) {
1507 if (!hash_compatible) {
1508 reserve(numeric_cast<size_type>(nb_elements,
1509 "Deserialized nb_elements is too big."));
1510 for (
slz_size_type ibucket = 0; ibucket < bucket_count_ds; ibucket++) {
1513 if (dist_from_ideal_bucket !=
1515 if (hash_stored_for_bucket) {
1526 bucket_count_ds,
"Deserialized bucket_count is too big.");
1533 "The GrowthPolicy is not the same even "
1534 "though hash_compatible is true.");
1538 nb_elements,
"Deserialized nb_elements is too big.");
1545 if (dist_from_ideal_bucket !=
1548 if (hash_stored_for_bucket) {
1550 truncated_hash = deserialize_value<std::uint32_t>(
deserializer);
1553 bucket.set_value_of_empty_bucket(
1554 dist_from_ideal_bucket, truncated_hash,
1577 "MINIMUM_MAX_LOAD_FACTOR should be < MAXIMUM_MAX_LOAD_FACTOR");
1579 "MINIMUM_MIN_LOAD_FACTOR should be < MAXIMUM_MIN_LOAD_FACTOR");
1581 "MAXIMUM_MIN_LOAD_FACTOR should be < MINIMUM_MAX_LOAD_FACTOR");
1596 return &empty_bucket;