NexusForce 1.0.0
A rigorously engineered full-stack C++ backend library.
载入中...
搜索中...
未找到
flat_hashtable.hpp
浏览该文件的文档.
1#ifndef NEFORCE_CORE_CONTAINER_FLAT_HASHTABLE_HPP__
2#define NEFORCE_CORE_CONTAINER_FLAT_HASHTABLE_HPP__
3
13
21NEFORCE_BEGIN_NAMESPACE__
22
65
66template <typename Value, typename Key, typename HashFcn, typename ExtractKey, typename EqualKey, typename Alloc>
67class flat_hashtable;
68
77template <bool IsConst, typename FlatHT>
78struct flat_hashtable_iterator : iiterator<flat_hashtable_iterator<IsConst, FlatHT>> {
79public:
80 using container_type = FlatHT;
81 using value_type = typename container_type::value_type;
82 using size_type = typename container_type::size_type;
83 using difference_type = typename container_type::difference_type;
85 using reference = conditional_t<IsConst, typename container_type::const_reference,
86 typename container_type::reference>;
87 using pointer = conditional_t<IsConst, typename container_type::const_pointer,
88 typename container_type::pointer>;
89
90private:
91 size_type index_ = 0;
92 const container_type* container_ = nullptr;
93
94 template <typename, typename, typename, typename, typename, typename>
95 friend class flat_hashtable;
96
97public:
98 flat_hashtable_iterator() noexcept = default;
99 ~flat_hashtable_iterator() = default;
100
101 flat_hashtable_iterator(const flat_hashtable_iterator&) noexcept = default;
102 flat_hashtable_iterator& operator=(const flat_hashtable_iterator&) noexcept = default;
103 flat_hashtable_iterator(flat_hashtable_iterator&&) noexcept = default;
104 flat_hashtable_iterator& operator=(flat_hashtable_iterator&&) noexcept = default;
105
111 flat_hashtable_iterator(const size_type index, const container_type* container) noexcept :
112 index_(index),
113 container_(container) {}
114
119 NEFORCE_NODISCARD reference dereference() const noexcept {
120 NEFORCE_DEBUG_VERIFY(container_ != nullptr, "null container in flat_hashtable_iterator");
121 NEFORCE_DEBUG_VERIFY(index_ < container_->capacity_, "index out of range in flat_hashtable_iterator");
122 return container_->data_[index_];
123 }
124
130 void increment() noexcept {
131 NEFORCE_DEBUG_VERIFY(container_ != nullptr, "null container in flat_hashtable_iterator");
132 ++index_;
133 while (index_ < container_->capacity_) {
134 const byte_t meta = container_->metadata_[index_];
136 return;
137 }
138 ++index_;
139 }
140 }
141
147 NEFORCE_NODISCARD bool equal_to(const flat_hashtable_iterator& rhs) const noexcept {
148 NEFORCE_DEBUG_VERIFY(container_ == rhs.container_, "comparing iterators from different containers");
149 return index_ == rhs.index_;
150 }
151
156 NEFORCE_NODISCARD size_type index() const noexcept { return index_; }
157
162 NEFORCE_NODISCARD const container_type* container() const noexcept { return container_; }
163};
164
178template <typename Value, typename Key, typename HashFcn, typename ExtractKey, typename EqualKey, typename Alloc>
179class flat_hashtable : public icollector<flat_hashtable<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc>> {
180public:
181 using key_type = Key;
182 using hasher = HashFcn;
183 using key_equal = EqualKey;
184 using value_type = Value;
185
186 using pointer = Value*;
187 using reference = Value&;
188 using const_pointer = const Value*;
189 using const_reference = const Value&;
192
193 using iterator = flat_hashtable_iterator<false, flat_hashtable>;
194 using const_iterator = flat_hashtable_iterator<true, flat_hashtable>;
195
196 using allocator_type = Alloc;
197
198 static constexpr byte_t FLAT_HT_EMPTY = 0x80;
199 static constexpr byte_t FLAT_HT_DELETED = 0xFE;
200 static constexpr byte_t FLAT_HT_H2_MASK = 0x7F;
201 static constexpr size_t npos = static_cast<size_t>(-1);
202
203private:
204 Value* data_ = nullptr;
205 byte_t* metadata_ = nullptr;
206 size_t capacity_ = 0;
207 size_t size_ = 0;
208 size_t growth_left_ = 0;
209 hasher hasher_{};
210 key_equal equals_{};
211 ExtractKey extracter_{};
212
213 compressed_pair<allocator_type, float> alloc_lf_{default_construct_tag{}, 0.875F};
214
215 template <bool, typename>
216 friend struct flat_hashtable_iterator;
217
218private:
224 static size_t next_power_of_2(const size_t n) noexcept {
225 if (n <= 16) {
226 return 16;
227 }
228 size_t result = 1;
229 while (result < n) {
230 result <<= 1;
231 }
232 return result;
233 }
234
240 NEFORCE_NODISCARD size_t hash_to_index(const size_t hash) const noexcept { return (hash >> 7) & (capacity_ - 1); }
241
247 static byte_t hash_to_h2(const size_t hash) noexcept { return static_cast<byte_t>(hash & FLAT_HT_H2_MASK); }
248
253 allocator_type& get_allocator() noexcept { return alloc_lf_.get_base(); }
254
259 void alloc_arrays(const size_t cap) {
260 if (cap == 0) {
261 return;
262 }
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);
268 data_ = nullptr;
269 NEFORCE_THROW_EXCEPTION(memory_exception("flat_hashtable metadata allocation failed"));
270 }
271 for (size_t i = 0; i < cap; ++i) {
272 metadata_[i] = FLAT_HT_EMPTY;
273 }
274 }
275
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) {
283 _NEFORCE destroy(&data_[i]);
284 }
285 }
286 allocator_type& alloc = get_allocator();
287 alloc.deallocate(data_, capacity_);
288 data_ = nullptr;
289 }
290 if (metadata_ != nullptr) {
291 ::operator delete(metadata_, std::nothrow);
292 metadata_ = nullptr;
293 }
294 }
295
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));
304 size_t idx = h1;
305 size_t first_deleted = npos;
306
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};
311 }
312 if (meta == FLAT_HT_DELETED) {
313 if (first_deleted == npos) {
314 first_deleted = idx;
315 }
316 } else if (meta == h2 && equals_(extracter_(data_[idx]), key)) {
317 return {idx, true};
318 }
319 idx = (idx + 1) & (capacity_ - 1);
320 }
321 return {first_deleted, false};
322 }
323
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));
332 size_t idx = h1;
333 size_t first_deleted = npos;
334
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);
338
339 for (size_t round = 0; round < capacity_; round += 16) {
340 // wrap-around safe load: metadata array is exactly capacity_ bytes,
341 // a 16-byte load from idx may cross the array boundary
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);
346 } else {
347 byte_t buf[16];
348 for (size_t k = 0; k < remaining; ++k) {
349 buf[k] = metadata_[idx + k];
350 }
351 for (size_t k = 0; k < 16 - remaining; ++k) {
352 buf[remaining + k] = metadata_[k];
353 }
354 meta_vec = simd::load_unaligned(buf);
355 }
356
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));
360
361 int match = h2_mask;
362 while (match != 0) {
363 const int bit = countr_zero(static_cast<uintptr_t>(match));
364 const size_t slot = (idx + bit) & (capacity_ - 1);
365 if (equals_(extracter_(data_[slot]), key)) {
366 return {slot, true};
367 }
368 match &= (match - 1);
369 }
370
371 if (empty_mask != 0) {
372 const int empty_bit = countr_zero(static_cast<uintptr_t>(empty_mask));
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};
381 }
382 }
383 return {empty_slot, false};
384 }
385
386 const int del = deleted_mask;
387 if ((del != 0) && first_deleted == npos) {
388 const int del_bit = countr_zero(static_cast<uintptr_t>(del));
389 first_deleted = (idx + del_bit) & (capacity_ - 1);
390 }
391
392 idx = (idx + 16) & (capacity_ - 1);
393 }
394 return {first_deleted, false};
395 }
396
401 NEFORCE_NODISCARD bool should_rehash() const noexcept { return growth_left_ == 0; }
402
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_) {
411 return;
412 }
413
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"));
420 }
421 for (size_t i = 0; i < new_capacity; ++i) {
422 new_metadata[i] = FLAT_HT_EMPTY;
423 }
424
425 const size_t old_capacity = capacity_;
426 Value* const old_data = data_;
427 byte_t* const old_metadata = metadata_;
428
429 try {
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);
436
437 size_t new_idx = h1;
438 bool placed = false;
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) {
442 break;
443 }
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) {
450 new_idx = run_end;
451 placed = true;
452 break;
453 }
454 if (rmn == h2 && equals_(extracter_(new_data[run_end]), key)) {
455 continue;
456 }
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) {
461 break;
462 }
463 }
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];
469 shift_end = prev;
470 }
471 new_idx = run_end;
472 placed = true;
473 break;
474 }
475 if (!placed) {
476 new_idx = run_end;
477 }
478 break;
479 }
480 new_idx = (new_idx + 1) & (new_capacity - 1);
481 }
482 _NEFORCE construct(&new_data[new_idx], _NEFORCE move(old_data[i]));
483 new_metadata[new_idx] = h2;
484 }
485 }
486 } catch (...) {
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]);
490 }
491 }
492 alloc.deallocate(new_data, new_capacity);
493 ::operator delete(new_metadata, std::nothrow);
494 throw;
495 }
496
497 if (old_data) {
498 alloc.deallocate(old_data, old_capacity);
499 }
500 if (old_metadata != nullptr) {
501 ::operator delete(old_metadata, std::nothrow);
502 }
503
504 data_ = new_data;
505 metadata_ = new_metadata;
506 capacity_ = new_capacity;
507 growth_left_ = static_cast<size_t>(static_cast<double>(capacity_) * max_load_factor()) - size_;
508 }
509
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)...);
520 metadata_[idx] = h2;
521 ++size_;
522 --growth_left_;
523 }
524
532 void shift_make_room(const size_t pos) noexcept {
533 size_t end = pos;
534 for (size_t i = 0; i < capacity_; ++i) {
535 end = (end + 1) & (capacity_ - 1);
536 const byte_t m = metadata_[end];
537 if (m == FLAT_HT_EMPTY || m == FLAT_HT_DELETED) {
538 break;
539 }
540 }
541 while (end != pos) {
542 const size_t prev = (end - 1) & (capacity_ - 1);
543 _NEFORCE construct(&data_[end], _NEFORCE move(data_[prev]));
544 _NEFORCE destroy(&data_[prev]);
545 metadata_[end] = metadata_[prev];
546 end = prev;
547 }
548 }
549
554 void copy_from(const flat_hashtable& other) {
555 if (other.capacity_ == 0) {
556 return;
557 }
558 alloc_arrays(other.capacity_);
559 capacity_ = other.capacity_;
560 growth_left_ = other.growth_left_;
561 try {
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]);
566 }
567 }
568 size_ = other.size_;
569 } catch (...) {
570 clear();
571 throw;
572 }
573 }
574
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);
582 });
583 if (count_lhs != count_rhs) {
584 return false;
585 }
586 }
587 return true;
588 }
589
590 bool equal_large(const flat_hashtable& rhs) const {
591 if (size_ != rhs.size_) {
592 return false;
593 }
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));
599 }
600 for (const_iterator it = rhs.begin(); it != rhs.end(); ++it) {
601 ptrs_rhs.push_back(&(*it));
602 }
603
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);
607 };
608 _NEFORCE sort(ptrs_lhs.begin(), ptrs_lhs.end(), key_less);
609 _NEFORCE sort(ptrs_rhs.begin(), ptrs_rhs.end(), rhs_key_less);
610
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)) {
617 return false;
618 }
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)) {
622 ++i;
623 }
624 while (j < n && rhs.equals_(rhs.extracter_(*ptrs_rhs[j]), key_l)) {
625 ++j;
626 }
627 const size_type count_l = i - i_start;
628 const size_type count_r = j - j_start;
629 if (count_l != count_r) {
630 return false;
631 }
632 for (size_type k = i_start; k < i; ++k) {
633 const value_type& val = *ptrs_lhs[k];
634 bool found = false;
635 for (size_type l = j_start; l < j; ++l) {
636 if (ptrs_rhs[l] && *ptrs_rhs[l] == val) {
637 ptrs_rhs[l] = nullptr;
638 found = true;
639 break;
640 }
641 }
642 if (!found) {
643 return false;
644 }
645 }
646 for (size_type l = j_start; l < j; ++l) {
647 if (ptrs_rhs[l] != nullptr) {
648 return false;
649 }
650 }
651 }
652 return true;
653 }
654
655 static iterator to_iterator(const const_iterator& iter) noexcept {
656 return iterator(iter.index(), const_cast<flat_hashtable*>(iter.container()));
657 }
658 static const_iterator to_const_iterator(const iterator& iter) noexcept {
659 return const_iterator(iter.index(), iter.container());
660 }
661
662public:
667 explicit flat_hashtable(const size_type n = 0) {
668 if (n > 0) {
669 const size_t cap = next_power_of_2(static_cast<size_t>(static_cast<double>(n) / max_load_factor()));
670 alloc_arrays(cap);
671 capacity_ = cap;
672 growth_left_ = static_cast<size_t>(static_cast<double>(cap) * max_load_factor());
673 }
674 }
675
681 flat_hashtable(const size_type n, const HashFcn& hf) :
682 hasher_(hf) {
683 if (n > 0) {
684 const size_t cap = next_power_of_2(static_cast<size_t>(static_cast<double>(n) / max_load_factor()));
685 alloc_arrays(cap);
686 capacity_ = cap;
687 growth_left_ = static_cast<size_t>(static_cast<double>(cap) * max_load_factor());
688 }
689 }
690
697 flat_hashtable(const size_type n, const HashFcn& hf, const EqualKey& eql) :
698 hasher_(hf),
699 equals_(eql) {
700 if (n > 0) {
701 const size_t cap = next_power_of_2(static_cast<size_t>(static_cast<double>(n) / max_load_factor()));
702 alloc_arrays(cap);
703 capacity_ = cap;
704 growth_left_ = static_cast<size_t>(static_cast<double>(cap) * max_load_factor());
705 }
706 }
707
715 flat_hashtable(const size_type n, const HashFcn& hf, const EqualKey& eql, const ExtractKey& ext) :
716 hasher_(hf),
717 equals_(eql),
718 extracter_(ext) {
719 if (n > 0) {
720 const size_t cap = next_power_of_2(static_cast<size_t>(static_cast<double>(n) / max_load_factor()));
721 alloc_arrays(cap);
722 capacity_ = cap;
723 growth_left_ = static_cast<size_t>(static_cast<double>(cap) * max_load_factor());
724 }
725 }
726
732 hasher_(other.hasher_),
733 equals_(other.equals_),
734 extracter_(other.extracter_),
735 alloc_lf_(other.alloc_lf_) {
736 copy_from(other);
737 }
738
745 if (_NEFORCE addressof(other) == this) {
746 return *this;
747 }
748 clear();
749 hasher_ = other.hasher_;
750 equals_ = other.equals_;
751 extracter_ = other.extracter_;
752 alloc_lf_ = other.alloc_lf_;
753 copy_from(other);
754 return *this;
755 }
756
761 flat_hashtable(flat_hashtable&& other) noexcept :
762 data_(other.data_),
763 metadata_(other.metadata_),
764 capacity_(other.capacity_),
765 size_(other.size_),
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;
773 other.capacity_ = 0;
774 other.size_ = 0;
775 other.growth_left_ = 0;
776 }
777
784 if (_NEFORCE addressof(other) == this) {
785 return *this;
786 }
787 clear();
788 swap(other);
789 return *this;
790 }
791
795 ~flat_hashtable() { free_arrays(); }
796
801 NEFORCE_NODISCARD iterator begin() noexcept {
802 for (size_t n = 0; n < capacity_; ++n) {
803 const byte_t meta = metadata_[n];
804 if (meta != FLAT_HT_EMPTY && meta != FLAT_HT_DELETED) {
805 return iterator(n, this);
806 }
807 }
808 return end();
809 }
810
815 NEFORCE_NODISCARD iterator end() noexcept { return iterator(capacity_, this); }
816
821 NEFORCE_NODISCARD const_iterator begin() const noexcept { return cbegin(); }
822
827 NEFORCE_NODISCARD const_iterator end() const noexcept { return cend(); }
828
833 NEFORCE_NODISCARD const_iterator cbegin() const noexcept {
834 for (size_t n = 0; n < capacity_; ++n) {
835 const byte_t meta = metadata_[n];
836 if (meta != FLAT_HT_EMPTY && meta != FLAT_HT_DELETED) {
837 return const_iterator(n, this);
838 }
839 }
840 return cend();
841 }
842
847 NEFORCE_NODISCARD const_iterator cend() const noexcept { return const_iterator(capacity_, this); }
848
853 NEFORCE_NODISCARD size_type size() const noexcept { return size_; }
854
859 NEFORCE_NODISCARD size_type max_size() const noexcept { return static_cast<size_type>(-1); }
860
865 NEFORCE_NODISCARD bool empty() const noexcept { return size_ == 0; }
866
871 NEFORCE_NODISCARD size_type capacity() const noexcept { return capacity_; }
872
877 NEFORCE_NODISCARD hasher hash_function() const noexcept(is_nothrow_copy_constructible_v<hasher>) { return hasher_; }
878
883 NEFORCE_NODISCARD key_equal key_eql() const noexcept(is_nothrow_copy_constructible_v<key_equal>) { return equals_; }
884
889 NEFORCE_NODISCARD float load_factor() const noexcept {
890 return capacity_ == 0 ? 0.0F : static_cast<float>(size_) / static_cast<float>(capacity_);
891 }
892
897 NEFORCE_NODISCARD float max_load_factor() const noexcept { return alloc_lf_.value; }
898
903 void max_load_factor(const float lf) noexcept {
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_;
907 }
908
913 void rehash(const size_type new_size) { rehash_impl(new_size); }
914
921 void reserve(const size_type n) {
922 if (n <= size_) {
923 return;
924 }
925 rehash(static_cast<size_t>(static_cast<double>(n) / max_load_factor()));
926 }
927
934 template <typename... Args>
936 if (should_rehash()) {
937 const size_type new_cap = capacity_ == 0 ? 16 : capacity_ * 2;
938 rehash(max(new_cap, static_cast<size_t>(static_cast<double>(size_ + 1) / max_load_factor())));
939 }
940
941 value_type tmp(_NEFORCE forward<Args>(args)...);
942 const key_type& key = extracter_(tmp);
943 const size_t hash = hasher_(key);
944 const byte_t h2 = hash_to_h2(hash);
945
946#ifdef NEFORCE_SIMD_SSE2
947 pair<size_t, bool> probe_result = probe_find_or_insert_simd(key, h2);
948#else
949 pair<size_t, bool> probe_result = probe_find_or_insert(key, h2);
950#endif
951
952 if (probe_result.second) {
953 return {iterator(probe_result.first, this), false};
954 }
955
956 const size_t insert_idx = probe_result.first;
957 _NEFORCE construct(&data_[insert_idx], _NEFORCE move(tmp));
958 metadata_[insert_idx] = h2;
959 ++size_;
960 --growth_left_;
961 return {iterator(insert_idx, this), true};
962 }
963
970 template <typename... Args>
971 iterator emplace_equal(Args&&... args) {
972 if (should_rehash()) {
973 const size_type new_cap = capacity_ == 0 ? 16 : capacity_ * 2;
974 rehash(max(new_cap, static_cast<size_t>(static_cast<double>(size_ + 1) / max_load_factor())));
975 }
976
977 value_type tmp(_NEFORCE forward<Args>(args)...);
978 const key_type& key = extracter_(tmp);
979 const size_t hash = hasher_(key);
980 const byte_t h2 = hash_to_h2(hash);
981 const size_t h1 = hash_to_index(hash);
982
983 size_t idx = h1;
984 size_t first_deleted = npos;
985 for (size_t i = 0; i < capacity_; ++i) {
986 const byte_t meta = metadata_[idx];
987 if (meta == FLAT_HT_EMPTY) {
988 const size_t insert_idx = (first_deleted != npos) ? first_deleted : idx;
989 construct_at(insert_idx, h2, _NEFORCE move(tmp));
990 return iterator(insert_idx, this);
991 }
992 if (meta == FLAT_HT_DELETED && first_deleted == npos) {
993 first_deleted = idx;
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);
998 if (run_end == 0) {
999 const size_type new_cap = capacity_ * 2;
1000 rehash(max(new_cap, static_cast<size_t>(static_cast<double>(size_ + 1) / max_load_factor())));
1001 return emplace_equal(_NEFORCE move(tmp));
1002 }
1003 const byte_t rm = metadata_[run_end];
1004 if (rm == FLAT_HT_EMPTY || rm == FLAT_HT_DELETED) {
1005 construct_at(run_end, h2, _NEFORCE move(tmp));
1006 return iterator(run_end, this);
1007 }
1008 if (rm == h2 && equals_(extracter_(data_[run_end]), key)) {
1009 continue;
1010 }
1011 shift_make_room(run_end);
1012 construct_at(run_end, h2, _NEFORCE move(tmp));
1013 return iterator(run_end, this);
1014 }
1015 NEFORCE_THROW_EXCEPTION(value_exception("flat_hashtable: no available slot for insert_equal"));
1016 }
1017 idx = (idx + 1) & (capacity_ - 1);
1018 }
1019
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"));
1023 }
1024 construct_at(insert_idx, h2, _NEFORCE move(tmp));
1025 return iterator(insert_idx, this);
1026 }
1027
1034
1041
1047 iterator insert_equal(const value_type& value) { return emplace_equal(value); }
1048
1054 iterator insert_equal(value_type&& value) { return emplace_equal(_NEFORCE move(value)); }
1055
1062 template <typename Iterator>
1064 size_type n = _NEFORCE distance(first, last);
1065 if (n > 0) {
1066 rehash(static_cast<size_t>(static_cast<double>(size_ + n) / max_load_factor()));
1067 }
1068 for (; n > 0; --n, ++first) {
1069 insert_unique(*first);
1070 }
1071 }
1072
1079 template <typename Iterator>
1081 for (; first != last; ++first) {
1082 insert_unique(*first);
1083 }
1084 }
1085
1090 void insert_unique(std::initializer_list<value_type> ilist) { insert_unique(ilist.begin(), ilist.end()); }
1091
1098 template <typename Iterator>
1100 size_type n = _NEFORCE distance(first, last);
1101 if (n > 0) {
1102 rehash(static_cast<size_t>(static_cast<double>(size_ + n) / max_load_factor()));
1103 }
1104 for (; n > 0; --n, ++first) {
1105 insert_equal(*first);
1106 }
1107 }
1108
1115 template <typename Iterator>
1117 for (; first != last; ++first) {
1118 insert_equal(*first);
1119 }
1120 }
1121
1126 void insert_equal(std::initializer_list<value_type> ilist) { insert_equal(ilist.begin(), ilist.end()); }
1127
1133 size_type erase(const key_type& key) noexcept {
1134 if (capacity_ == 0) {
1135 return 0;
1136 }
1137
1138 const size_t hash = hasher_(key);
1139 const byte_t h2 = hash_to_h2(hash);
1140 size_t idx = hash_to_index(hash);
1141 size_type erased = 0;
1142
1143 for (size_t i = 0; i < capacity_; ++i) {
1144 const byte_t meta = metadata_[idx];
1145 if (meta == FLAT_HT_EMPTY) {
1146 break;
1147 }
1148 if (meta == h2 && equals_(extracter_(data_[idx]), key)) {
1149 _NEFORCE destroy(&data_[idx]);
1150 metadata_[idx] = FLAT_HT_DELETED;
1151 ++erased;
1152 --size_;
1153 }
1154 idx = (idx + 1) & (capacity_ - 1);
1155 }
1156 return erased;
1157 }
1158
1164 iterator erase(const iterator& position) noexcept {
1165 if (position.container() != this || position.index() >= capacity_) {
1166 return end();
1167 }
1168 const byte_t meta = metadata_[position.index()];
1169 if (meta == FLAT_HT_EMPTY || meta == FLAT_HT_DELETED) {
1170 return end();
1171 }
1172
1173 _NEFORCE destroy(&data_[position.index()]);
1174 metadata_[position.index()] = FLAT_HT_DELETED;
1175 --size_;
1176
1177 size_t next = position.index() + 1;
1178 while (next < capacity_) {
1179 const byte_t next_meta = metadata_[next];
1180 if (next_meta != FLAT_HT_EMPTY && next_meta != FLAT_HT_DELETED) {
1181 return iterator(next, this);
1182 }
1183 ++next;
1184 }
1185 return end();
1186 }
1187
1194 iterator erase(iterator first, iterator last) noexcept {
1195 if (first == last) {
1196 return last;
1197 }
1198 if (first.container() != this || (last.container() != this && last != end())) {
1199 return end();
1200 }
1201
1202 for (size_t idx = first.index(); idx < last.index() && idx < capacity_; ++idx) {
1203 const byte_t meta = metadata_[idx];
1204 if (meta != FLAT_HT_EMPTY && meta != FLAT_HT_DELETED) {
1205 _NEFORCE destroy(&data_[idx]);
1206 metadata_[idx] = FLAT_HT_DELETED;
1207 --size_;
1208 }
1209 }
1210 return last;
1211 }
1212
1218 const_iterator erase(const const_iterator& position) noexcept {
1219 return to_const_iterator(erase(to_iterator(position)));
1220 }
1221
1229 return to_const_iterator(erase(to_iterator(first), to_iterator(last)));
1230 }
1231
1235 void clear() noexcept {
1236 for (size_t i = 0; i < capacity_; ++i) {
1237 if (metadata_[i] != FLAT_HT_EMPTY && metadata_[i] != FLAT_HT_DELETED) {
1238 _NEFORCE destroy(&data_[i]);
1239 }
1240 metadata_[i] = FLAT_HT_EMPTY;
1241 }
1242 size_ = 0;
1243 growth_left_ = static_cast<size_t>(static_cast<double>(capacity_) * max_load_factor());
1244 }
1245
1251 NEFORCE_NODISCARD iterator find(const key_type& key) noexcept {
1252 if (capacity_ == 0) {
1253 return end();
1254 }
1255 const size_t hash = hasher_(key);
1256 const byte_t h2 = hash_to_h2(hash);
1257 size_t idx = hash_to_index(hash);
1258
1259 for (size_t i = 0; i < capacity_; ++i) {
1260 const byte_t meta = metadata_[idx];
1261 if (meta == FLAT_HT_EMPTY) {
1262 return end();
1263 }
1264 if (meta == h2 && equals_(extracter_(data_[idx]), key)) {
1265 return iterator(idx, this);
1266 }
1267 idx = (idx + 1) & (capacity_ - 1);
1268 }
1269 return end();
1270 }
1271
1277 NEFORCE_NODISCARD const_iterator find(const key_type& key) const noexcept {
1278 if (capacity_ == 0) {
1279 return cend();
1280 }
1281 const size_t hash = hasher_(key);
1282 const byte_t h2 = hash_to_h2(hash);
1283 size_t idx = hash_to_index(hash);
1284
1285 for (size_t i = 0; i < capacity_; ++i) {
1286 const byte_t meta = metadata_[idx];
1287 if (meta == FLAT_HT_EMPTY) {
1288 return cend();
1289 }
1290 if (meta == h2 && equals_(extracter_(data_[idx]), key)) {
1291 return const_iterator(idx, this);
1292 }
1293 idx = (idx + 1) & (capacity_ - 1);
1294 }
1295 return cend();
1296 }
1297
1303 NEFORCE_NODISCARD size_type count(const key_type& key) const noexcept {
1304 if (capacity_ == 0) {
1305 return 0;
1306 }
1307 const size_t hash = hasher_(key);
1308 const byte_t h2 = hash_to_h2(hash);
1309 size_t idx = hash_to_index(hash);
1310 size_type result = 0;
1311
1312 for (size_t i = 0; i < capacity_; ++i) {
1313 const byte_t meta = metadata_[idx];
1314 if (meta == FLAT_HT_EMPTY) {
1315 break;
1316 }
1317 if (meta == h2 && equals_(extracter_(data_[idx]), key)) {
1318 ++result;
1319 }
1320 idx = (idx + 1) & (capacity_ - 1);
1321 }
1322 return result;
1323 }
1324
1330 NEFORCE_NODISCARD bool contains(const key_type& key) const noexcept { return find(key) != cend(); }
1331
1337 NEFORCE_NODISCARD pair<iterator, iterator> equal_range(const key_type& key) {
1338 if (capacity_ == 0) {
1339 return {end(), end()};
1340 }
1341 const size_t hash = hasher_(key);
1342 const byte_t h2 = hash_to_h2(hash);
1343 size_t idx = hash_to_index(hash);
1344
1345 size_t first_idx = npos;
1346 for (size_t i = 0; i < capacity_; ++i) {
1347 const byte_t meta = metadata_[idx];
1348 if (meta == FLAT_HT_EMPTY) {
1349 return {end(), end()};
1350 }
1351 if (meta == h2 && equals_(extracter_(data_[idx]), key)) {
1352 first_idx = idx;
1353 break;
1354 }
1355 idx = (idx + 1) & (capacity_ - 1);
1356 }
1357 if (first_idx == npos) {
1358 return {end(), end()};
1359 }
1360
1361 size_t last_idx = first_idx;
1362 do {
1363 last_idx = (last_idx + 1) & (capacity_ - 1);
1364 if (last_idx == first_idx) {
1365 return {iterator(first_idx, this), end()};
1366 }
1367 } while (metadata_[last_idx] != FLAT_HT_EMPTY && equals_(extracter_(data_[last_idx]), key));
1368
1369 while (last_idx != first_idx &&
1370 (metadata_[last_idx] == FLAT_HT_EMPTY || metadata_[last_idx] == FLAT_HT_DELETED)) {
1371 last_idx = (last_idx + 1) & (capacity_ - 1);
1372 }
1373 if (last_idx == first_idx || last_idx < first_idx) {
1374 // wraparound: the range extends past the end of the array,
1375 // second iterator points to end() since forward iteration
1376 // cannot jump from the end back to the beginning
1377 return {iterator(first_idx, this), end()};
1378 }
1379 return {iterator(first_idx, this), iterator(last_idx, this)};
1380 }
1381
1387 NEFORCE_NODISCARD pair<const_iterator, const_iterator> equal_range(const key_type& key) const {
1388 if (capacity_ == 0) {
1389 return {cend(), cend()};
1390 }
1391 const size_t hash = hasher_(key);
1392 const byte_t h2 = hash_to_h2(hash);
1393 size_t idx = hash_to_index(hash);
1394
1395 size_t first_idx = npos;
1396 for (size_t i = 0; i < capacity_; ++i) {
1397 const byte_t meta = metadata_[idx];
1398 if (meta == FLAT_HT_EMPTY) {
1399 return {cend(), cend()};
1400 }
1401 if (meta == h2 && equals_(extracter_(data_[idx]), key)) {
1402 first_idx = idx;
1403 break;
1404 }
1405 idx = (idx + 1) & (capacity_ - 1);
1406 }
1407 if (first_idx == npos) {
1408 return {cend(), cend()};
1409 }
1410
1411 size_t last_idx = first_idx;
1412 do {
1413 last_idx = (last_idx + 1) & (capacity_ - 1);
1414 if (last_idx == first_idx) {
1415 return {const_iterator(first_idx, this), cend()};
1416 }
1417 } while (metadata_[last_idx] != FLAT_HT_EMPTY && equals_(extracter_(data_[last_idx]), key));
1418
1419 while (last_idx != first_idx &&
1420 (metadata_[last_idx] == FLAT_HT_EMPTY || metadata_[last_idx] == FLAT_HT_DELETED)) {
1421 last_idx = (last_idx + 1) & (capacity_ - 1);
1422 }
1423 if (last_idx == first_idx || last_idx < first_idx) {
1424 // wraparound: the range extends past the end of the array
1425 return {const_iterator(first_idx, this), cend()};
1426 }
1427 return {const_iterator(first_idx, this), const_iterator(last_idx, this)};
1428 }
1429
1434 void swap(flat_hashtable& other) noexcept {
1435 if (_NEFORCE addressof(other) == this) {
1436 return;
1437 }
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_);
1447 }
1448
1454 NEFORCE_NODISCARD bool equal_to(const flat_hashtable& rhs) const {
1455 if (size_ != rhs.size_) {
1456 return false;
1457 }
1458 if (size_ == 0) {
1459 return true;
1460 }
1461 if (this == &rhs) {
1462 return true;
1463 }
1464 if (size_ < 100) {
1465 return equal_small(rhs);
1466 }
1467 return equal_large(rhs);
1468 }
1469
1475 NEFORCE_NODISCARD bool less_than(const flat_hashtable& rhs) const
1476 noexcept(noexcept(_NEFORCE lexicographical_compare(cbegin(), cend(), rhs.cbegin(), rhs.cend()))) {
1477 return _NEFORCE lexicographical_compare(cbegin(), cend(), rhs.cbegin(), rhs.cend());
1478 }
1479};
1480 // FlatHashTable
1482
1483NEFORCE_END_NAMESPACE__
1484#endif // NEFORCE_CORE_CONTAINER_FLAT_HASHTABLE_HPP__
位操作函数
跨平台 SIMD 字节级操作
Alloc allocator_type
分配器类型
enable_if_t<!is_ranges_fwd_iter_v< Iterator > > insert_equal(Iterator first, Iterator last)
范围插入元素(允许重复键,非前向迭代器版本)
size_type count(const key_type &key) const noexcept
统计具有指定键的元素数量
flat_hashtable(const size_type n, const HashFcn &hf)
构造函数,指定初始容量和哈希函数
iterator insert_equal(value_type &&value)
插入元素(允许重复键,移动版本)
hasher hash_function() const noexcept(is_nothrow_copy_constructible_v< hasher >)
获取哈希函数对象
flat_hashtable(const size_type n, const HashFcn &hf, const EqualKey &eql)
构造函数,指定初始容量、哈希函数和相等比较函数
HashFcn hasher
哈希函数类型
const_iterator erase(const const_iterator &position) noexcept
删除指定位置的元素(常量迭代器版本)
iterator erase(const iterator &position) noexcept
删除指定位置的元素
void max_load_factor(const float lf) noexcept
设置最大负载因子
enable_if_t< is_ranges_fwd_iter_v< Iterator > > insert_equal(Iterator first, Iterator last)
范围插入元素(允许重复键,前向迭代器版本)
flat_hashtable_iterator< true, flat_hashtable > const_iterator
常量迭代器类型
enable_if_t< is_ranges_fwd_iter_v< Iterator > > insert_unique(Iterator first, Iterator last)
范围插入元素(唯一键,前向迭代器版本)
size_type erase(const key_type &key) noexcept
删除所有具有指定键的元素
flat_hashtable & operator=(const flat_hashtable &other)
拷贝赋值运算符
pair< iterator, bool > emplace_unique(Args &&... args)
构造元素(唯一键版本)
EqualKey key_equal
键相等比较函数类型
pair< iterator, bool > insert_unique(const value_type &value)
插入元素(唯一键,拷贝版本)
pair< iterator, iterator > equal_range(const key_type &key)
获取等于指定键的元素范围
void insert_unique(std::initializer_list< value_type > ilist)
初始化列表插入(唯一键)
pair< const_iterator, const_iterator > equal_range(const key_type &key) const
获取等于指定键的元素范围(常量版本)
bool empty() const noexcept
检查是否为空
static constexpr byte_t FLAT_HT_EMPTY
EMPTY 元数据标记
flat_hashtable(flat_hashtable &&other) noexcept
移动构造函数
flat_hashtable(const size_type n, const HashFcn &hf, const EqualKey &eql, const ExtractKey &ext)
构造函数,指定所有函数对象
size_type max_size() const noexcept
获取最大可能大小
const_iterator begin() const noexcept
获取常量起始迭代器
static constexpr byte_t FLAT_HT_DELETED
DELETED 元数据标记
const_iterator erase(const_iterator first, const_iterator last) noexcept
删除指定范围内的常量元素
void rehash(const size_type new_size)
重新哈希,调整容量
ptrdiff_t difference_type
差值类型
size_type size() const noexcept
获取元素数量
iterator emplace_equal(Args &&... args)
构造元素(允许重复键版本)
flat_hashtable(const flat_hashtable &other)
拷贝构造函数
flat_hashtable(const size_type n=0)
构造函数,指定初始容量
size_type capacity() const noexcept
获取容量(slot 总数)
bool less_than(const flat_hashtable &rhs) const noexcept(noexcept(_NEFORCE lexicographical_compare(cbegin(), cend(), rhs.cbegin(), rhs.cend())))
小于比较操作符
bool equal_to(const flat_hashtable &rhs) const
相等比较操作符
key_equal key_eql() const noexcept(is_nothrow_copy_constructible_v< key_equal >)
获取键相等比较函数对象
flat_hashtable & operator=(flat_hashtable &&other) noexcept
移动赋值运算符
iterator begin() noexcept
获取起始迭代器
iterator erase(iterator first, iterator last) noexcept
删除指定范围内的元素
const_iterator end() const noexcept
获取常量结束迭代器
void reserve(const size_type n)
预留空间
pair< iterator, bool > insert_unique(value_type &&value)
插入元素(唯一键,移动版本)
flat_hashtable_iterator< false, flat_hashtable > iterator
迭代器类型
float load_factor() const noexcept
获取当前负载因子
iterator insert_equal(const value_type &value)
插入元素(允许重复键,拷贝版本)
const Value & const_reference
常量引用类型
enable_if_t<!is_ranges_fwd_iter_v< Iterator > > insert_unique(Iterator first, Iterator last)
范围插入元素(唯一键,非前向迭代器版本)
iterator find(const key_type &key) noexcept
查找具有指定键的元素
const_iterator find(const key_type &key) const noexcept
查找具有指定键的元素(常量版本)
bool contains(const key_type &key) const noexcept
检查是否包含指定键
const Value * const_pointer
常量指针类型
void insert_equal(std::initializer_list< value_type > ilist)
初始化列表插入(允许重复键)
内存构造和销毁函数
constexpr T * addressof(T &x) noexcept
获取对象的地址
constexpr T && forward(remove_reference_t< T > &x) noexcept
完美转发左值
constexpr int countr_zero(const uintptr_t x) noexcept
计算整数尾随零的个数
constexpr bool lexicographical_compare(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2, Compare comp) noexcept(noexcept(++first1) &&noexcept(++first2) &&noexcept(comp(*first1, *first2)) &&noexcept(first1==last1 &&first2 !=last2))
字典序比较两个范围
constexpr const T & max(const T &a, const T &b, Compare comp) noexcept(noexcept(comp(a, b)))
返回两个值中的较大者
unsigned char byte_t
字节类型,定义为无符号字符
constexpr iter_difference_t< Iterator > count_if(Iterator first, Iterator last, const T &value, BinaryPredicate pred)
统计范围内满足二元谓词的元素数量
constexpr T * construct(T *ptr, Args &&... args) noexcept(is_nothrow_constructible_v< T, Args... >)
在指定内存位置构造对象
constexpr void destroy(T *pointer) noexcept(is_nothrow_destructible_v< T >)
销毁单个对象
constexpr iter_difference_t< Iterator > distance(Iterator first, Iterator last)
计算两个迭代器之间的距离
constexpr Iterator prev(Iterator iter, iter_difference_t< Iterator > n=1)
获取迭代器的前一个位置
constexpr Iterator next(Iterator iter, iter_difference_t< Iterator > n=1)
获取迭代器的后一个位置
constexpr decimal_t round(const decimal_t x) noexcept
四舍五入
uint64_t size_t
无符号大小类型
uint64_t uintptr_t
可容纳指针的无符号整数类型
int64_t ptrdiff_t
指针差类型
constexpr Iterator2 move(Iterator1 first, Iterator1 last, Iterator2 result) noexcept(noexcept(inner::__move_aux(first, last, result)))
移动范围元素
void sort(Iterator first, Iterator last, Compare comp)
标准排序
constexpr decltype(auto) end(Container &cont) noexcept(noexcept(cont.end()))
获取容器的结束迭代器
constexpr decltype(auto) begin(Container &cont) noexcept(noexcept(cont.begin()))
获取容器的起始迭代器
constexpr bool is_nothrow_copy_constructible_v
is_nothrow_copy_constructible的便捷变量模板
typename enable_if< Test, T >::type enable_if_t
enable_if的便捷别名
typename conditional< Test, T1, T2 >::type conditional_t
conditional的便捷别名
集合器接口
迭代器接口
排序算法
typename container_type::size_type size_type
大小类型
reference dereference() const noexcept
解引用操作
conditional_t< IsConst, typename container_type::const_reference, typename container_type::reference > reference
引用类型
bool equal_to(const flat_hashtable_iterator &rhs) const noexcept
相等比较
typename container_type::difference_type difference_type
差值类型
void increment() noexcept
递增操作
forward_iterator_tag iterator_category
前向迭代器
conditional_t< IsConst, typename container_type::const_pointer, typename container_type::pointer > pointer
指针类型
typename container_type::value_type value_type
值类型
哈希函数的主模板
集合器接口模板
迭代器接口模板
存储两个值的元组对
T2 second
第二个元素
T1 first
第一个元素
动态大小数组容器