24 #ifndef TSL_ARRAY_HASH_H 25 #define TSL_ARRAY_HASH_H 38 #include <type_traits> 49 # if __has_include
(<string_view>) && __cplusplus
>= 201703L
50 # define TSL_AH_HAS_STRING_VIEW 55 #ifdef TSL_AH_HAS_STRING_VIEW 56 # include <string_view> 61 # define tsl_ah_assert(expr) assert(expr) 63 # define tsl_ah_assert(expr) (static_cast<void>(0
)) 78 #ifdef TSL_AH_HAS_STRING_VIEW 86 std::size_t operator()(
const CharT* key, std::size_t key_size)
const {
87 static const std::size_t init = std::size_t((
sizeof(std::size_t) == 8)?0xcbf29ce484222325:0x811c9dc5);
88 static const std::size_t multiplier = std::size_t((
sizeof(std::size_t) == 8)?0x100000001b3:0x1000193);
90 std::size_t hash = init;
91 for (std::size_t i = 0; i < key_size; ++i) {
101 template<
class CharT>
103 bool operator()(
const CharT* key_lhs, std::size_t key_size_lhs,
104 const CharT* key_rhs, std::size_t key_size_rhs)
const 106 if(key_size_lhs != key_size_rhs) {
110 return std::memcmp(key_lhs, key_rhs, key_size_lhs *
sizeof(CharT)) == 0;
119 template<
typename T,
typename =
void>
120 struct is_iterator: std::false_type {
124 struct is_iterator<T,
typename std::enable_if<!std::is_same<
typename std::iterator_traits<T>::iterator_category,
void>::value>::type>: std::true_type {
127 static constexpr bool is_power_of_two(std::size_t value) {
128 return value != 0 && (value & (value - 1)) == 0;
137 using slz_size_type = std::uint64_t;
139 template<
class T,
class Deserializer>
140 static T deserialize_value(Deserializer& deserializer) {
142 #if defined (_MSC_VER) && _MSC_VER < 1910
143 return deserializer.Deserializer::operator()<T>();
145 return deserializer.Deserializer::
template operator()<T>();
165 template<
class CharT,
169 bool StoreNullTerminator>
172 using has_mapped_type =
typename std::integral_constant<
bool, !std::is_same<U,
void>::value>;
174 static_assert(!has_mapped_type<T>::value || std::is_unsigned<T>::value,
175 "T should be either void or an unsigned type.");
177 static_assert(std::is_unsigned<KeySizeT>::value,
"KeySizeT should be an unsigned type.");
180 template<
bool IsConst>
181 class array_bucket_iterator;
183 using char_type = CharT;
184 using key_size_type = KeySizeT;
185 using mapped_type = T;
186 using size_type = std::size_t;
187 using key_equal = KeyEqual;
188 using iterator = array_bucket_iterator<
false>;
189 using const_iterator = array_bucket_iterator<
true>;
191 static_assert(
sizeof(KeySizeT) <=
sizeof(size_type),
"sizeof(KeySizeT) should be <= sizeof(std::size_t;)");
192 static_assert(std::is_unsigned<size_type>::value,
"");
202 static constexpr size_type sizeof_in_buff()
noexcept {
203 static_assert(is_power_of_two(
sizeof(U)),
"sizeof(U) should be a power of two.");
204 static_assert(is_power_of_two(
sizeof(CharT)),
"sizeof(CharT) should be a power of two.");
206 return std::max(
sizeof(U),
sizeof(CharT));
213 static constexpr size_type size_as_char_t()
noexcept {
214 return sizeof_in_buff<U>() /
sizeof(CharT);
217 static key_size_type read_key_size(
const CharT* buffer)
noexcept {
218 key_size_type key_size;
219 std::memcpy(&key_size, buffer,
sizeof(key_size));
224 static mapped_type read_value(
const CharT* buffer)
noexcept {
226 std::memcpy(&value, buffer,
sizeof(value));
231 static bool is_end_of_bucket(
const CharT* buffer)
noexcept {
232 return read_key_size(buffer) == END_OF_BUCKET;
239 template<
class U = T,
typename std::enable_if<!has_mapped_type<U>::value>::type* =
nullptr>
240 static size_type entry_required_bytes(size_type key_size)
noexcept {
241 return sizeof_in_buff<key_size_type>() + (key_size + KEY_EXTRA_SIZE) *
sizeof(CharT);
244 template<
class U = T,
typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
245 static size_type entry_required_bytes(size_type key_size)
noexcept {
246 return sizeof_in_buff<key_size_type>() + (key_size + KEY_EXTRA_SIZE) *
sizeof(CharT) +
247 sizeof_in_buff<mapped_type>();
254 static size_type entry_size_bytes(
const CharT* buffer)
noexcept {
255 return entry_required_bytes(read_key_size(buffer));
259 template<
bool IsConst>
260 class array_bucket_iterator {
261 friend class array_bucket;
263 using buffer_type =
typename std::conditional<IsConst,
const CharT, CharT>::type;
265 explicit array_bucket_iterator(buffer_type* position)
noexcept: m_position(position) {
269 using iterator_category = std::forward_iterator_tag;
270 using value_type =
void;
271 using difference_type = std::ptrdiff_t;
272 using reference =
void;
273 using pointer =
void;
276 array_bucket_iterator()
noexcept: m_position(
nullptr) {
279 const CharT* key()
const {
280 return m_position + size_as_char_t<key_size_type>();
283 size_type key_size()
const {
284 return read_key_size(m_position);
287 template<
class U = T,
typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
289 return read_value(m_position + size_as_char_t<key_size_type>() + key_size() + KEY_EXTRA_SIZE);
293 template<
class U = T,
typename std::enable_if<has_mapped_type<U>::value && !IsConst && std::is_same<U, T>::value>::type* =
nullptr>
294 void set_value(U value)
noexcept {
295 std::memcpy(m_position + size_as_char_t<key_size_type>() + key_size() + KEY_EXTRA_SIZE,
296 &value,
sizeof(value));
299 array_bucket_iterator& operator++() {
300 m_position += entry_size_bytes(m_position)/
sizeof(CharT);
301 if(is_end_of_bucket(m_position)) {
302 m_position =
nullptr;
308 array_bucket_iterator operator++(
int) {
309 array_bucket_iterator tmp(*
this);
315 friend bool operator==(
const array_bucket_iterator& lhs,
const array_bucket_iterator& rhs) {
316 return lhs.m_position == rhs.m_position;
319 friend bool operator!=(
const array_bucket_iterator& lhs,
const array_bucket_iterator& rhs) {
320 return !(lhs == rhs);
324 buffer_type* m_position;
329 static iterator end_it()
noexcept {
330 return iterator(
nullptr);
333 static const_iterator cend_it()
noexcept {
334 return const_iterator(
nullptr);
338 array_bucket(): m_buffer(
nullptr) {
344 array_bucket(std::size_t size): m_buffer(
nullptr) {
349 m_buffer =
static_cast<CharT*>(std::malloc(size*
sizeof(CharT) + sizeof_in_buff<
decltype(END_OF_BUCKET)>()));
350 if(m_buffer ==
nullptr) {
351 throw std::bad_alloc();
354 const auto end_of_bucket = END_OF_BUCKET;
355 std::memcpy(m_buffer, &end_of_bucket,
sizeof(end_of_bucket));
362 array_bucket(
const array_bucket& other) {
363 if(other.m_buffer ==
nullptr) {
368 const size_type other_buffer_size = other.size();
369 m_buffer =
static_cast<CharT*>(std::malloc(other_buffer_size*
sizeof(CharT) + sizeof_in_buff<
decltype(END_OF_BUCKET)>()));
370 if(m_buffer ==
nullptr) {
371 throw std::bad_alloc();
374 std::memcpy(m_buffer, other.m_buffer, other_buffer_size*
sizeof(CharT));
376 const auto end_of_bucket = END_OF_BUCKET;
377 std::memcpy(m_buffer + other_buffer_size, &end_of_bucket,
sizeof(end_of_bucket));
380 array_bucket(array_bucket&& other)
noexcept: m_buffer(other.m_buffer) {
381 other.m_buffer =
nullptr;
384 array_bucket& operator=(array_bucket other)
noexcept {
390 void swap(array_bucket& other)
noexcept {
391 std::swap(m_buffer, other.m_buffer);
394 iterator begin()
noexcept {
return iterator(m_buffer); }
395 iterator end()
noexcept {
return iterator(
nullptr); }
396 const_iterator begin()
const noexcept {
return cbegin(); }
397 const_iterator end()
const noexcept {
return cend(); }
398 const_iterator cbegin()
const noexcept {
return const_iterator(m_buffer); }
399 const_iterator cend()
const noexcept {
return const_iterator(
nullptr); }
407 std::pair<const_iterator,
bool> find_or_end_of_bucket(
const CharT* key, size_type key_size)
const noexcept {
408 if(m_buffer ==
nullptr) {
409 return std::make_pair(cend(),
false);
412 const CharT* buffer_ptr_in_out = m_buffer;
413 const bool found = find_or_end_of_bucket_impl(key, key_size, buffer_ptr_in_out);
415 return std::make_pair(const_iterator(buffer_ptr_in_out), found);
425 template<
class... ValueArgs>
426 const_iterator append(const_iterator end_of_bucket,
const CharT* key, size_type key_size,
427 ValueArgs&&... value)
429 const key_size_type key_sz = as_key_size_type(key_size);
431 if(end_of_bucket == cend()) {
434 const size_type buffer_size = entry_required_bytes(key_sz) + sizeof_in_buff<
decltype(END_OF_BUCKET)>();
436 m_buffer =
static_cast<CharT*>(std::malloc(buffer_size));
437 if(m_buffer ==
nullptr) {
438 throw std::bad_alloc();
441 append_impl(key, key_sz, m_buffer, std::forward<ValueArgs>(value)...);
443 return const_iterator(m_buffer);
448 const size_type current_size = ((end_of_bucket.m_position + size_as_char_t<
decltype(END_OF_BUCKET)>()) -
449 m_buffer) *
sizeof(CharT);
450 const size_type new_size = current_size + entry_required_bytes(key_sz);
453 CharT* new_buffer =
static_cast<CharT*>(std::realloc(m_buffer, new_size));
454 if(new_buffer ==
nullptr) {
455 throw std::bad_alloc();
457 m_buffer = new_buffer;
460 CharT* buffer_append_pos = m_buffer + current_size /
sizeof(CharT) -
461 size_as_char_t<
decltype(END_OF_BUCKET)>();
462 append_impl(key, key_sz, buffer_append_pos, std::forward<ValueArgs>(value)...);
464 return const_iterator(buffer_append_pos);
469 const_iterator erase(const_iterator position)
noexcept {
470 tsl_ah_assert(position.m_position !=
nullptr && !is_end_of_bucket(position.m_position));
473 CharT* start_entry = m_buffer + (position.m_position - m_buffer);
474 CharT* start_next_entry = start_entry + entry_size_bytes(start_entry) /
sizeof(CharT);
477 CharT* end_buffer_ptr = start_next_entry;
478 while(!is_end_of_bucket(end_buffer_ptr)) {
479 end_buffer_ptr += entry_size_bytes(end_buffer_ptr) /
sizeof(CharT);
481 end_buffer_ptr += size_as_char_t<
decltype(END_OF_BUCKET)>();
484 const size_type size_to_move = (end_buffer_ptr - start_next_entry) *
sizeof(CharT);
485 std::memmove(start_entry, start_next_entry, size_to_move);
488 if(is_end_of_bucket(m_buffer)) {
492 else if(is_end_of_bucket(start_entry)) {
496 return const_iterator(start_entry);
503 bool erase(
const CharT* key, size_type key_size)
noexcept {
504 if(m_buffer ==
nullptr) {
508 const CharT* entry_buffer_ptr_in_out = m_buffer;
509 bool found = find_or_end_of_bucket_impl(key, key_size, entry_buffer_ptr_in_out);
511 erase(const_iterator(entry_buffer_ptr_in_out));
524 template<
class... ValueArgs>
525 void append_in_reserved_bucket_no_check(
const CharT* key, size_type key_size, ValueArgs&&... value)
noexcept {
526 CharT* buffer_ptr = m_buffer;
527 while(!is_end_of_bucket(buffer_ptr)) {
528 buffer_ptr += entry_size_bytes(buffer_ptr)/
sizeof(CharT);
531 append_impl(key, key_size_type(key_size), buffer_ptr, std::forward<ValueArgs>(value)...);
534 bool empty()
const noexcept {
535 return m_buffer ==
nullptr || is_end_of_bucket(m_buffer);
538 void clear()
noexcept {
543 iterator mutable_iterator(const_iterator pos)
noexcept {
544 return iterator(m_buffer + (pos.m_position - m_buffer));
547 template<
class Serializer>
548 void serialize(Serializer& serializer)
const {
549 const slz_size_type bucket_size = size();
552 serializer(bucket_size);
553 serializer(m_buffer, bucket_size);
556 template<
class Deserializer>
557 static array_bucket deserialize(Deserializer& deserializer) {
559 const slz_size_type bucket_size_ds = deserialize_value<slz_size_type>(deserializer);
561 if(bucket_size_ds == 0) {
566 if(bucket_size_ds > std::numeric_limits<std::size_t>::max()) {
567 throw std::runtime_error(
"Deserialized bucket_size is bigger than the max value of std::size_t on the current platform.");
570 const std::size_t bucket_size =
static_cast<std::size_t>(bucket_size_ds);
571 bucket.m_buffer =
static_cast<CharT*>(std::malloc(bucket_size*
sizeof(CharT) + sizeof_in_buff<
decltype(END_OF_BUCKET)>()));
572 if(bucket.m_buffer ==
nullptr) {
573 throw std::bad_alloc();
577 deserializer(bucket.m_buffer, bucket_size);
579 const auto end_of_bucket = END_OF_BUCKET;
580 std::memcpy(bucket.m_buffer + bucket_size, &end_of_bucket,
sizeof(end_of_bucket));
588 key_size_type as_key_size_type(size_type key_size)
const {
589 if(key_size > MAX_KEY_SIZE) {
590 throw std::length_error(
"Key is too long.");
593 return key_size_type(key_size);
603 bool find_or_end_of_bucket_impl(
const CharT* key, size_type key_size,
604 const CharT* & buffer_ptr_in_out)
const noexcept 606 while(!is_end_of_bucket(buffer_ptr_in_out)) {
607 const key_size_type buffer_key_size = read_key_size(buffer_ptr_in_out);
608 const CharT* buffer_str = buffer_ptr_in_out + size_as_char_t<key_size_type>();
609 if(KeyEqual()(buffer_str, buffer_key_size, key, key_size)) {
613 buffer_ptr_in_out += entry_size_bytes(buffer_ptr_in_out)/
sizeof(CharT);
619 template<
typename U = T,
typename std::enable_if<!has_mapped_type<U>::value>::type* =
nullptr>
620 void append_impl(
const CharT* key, key_size_type key_size, CharT* buffer_append_pos)
noexcept {
621 std::memcpy(buffer_append_pos, &key_size,
sizeof(key_size));
622 buffer_append_pos += size_as_char_t<key_size_type>();
624 std::memcpy(buffer_append_pos, key, key_size *
sizeof(CharT));
625 buffer_append_pos += key_size;
627 const CharT zero = 0;
628 std::memcpy(buffer_append_pos, &zero, KEY_EXTRA_SIZE *
sizeof(CharT));
629 buffer_append_pos += KEY_EXTRA_SIZE;
631 const auto end_of_bucket = END_OF_BUCKET;
632 std::memcpy(buffer_append_pos, &end_of_bucket,
sizeof(end_of_bucket));
635 template<
typename U = T,
typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
636 void append_impl(
const CharT* key, key_size_type key_size, CharT* buffer_append_pos,
637 typename array_bucket<CharT, U, KeyEqual, KeySizeT, StoreNullTerminator>::mapped_type value)
noexcept 639 std::memcpy(buffer_append_pos, &key_size,
sizeof(key_size));
640 buffer_append_pos += size_as_char_t<key_size_type>();
642 std::memcpy(buffer_append_pos, key, key_size *
sizeof(CharT));
643 buffer_append_pos += key_size;
645 const CharT zero = 0;
646 std::memcpy(buffer_append_pos, &zero, KEY_EXTRA_SIZE *
sizeof(CharT));
647 buffer_append_pos += KEY_EXTRA_SIZE;
649 std::memcpy(buffer_append_pos, &value,
sizeof(value));
650 buffer_append_pos += size_as_char_t<mapped_type>();
652 const auto end_of_bucket = END_OF_BUCKET;
653 std::memcpy(buffer_append_pos, &end_of_bucket,
sizeof(end_of_bucket));
660 size_type size()
const noexcept {
661 if(m_buffer ==
nullptr) {
665 CharT* buffer_ptr = m_buffer;
666 while(!is_end_of_bucket(buffer_ptr)) {
667 buffer_ptr += entry_size_bytes(buffer_ptr)/
sizeof(CharT);
670 return buffer_ptr - m_buffer;
674 static const key_size_type END_OF_BUCKET = std::numeric_limits<key_size_type>::max();
675 static const key_size_type KEY_EXTRA_SIZE = StoreNullTerminator?1:0;
680 static const key_size_type MAX_KEY_SIZE =
682 key_size_type(std::numeric_limits<key_size_type>::max() - KEY_EXTRA_SIZE - 1);
687 class value_container {
689 void clear()
noexcept {
693 void reserve(std::size_t new_cap) {
694 m_values.reserve(new_cap);
697 void shrink_to_fit() {
698 m_values.shrink_to_fit();
701 friend void swap(value_container& lhs, value_container& rhs) {
702 lhs.m_values.swap(rhs.m_values);
706 static constexpr float VECTOR_GROWTH_RATE = 1.5f;
709 std::vector<T> m_values;
713 class value_container<
void> {
715 void clear()
noexcept {
718 void shrink_to_fit() {
721 void reserve(std::size_t ) {
734 template<
class CharT,
738 bool StoreNullTerminator,
742 class array_hash:
private value_container<T>,
private Hash,
private GrowthPolicy {
745 using has_mapped_type =
typename std::integral_constant<
bool, !std::is_same<U,
void>::value>;
752 typename std::conditional<has_mapped_type<T>::value,
755 KeyEqual, KeySizeT, StoreNullTerminator>;
758 template<
bool IsConst>
761 using char_type = CharT;
762 using key_size_type = KeySizeT;
763 using index_size_type = IndexSizeT;
764 using size_type = std::size_t;
766 using key_equal = KeyEqual;
775 template<
bool IsConst>
780 using iterator_array_bucket =
typename array_bucket::const_iterator;
782 using iterator_buckets =
typename std::conditional<IsConst,
783 typename std::vector<array_bucket>::const_iterator,
784 typename std::vector<array_bucket>::iterator>::type;
786 using array_hash_ptr =
typename std::conditional<IsConst,
791 using iterator_category = std::forward_iterator_tag;
792 using value_type =
typename std::conditional<has_mapped_type<T>::value, T,
void>::type;
793 using difference_type = std::ptrdiff_t;
794 using reference =
typename std::conditional<has_mapped_type<T>::value,
795 typename std::conditional<
797 typename std::add_lvalue_reference<
const T>::type,
798 typename std::add_lvalue_reference<T>::type>::type,
800 using pointer =
typename std::conditional<has_mapped_type<T>::value,
801 typename std::conditional<IsConst,
const T*, T*>::type,
806 array_hash_iterator(iterator_buckets buckets_iterator, iterator_array_bucket array_bucket_iterator,
807 array_hash_ptr array_hash_p)
noexcept:
808 m_buckets_iterator(buckets_iterator),
809 m_array_bucket_iterator(array_bucket_iterator),
810 m_array_hash(array_hash_p)
820 m_buckets_iterator(other.m_buckets_iterator),
821 m_array_bucket_iterator(other.m_array_bucket_iterator),
822 m_array_hash(other.m_array_hash)
826 const CharT*
key()
const {
827 return m_array_bucket_iterator.key();
831 return m_array_bucket_iterator.key_size();
834 #ifdef TSL_AH_HAS_STRING_VIEW 840 template<
class U = T,
typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
842 return this->m_array_hash->m_values[value_position()];
845 template<
class U = T,
typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
850 template<
class U = T,
typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
852 return std::addressof(value());
856 tsl_ah_assert(m_buckets_iterator != m_array_hash->m_buckets.end());
857 tsl_ah_assert(m_array_bucket_iterator != m_buckets_iterator->cend());
859 ++m_array_bucket_iterator;
860 if(m_array_bucket_iterator == m_buckets_iterator->cend()) {
862 ++m_buckets_iterator;
863 }
while(m_buckets_iterator != m_array_hash->m_buckets.end() &&
864 m_buckets_iterator->empty());
866 if(m_buckets_iterator != m_array_hash->m_buckets.end()) {
867 m_array_bucket_iterator = m_buckets_iterator->cbegin();
882 return lhs.m_buckets_iterator == rhs.m_buckets_iterator &&
883 lhs.m_array_bucket_iterator == rhs.m_array_bucket_iterator &&
884 lhs.m_array_hash == rhs.m_array_hash;
888 return !(lhs == rhs);
892 template<
class U = T,
typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
893 IndexSizeT value_position()
const {
894 return this->m_array_bucket_iterator.value();
898 iterator_buckets m_buckets_iterator;
899 iterator_array_bucket m_array_bucket_iterator;
901 array_hash_ptr m_array_hash;
909 float max_load_factor): value_container<T>(),
911 GrowthPolicy(bucket_count),
913 throw std::length_error(
"The map exceeds its maxmimum bucket count."):
915 m_first_or_empty_bucket(m_buckets.empty()?static_empty_bucket_ptr():m_buckets.data()),
918 this->max_load_factor(max_load_factor);
924 m_buckets(other.m_buckets),
925 m_first_or_empty_bucket(m_buckets.empty()?static_empty_bucket_ptr():m_buckets.data()),
926 m_nb_elements(other.m_nb_elements),
927 m_max_load_factor(other.m_max_load_factor),
928 m_load_threshold(other.m_load_threshold)
936 : value_container<T>(std::move(other)),
937 Hash(std::move(other)),
938 GrowthPolicy(std::move(other)),
939 m_buckets(std::move(other.m_buckets)),
940 m_first_or_empty_bucket(m_buckets.empty()?static_empty_bucket_ptr():m_buckets.data()),
941 m_nb_elements(other.m_nb_elements),
942 m_max_load_factor(other.m_max_load_factor),
943 m_load_threshold(other.m_load_threshold)
945 other.value_container<T>::clear();
946 other.GrowthPolicy::clear();
947 other.m_buckets.clear();
948 other.m_first_or_empty_bucket = static_empty_bucket_ptr();
949 other.m_nb_elements = 0;
950 other.m_load_threshold = 0;
955 value_container<T>::operator=(other);
956 Hash::operator=(other);
957 GrowthPolicy::operator=(other);
959 m_buckets = other.m_buckets;
960 m_first_or_empty_bucket = m_buckets.empty()?static_empty_bucket_ptr():
962 m_nb_elements = other.m_nb_elements;
963 m_max_load_factor = other.m_max_load_factor;
964 m_load_threshold = other.m_load_threshold;
982 auto begin = m_buckets.begin();
983 while(begin != m_buckets.end() && begin->empty()) {
987 return (begin != m_buckets.end())?iterator(begin, begin->cbegin(),
this):end();
990 const_iterator
begin()
const noexcept {
995 auto begin = m_buckets.cbegin();
996 while(begin != m_buckets.cend() && begin->empty()) {
1000 return (begin != m_buckets.cend())?const_iterator(begin, begin->cbegin(),
this):
cend();
1004 return iterator(m_buckets.end(), array_bucket::cend_it(),
this);
1007 const_iterator
end()
const noexcept {
1011 const_iterator
cend()
const noexcept {
1012 return const_iterator(m_buckets.end(), array_bucket::cend_it(),
this);
1020 return m_nb_elements == 0;
1024 return m_nb_elements;
1028 return std::numeric_limits<IndexSizeT>::max();
1036 clear_old_erased_values();
1037 value_container<T>::shrink_to_fit();
1039 rehash_impl(size_type(std::ceil(
float(
size())/max_load_factor())));
1046 value_container<T>::clear();
1048 for(
auto& bucket : m_buckets) {
1057 template<
class... ValueArgs>
1058 std::pair<iterator,
bool>
emplace(
const CharT* key, size_type key_size, ValueArgs&&... value_args) {
1059 const std::size_t hash = hash_key(key, key_size);
1060 std::size_t ibucket = bucket_for_hash(hash);
1062 auto it_find = m_first_or_empty_bucket[ibucket].find_or_end_of_bucket(key, key_size);
1063 if(it_find.second) {
1064 return std::make_pair(iterator(m_buckets.begin() + ibucket, it_find.first,
this),
false);
1067 if(grow_on_high_load()) {
1068 ibucket = bucket_for_hash(hash);
1069 it_find = m_first_or_empty_bucket[ibucket].find_or_end_of_bucket(key, key_size);
1072 return emplace_impl(ibucket, it_find.first, key, key_size, std::forward<ValueArgs>(value_args)...);
1077 auto it = emplace(key, key_size, std::forward<M>(obj));
1079 it.first.value() = std::forward<M>(obj);
1088 if(shoud_clear_old_erased_values()) {
1089 clear_old_erased_values();
1095 iterator
erase(const_iterator first, const_iterator last) {
1109 while(to_delete.m_buckets_iterator != last.m_buckets_iterator) {
1110 to_delete = erase_from_bucket(to_delete);
1113 std::size_t nb_elements_until_last = std::distance(to_delete.m_array_bucket_iterator,
1114 last.m_array_bucket_iterator);
1115 while(nb_elements_until_last > 0) {
1116 to_delete = erase_from_bucket(to_delete);
1117 nb_elements_until_last--;
1120 if(shoud_clear_old_erased_values()) {
1121 clear_old_erased_values();
1129 size_type
erase(
const CharT* key, size_type key_size) {
1130 return erase(key, key_size, hash_key(key, key_size));
1133 size_type
erase(
const CharT* key, size_type key_size, std::size_t hash) {
1134 if(shoud_clear_old_erased_values()) {
1135 clear_old_erased_values();
1138 const std::size_t ibucket = bucket_for_hash(hash);
1139 if(m_first_or_empty_bucket[ibucket].erase(key, key_size)) {
1153 swap(
static_cast<value_container<T>&>(*
this),
static_cast<value_container<T>&>(other));
1154 swap(
static_cast<Hash&>(*
this),
static_cast<Hash&>(other));
1155 swap(
static_cast<GrowthPolicy&>(*
this),
static_cast<GrowthPolicy&>(other));
1156 swap(m_buckets, other.m_buckets);
1157 swap(m_first_or_empty_bucket, other.m_first_or_empty_bucket);
1158 swap(m_nb_elements, other.m_nb_elements);
1159 swap(m_max_load_factor, other.m_max_load_factor);
1160 swap(m_load_threshold, other.m_load_threshold);
1166 template<
class U = T,
typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
1167 U&
at(
const CharT* key, size_type key_size) {
1168 return at(key, key_size, hash_key(key, key_size));
1171 template<
class U = T,
typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
1172 const U&
at(
const CharT* key, size_type key_size)
const {
1173 return at(key, key_size, hash_key(key, key_size));
1176 template<
class U = T,
typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
1177 U&
at(
const CharT* key, size_type key_size, std::size_t hash) {
1178 return const_cast<U&>(
static_cast<
const array_hash*>(
this)->at(key, key_size, hash));
1181 template<
class U = T,
typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
1182 const U&
at(
const CharT* key, size_type key_size, std::size_t hash)
const {
1183 const std::size_t ibucket = bucket_for_hash(hash);
1185 auto it_find = m_first_or_empty_bucket[ibucket].find_or_end_of_bucket(key, key_size);
1186 if(it_find.second) {
1187 return this->m_values[it_find.first.value()];
1190 throw std::out_of_range(
"Couldn't find key.");
1196 template<
class U = T,
typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
1198 const std::size_t hash = hash_key(key, key_size);
1199 std::size_t ibucket = bucket_for_hash(hash);
1201 auto it_find = m_first_or_empty_bucket[ibucket].find_or_end_of_bucket(key, key_size);
1202 if(it_find.second) {
1203 return this->m_values[it_find.first.value()];
1206 if(grow_on_high_load()) {
1207 ibucket = bucket_for_hash(hash);
1208 it_find = m_buckets[ibucket].find_or_end_of_bucket(key, key_size);
1211 return emplace_impl(ibucket, it_find.first, key, key_size, U{}).first.value();
1217 size_type
count(
const CharT* key, size_type key_size)
const {
1218 return count(key, key_size, hash_key(key, key_size));
1221 size_type
count(
const CharT* key, size_type key_size, std::size_t hash)
const {
1222 const std::size_t ibucket = bucket_for_hash(hash);
1224 auto it_find = m_first_or_empty_bucket[ibucket].find_or_end_of_bucket(key, key_size);
1225 if(it_find.second) {
1235 iterator
find(
const CharT* key, size_type key_size) {
1236 return find(key, key_size, hash_key(key, key_size));
1239 const_iterator
find(
const CharT* key, size_type key_size)
const {
1240 return find(key, key_size, hash_key(key, key_size));
1243 iterator
find(
const CharT* key, size_type key_size, std::size_t hash) {
1244 const std::size_t ibucket = bucket_for_hash(hash);
1246 auto it_find = m_first_or_empty_bucket[ibucket].find_or_end_of_bucket(key, key_size);
1247 if(it_find.second) {
1248 return iterator(m_buckets.begin() + ibucket, it_find.first,
this);
1255 const_iterator
find(
const CharT* key, size_type key_size, std::size_t hash)
const {
1256 const std::size_t ibucket = bucket_for_hash(hash);
1258 auto it_find = m_first_or_empty_bucket[ibucket].find_or_end_of_bucket(key, key_size);
1259 if(it_find.second) {
1260 return const_iterator(m_buckets.cbegin() + ibucket, it_find.first,
this);
1269 std::pair<iterator, iterator>
equal_range(
const CharT* key, size_type key_size) {
1270 return equal_range(key, key_size, hash_key(key, key_size));
1273 std::pair<const_iterator, const_iterator>
equal_range(
const CharT* key, size_type key_size)
const {
1274 return equal_range(key, key_size, hash_key(key, key_size));
1277 std::pair<iterator, iterator>
equal_range(
const CharT* key, size_type key_size, std::size_t hash) {
1278 iterator it = find(key, key_size, hash);
1279 return std::make_pair(it, (it == end())?it:std::next(it));
1282 std::pair<const_iterator, const_iterator>
equal_range(
const CharT* key, size_type key_size,
1283 std::size_t hash)
const 1285 const_iterator it = find(key, key_size, hash);
1286 return std::make_pair(it, (it ==
cend())?it:std::next(it));
1293 return m_buckets.size();
1297 return std::min(GrowthPolicy::max_bucket_count(), m_buckets.max_size());
1309 return m_max_load_factor;
1313 m_max_load_factor = std::max(0.1f, ml);
1314 m_load_threshold = size_type(
float(
bucket_count())*m_max_load_factor);
1318 count = std::max(count, size_type(std::ceil(
float(
size())/max_load_factor())));
1323 rehash(size_type(std::ceil(
float(count)/max_load_factor()))
);
1330 return static_cast<
const hasher&>(*
this);
1342 auto it_bucket = m_buckets.begin() + std::distance(m_buckets.cbegin(), it.m_buckets_iterator);
1343 return iterator(it_bucket, it.m_array_bucket_iterator,
this);
1346 template<
class Serializer>
1348 serialize_impl(serializer);
1351 template<
class Deserializer>
1353 deserialize_impl(deserializer, hash_compatible);
1357 std::size_t hash_key(
const CharT* key, size_type key_size)
const {
1358 return Hash::operator()(key, key_size);
1361 std::size_t bucket_for_hash(std::size_t hash)
const {
1362 return GrowthPolicy::bucket_for_hash(hash);
1370 iterator erase_from_bucket(iterator pos)
noexcept {
1371 auto array_bucket_next_it = pos.m_buckets_iterator->erase(pos.m_array_bucket_iterator);
1374 if(array_bucket_next_it != pos.m_buckets_iterator->cend()) {
1375 return iterator(pos.m_buckets_iterator, array_bucket_next_it,
this);
1379 ++pos.m_buckets_iterator;
1380 }
while(pos.m_buckets_iterator != m_buckets.end() && pos.m_buckets_iterator->empty());
1382 if(pos.m_buckets_iterator != m_buckets.end()) {
1383 return iterator(pos.m_buckets_iterator, pos.m_buckets_iterator->cbegin(),
this);
1392 template<
class U = T,
typename std::enable_if<!has_mapped_type<U>::value>::type* =
nullptr>
1393 bool shoud_clear_old_erased_values(
float = DEFAULT_CLEAR_OLD_ERASED_VALUE_THRESHOLD)
const {
1397 template<
class U = T,
typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
1398 bool shoud_clear_old_erased_values(
float threshold = DEFAULT_CLEAR_OLD_ERASED_VALUE_THRESHOLD)
const {
1399 if(
this->m_values.size() == 0) {
1403 return float(m_nb_elements)/
float(
this->m_values.size()) < threshold;
1406 template<
class U = T,
typename std::enable_if<!has_mapped_type<U>::value>::type* =
nullptr>
1407 void clear_old_erased_values() {
1410 template<
class U = T,
typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
1411 void clear_old_erased_values() {
1412 static_assert(std::is_nothrow_move_constructible<U>::value ||
1413 std::is_copy_constructible<U>::value,
1414 "mapped_value must be either copy constructible or nothrow move constructible.");
1416 if(m_nb_elements ==
this->m_values.size()) {
1420 std::vector<T> new_values;
1421 new_values.reserve(
size());
1423 for(
auto it = begin(); it != end(); ++it) {
1424 new_values.push_back(std::move_if_noexcept(it.value()));
1428 IndexSizeT ivalue = 0;
1429 for(
auto it = begin(); it != end(); ++it) {
1430 auto it_array_bucket = it.m_buckets_iterator->mutable_iterator(it.m_array_bucket_iterator);
1431 it_array_bucket.set_value(ivalue);
1435 new_values.swap(
this->m_values);
1442 bool grow_on_high_load() {
1443 if(
size() >= m_load_threshold) {
1444 rehash_impl(GrowthPolicy::next_bucket_count());
1451 template<
class... ValueArgs,
class U = T,
typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
1452 std::pair<iterator,
bool> emplace_impl(std::size_t ibucket,
typename array_bucket::const_iterator end_of_bucket,
1453 const CharT* key, size_type key_size, ValueArgs&&... value_args)
1457 clear_old_erased_values();
1459 throw std::length_error(
"Can't insert value, too much values in the map.");
1463 if(
this->m_values.size() ==
this->m_values.capacity()) {
1464 this->m_values.reserve(std::size_t(
float(
this->m_values.size()) * value_container<T>::VECTOR_GROWTH_RATE));
1468 this->m_values.emplace_back(std::forward<ValueArgs>(value_args)...);
1471 auto it = m_first_or_empty_bucket[ibucket].append(end_of_bucket, key, key_size, IndexSizeT(
this->m_values.size() - 1));
1474 return std::make_pair(iterator(m_buckets.begin() + ibucket, it,
this),
true);
1477 this->m_values.pop_back();
1482 template<
class U = T,
typename std::enable_if<!has_mapped_type<U>::value>::type* =
nullptr>
1483 std::pair<iterator,
bool> emplace_impl(std::size_t ibucket,
typename array_bucket::const_iterator end_of_bucket,
1484 const CharT* key, size_type key_size)
1487 throw std::length_error(
"Can't insert value, too much values in the map.");
1490 auto it = m_first_or_empty_bucket[ibucket].append(end_of_bucket, key, key_size);
1493 return std::make_pair(iterator(m_buckets.begin() + ibucket, it,
this),
true);
1496 void rehash_impl(size_type bucket_count) {
1497 GrowthPolicy new_growth_policy(bucket_count);
1498 if(bucket_count ==
this->bucket_count()) {
1503 if(shoud_clear_old_erased_values(0.9f)) {
1504 clear_old_erased_values();
1508 std::vector<std::size_t> required_size_for_bucket(bucket_count, 0);
1509 std::vector<std::size_t> bucket_for_ivalue(
size(), 0);
1511 std::size_t ivalue = 0;
1512 for(
auto it = begin(); it != end(); ++it) {
1513 const std::size_t hash = hash_key(it.key(), it.key_size());
1514 const std::size_t ibucket = new_growth_policy.bucket_for_hash(hash);
1516 bucket_for_ivalue[ivalue] = ibucket;
1517 required_size_for_bucket[ibucket] += array_bucket::entry_required_bytes(it.key_size());
1524 std::vector<array_bucket> new_buckets;
1525 new_buckets.reserve(bucket_count);
1526 for(std::size_t ibucket = 0; ibucket < bucket_count; ibucket++) {
1527 new_buckets.emplace_back(required_size_for_bucket[ibucket]);
1532 for(
auto it = begin(); it != end(); ++it) {
1533 const std::size_t ibucket = bucket_for_ivalue[ivalue];
1534 append_iterator_in_reserved_bucket_no_check(new_buckets[ibucket], it);
1541 swap(
static_cast<GrowthPolicy&>(*
this), new_growth_policy);
1543 m_buckets.swap(new_buckets);
1544 m_first_or_empty_bucket = m_buckets.data();
1547 max_load_factor(m_max_load_factor);
1550 template<
class U = T,
typename std::enable_if<!has_mapped_type<U>::value>::type* =
nullptr>
1551 void append_iterator_in_reserved_bucket_no_check(array_bucket& bucket, iterator it) {
1552 bucket.append_in_reserved_bucket_no_check(it.key(), it.key_size());
1555 template<
class U = T,
typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
1556 void append_iterator_in_reserved_bucket_no_check(array_bucket& bucket, iterator it) {
1557 bucket.append_in_reserved_bucket_no_check(it.key(), it.key_size(), it.value_position());
1575 template<
class Serializer>
1576 void serialize_impl(Serializer& serializer)
const {
1577 const slz_size_type version = SERIALIZATION_PROTOCOL_VERSION;
1578 serializer(version);
1580 const slz_size_type bucket_count = m_buckets.size();
1581 serializer(bucket_count);
1583 const slz_size_type nb_elements = m_nb_elements;
1584 serializer(nb_elements);
1586 const float max_load_factor = m_max_load_factor;
1587 serializer(max_load_factor);
1589 for(
const array_bucket& bucket: m_buckets) {
1590 bucket.serialize(serializer);
1591 serialize_bucket_values(serializer, bucket);
1595 template<
class Serializer,
class U = T,
1596 typename std::enable_if<!has_mapped_type<U>::value>::type* =
nullptr>
1597 void serialize_bucket_values(Serializer& ,
const array_bucket& )
const {
1600 template<
class Serializer,
class U = T,
1601 typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
1602 void serialize_bucket_values(Serializer& serializer,
const array_bucket& bucket)
const {
1603 for(
auto it = bucket.begin(); it != bucket.end(); ++it) {
1604 serializer(
this->m_values[it.value()]);
1608 template<
class Deserializer>
1609 void deserialize_impl(Deserializer& deserializer,
bool hash_compatible) {
1612 const slz_size_type version = deserialize_value<slz_size_type>(deserializer);
1615 if(version != SERIALIZATION_PROTOCOL_VERSION) {
1616 throw std::runtime_error(
"Can't deserialize the array_map/set. The protocol version header is invalid.");
1619 const slz_size_type bucket_count_ds = deserialize_value<slz_size_type>(deserializer);
1620 const slz_size_type nb_elements = deserialize_value<slz_size_type>(deserializer);
1621 const float max_load_factor = deserialize_value<
float>(deserializer);
1625 if(nb_elements > std::numeric_limits<IndexSizeT>::max()) {
1626 throw std::runtime_error(
"Deserialized nb_elements is bigger than the max value of IndexSizeT.");
1628 m_nb_elements =
static_cast<IndexSizeT>(nb_elements);
1631 if(bucket_count_ds > std::numeric_limits<std::size_t>::max()) {
1632 throw std::runtime_error(
"Deserialized bucket_count is bigger than the max value of std::size_t on the current platform.");
1634 std::size_t bucket_count =
static_cast<std::size_t>(bucket_count_ds);
1635 GrowthPolicy::operator=(GrowthPolicy(bucket_count));
1638 this->max_load_factor(max_load_factor);
1639 value_container<T>::reserve(m_nb_elements);
1642 if(hash_compatible) {
1643 if(bucket_count != bucket_count_ds) {
1644 throw std::runtime_error(
"The GrowthPolicy is not the same even though hash_compatible is true.");
1647 m_buckets.reserve(bucket_count);
1648 for(std::size_t i = 0; i < bucket_count; i++) {
1649 m_buckets.push_back(array_bucket::deserialize(deserializer));
1650 deserialize_bucket_values(deserializer, m_buckets.back());
1654 m_buckets.resize(bucket_count);
1655 for(std::size_t i = 0; i < bucket_count; i++) {
1657 array_bucket bucket = array_bucket::deserialize(deserializer);
1658 deserialize_bucket_values(deserializer, bucket);
1660 for(
auto it_val = bucket.cbegin(); it_val != bucket.cend(); ++it_val) {
1661 const std::size_t ibucket = bucket_for_hash(hash_key(it_val.key(), it_val.key_size()));
1663 auto it_find = m_buckets[ibucket].find_or_end_of_bucket(it_val.key(), it_val.key_size());
1664 if(it_find.second) {
1665 throw std::runtime_error(
"Error on deserialization, the same key is presents multiple times.");
1668 append_array_bucket_iterator_in_bucket(m_buckets[ibucket], it_find.first, it_val);
1673 m_first_or_empty_bucket = m_buckets.data();
1677 throw std::runtime_error(
"");
1681 template<
class Deserializer,
class U = T,
1682 typename std::enable_if<!has_mapped_type<U>::value>::type* =
nullptr>
1683 void deserialize_bucket_values(Deserializer& , array_bucket& ) {
1686 template<
class Deserializer,
class U = T,
1687 typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
1688 void deserialize_bucket_values(Deserializer& deserializer, array_bucket& bucket) {
1689 for(
auto it = bucket.begin(); it != bucket.end(); ++it) {
1690 this->m_values.emplace_back(deserialize_value<U>(deserializer));
1692 tsl_ah_assert(
this->m_values.size() - 1 <= std::numeric_limits<IndexSizeT>::max());
1693 it.set_value(
static_cast<IndexSizeT>(
this->m_values.size() - 1));
1697 template<
class U = T,
typename std::enable_if<!has_mapped_type<U>::value>::type* =
nullptr>
1698 void append_array_bucket_iterator_in_bucket(array_bucket& bucket,
1699 typename array_bucket::const_iterator end_of_bucket,
1700 typename array_bucket::const_iterator it_val)
1702 bucket.append(end_of_bucket, it_val.key(), it_val.key_size());
1705 template<
class U = T,
typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
1706 void append_array_bucket_iterator_in_bucket(array_bucket& bucket,
1707 typename array_bucket::const_iterator end_of_bucket,
1708 typename array_bucket::const_iterator it_val)
1710 bucket.append(end_of_bucket, it_val.key(), it_val.key_size(), it_val.value());
1722 static const slz_size_type SERIALIZATION_PROTOCOL_VERSION = 1;
1725 static constexpr float DEFAULT_CLEAR_OLD_ERASED_VALUE_THRESHOLD = 0.6f;
1731 array_bucket* static_empty_bucket_ptr() {
1732 static array_bucket empty_bucket;
1733 return &empty_bucket;
1737 std::vector<array_bucket> m_buckets;
1746 array_bucket* m_first_or_empty_bucket;
1748 IndexSizeT m_nb_elements;
1749 float m_max_load_factor;
1750 size_type m_load_threshold;
iterator begin() noexcept
Definition: array_hash.h:981
array_hash_iterator(const array_hash_iterator< false > &other) noexcept
Definition: array_hash.h:819
std::pair< iterator, bool > insert_or_assign(const CharT *key, size_type key_size, M &&obj)
Definition: array_hash.h:1076
iterator end() noexcept
Definition: array_hash.h:1003
array_hash_iterator & operator++()
Definition: array_hash.h:855
const_iterator cend() const noexcept
Definition: array_hash.h:1011
void rehash(size_type count)
Definition: array_hash.h:1317
void shrink_to_fit()
Definition: array_hash.h:1035
Definition: array_growth_policy.h:40
size_type max_key_size() const noexcept
Definition: array_hash.h:1031
float max_load_factor() const
Definition: array_hash.h:1308
U & at(const CharT *key, size_type key_size, std::size_t hash)
Definition: array_hash.h:1177
Definition: array_growth_policy.h:39
size_type max_bucket_count() const
Definition: array_hash.h:1296
float load_factor() const
Definition: array_hash.h:1304
size_type count(const CharT *key, size_type key_size) const
Definition: array_hash.h:1217
const U & at(const CharT *key, size_type key_size) const
Definition: array_hash.h:1172
Definition: array_hash.h:117
std::pair< const_iterator, const_iterator > equal_range(const CharT *key, size_type key_size) const
Definition: array_hash.h:1273
bool empty() const noexcept
Definition: array_hash.h:1019
static constexpr float DEFAULT_MAX_LOAD_FACTOR
Definition: array_hash.h:1715
std::basic_string_view< CharT > key_sv() const
Definition: array_hash.h:835
iterator erase(const_iterator pos)
Definition: array_hash.h:1087
U & access_operator(const CharT *key, size_type key_size)
Definition: array_hash.h:1197
Definition: array_hash.h:102
hasher hash_function() const
Definition: array_hash.h:1329
void deserialize(Deserializer &deserializer, bool hash_compatible)
Definition: array_hash.h:1352
size_type erase(const CharT *key, size_type key_size, std::size_t hash)
Definition: array_hash.h:1133
const_iterator begin() const noexcept
Definition: array_hash.h:990
reference value() const
Definition: array_hash.h:841
iterator find(const CharT *key, size_type key_size)
Definition: array_hash.h:1235
void clear() noexcept
Definition: array_hash.h:1045
#define tsl_ah_assert(expr)
Definition: array_hash.h:63
bool operator()(const CharT *key_lhs, std::size_t key_size_lhs, const CharT *key_rhs, std::size_t key_size_rhs) const
Definition: array_hash.h:103
size_type size() const noexcept
Definition: array_hash.h:1023
array_hash(const array_hash &other)
Definition: array_hash.h:921
array_hash(array_hash &&other) noexcept(std::is_nothrow_move_constructible< value_container< T >>::value &&std::is_nothrow_move_constructible< Hash >::value &&std::is_nothrow_move_constructible< GrowthPolicy >::value &&std::is_nothrow_move_constructible< std::vector< array_bucket >>::value)
Definition: array_hash.h:932
std::pair< iterator, bool > emplace(const CharT *key, size_type key_size, ValueArgs &&... value_args)
Definition: array_hash.h:1058
size_type erase(const CharT *key, size_type key_size)
Definition: array_hash.h:1129
void max_load_factor(float ml)
Definition: array_hash.h:1312
size_type count(const CharT *key, size_type key_size, std::size_t hash) const
Definition: array_hash.h:1221
key_equal key_eq() const
Definition: array_hash.h:1334
void serialize(Serializer &serializer) const
Definition: array_hash.h:1347
array_hash & operator=(array_hash &&other)
Definition: array_hash.h:970
array_hash & operator=(const array_hash &other)
Definition: array_hash.h:953
const_iterator find(const CharT *key, size_type key_size) const
Definition: array_hash.h:1239
const CharT * key() const
Definition: array_hash.h:826
const U & at(const CharT *key, size_type key_size, std::size_t hash) const
Definition: array_hash.h:1182
friend bool operator!=(const array_hash_iterator &lhs, const array_hash_iterator &rhs)
Definition: array_hash.h:887
std::pair< iterator, iterator > equal_range(const CharT *key, size_type key_size)
Definition: array_hash.h:1269
std::size_t operator()(const CharT *key, std::size_t key_size) const
Definition: array_hash.h:79
pointer operator->() const
Definition: array_hash.h:851
U & at(const CharT *key, size_type key_size)
Definition: array_hash.h:1167
array_hash_iterator() noexcept
Definition: array_hash.h:816
size_type bucket_count() const
Definition: array_hash.h:1292
const_iterator find(const CharT *key, size_type key_size, std::size_t hash) const
Definition: array_hash.h:1255
void reserve(size_type count)
Definition: array_hash.h:1322
static const size_type DEFAULT_INIT_BUCKET_COUNT
Definition: array_hash.h:1714
static const size_type MAX_KEY_SIZE
Definition: array_hash.h:1716
iterator find(const CharT *key, size_type key_size, std::size_t hash)
Definition: array_hash.h:1243
void swap(array_hash &other)
Definition: array_hash.h:1150
const_iterator cbegin() const noexcept
Definition: array_hash.h:994
const_iterator end() const noexcept
Definition: array_hash.h:1007
size_type key_size() const
Definition: array_hash.h:830
reference operator*() const
Definition: array_hash.h:846
Definition: array_hash.h:742
array_hash_iterator operator++(int)
Definition: array_hash.h:874
array_hash(size_type bucket_count, const Hash &hash, float max_load_factor)
Definition: array_hash.h:907
friend bool operator==(const array_hash_iterator &lhs, const array_hash_iterator &rhs)
Definition: array_hash.h:881
std::pair< const_iterator, const_iterator > equal_range(const CharT *key, size_type key_size, std::size_t hash) const
Definition: array_hash.h:1282
iterator mutable_iterator(const_iterator it) noexcept
Definition: array_hash.h:1341
std::pair< iterator, iterator > equal_range(const CharT *key, size_type key_size, std::size_t hash)
Definition: array_hash.h:1277
size_type max_size() const noexcept
Definition: array_hash.h:1027
iterator erase(const_iterator first, const_iterator last)
Definition: array_hash.h:1095