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>
69 template <
typename T,
typename U>
71 const char* error_message =
"numeric_cast() failed.") {
72 T ret =
static_cast<T
>(value);
73 if (
static_cast<U
>(ret) != value) {
77 const bool is_same_signedness =
78 (std::is_unsigned<T>::value && std::is_unsigned<U>::value) ||
79 (std::is_signed<T>::value && std::is_signed<U>::value);
80 if (!is_same_signedness && (ret < T{}) != (value < U{})) {
89 template <
class T,
class Deserializer>
93 #if defined(_MSC_VER) && _MSC_VER < 1910
96 return deserializer.Deserializer::template operator()<T>();
106 static_assert(std::numeric_limits<slz_size_type>::max() >=
107 std::numeric_limits<std::size_t>::max(),
108 "slz_size_type must be >= std::size_t");
116 template <
bool StoreHash>
163 template <
typename ValueType,
bool StoreHash>
186 std::is_nothrow_copy_constructible<value_type>::value)
190 if (!other.empty()) {
191 ::new (
static_cast<void*
>(std::addressof(
m_value)))
204 std::is_nothrow_move_constructible<value_type>::value)
208 if (!other.empty()) {
209 ::new (
static_cast<void*
>(std::addressof(
m_value)))
217 std::is_nothrow_copy_constructible<value_type>::value) {
218 if (
this != &other) {
221 bucket_hash::operator=(other);
222 if (!other.empty()) {
223 ::new (
static_cast<void*
>(std::addressof(
m_value)))
251 return *std::launder(
257 return *std::launder(
269 template <
typename... Args>
272 Args&&... value_type_args) {
276 ::new (
static_cast<void*
>(std::addressof(
m_value)))
277 value_type(std::forward<Args>(value_type_args)...);
310 value().~value_type();
317 std::numeric_limits<distance_type>::max() - 1,
318 "DIST_FROM_IDEAL_BUCKET_LIMIT must be <= "
319 "std::numeric_limits<distance_type>::max() - 1.");
346 template <
class ValueType,
class KeySelect,
class ValueSelect,
class Hash,
347 class KeyEqual,
class Allocator,
bool StoreHash,
class GrowthPolicy>
348 class robin_hash :
private Hash,
private KeyEqual,
private GrowthPolicy {
350 template <
typename U>
352 typename std::integral_constant<bool, !std::is_same<U, void>::value>;
355 noexcept(std::declval<GrowthPolicy>().
bucket_for_hash(std::size_t(0))),
356 "GrowthPolicy::bucket_for_hash must be noexcept.");
357 static_assert(noexcept(std::declval<GrowthPolicy>().
clear()),
358 "GrowthPolicy::clear must be noexcept.");
361 template <
bool IsConst>
391 (!std::is_arithmetic<key_type>::value ||
392 !std::is_same<Hash, std::hash<key_type>>::value));
414 std::numeric_limits<truncated_hash_type>::max();
441 template <
bool IsConst>
442 class robin_iterator {
462 template <
bool TIsConst = IsConst,
463 typename std::enable_if<TIsConst>::type* =
nullptr>
473 return KeySelect()(
m_bucket->value());
476 template <
class U = ValueSelect,
477 typename std::enable_if<has_mapped_type<U>::value &&
478 IsConst>::type* =
nullptr>
479 const typename U::value_type&
value()
const {
483 template <
class U = ValueSelect,
484 typename std::enable_if<has_mapped_type<U>::value &&
485 !IsConst>::type* =
nullptr>
486 typename U::value_type&
value()
const {
517 return lhs.m_bucket == rhs.m_bucket;
522 return !(lhs == rhs);
531 const Allocator& alloc,
546 "The map exceeds its maximum bucket count.");
574 std::is_nothrow_move_constructible<
575 Hash>::value&& std::is_nothrow_move_constructible<KeyEqual>::value&&
576 std::is_nothrow_move_constructible<GrowthPolicy>::value&&
577 std::is_nothrow_move_constructible<buckets_container_type>::value)
578 : Hash(
std::move(static_cast<Hash&>(other))),
579 KeyEqual(
std::move(static_cast<KeyEqual&>(other))),
580 GrowthPolicy(
std::move(static_cast<GrowthPolicy&>(other))),
591 other.clear_and_shrink();
595 if (&other !=
this) {
596 Hash::operator=(other);
597 KeyEqual::operator=(other);
598 GrowthPolicy::operator=(other);
619 other.clear_and_shrink();
684 template <
typename P>
686 return insert_impl(KeySelect()(value), std::forward<P>(value));
689 template <
typename P>
691 if (hint !=
cend() &&
696 return insert(std::forward<P>(value)).first;
699 template <
class InputIt>
700 void insert(InputIt first, InputIt last) {
702 std::forward_iterator_tag,
703 typename std::iterator_traits<InputIt>::iterator_category>::value) {
704 const auto nb_elements_insert = std::distance(first, last);
708 if (nb_elements_insert > 0 &&
709 nb_free_buckets <
size_type(nb_elements_insert)) {
714 for (; first != last; ++first) {
719 template <
class K,
class M>
721 auto it =
try_emplace(std::forward<K>(key), std::forward<M>(obj));
723 it.first.value() = std::forward<M>(obj);
729 template <
class K,
class M>
733 it.value() = std::forward<M>(obj);
741 template <
class... Args>
742 std::pair<iterator, bool>
emplace(Args&&... args) {
746 template <
class... Args>
751 template <
class K,
class... Args>
754 std::forward_as_tuple(std::forward<K>(key)),
755 std::forward_as_tuple(std::forward<Args>(args)...));
758 template <
class K,
class... Args>
764 return try_emplace(std::forward<K>(key), std::forward<Args>(args)...).first;
783 if (pos.m_bucket->empty()) {
799 for (
auto it = first_mutable.m_bucket; it != last_mutable.m_bucket; ++it) {
806 if (last_mutable ==
end()) {
815 std::size_t icloser_bucket =
816 static_cast<std::size_t
>(first_mutable.m_bucket -
m_buckets);
817 std::size_t ito_move_closer_value =
818 static_cast<std::size_t
>(last_mutable.m_bucket -
m_buckets);
821 const std::size_t ireturn_bucket =
822 ito_move_closer_value -
824 ito_move_closer_value - icloser_bucket,
826 m_buckets[ito_move_closer_value].dist_from_ideal_bucket()));
829 m_buckets[ito_move_closer_value].dist_from_ideal_bucket() > 0) {
831 ito_move_closer_value -
833 ito_move_closer_value - icloser_bucket,
835 m_buckets[ito_move_closer_value].dist_from_ideal_bucket()));
839 m_buckets[ito_move_closer_value].dist_from_ideal_bucket() -
840 (ito_move_closer_value - icloser_bucket));
842 new_distance,
m_buckets[ito_move_closer_value].truncated_hash(),
843 std::move(
m_buckets[ito_move_closer_value].value()));
847 ++ito_move_closer_value;
862 auto it =
find(key, hash);
874 swap(
static_cast<Hash&
>(*
this),
static_cast<Hash&
>(other));
875 swap(
static_cast<KeyEqual&
>(*
this),
static_cast<KeyEqual&
>(other));
876 swap(
static_cast<GrowthPolicy&
>(*
this),
static_cast<GrowthPolicy&
>(other));
891 template <
class K,
class U = ValueSelect,
892 typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
893 typename U::value_type&
at(
const K& key) {
897 template <
class K,
class U = ValueSelect,
898 typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
899 typename U::value_type&
at(
const K& key, std::size_t hash) {
900 return const_cast<typename U::value_type&
>(
904 template <
class K,
class U = ValueSelect,
905 typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
906 const typename U::value_type&
at(
const K& key)
const {
910 template <
class K,
class U = ValueSelect,
911 typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
912 const typename U::value_type&
at(
const K& key, std::size_t hash)
const {
913 auto it =
find(key, hash);
921 template <
class K,
class U = ValueSelect,
922 typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
924 return try_emplace(std::forward<K>(key)).first.value();
967 bool contains(
const K& key, std::size_t hash)
const {
968 return count(key, hash) != 0;
977 std::pair<iterator, iterator>
equal_range(
const K& key, std::size_t hash) {
979 return std::make_pair(it, (it ==
end()) ? it : std::next(it));
983 std::pair<const_iterator, const_iterator>
equal_range(
const K& key)
const {
989 const K& key, std::size_t hash)
const {
991 return std::make_pair(it, (it ==
cend()) ? it : std::next(it));
1000 return std::min(GrowthPolicy::max_bucket_count(),
1032 count_ = std::max(count_,
1055 template <
class Serializer>
1060 template <
class Deserializer>
1068 return Hash::operator()(key);
1071 template <
class K1,
class K2>
1073 return KeyEqual::operator()(key1, key2);
1077 const std::size_t bucket = GrowthPolicy::bucket_for_hash(hash);
1084 template <
class U = GrowthPolicy,
1085 typename std::enable_if<is_power_of_two_policy<U>::value>::type* =
1090 return (index + 1) & this->m_mask;
1093 template <
class U = GrowthPolicy,
1094 typename std::enable_if<!is_power_of_two_policy<U>::value>::type* =
1114 while (dist_from_ideal_bucket <=
1115 m_buckets[ibucket].dist_from_ideal_bucket()) {
1118 m_buckets[ibucket].bucket_hash_equal(hash)) &&
1124 dist_from_ideal_bucket++;
1131 pos.m_bucket->clear();
1141 std::size_t previous_ibucket =
1142 static_cast<std::size_t
>(pos.m_bucket -
m_buckets);
1143 std::size_t ibucket =
next_bucket(previous_ibucket);
1145 while (
m_buckets[ibucket].dist_from_ideal_bucket() > 0) {
1151 new_distance,
m_buckets[ibucket].truncated_hash(),
1155 previous_ibucket = ibucket;
1161 template <
class K,
class... Args>
1163 Args&&... value_type_args) {
1164 const std::size_t hash =
hash_key(key);
1169 while (dist_from_ideal_bucket <=
1170 m_buckets[ibucket].dist_from_ideal_bucket()) {
1172 m_buckets[ibucket].bucket_hash_equal(hash)) &&
1178 dist_from_ideal_bucket++;
1183 dist_from_ideal_bucket = 0;
1185 while (dist_from_ideal_bucket <=
1186 m_buckets[ibucket].dist_from_ideal_bucket()) {
1188 dist_from_ideal_bucket++;
1195 std::forward<Args>(value_type_args)...);
1199 std::forward<Args>(value_type_args)...);
1210 template <
class... Args>
1213 value_type value(std::forward<Args>(value_type_args)...);
1234 m_buckets[ibucket].dist_from_ideal_bucket());
1238 dist_from_ideal_bucket++;
1241 if (dist_from_ideal_bucket >
1242 m_buckets[ibucket].dist_from_ideal_bucket()) {
1243 if (dist_from_ideal_bucket >
1257 dist_from_ideal_bucket++;
1265 robin_hash new_table(count_,
static_cast<Hash&
>(*
this),
1270 const bool use_stored_hash =
1273 if (bucket.empty()) {
1277 const std::size_t hash =
1278 use_stored_hash ? bucket.truncated_hash()
1279 : new_table.
hash_key(KeySelect()(bucket.value()));
1283 std::move(bucket.value()));
1287 new_table.
swap(*
this);
1291 GrowthPolicy::clear();
1305 if (dist_from_ideal_bucket >
1306 m_buckets[ibucket].dist_from_ideal_bucket()) {
1309 hash, std::move(value));
1317 dist_from_ideal_bucket++;
1331 curr_dist_from_ideal_bucket >
1352 template <
class Serializer>
1360 const std::int16_t hash_stored_for_bucket =
1377 if (bucket.empty()) {
1378 const std::int16_t empty_bucket =
1382 const std::int16_t dist_from_ideal_bucket =
1383 bucket.dist_from_ideal_bucket();
1386 const std::uint32_t truncated_hash = bucket.truncated_hash();
1394 template <
class Deserializer>
1404 "Can't deserialize the ordered_map/set. "
1405 "The protocol version header is invalid.");
1408 const bool hash_stored_for_bucket =
1409 deserialize_value<std::int16_t>(
deserializer) ? true :
false;
1410 if (hash_compatible &&
STORE_HASH != hash_stored_for_bucket) {
1413 "Can't deserialize a map with a different StoreHash "
1414 "than the one used during the serialization when "
1415 "hash compatibility is used");
1429 "Invalid min_load_factor. Check that the serializer "
1430 "and deserializer support floats correctly as they "
1431 "can be converted implicitly to ints.");
1438 "Invalid max_load_factor. Check that the serializer "
1439 "and deserializer support floats correctly as they "
1440 "can be converted implicitly to ints.");
1446 if (bucket_count_ds == 0) {
1451 if (!hash_compatible) {
1452 reserve(numeric_cast<size_type>(nb_elements,
1453 "Deserialized nb_elements is too big."));
1454 for (
slz_size_type ibucket = 0; ibucket < bucket_count_ds; ibucket++) {
1457 if (dist_from_ideal_bucket !=
1459 if (hash_stored_for_bucket) {
1470 bucket_count_ds,
"Deserialized bucket_count is too big.");
1480 "The GrowthPolicy is not the same even "
1481 "though hash_compatible is true.");
1485 nb_elements,
"Deserialized nb_elements is too big.");
1492 if (dist_from_ideal_bucket !=
1495 if (hash_stored_for_bucket) {
1497 truncated_hash = deserialize_value<std::uint32_t>(
deserializer);
1500 bucket.set_value_of_empty_bucket(
1501 dist_from_ideal_bucket, truncated_hash,
1524 "MINIMUM_MAX_LOAD_FACTOR should be < MAXIMUM_MAX_LOAD_FACTOR");
1526 "MINIMUM_MIN_LOAD_FACTOR should be < MAXIMUM_MIN_LOAD_FACTOR");
1528 "MAXIMUM_MIN_LOAD_FACTOR should be < MINIMUM_MAX_LOAD_FACTOR");
1543 return &empty_bucket;