8#include "Stroika/Foundation/Execution/Exceptions.h"
9#include "Stroika/Foundation/Memory/Common.h"
18 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
20 :
HashTable{kBufferedBuckets_, hashFunction, keyComparer}
23 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
25 : fHasher_{hashFunction}
26 , fKeyComparer_{keyComparer}
30 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
35 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
40 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
43 return ForwardIterator{
this};
45 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
46 constexpr auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::end () -> ForwardIterator
48 return ForwardIterator{};
50 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
51 inline void HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::MoveIteratorHereAfterClone (ForwardIterator* pi,
const HashTable* movedFrom)
const
56#if qStroika_Foundation_Debug_AssertionsChecked
57 Require (pi->fData_ == movedFrom);
59 Require (this->bucket_count () == movedFrom->bucket_count ());
62 if constexpr (derived_from<LayoutType_, HashTable_Support::SeparateChainingTag>) {
65#if qStroika_Foundation_Debug_AssertionsChecked
69 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
72 size_t hashVal = Hash_ (t.fKey);
73 if constexpr (derived_from<LayoutType_, HashTable_Support::SeparateChainingTag>) {
74 switch (TraitsType::kAddOrExtendOrReplace) {
76 for (
auto i : fBuckets_[hashVal].fElements) {
77 if (this->fKeyComparer_ (i.fKey, t.fKey)) {
85 for (
auto i = fBuckets_[hashVal].fElements.begin (); i != fBuckets_[hashVal].fElements.end (); ++i) {
86 if (this->fKeyComparer_ (i->fKey, t.fKey)) {
87 if constexpr (same_as<MAPPED_TYPE, void>) {
102 for (
auto i : fBuckets_[hashVal].fElements) {
103 if (this->fKeyComparer_ (i.fKey, t.fKey)) {
114 fBuckets_[hashVal].fElements.push_back (t);
120 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
122 requires (same_as<MAPPED_TYPE, void>)
126 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
127 template <same_as<MAPPED_TYPE> MAPPED_TYPE2>
129 requires (not same_as<MAPPED_TYPE, void>)
133 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
136 Add (p.first, p.second);
138 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
141 size_t hashVal = Hash_ (t);
142 if constexpr (derived_from<LayoutType_, HashTable_Support::SeparateChainingTag>) {
143 for (
auto i : fBuckets_[hashVal].fElements) {
144 if (this->fKeyComparer_ (i.fKey, t)) {
151 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
154 if constexpr (derived_from<LayoutType_, HashTable_Support::SeparateChainingTag>) {
155 fBuckets_[i.fBucketIndex_].fElements.Remove (i.fIntraBucketIndex_);
157 if (nextI !=
nullptr) {
159 if (nextI->fIntraBucketIndex_ == fBuckets_[nextI->fBucketIndex_].fElements.size ()) {
160 ++nextI->fBucketIndex_;
161 nextI->fIntraBucketIndex_ = 0;
162 nextI->AdvanceOverEmptyBuckets_ ();
167 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
172 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
173 bool HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::RemoveIf (
const key_type& t)
175 size_t hashVal = Hash_ (t);
176 if constexpr (derived_from<LayoutType_, HashTable_Support::SeparateChainingTag>) {
177 for (
auto i = fBuckets_[hashVal].fElements.begin (); i != fBuckets_[hashVal].fElements.end (); ++i) {
178 if (this->fKeyComparer_ (i->fKey, t)) {
179 fBuckets_[hashVal].fElements.Remove (i - fBuckets_[hashVal].fElements.begin ());
188 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
191 ForwardIterator next{};
195 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
198 ForwardIterator next{};
199 Remove (ForwardIterator{
this, i}, &next);
200 return next.GetUnderlyingIteratorRep ();
203 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
206 size_t useBucketCount = Math::AtLeast (Math::PrimeAtLeastThisBig (newBucketCount), kBufferedBuckets_);
207 if (useBucketCount != fBuckets_.size ()) {
208 if (this->empty ()) {
209 fBuckets_.resize (newBucketCount);
214 HashTable n{newBucketCount, fHasher_, fKeyComparer_};
215 for (
auto i : *
this) {
219 fBuckets_ = move (n.fBuckets_);
223 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
227 float lf = load_factor ();
228 if constexpr (TRAITS::kAutoShrinkBucketCount) {
229 float thresholdBelowWhichWeShouldShrink = fMaxLoadFactor_ / 10;
230 if (lf < thresholdBelowWhichWeShouldShrink) {
231 float targetLoadFactor = fMaxLoadFactor_ * 1.5;
232 size_t targetBucketCount =
233 Containers::Support::ReserveTweaks::GetScaledUpCapacity (
static_cast<size_t> (targetLoadFactor * fCachedSize_ + 1));
234 ReHash (targetBucketCount);
238 if (lf > fMaxLoadFactor_) {
239 float targetLoadFactor = fMaxLoadFactor_ * 1.5f;
240 size_t targetBucketCount =
241 Containers::Support::ReserveTweaks::GetScaledUpCapacity (
static_cast<size_t> (targetLoadFactor * fCachedSize_ + 1));
242 ReHash (targetBucketCount);
245 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
248 return fBuckets_.size ();
250 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
253 Require (bucketIdx < bucket_count ());
254 return fBuckets_[bucketIdx].fElements.size ();
256 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
261 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
264 return fCachedSize_ == 0;
266 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
269 return static_cast<float> (fCachedSize_) / fBuckets_.size ();
271 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
274 return fMaxLoadFactor_;
276 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
280 fMaxLoadFactor_ = mlf;
282 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
285 if (fCachedSize_ != 0) {
286 for (
auto& bi : fBuckets_) {
287 bi.fElements.clear ();
292 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
295 size_t hashVal = Hash_ (key);
296 if constexpr (derived_from<LayoutType_, HashTable_Support::SeparateChainingTag>) {
297 for (
auto i : this->fBuckets_[hashVal].fElements) {
298 if (this->fKeyComparer_ (i.fKey, key)) {
305 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
306 template <invocable<
typename TRAITS::value_type> FUNCTION>
312 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
313 template <invocable<
typename TRAITS::value_type> FUNCTION>
316 if constexpr (derived_from<LayoutType_, HashTable_Support::SeparateChainingTag>) {
320#if __cpp_lib_execution >= 201603L
322 std::for_each (execution::par, fBuckets_.begin (), fBuckets_.end (), [&] (
const BucketType_& bi) {
323 std::for_each (execution::par, bi.fElements.begin (), bi.fElements.end (),
324 [&] (const value_type& v) { forward<FUNCTION> (doToElement) (v); });
330 for (
const auto& bi : this->fBuckets_) {
331 for (
const auto& i : bi.fElements) {
332 forward<FUNCTION> (doToElement) (i);
339 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
342 size_t hashVal = Hash_ (key);
343 if constexpr (derived_from<LayoutType_, HashTable_Support::SeparateChainingTag>) {
345 for (
auto i : fBuckets_[hashVal].fElements) {
346 if (this->fKeyComparer_ (i.fKey, key)) {
352 return ForwardIterator{};
354 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
355 template <
typename ARG_T>
356 auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::Find (ARG_T key)
const -> ForwardIterator
357 requires (not same_as<typename TRAITS::AlternateFindType, void> and same_as<remove_cvref_t<ARG_T>,
typename TRAITS::AlternateFindType>)
359 size_t hashVal = Hash_ (key);
360 if constexpr (derived_from<LayoutType_, HashTable_Support::SeparateChainingTag>) {
362 for (
auto i : this->fBuckets_[hashVal].fElements) {
363 if (this->fKeyComparer_ (i.fKey, key)) {
364 return ForwardIterator{
this, make_tuple (hashVal, idx)};
369 return ForwardIterator{};
371 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
372 template <predicate<
typename TRAITS::key_type> FUNCTION>
373 auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::Find (FUNCTION&& firstThat)
const -> ForwardIterator
375 if constexpr (derived_from<LayoutType_, HashTable_Support::SeparateChainingTag>) {
377 for (
const auto& bi : fBuckets_) {
379 for (
const auto& i : bi.fElements) {
380 if (forward<FUNCTION> (firstThat) (i)) {
381 return ForwardIterator{
this, make_tuple (hashVal, idx)};
388 return ForwardIterator{};
390 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
393 return this->Find (key);
395 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
396 template <
typename ARG_T>
398 requires (not same_as<typename TRAITS::AlternateFindType, void> and same_as<remove_cvref_t<ARG_T>,
typename TRAITS::AlternateFindType>)
402 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
403 template <
typename CHECKED_T>
405 requires (not same_as<MAPPED_TYPE, void>)
407 size_t bucketIndex = get<0> (it.GetUnderlyingIteratorRep ());
408 size_t intraBucketIndex = get<1> (it.GetUnderlyingIteratorRep ());
409 this->fBuckets_[bucketIndex].fElements[intraBucketIndex].fValue = newValue;
411 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
412 constexpr void HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::Invariant () const noexcept
414#if qStroika_Foundation_Debug_AssertionsChecked
418#if qStroika_Foundation_Debug_AssertionsChecked
419 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
430 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
431 constexpr HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::ForwardIterator::ForwardIterator (
const HashTable* data) noexcept
432 : ForwardIterator{data, make_tuple (0, 0)}
434 AdvanceOverEmptyBuckets_ ();
436 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
437 constexpr HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::ForwardIterator::ForwardIterator (
const HashTable* data, UnderlyingIteratorRep startAt) noexcept
439 , fBucketIndex_{get<0> (startAt)}
440 , fIntraBucketIndex_{get<1> (startAt)}
444#if qStroika_Foundation_Debug_AssertionsChecked
445 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
446 HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::ForwardIterator::~ForwardIterator ()
451 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
454 return not this->AtEnd ();
456 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
459 Assert (this->fData_ ==
nullptr or this->fBucketIndex_ <= this->fData_->bucket_count ());
460 return this->fData_ ==
nullptr or this->fBucketIndex_ == this->fData_->bucket_count ();
462 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
465 Require (not AtEnd ());
466 return fData_->fBuckets_[this->fBucketIndex_].fElements[this->fIntraBucketIndex_];
468 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
469 inline auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::ForwardIterator::operator->() const -> const value_type*
471 Require (not this->AtEnd ());
472 return &this->fData_->fBuckets_[this->fBucketIndex_].fElements[this->fIntraBucketIndex_];
474 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
477 Require (fData_ == rhs.fData_ or fData_ ==
nullptr or rhs.fData_ ==
nullptr);
478 bool done = this->AtEnd ();
479 bool rDone = rhs.AtEnd ();
480 if (done and rDone) {
487 return this->fBucketIndex_ == rhs.fBucketIndex_ and this->fIntraBucketIndex_ == rhs.fIntraBucketIndex_;
489 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
492 return make_tuple (this->fBucketIndex_, this->fIntraBucketIndex_);
494 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
497 this->fBucketIndex_ = get<0> (l);
498 this->fIntraBucketIndex_ = get<1> (l);
501 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
502 inline auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::ForwardIterator::operator++ () -> ForwardIterator&
504 Require (not this->AtEnd ());
506 ++this->fIntraBucketIndex_;
507 Assert (this->fIntraBucketIndex_ <= this->fData_->bucket_size (this->fBucketIndex_));
508 if (this->fIntraBucketIndex_ == this->fData_->bucket_size (this->fBucketIndex_)) {
509 ++this->fBucketIndex_;
510 this->fIntraBucketIndex_ = 0;
512 AdvanceOverEmptyBuckets_ ();
515 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
518 ForwardIterator result = *
this;
522 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
525 while (this->fBucketIndex_ < this->fData_->bucket_count () and this->fData_->bucket_size (this->fBucketIndex_) == 0) {
526 ++this->fBucketIndex_;
529 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
532#if qStroika_Foundation_Debug_AssertionsChecked
533 Require (data == this->fData_);
536 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
539#if qStroika_Foundation_Debug_AssertionsChecked
543#if qStroika_Foundation_Debug_AssertionsChecked
544 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
#define RequireNotNull(p)
#define AssertNotReached()
implement hash table support in a lightweight standard template library style. Use traits to describe...
nonvirtual size_t size() const
nonvirtual ForwardIterator erase(const ForwardIterator &i)
stdlib like names and semantics (though may want to rethink the ForwardIterator vs UnderlyingIterator...
nonvirtual KeyEqualsComparerType key_eq() const
nonvirtual bool empty() const
nonvirtual bool contains(ArgByValueType< key_type > key) const
nonvirtual void insert(const pair< KEY_TYPE, MAPPED_TYPE > &p)
somewhat stdlib-like names - that will do what is expected of someone from stdc++,...
nonvirtual size_t bucket_count() const
HashTable(const KeyHasherType &hashFunction={}, const KeyEqualsComparerType &keyComparer={})
tuple< size_t, size_t > UnderlyingIteratorRep
nonvirtual void ReHash(size_t newBucketCount)
nonvirtual ForwardIterator Find(ArgByValueType< key_type > key) const
nonvirtual KeyHasherType hash_function() const
nonvirtual size_t bucket_size(size_t bucketIdx) const
nonvirtual void ReHashIfNeeded()
nonvirtual void Remove(const ForwardIterator &i, ForwardIterator *nextI=nullptr)
nonvirtual float load_factor() const
average number of elements per bucket
nonvirtual bool Add(const value_type &t)
Add an item (key value pair typically, but the value can be void). Return true on list change; Respec...
nonvirtual float max_load_factor() const
average number of elements per bucket
nonvirtual void Apply(FUNCTION &&doToElement) const
shared_lock< const AssertExternallySynchronizedChecker > ReadContext
Instantiate AssertExternallySynchronizedChecker::ReadContext to designate an area of code where prote...
SequencePolicy
equivalent which of 4 types being used std::execution::sequenced_policy, parallel_policy,...
@ ePar
must synchronize shared data, can use mutex (or atomics), cuz each parallel execution in real thread
@ eSeq
default case - not parallelized
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...