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 = Support::ReserveTweaks::GetScaledUpCapacity (
static_cast<size_t> (targetLoadFactor * fCachedSize_ + 1));
233 ReHash (targetBucketCount);
237 if (lf > fMaxLoadFactor_) {
238 float targetLoadFactor = fMaxLoadFactor_ * 1.5f;
239 size_t targetBucketCount = Support::ReserveTweaks::GetScaledUpCapacity (
static_cast<size_t> (targetLoadFactor * fCachedSize_ + 1));
240 ReHash (targetBucketCount);
243 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
246 return fBuckets_.size ();
248 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
251 Require (bucketIdx < bucket_count ());
252 return fBuckets_[bucketIdx].fElements.size ();
254 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
259 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
262 return fCachedSize_ == 0;
264 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
267 return static_cast<float> (fCachedSize_) / fBuckets_.size ();
269 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
272 return fMaxLoadFactor_;
274 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
278 fMaxLoadFactor_ = mlf;
280 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
283 if (fCachedSize_ != 0) {
284 for (
auto& bi : fBuckets_) {
285 bi.fElements.clear ();
290 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
293 size_t hashVal = Hash_ (key);
294 if constexpr (derived_from<LayoutType_, HashTable_Support::SeparateChainingTag>) {
295 for (
auto i : fBuckets_[hashVal].fElements) {
296 if (this->fKeyComparer_ (i.fKey, key)) {
303 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
304 template <invocable<
typename TRAITS::value_type> FUNCTION>
307 if constexpr (derived_from<LayoutType_, HashTable_Support::SeparateChainingTag>) {
310#if __cpp_lib_execution < 201603L
313 for (
const auto& bi : fBuckets_) {
314 for (
const auto& i : bi.fElements) {
315 forward<FUNCTION> (doToElement) (i);
319#if __cpp_lib_execution >= 201603L
321 std::for_each (execution::par, fBuckets_.begin (), fBuckets_.end (), [&] (
const BucketType_& bi) {
322 std::for_each (execution::par, bi.fElements.begin (), bi.fElements.end (),
323 [&] (const value_type& v) { forward<FUNCTION> (doToElement) (v); });
330 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
333 size_t hashVal = Hash_ (key);
334 if constexpr (derived_from<LayoutType_, HashTable_Support::SeparateChainingTag>) {
336 for (
auto i : fBuckets_[hashVal].fElements) {
337 if (this->fKeyComparer_ (i.fKey, key)) {
338 return ForwardIterator{
this, make_tuple (hashVal, idx)};
343 return ForwardIterator{};
345 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
346 template <
typename ARG_T>
347 auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::Find (ARG_T key)
const -> ForwardIterator
348 requires (not same_as<typename TRAITS::AlternateFindType, void> and same_as<remove_cvref_t<ARG_T>,
typename TRAITS::AlternateFindType>)
350 size_t hashVal = Hash_ (key);
351 if constexpr (derived_from<LayoutType_, HashTable_Support::SeparateChainingTag>) {
353 for (
auto i : fBuckets_[hashVal].fElements) {
354 if (this->fKeyComparer_ (i.fKey, key)) {
355 return ForwardIterator{
this, make_tuple (hashVal, idx)};
360 return ForwardIterator{};
362 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
363 template <predicate<
typename TRAITS::key_type> FUNCTION>
364 auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::Find (FUNCTION&& firstThat)
const -> ForwardIterator
366 if constexpr (derived_from<LayoutType_, HashTable_Support::SeparateChainingTag>) {
368 for (
const auto& bi : fBuckets_) {
370 for (
const auto& i : bi.fElements) {
371 if (forward<FUNCTION> (firstThat) (i)) {
372 return ForwardIterator{
this, make_tuple (hashVal, idx)};
379 return ForwardIterator{};
381 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
384 return this->Find (key);
386 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
387 template <
typename ARG_T>
389 requires (not same_as<typename TRAITS::AlternateFindType, void> and same_as<remove_cvref_t<ARG_T>,
typename TRAITS::AlternateFindType>)
391 return this->Find (key);
393 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
394 template <
typename CHECKED_T>
395 void HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::Update (
const ForwardIterator& it, ArgByValueType<CHECKED_T> newValue)
396 requires (not same_as<MAPPED_TYPE, void>)
398 size_t bucketIndex = get<0> (it.GetUnderlyingIteratorRep ());
399 size_t intraBucketIndex = get<1> (it.GetUnderlyingIteratorRep ());
400 this->fBuckets_[bucketIndex].fElements[intraBucketIndex].fValue = newValue;
402 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
405#if qStroika_Foundation_Debug_AssertionsChecked
409#if qStroika_Foundation_Debug_AssertionsChecked
410 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
411 void HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::Invariant_ () const noexcept
421 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
422 constexpr HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::ForwardIterator::ForwardIterator (
const HashTable* data) noexcept
423 : ForwardIterator{data, make_tuple (0, 0)}
425 AdvanceOverEmptyBuckets_ ();
427 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
428 constexpr HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::ForwardIterator::ForwardIterator (
const HashTable* data, UnderlyingIteratorRep startAt) noexcept
430 , fBucketIndex_{get<0> (startAt)}
431 , fIntraBucketIndex_{get<1> (startAt)}
435#if qStroika_Foundation_Debug_AssertionsChecked
436 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
437 HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::ForwardIterator::~ForwardIterator ()
442 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
447 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
450 Assert (fData_ ==
nullptr or fBucketIndex_ <= fData_->bucket_count ());
451 return fData_ ==
nullptr or fBucketIndex_ == fData_->bucket_count ();
453 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
456 Require (not Done ());
457 return fData_->fBuckets_[fBucketIndex_].fElements[fIntraBucketIndex_];
459 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
460 inline auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::ForwardIterator::operator->() const -> const value_type*
462 Require (not Done ());
463 return &fData_->fBuckets_[fBucketIndex_].fElements[fIntraBucketIndex_];
465 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
468 Require (fData_ == rhs.fData_ or fData_ ==
nullptr or rhs.fData_ ==
nullptr);
470 bool rDone = rhs.Done ();
471 if (done and rDone) {
478 return this->fBucketIndex_ == rhs.fBucketIndex_ and this->fIntraBucketIndex_ == rhs.fIntraBucketIndex_;
480 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
483 return make_tuple (fBucketIndex_, fIntraBucketIndex_);
485 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
488 fBucketIndex_ = get<0> (l);
489 fIntraBucketIndex_ = get<1> (l);
492 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
493 inline auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::ForwardIterator::operator++ () -> ForwardIterator&
495 Require (not Done ());
497 ++fIntraBucketIndex_;
498 Assert (fIntraBucketIndex_ <= fData_->bucket_size (fBucketIndex_));
499 if (fIntraBucketIndex_ == fData_->bucket_size (fBucketIndex_)) {
501 fIntraBucketIndex_ = 0;
503 AdvanceOverEmptyBuckets_ ();
506 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
509 ForwardIterator result = *
this;
513 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
514 inline void HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::ForwardIterator::AdvanceOverEmptyBuckets_ ()
516 while (fBucketIndex_ < fData_->bucket_count () and fData_->bucket_size (fBucketIndex_) == 0) {
520 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
523#if qStroika_Foundation_Debug_AssertionsChecked
524 Require (data == fData_);
527 template <
typename KEY_TYPE,
typename MAPPED_TYPE, HashTable_Support::IVal
idTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
530#if qStroika_Foundation_Debug_AssertionsChecked
534#if qStroika_Foundation_Debug_AssertionsChecked
535 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 void Apply(FUNCTION &&doToElement, Execution::SequencePolicy seq=Execution::SequencePolicy::eDEFAULT) const
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 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
shared_lock< const AssertExternallySynchronizedMutex > ReadContext
Instantiate AssertExternallySynchronizedMutex::ReadContext to designate an area of code where protect...
SequencePolicy
equivalent which of 4 types being used std::execution::sequenced_policy, parallel_policy,...
@ eSeq
default case - not parallelized
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...