17 return (value * 870) >> 10U;
25 return (value * 409) >> 10U;
37template<
typename K,
typename T,
bool is_map,
typename Hash,
typename KeyEqual,
typename Allocator>
41 using mapped_type = T;
42 using value_type = std::conditional_t<is_map, std::pair<key_type, mapped_type>, key_type>;
43 using allocator_type = Allocator;
44 using allocator_traits = std::allocator_traits<allocator_type>;
46 using key_equal = KeyEqual;
47 using size_type = std::size_t;
49 constexpr static size_type default_bucket_count = 16;
54 bool occupied{
false };
59 using info_storage = std::vector<bucket_info>;
60 using info_iterator =
typename info_storage::iterator;
61 using info_const_iterator =
typename info_storage::const_iterator;
70 constexpr buckets(
const allocator_type& alloc,
typename allocator_type::size_type size) :
71 a(alloc), b(a.allocate(size)), e(b + size), z(e) {
77 constexpr buckets(
const buckets& rhs) =
delete;
82 constexpr buckets(buckets&& rhs)
noexcept {
90 constexpr auto operator=(
const buckets& rhs) -> buckets& =
delete;
96 constexpr auto operator=(buckets&& rhs)
noexcept -> buckets& {
102 constexpr ~buckets() {
103 a.deallocate(b, e - b);
109 void swap(buckets&& rhs)
noexcept {
119 constexpr auto allocator() noexcept -> allocator_type& {
126 constexpr auto allocator() const noexcept -> const allocator_type& {
133 constexpr auto begin() noexcept -> value_type* {
140 constexpr auto begin() const noexcept -> const value_type* {
147 constexpr auto end() noexcept -> value_type* {
154 constexpr auto end() const noexcept -> const value_type* {
161 [[nodiscard]]
constexpr auto size() const noexcept -> size_type {
175 template<
bool is_const = true>
176 class iterator_impl {
178 using iterator_concept = std::forward_iterator_tag;
179 using iterator_category = std::forward_iterator_tag;
180 using difference_type = int;
181 using value_type = hash_table::value_type;
182 using element_type = hash_table::value_type;
183 using reference = std::conditional_t<is_const, const value_type&, value_type&>;
184 using pointer = std::conditional_t<is_const, const value_type*, value_type*>;
185 using info_iter = std::conditional_t<is_const, info_const_iterator, info_iterator>;
187 constexpr iterator_impl() =
default;
188 constexpr ~iterator_impl() =
default;
195 constexpr explicit iterator_impl(pointer ptr, pointer end, info_iter info) noexcept :
196 _ptr(ptr), _end(end), _info(info) {
199 if (_ptr != _end && !_info->occupied) {
204 constexpr iterator_impl(
const iterator_impl& other)
noexcept =
default;
205 constexpr auto operator=(
const iterator_impl& other)
noexcept -> iterator_impl& =
default;
206 constexpr iterator_impl(iterator_impl&& other)
noexcept =
default;
207 constexpr auto operator=(iterator_impl&& other)
noexcept -> iterator_impl& =
default;
212 constexpr auto operator++() noexcept -> iterator_impl& {
222 constexpr auto operator++(
int)
noexcept -> iterator_impl {
223 iterator_impl tmp(*
this);
231 constexpr auto operator*() const noexcept -> reference {
238 constexpr auto operator->() const noexcept -> pointer {
246 constexpr auto operator<=>(
const iterator_impl& rhs)
const noexcept =
default;
249 constexpr void fast_forward() noexcept {
250 while (_ptr != _end && !_info->occupied) {
262 using iterator = iterator_impl<false>;
263 using const_iterator = iterator_impl<true>;
266 constexpr hash_table() : _buckets(allocator_type(), default_bucket_count), _info(default_bucket_count) {
270 constexpr ~hash_table() {
271 for (
auto& value : *this) {
272 allocator_traits::destroy(_buckets.allocator(), std::addressof(value));
282 constexpr explicit hash_table(size_type bucket_count,
283 const Hash& hash = Hash(),
284 const key_equal& equal = key_equal(),
285 const Allocator& alloc = Allocator()) :
286 _buckets(alloc, bucket_count), _info(bucket_count), _hash(hash), _equal(equal) {
287 assert((bucket_count % 2 == 0) &&
"Bucket count must be a power of two");
293 constexpr explicit hash_table(
const Allocator& alloc) : hash_table(default_bucket_count, alloc) {
300 constexpr hash_table(size_type bucket_count,
const Allocator& alloc) : hash_table(bucket_count, Hash(), alloc) {
308 constexpr hash_table(size_type bucket_count,
const Hash& hash,
const Allocator& alloc) :
309 hash_table(bucket_count, hash, key_equal(), alloc) {
322 template<
typename input_iterator>
323 constexpr hash_table(input_iterator first,
325 size_type bucket_count = default_bucket_count,
326 const Hash& hash = Hash(),
327 const key_equal& equal = key_equal(),
328 const Allocator& alloc = Allocator()) :
329 _buckets(alloc, bucket_count), _info(default_bucket_count), _equal(equal), _hash(hash) {
330 assert((bucket_count % 2 == 0) &&
"Bucket count must be a power of two");
331 for (; first != last; ++first) {
332 emplace_or_assign_impl(std::move(*first));
344 template<
typename input_iterator>
345 constexpr hash_table(input_iterator first,
347 size_type bucket_count,
348 const Allocator& alloc = Allocator()) : hash_table(first, last, bucket_count, Hash(), key_equal(), alloc) {
360 template<
typename input_iterator>
361 constexpr hash_table(input_iterator first,
363 size_type bucket_count,
365 const Allocator& alloc = Allocator()) : hash_table(first, last, bucket_count, hash, key_equal(), alloc) {
375 constexpr hash_table(std::initializer_list<value_type> init,
376 size_type bucket_count = default_bucket_count,
377 const Hash& hash = Hash(),
378 const KeyEqual& equal = KeyEqual(),
379 const Allocator& alloc = Allocator()) : hash_table(init.begin(), init.end(), bucket_count, hash, equal, alloc) {
385 constexpr hash_table(
const hash_table& rhs) :
386 _info(rhs._info), _size(rhs._size), _hash(rhs._hash), _equal(rhs._equal),
387 _buckets(rhs._buckets.allocator(), rhs._buckets.size()) {
388 for (std::size_t idx{};
const auto& info : _info) {
390 std::uninitialized_copy_n(rhs._buckets.begin() + idx, 1, _buckets.begin() + idx);
399 constexpr auto operator=(
const hash_table& rhs) -> hash_table& {
400 hash_table copy(rhs);
401 swap(std::move(copy));
408 constexpr hash_table(hash_table&& rhs)
noexcept {
409 swap(std::move(rhs));
415 constexpr auto operator=(hash_table&& rhs)
noexcept -> hash_table& {
416 swap(std::move(rhs));
423 constexpr void swap(hash_table&& rhs) {
424 std::swap(_buckets, rhs._buckets);
425 std::swap(_info, rhs._info);
426 std::swap(_size, rhs._size);
427 std::swap(_equal, rhs._equal);
428 std::swap(_hash, rhs._hash);
434 constexpr auto get_allocator() const noexcept -> allocator_type {
435 return _buckets.allocator();
442 constexpr auto operator[](
const key_type& key) -> mapped_type&
445 if (
auto iter = find(key); iter != end()) {
448 auto [iter, _] = emplace_impl(std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple());
458 auto at(
const key_type& key) -> mapped_type&
461 if (
auto iter = find_impl(*
this, key); iter != end()) {
464 throw std::out_of_range{
"key not found in hash_table" };
473 auto at(
const key_type& key)
const ->
const mapped_type&
476 if (
auto iter = find_impl(*
this, key); iter != end()) {
479 throw std::out_of_range{
"key not found in hash_table" };
483 void clear() noexcept {
485 for (
auto& value : *this) {
486 allocator_traits::destroy(_buckets.allocator(), std::addressof(value));
489 for (
auto& info : _info) {
490 info = bucket_info{};
500 auto insert(
const value_type& value) -> std::pair<iterator, bool> {
501 return emplace_impl(value);
509 auto insert([[maybe_unused]] iterator hint, value_type&& value) -> iterator {
510 return emplace_impl(std::move(value)).first;
518 auto insert([[maybe_unused]] iterator hint,
const value_type& value) -> iterator {
519 return emplace_impl(value).first;
527 auto insert_or_assign(
const value_type& value) -> std::pair<iterator, bool> {
528 return emplace_or_assign_impl(value);
538 template<
typename... Args>
539 auto emplace(Args&&... args) -> std::pair<iterator, bool> {
540 return emplace_impl(std::forward<Args>(args)...);
546 void erase(
const key_type& key) {
547 return erase_impl(key);
555 void erase(C&& comparable_key) {
556 return erase_impl(std::forward<C>(comparable_key));
562 void erase(const_iterator pos) {
563 return erase_impl(get_key(*pos));
569 void erase(iterator pos) {
570 return erase_impl(get_key(*pos));
578 [[nodiscard]]
auto count(
const key_type& key)
const noexcept -> size_type {
579 return find_impl(*
this, key) != end() ? 1 : 0;
587 [[nodiscard]]
auto contains(
const key_type& key)
const noexcept ->
bool {
588 return count(key) > 0;
595 [[nodiscard]]
auto find(
const auto& key)
noexcept -> iterator {
596 return find_impl(*
this, key);
603 [[nodiscard]]
auto find(
const key_type& key)
const noexcept -> const_iterator {
604 return find_impl(*
this, key);
610 void reserve(size_type new_size) {
611 if (new_size < _size) {
615 hash_table h_table(new_size, _hash, _equal, _buckets.allocator());
617 for (
auto& value : *this) {
618 h_table._emplace_impl(std::move(value));
621 std::swap(_buckets, h_table._buckets);
622 std::swap(_info, h_table._info);
623 std::swap(_size, h_table._size);
624 std::swap(_equal, h_table._equal);
625 std::swap(_hash, h_table._hash);
631 [[nodiscard]]
constexpr auto begin() noexcept -> iterator {
632 return iterator{ _buckets.begin(), _buckets.end(), _info.begin() };
638 [[nodiscard]]
constexpr auto end() noexcept -> iterator {
639 return iterator{ _buckets.end(), _buckets.end(), _info.end() };
645 [[nodiscard]]
constexpr auto begin() const noexcept -> const_iterator {
646 return const_iterator{ _buckets.begin(), _buckets.end(), _info.begin() };
652 [[nodiscard]]
constexpr auto end() const noexcept -> const_iterator {
653 return const_iterator{ _buckets.end(), _buckets.end(), _info.end() };
659 [[nodiscard]]
constexpr auto cbegin() const noexcept -> iterator {
666 [[nodiscard]]
constexpr auto cend() const noexcept -> iterator {
673 [[nodiscard]]
constexpr auto size() const noexcept -> size_type {
681 [[nodiscard]]
constexpr auto empty() const noexcept ->
bool {
687 void erase_impl(C&& key) {
688 size_type bucket_size = _buckets.size();
690 size_type hash = _hash(key);
692 size_type index =
mod_2n(hash, bucket_size);
695 auto info = _info.begin();
697 ptr = _buckets.begin() + index;
698 info = _info.begin() + index;
700 if (!info->occupied || psl > info->psl) {
704 if (info->hash != hash || !_equal(get_key(*ptr), key)) {
705 index =
mod_2n(index + 1, bucket_size);
713 value_type temp = std::move(*ptr);
717 info->occupied =
false;
719 index =
mod_2n(index + 1, bucket_size);
720 auto nptr = _buckets.begin() + index;
721 auto n_info = _info.begin() + index;
723 if (!n_info->occupied || n_info->psl == 0) {
728 *ptr = std::move(*nptr);
730 *info = std::move(*n_info);
735 if (_size > default_bucket_count && _size < threshold) {
736 const auto new_size = bucket_size >> size_type{ 1 };
741 template<
typename... Args>
742 constexpr auto emplace_impl(Args&&... args) -> std::pair<iterator, bool> {
743 size_type buckets_size = _buckets.size();
745 if (_size > threshold) {
746 const auto new_size = buckets_size << size_type{ 1 };
750 return _emplace_impl(std::forward<Args>(args)...);
753 template<
typename... Args>
754 constexpr auto emplace_or_assign_impl(Args&&... args) -> std::pair<iterator, bool> {
755 size_type buckets_size = _buckets.size();
757 if (_size > threshold) {
758 const auto new_size = buckets_size << size_type{ 1 };
761 return _emplace_or_assign_impl(std::forward<Args>(args)...);
764 template<
typename... Args>
765 constexpr auto _emplace_or_assign_impl(Args&&... args) -> std::pair<iterator, bool> {
766 size_type buckets_size = _buckets.size();
767 value_type temp(std::forward<Args>(args)...);
768 size_type hash = _hash(get_key(temp));
770 bucket_info n_info = {
true, hash, psl };
771 size_type index =
mod_2n(hash, buckets_size);
773 value_type* ret{
nullptr };
774 info_iterator ret_info{};
777 value_type* ptr = _buckets.begin() + index;
778 auto info = _info.begin() + index;
779 if (info->occupied) {
780 if (info->hash == hash && _equal(get_key(*ptr), get_key(temp))) {
781 get_value(*ptr) = std::move(get_value(temp));
782 return std::make_pair(create_iterator(ptr, info),
false);
785 if (n_info.psl > info->psl) {
786 std::swap(temp, *ptr);
787 std::swap(n_info, *info);
794 index =
mod_2n(index + 1, buckets_size);
797 allocator_traits::construct(_buckets.allocator(), ptr, std::move(temp));
804 return std::make_pair(create_iterator(ret, ret_info),
true);
808 template<
typename... Args>
809 constexpr auto _emplace_impl(Args&&... args) ->
decltype(
auto) {
810 size_type buckets_size = _buckets.size();
811 value_type temp(std::forward<Args>(args)...);
812 size_type hash = _hash(get_key(temp));
814 bucket_info n_info = {
true, hash, psl };
815 size_type index =
mod_2n(hash, buckets_size);
817 value_type* ret{
nullptr };
818 info_iterator ret_info{};
821 value_type* ptr = _buckets.begin() + index;
822 auto info = _info.begin() + index;
823 if (info->occupied) {
824 if (info->hash == hash && _equal(get_key(*ptr), get_key(temp))) {
825 return std::make_pair(create_iterator(ptr, info),
false);
828 if (n_info.psl > info->psl) {
829 std::swap(temp, *ptr);
830 std::swap(n_info, *info);
837 index =
mod_2n(index + 1, buckets_size);
840 allocator_traits::construct(_buckets.allocator(), ptr, std::move(temp));
847 return std::make_pair(create_iterator(ret, ret_info),
true);
851 static constexpr auto find_impl(
auto& self,
const key_type& key) ->
decltype(
auto) {
852 size_type buckets_size = self._buckets.size();
856 size_type hash = self._hash(key);
857 size_type index =
mod_2n(hash, buckets_size);
859 size_type probes = 0;
861 auto ptr = self._buckets.begin() + index;
862 auto info = self._info.begin() + index;
863 if (info->occupied && info->hash == hash && self._equal(get_key(*ptr), key)) {
864 return self.create_iterator(ptr, info);
866 if (!info->occupied || probes > info->psl) {
870 index =
mod_2n(index + 1, buckets_size);
874 constexpr auto create_iterator(value_type* ptr, info_iterator info)
noexcept -> iterator {
875 return iterator{ ptr, _buckets.end(), info };
878 constexpr auto create_iterator(
const value_type* ptr, info_const_iterator info)
const noexcept -> const_iterator {
879 return const_iterator{ ptr, _buckets.end(), info };
882 constexpr static auto get_key(
auto& entry) ->
auto& {
883 if constexpr (is_map) {
890 constexpr static auto get_value(
auto& entry) ->
auto& {
891 if constexpr (is_map) {
899 buckets _buckets{ allocator_type(), default_bucket_count };
900 info_storage _info{ default_bucket_count };
Definition component.hpp:17
constexpr auto mod_2n(auto value, auto divisor) noexcept -> decltype(auto)
Calculate the value % b=2^n.
Definition bits.hpp:10
constexpr auto approx_40_percent(auto value) noexcept -> decltype(auto)
Approximately calculate 40% of passed value. 40% is used as a reserve threshold.
Definition hash_table.hpp:24
constexpr auto approx_85_percent(auto value) noexcept -> decltype(auto)
Approximately calculate 85% of passed value. 85% is used as a reserve threshold.
Definition hash_table.hpp:16