193 using iterator = flat_hashtable_iterator<false, flat_hashtable>;
201 static constexpr size_t npos =
static_cast<size_t>(-1);
204 Value* data_ =
nullptr;
205 byte_t* metadata_ =
nullptr;
206 size_t capacity_ = 0;
208 size_t growth_left_ = 0;
211 ExtractKey extracter_{};
213 compressed_pair<allocator_type, float> alloc_lf_{default_construct_tag{}, 0.875F};
215 template <
bool,
typename>
216 friend struct flat_hashtable_iterator;
224 static size_t next_power_of_2(
const size_t n)
noexcept {
240 NEFORCE_NODISCARD
size_t hash_to_index(
const size_t hash)
const noexcept {
return (hash >> 7) & (capacity_ - 1); }
247 static byte_t hash_to_h2(
const size_t hash)
noexcept {
return static_cast<byte_t>(hash & FLAT_HT_H2_MASK); }
253 allocator_type& get_allocator() noexcept {
return alloc_lf_.get_base(); }
259 void alloc_arrays(
const size_t cap) {
263 allocator_type& alloc = get_allocator();
264 data_ = alloc.allocate(cap);
265 metadata_ =
static_cast<byte_t*
>(::operator
new(cap *
sizeof(
byte_t), std::nothrow));
266 if (metadata_ ==
nullptr) {
267 alloc.deallocate(data_, cap);
269 NEFORCE_THROW_EXCEPTION(memory_exception(
"flat_hashtable metadata allocation failed"));
271 for (
size_t i = 0; i < cap; ++i) {
272 metadata_[i] = FLAT_HT_EMPTY;
279 void free_arrays() noexcept {
280 if (data_ && capacity_ > 0) {
281 for (
size_t i = 0; i < capacity_; ++i) {
282 if (metadata_[i] != FLAT_HT_EMPTY && metadata_[i] != FLAT_HT_DELETED) {
286 allocator_type& alloc = get_allocator();
287 alloc.deallocate(data_, capacity_);
290 if (metadata_ !=
nullptr) {
291 ::operator
delete(metadata_, std::nothrow);
302 pair<size_t, bool> probe_find_or_insert(
const key_type& key,
const byte_t h2)
const noexcept {
303 const size_t h1 = hash_to_index(hasher_(key));
305 size_t first_deleted = npos;
307 for (
size_t i = 0; i < capacity_; ++i) {
308 const byte_t meta = metadata_[idx];
309 if (meta == FLAT_HT_EMPTY) {
310 return {first_deleted != npos ? first_deleted : idx,
false};
312 if (meta == FLAT_HT_DELETED) {
313 if (first_deleted == npos) {
316 }
else if (meta == h2 && equals_(extracter_(data_[idx]), key)) {
319 idx = (idx + 1) & (capacity_ - 1);
321 return {first_deleted,
false};
330 pair<size_t, bool> probe_find_or_insert_simd(
const key_type& key,
const byte_t h2)
const noexcept {
331 const size_t h1 = hash_to_index(hasher_(key));
333 size_t first_deleted = npos;
335 const simd::vec128_t h2_vec = simd::fill_byte(h2);
336 const simd::vec128_t empty_vec = simd::fill_byte(FLAT_HT_EMPTY);
337 const simd::vec128_t deleted_vec = simd::fill_byte(FLAT_HT_DELETED);
339 for (
size_t round = 0;
round < capacity_;
round += 16) {
342 simd::vec128_t meta_vec;
343 const size_t remaining = capacity_ - idx;
344 if (remaining >= 16) {
345 meta_vec = simd::load_unaligned(metadata_ + idx);
348 for (
size_t k = 0; k < remaining; ++k) {
349 buf[k] = metadata_[idx + k];
351 for (
size_t k = 0; k < 16 - remaining; ++k) {
352 buf[remaining + k] = metadata_[k];
354 meta_vec = simd::load_unaligned(buf);
357 const int h2_mask = simd::to_bitmask(simd::match_bytes(meta_vec, h2_vec));
358 const int empty_mask = simd::to_bitmask(simd::match_bytes(meta_vec, empty_vec));
359 const int deleted_mask = simd::to_bitmask(simd::match_bytes(meta_vec, deleted_vec));
364 const size_t slot = (idx + bit) & (capacity_ - 1);
365 if (equals_(extracter_(data_[slot]), key)) {
368 match &= (match - 1);
371 if (empty_mask != 0) {
373 const size_t empty_slot = (idx + empty_bit) & (capacity_ - 1);
374 if (first_deleted != npos) {
375 const size_t probe_dist_empty =
376 (empty_slot >= h1) ? (empty_slot - h1) : (capacity_ - h1 + empty_slot);
377 const size_t probe_dist_del =
378 (first_deleted >= h1) ? (first_deleted - h1) : (capacity_ - h1 + first_deleted);
379 if (probe_dist_del < probe_dist_empty) {
380 return {first_deleted,
false};
383 return {empty_slot,
false};
386 const int del = deleted_mask;
387 if ((del != 0) && first_deleted == npos) {
389 first_deleted = (idx + del_bit) & (capacity_ - 1);
392 idx = (idx + 16) & (capacity_ - 1);
394 return {first_deleted,
false};
401 NEFORCE_NODISCARD
bool should_rehash() const noexcept {
return growth_left_ == 0; }
407 void rehash_impl(
const size_t min_capacity) {
408 const size_t needed =
max(min_capacity,
static_cast<size_t>(
static_cast<double>(size_) / max_load_factor()));
409 const size_t new_capacity = next_power_of_2(needed);
410 if (new_capacity <= capacity_) {
414 allocator_type& alloc = get_allocator();
415 Value* new_data = alloc.allocate(new_capacity);
416 auto* new_metadata =
static_cast<byte_t*
>(::operator
new(new_capacity *
sizeof(
byte_t), std::nothrow));
417 if (new_metadata ==
nullptr) {
418 alloc.deallocate(new_data, new_capacity);
419 NEFORCE_THROW_EXCEPTION(memory_exception(
"flat_hashtable rehash metadata allocation failed"));
421 for (
size_t i = 0; i < new_capacity; ++i) {
422 new_metadata[i] = FLAT_HT_EMPTY;
425 const size_t old_capacity = capacity_;
426 Value*
const old_data = data_;
427 byte_t*
const old_metadata = metadata_;
430 for (
size_t i = 0; i < old_capacity; ++i) {
431 if (old_metadata[i] != FLAT_HT_EMPTY && old_metadata[i] != FLAT_HT_DELETED) {
432 const key_type& key = extracter_(old_data[i]);
433 const size_t hash = hasher_(key);
434 const byte_t h2 = hash_to_h2(hash);
435 const size_t h1 = (hash >> 7) & (new_capacity - 1);
439 for (
size_t j = 0; j < new_capacity; ++j) {
440 const byte_t nm = new_metadata[new_idx];
441 if (nm == FLAT_HT_EMPTY) {
444 if (nm == h2 && equals_(extracter_(new_data[new_idx]), key)) {
445 size_t run_end = new_idx;
446 for (
size_t k = j + 1; k < new_capacity; ++k) {
447 run_end = (run_end + 1) & (new_capacity - 1);
448 const byte_t rmn = new_metadata[run_end];
449 if (rmn == FLAT_HT_EMPTY) {
454 if (rmn == h2 && equals_(extracter_(new_data[run_end]), key)) {
457 size_t shift_end = run_end;
458 for (
size_t m = 0; m < new_capacity; ++m) {
459 shift_end = (shift_end + 1) & (new_capacity - 1);
460 if (new_metadata[shift_end] == FLAT_HT_EMPTY) {
464 while (shift_end != run_end) {
465 const size_t prev = (shift_end - 1) & (new_capacity - 1);
466 _NEFORCE
construct(&new_data[shift_end], _NEFORCE
move(new_data[prev]));
467 _NEFORCE
destroy(&new_data[prev]);
468 new_metadata[shift_end] = new_metadata[
prev];
480 new_idx = (new_idx + 1) & (new_capacity - 1);
482 _NEFORCE
construct(&new_data[new_idx], _NEFORCE
move(old_data[i]));
483 new_metadata[new_idx] = h2;
487 for (
size_t i = 0; i < new_capacity; ++i) {
488 if (new_metadata[i] != FLAT_HT_EMPTY && new_metadata[i] != FLAT_HT_DELETED) {
489 _NEFORCE
destroy(&new_data[i]);
492 alloc.deallocate(new_data, new_capacity);
493 ::operator
delete(new_metadata, std::nothrow);
498 alloc.deallocate(old_data, old_capacity);
500 if (old_metadata !=
nullptr) {
501 ::operator
delete(old_metadata, std::nothrow);
505 metadata_ = new_metadata;
506 capacity_ = new_capacity;
507 growth_left_ =
static_cast<size_t>(
static_cast<double>(capacity_) * max_load_factor()) - size_;
517 template <
typename... Args>
518 void construct_at(
const size_t idx,
const byte_t h2, Args&&... args) {
519 _NEFORCE
construct(&data_[idx], _NEFORCE forward<Args>(args)...);
532 void shift_make_room(
const size_t pos)
noexcept {
534 for (
size_t i = 0; i < capacity_; ++i) {
535 end = (
end + 1) & (capacity_ - 1);
537 if (m == FLAT_HT_EMPTY || m == FLAT_HT_DELETED) {
542 const size_t prev = (
end - 1) & (capacity_ - 1);
544 _NEFORCE
destroy(&data_[prev]);
545 metadata_[
end] = metadata_[
prev];
554 void copy_from(
const flat_hashtable& other) {
555 if (other.capacity_ == 0) {
558 alloc_arrays(other.capacity_);
559 capacity_ = other.capacity_;
560 growth_left_ = other.growth_left_;
562 for (
size_t i = 0; i < other.capacity_; ++i) {
563 metadata_[i] = other.metadata_[i];
564 if (other.metadata_[i] != FLAT_HT_EMPTY && other.metadata_[i] != FLAT_HT_DELETED) {
565 _NEFORCE
construct(&data_[i], other.data_[i]);
575 bool equal_small(
const flat_hashtable& rhs)
const {
576 for (const_iterator iter =
begin(); iter !=
end(); ++iter) {
577 const key_type& key = extracter_(*iter);
578 const size_t count_lhs = _NEFORCE
count_if(
579 begin(),
end(), [
this, &key](
const value_type& val) {
return equals_(extracter_(val), key); });
580 const size_t count_rhs = _NEFORCE
count_if(rhs.begin(), rhs.end(), [&rhs, &key](
const value_type& val) {
581 return rhs.equals_(rhs.extracter_(val), key);
583 if (count_lhs != count_rhs) {
590 bool equal_large(
const flat_hashtable& rhs)
const {
591 if (size_ != rhs.size_) {
594 vector<const value_type*> ptrs_lhs, ptrs_rhs;
595 ptrs_lhs.reserve(size_);
596 ptrs_rhs.reserve(size_);
597 for (const_iterator it =
begin(); it !=
end(); ++it) {
598 ptrs_lhs.push_back(&(*it));
600 for (const_iterator it = rhs.begin(); it != rhs.end(); ++it) {
601 ptrs_rhs.push_back(&(*it));
604 auto key_less = [
this](
const value_type* a,
const value_type* b) {
return extracter_(*a) < extracter_(*b); };
605 auto rhs_key_less = [&rhs](
const value_type* a,
const value_type* b) {
606 return rhs.extracter_(*a) < rhs.extracter_(*b);
608 _NEFORCE
sort(ptrs_lhs.begin(), ptrs_lhs.end(), key_less);
609 _NEFORCE
sort(ptrs_rhs.begin(), ptrs_rhs.end(), rhs_key_less);
611 size_type i = 0, j = 0;
612 const size_type n = ptrs_lhs.size();
613 while (i < n && j < n) {
614 const key_type& key_l = extracter_(*ptrs_lhs[i]);
615 const key_type& key_r = rhs.extracter_(*ptrs_rhs[j]);
616 if (!equals_(key_l, key_r)) {
619 const size_type i_start = i;
620 const size_type j_start = j;
621 while (i < n && equals_(extracter_(*ptrs_lhs[i]), key_l)) {
624 while (j < n && rhs.equals_(rhs.extracter_(*ptrs_rhs[j]), key_l)) {
627 const size_type count_l = i - i_start;
628 const size_type count_r = j - j_start;
629 if (count_l != count_r) {
632 for (size_type k = i_start; k < i; ++k) {
633 const value_type& val = *ptrs_lhs[k];
635 for (size_type l = j_start; l < j; ++l) {
636 if (ptrs_rhs[l] && *ptrs_rhs[l] == val) {
637 ptrs_rhs[l] =
nullptr;
646 for (size_type l = j_start; l < j; ++l) {
647 if (ptrs_rhs[l] !=
nullptr) {
655 static iterator to_iterator(
const const_iterator& iter)
noexcept {
656 return iterator(iter.index(),
const_cast<flat_hashtable*
>(iter.container()));
658 static const_iterator to_const_iterator(
const iterator& iter)
noexcept {
659 return const_iterator(iter.index(), iter.container());
669 const size_t cap = next_power_of_2(
static_cast<size_t>(
static_cast<double>(n) /
max_load_factor()));
672 growth_left_ =
static_cast<size_t>(
static_cast<double>(cap) *
max_load_factor());
684 const size_t cap = next_power_of_2(
static_cast<size_t>(
static_cast<double>(n) /
max_load_factor()));
687 growth_left_ =
static_cast<size_t>(
static_cast<double>(cap) *
max_load_factor());
701 const size_t cap = next_power_of_2(
static_cast<size_t>(
static_cast<double>(n) /
max_load_factor()));
704 growth_left_ =
static_cast<size_t>(
static_cast<double>(cap) *
max_load_factor());
720 const size_t cap = next_power_of_2(
static_cast<size_t>(
static_cast<double>(n) /
max_load_factor()));
723 growth_left_ =
static_cast<size_t>(
static_cast<double>(cap) *
max_load_factor());
732 hasher_(other.hasher_),
733 equals_(other.equals_),
734 extracter_(other.extracter_),
735 alloc_lf_(other.alloc_lf_) {
749 hasher_ = other.hasher_;
750 equals_ = other.equals_;
751 extracter_ = other.extracter_;
752 alloc_lf_ = other.alloc_lf_;
763 metadata_(other.metadata_),
764 capacity_(other.capacity_),
766 growth_left_(other.growth_left_),
767 hasher_(_NEFORCE
move(other.hasher_)),
768 equals_(_NEFORCE
move(other.equals_)),
769 extracter_(_NEFORCE
move(other.extracter_)),
770 alloc_lf_(_NEFORCE
move(other.alloc_lf_)) {
771 other.data_ =
nullptr;
772 other.metadata_ =
nullptr;
775 other.growth_left_ = 0;
802 for (
size_t n = 0; n < capacity_; ++n) {
803 const byte_t meta = metadata_[n];
834 for (
size_t n = 0; n < capacity_; ++n) {
835 const byte_t meta = metadata_[n];
865 NEFORCE_NODISCARD
bool empty() const noexcept {
return size_ == 0; }
890 return capacity_ == 0 ? 0.0F :
static_cast<float>(size_) /
static_cast<float>(capacity_);
904 NEFORCE_DEBUG_VERIFY(lf > 0,
"flat_hashtable load factor invalid.");
905 alloc_lf_.value = lf;
906 growth_left_ =
static_cast<size_t>(
static_cast<double>(capacity_) * lf) - size_;
934 template <
typename... Args>
936 if (should_rehash()) {
937 const size_type new_cap = capacity_ == 0 ? 16 : capacity_ * 2;
942 const key_type& key = extracter_(tmp);
943 const size_t hash = hasher_(key);
946#ifdef NEFORCE_SIMD_SSE2
952 if (probe_result.
second) {
956 const size_t insert_idx = probe_result.
first;
958 metadata_[insert_idx] = h2;
961 return {
iterator(insert_idx,
this),
true};
970 template <
typename... Args>
972 if (should_rehash()) {
973 const size_type new_cap = capacity_ == 0 ? 16 : capacity_ * 2;
978 const key_type& key = extracter_(tmp);
979 const size_t hash = hasher_(key);
981 const size_t h1 = hash_to_index(
hash);
984 size_t first_deleted = npos;
985 for (
size_t i = 0; i < capacity_; ++i) {
986 const byte_t meta = metadata_[idx];
988 const size_t insert_idx = (first_deleted != npos) ? first_deleted : idx;
989 construct_at(insert_idx, h2, _NEFORCE
move(tmp));
994 }
else if (meta == h2 && equals_(extracter_(data_[idx]), key)) {
995 size_t run_end = idx;
996 for (
size_t j = i + 1; j < capacity_; ++j) {
997 run_end = (run_end + 1) & (capacity_ - 1);
1003 const byte_t rm = metadata_[run_end];
1005 construct_at(run_end, h2, _NEFORCE
move(tmp));
1008 if (rm == h2 && equals_(extracter_(data_[run_end]), key)) {
1011 shift_make_room(run_end);
1012 construct_at(run_end, h2, _NEFORCE
move(tmp));
1015 NEFORCE_THROW_EXCEPTION(
value_exception(
"flat_hashtable: no available slot for insert_equal"));
1017 idx = (idx + 1) & (capacity_ - 1);
1020 const size_t insert_idx = first_deleted;
1021 if (insert_idx == npos) {
1022 NEFORCE_THROW_EXCEPTION(
value_exception(
"flat_hashtable: no available slot for insert_equal"));
1024 construct_at(insert_idx, h2, _NEFORCE
move(tmp));
1062 template <
typename Iterator>
1068 for (; n > 0; --n, ++first) {
1079 template <
typename Iterator>
1081 for (; first != last; ++first) {
1098 template <
typename Iterator>
1104 for (; n > 0; --n, ++first) {
1115 template <
typename Iterator>
1117 for (; first != last; ++first) {
1134 if (capacity_ == 0) {
1138 const size_t hash = hasher_(key);
1140 size_t idx = hash_to_index(
hash);
1143 for (
size_t i = 0; i < capacity_; ++i) {
1144 const byte_t meta = metadata_[idx];
1148 if (meta == h2 && equals_(extracter_(data_[idx]), key)) {
1149 _NEFORCE
destroy(&data_[idx]);
1154 idx = (idx + 1) & (capacity_ - 1);
1165 if (position.container() !=
this || position.index() >= capacity_) {
1168 const byte_t meta = metadata_[position.index()];
1173 _NEFORCE
destroy(&data_[position.index()]);
1177 size_t next = position.index() + 1;
1178 while (
next < capacity_) {
1195 if (first == last) {
1198 if (first.container() !=
this || (last.container() !=
this && last !=
end())) {
1202 for (
size_t idx = first.index(); idx < last.index() && idx < capacity_; ++idx) {
1203 const byte_t meta = metadata_[idx];
1205 _NEFORCE
destroy(&data_[idx]);
1219 return to_const_iterator(
erase(to_iterator(position)));
1229 return to_const_iterator(
erase(to_iterator(first), to_iterator(last)));
1236 for (
size_t i = 0; i < capacity_; ++i) {
1243 growth_left_ =
static_cast<size_t>(
static_cast<double>(capacity_) *
max_load_factor());
1252 if (capacity_ == 0) {
1255 const size_t hash = hasher_(key);
1257 size_t idx = hash_to_index(
hash);
1259 for (
size_t i = 0; i < capacity_; ++i) {
1260 const byte_t meta = metadata_[idx];
1264 if (meta == h2 && equals_(extracter_(data_[idx]), key)) {
1267 idx = (idx + 1) & (capacity_ - 1);
1278 if (capacity_ == 0) {
1281 const size_t hash = hasher_(key);
1283 size_t idx = hash_to_index(
hash);
1285 for (
size_t i = 0; i < capacity_; ++i) {
1286 const byte_t meta = metadata_[idx];
1290 if (meta == h2 && equals_(extracter_(data_[idx]), key)) {
1293 idx = (idx + 1) & (capacity_ - 1);
1304 if (capacity_ == 0) {
1307 const size_t hash = hasher_(key);
1309 size_t idx = hash_to_index(
hash);
1312 for (
size_t i = 0; i < capacity_; ++i) {
1313 const byte_t meta = metadata_[idx];
1317 if (meta == h2 && equals_(extracter_(data_[idx]), key)) {
1320 idx = (idx + 1) & (capacity_ - 1);
1338 if (capacity_ == 0) {
1341 const size_t hash = hasher_(key);
1343 size_t idx = hash_to_index(
hash);
1345 size_t first_idx = npos;
1346 for (
size_t i = 0; i < capacity_; ++i) {
1347 const byte_t meta = metadata_[idx];
1351 if (meta == h2 && equals_(extracter_(data_[idx]), key)) {
1355 idx = (idx + 1) & (capacity_ - 1);
1357 if (first_idx == npos) {
1361 size_t last_idx = first_idx;
1363 last_idx = (last_idx + 1) & (capacity_ - 1);
1364 if (last_idx == first_idx) {
1367 }
while (metadata_[last_idx] !=
FLAT_HT_EMPTY && equals_(extracter_(data_[last_idx]), key));
1369 while (last_idx != first_idx &&
1371 last_idx = (last_idx + 1) & (capacity_ - 1);
1373 if (last_idx == first_idx || last_idx < first_idx) {
1388 if (capacity_ == 0) {
1391 const size_t hash = hasher_(key);
1393 size_t idx = hash_to_index(
hash);
1395 size_t first_idx = npos;
1396 for (
size_t i = 0; i < capacity_; ++i) {
1397 const byte_t meta = metadata_[idx];
1401 if (meta == h2 && equals_(extracter_(data_[idx]), key)) {
1405 idx = (idx + 1) & (capacity_ - 1);
1407 if (first_idx == npos) {
1411 size_t last_idx = first_idx;
1413 last_idx = (last_idx + 1) & (capacity_ - 1);
1414 if (last_idx == first_idx) {
1417 }
while (metadata_[last_idx] !=
FLAT_HT_EMPTY && equals_(extracter_(data_[last_idx]), key));
1419 while (last_idx != first_idx &&
1421 last_idx = (last_idx + 1) & (capacity_ - 1);
1423 if (last_idx == first_idx || last_idx < first_idx) {
1435 if (_NEFORCE
addressof(other) ==
this) {
1438 _NEFORCE
swap(data_, other.data_);
1439 _NEFORCE
swap(metadata_, other.metadata_);
1440 _NEFORCE
swap(capacity_, other.capacity_);
1441 _NEFORCE
swap(size_, other.size_);
1442 _NEFORCE
swap(growth_left_, other.growth_left_);
1443 _NEFORCE
swap(hasher_, other.hasher_);
1444 _NEFORCE
swap(equals_, other.equals_);
1445 _NEFORCE
swap(extracter_, other.extracter_);
1446 alloc_lf_.swap(other.alloc_lf_);
1455 if (size_ != rhs.size_) {
1465 return equal_small(rhs);
1467 return equal_large(rhs);