Stroika Library 3.0d18
 
Loading...
Searching...
No Matches
HashTable.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include <random>
5
8#include "Stroika/Foundation/Execution/Exceptions.h"
9#include "Stroika/Foundation/Memory/Common.h"
10
12
13 /*
14 ********************************************************************************
15 ****************** HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS> ********************
16 ********************************************************************************
17 */
18 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
19 inline HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::HashTable (const KeyHasherType& hashFunction, const KeyEqualsComparerType& keyComparer)
20 : HashTable{kBufferedBuckets_, hashFunction, keyComparer}
21 {
22 }
23 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
24 inline HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::HashTable (size_t bucketCount, const KeyHasherType& hashFunction, const KeyEqualsComparerType& keyComparer)
25 : fHasher_{hashFunction}
26 , fKeyComparer_{keyComparer}
27 {
28 ReHash (bucketCount);
29 }
30 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
31 inline auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::hash_function () const -> KeyHasherType
32 {
33 return fHasher_;
34 }
35 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
36 inline auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::key_eq () const -> KeyEqualsComparerType
37 {
38 return fKeyComparer_;
39 }
40 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
41 inline auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::begin () -> ForwardIterator
42 {
43 return ForwardIterator{this};
44 }
45 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
46 constexpr auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::end () -> ForwardIterator
47 {
48 return ForwardIterator{};
49 }
50 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
51 inline void HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::MoveIteratorHereAfterClone (ForwardIterator* pi, const HashTable* movedFrom) const
52 {
54 RequireNotNull (pi);
55 RequireNotNull (movedFrom);
56#if qStroika_Foundation_Debug_AssertionsChecked
57 Require (pi->fData_ == movedFrom);
58#endif
59 Require (this->bucket_count () == movedFrom->bucket_count ());
60 //Require (this->fHasher_ == movedFrom->fHasher_); // logically required but not equals comparable
61 // Also require no changes to this after clone!!! - cuz those could re-order elements
62 if constexpr (derived_from<LayoutType_, HashTable_Support::SeparateChainingTag>) {
63 // then easy - cuz iterator rep is same - index into bucket list and index into array within bucket
64 }
65#if qStroika_Foundation_Debug_AssertionsChecked
66 pi->fData_ = this;
67#endif
68 }
69 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
70 inline bool HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::Add (const value_type& t)
71 {
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)) {
78 return false;
79 }
80 }
81 // fall through and do default - append
82 } break;
84 // must scan to see if present...
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>) {
88 *i = t;
89 }
90 else {
91 i->fValue = t.fValue;
92 }
93 return true;
94 }
95 }
96 // fall through and do default - append
97 } break;
99 // fall through and do default - append
100 } break;
102 for (auto i : fBuckets_[hashVal].fElements) {
103 if (this->fKeyComparer_ (i.fKey, t.fKey)) {
104 static const auto kExcept_ = Execution::RuntimeErrorException<logic_error>{"Duplicates not allowed"sv};
105 Execution::Throw (kExcept_);
106 }
107 }
108 // fall through and do default - append
109 } break;
110 default:
112 }
113 // common case handled by fallthrough
114 fBuckets_[hashVal].fElements.push_back (t);
115 ++fCachedSize_;
116 ReHashIfNeeded ();
117 return true;
118 }
119 }
120 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
121 inline bool HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::Add (const key_type& t)
122 requires (same_as<MAPPED_TYPE, void>)
123 {
125 }
126 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
127 template <same_as<MAPPED_TYPE> MAPPED_TYPE2>
128 inline bool HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::Add (const key_type& t, const MAPPED_TYPE2& m)
129 requires (not same_as<MAPPED_TYPE, void>)
130 {
132 }
133 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
134 inline void HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::insert (const pair<KEY_TYPE, MAPPED_TYPE>& p)
135 {
136 Add (p.first, p.second);
137 }
138 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
139 auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::Lookup (const key_type& t) -> optional<value_type>
140 {
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)) {
145 return i;
146 }
147 }
148 }
149 return nullopt;
150 }
151 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
153 {
154 if constexpr (derived_from<LayoutType_, HashTable_Support::SeparateChainingTag>) {
155 fBuckets_[i.fBucketIndex_].fElements.Remove (i.fIntraBucketIndex_);
156 --fCachedSize_;
157 if (nextI != nullptr) {
158 *nextI = i;
159 if (nextI->fIntraBucketIndex_ == fBuckets_[nextI->fBucketIndex_].fElements.size ()) {
160 ++nextI->fBucketIndex_;
161 nextI->fIntraBucketIndex_ = 0;
162 nextI->AdvanceOverEmptyBuckets_ ();
163 }
164 }
165 }
166 }
167 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
168 inline void HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::Remove (const key_type& t)
169 {
170 Verify (RemoveIf (t));
171 }
172 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<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 ());
180 --fCachedSize_;
181 return true;
182 }
183 }
184 }
185 return false;
186 }
187
188 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
191 ForwardIterator next{};
192 Remove (i, &next);
193 return next;
194 }
195 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
196 inline auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::erase (const UnderlyingIteratorRep& i) -> UnderlyingIteratorRep
197 {
198 ForwardIterator next{};
199 Remove (ForwardIterator{this, i}, &next);
200 return next.GetUnderlyingIteratorRep ();
201 }
202
203 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
205 {
206 size_t useBucketCount = Math::AtLeast (Math::PrimeAtLeastThisBig (newBucketCount), kBufferedBuckets_);
207 if (useBucketCount != fBuckets_.size ()) {
208 if (this->empty ()) {
209 fBuckets_.resize (newBucketCount);
210 }
211 else {
212 Debug::TraceContextBumper ctx{"ReHash - rehashing"};
213 // fill in new by iterating, so basically cost of a whole new copy of all the data
214 HashTable n{newBucketCount, fHasher_, fKeyComparer_};
215 for (auto i : *this) {
216 n.Add (i);
217 }
218 // this move is expensive - perhaps better to indirect buckets_ into HEAP object so this is cheaper
219 fBuckets_ = move (n.fBuckets_);
220 }
221 }
222 }
223 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
225 {
226 // @todo consider the logic that makes sense here - look at std c++ unordered_set impl - and compare...
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; // NO IDEA how much to use here?
232 size_t targetBucketCount = Support::ReserveTweaks::GetScaledUpCapacity (static_cast<size_t> (targetLoadFactor * fCachedSize_ + 1));
233 ReHash (targetBucketCount);
234 return;
235 }
236 }
237 if (lf > fMaxLoadFactor_) {
238 float targetLoadFactor = fMaxLoadFactor_ * 1.5f; // NO IDEA how much to use here?
239 size_t targetBucketCount = Support::ReserveTweaks::GetScaledUpCapacity (static_cast<size_t> (targetLoadFactor * fCachedSize_ + 1));
240 ReHash (targetBucketCount);
241 }
242 }
243 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
245 {
246 return fBuckets_.size ();
247 }
248 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
249 inline size_t HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::bucket_size (size_t bucketIdx) const
250 {
251 Require (bucketIdx < bucket_count ());
252 return fBuckets_[bucketIdx].fElements.size ();
253 }
254 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
256 {
257 return fCachedSize_;
258 }
259 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
261 {
262 return fCachedSize_ == 0;
263 }
264 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
266 {
267 return static_cast<float> (fCachedSize_) / fBuckets_.size ();
268 }
269 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
271 {
272 return fMaxLoadFactor_;
273 }
274 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
276 {
277 Require (mlf > 0.0);
278 fMaxLoadFactor_ = mlf;
279 }
280 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
282 {
283 if (fCachedSize_ != 0) {
284 for (auto& bi : fBuckets_) {
285 bi.fElements.clear ();
286 }
287 fCachedSize_ = 0;
288 }
290 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
291 inline bool HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::contains (ArgByValueType<key_type> key) const
292 {
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)) {
297 return true;
298 }
299 }
300 }
301 return false;
302 }
303 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
304 template <invocable<typename TRAITS::value_type> FUNCTION>
306 {
307 if constexpr (derived_from<LayoutType_, HashTable_Support::SeparateChainingTag>) {
308 switch (seq) {
310#if __cpp_lib_execution < 201603L
311 default:
312#endif
313 for (const auto& bi : fBuckets_) {
314 for (const auto& i : bi.fElements) {
315 forward<FUNCTION> (doToElement) (i);
316 }
318 break;
319#if __cpp_lib_execution >= 201603L
320 default:
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); });
324 });
325#endif
326 break;
327 }
328 }
329 }
330 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
331 auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::Find (ArgByValueType<key_type> key) const -> ForwardIterator
333 size_t hashVal = Hash_ (key);
334 if constexpr (derived_from<LayoutType_, HashTable_Support::SeparateChainingTag>) {
335 size_t idx{0};
336 for (auto i : fBuckets_[hashVal].fElements) {
337 if (this->fKeyComparer_ (i.fKey, key)) {
338 return ForwardIterator{this, make_tuple (hashVal, idx)};
339 }
340 ++idx;
341 }
342 }
343 return ForwardIterator{};
344 }
345 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<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>) {
352 size_t idx{0};
353 for (auto i : fBuckets_[hashVal].fElements) {
354 if (this->fKeyComparer_ (i.fKey, key)) {
355 return ForwardIterator{this, make_tuple (hashVal, idx)};
356 }
357 ++idx;
358 }
360 return ForwardIterator{};
361 }
362 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<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
365 {
366 if constexpr (derived_from<LayoutType_, HashTable_Support::SeparateChainingTag>) {
367 size_t hashVal{0};
368 for (const auto& bi : fBuckets_) {
369 size_t idx{0};
370 for (const auto& i : bi.fElements) {
371 if (forward<FUNCTION> (firstThat) (i)) {
372 return ForwardIterator{this, make_tuple (hashVal, idx)};
373 }
374 ++idx;
375 }
376 ++hashVal;
377 }
378 }
379 return ForwardIterator{};
380 }
381 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
382 inline auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::find (ArgByValueType<key_type> key) const -> ForwardIterator
384 return this->Find (key);
385 }
386 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
387 template <typename ARG_T>
388 inline auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::find (ARG_T key) const -> ForwardIterator
389 requires (not same_as<typename TRAITS::AlternateFindType, void> and same_as<remove_cvref_t<ARG_T>, typename TRAITS::AlternateFindType>)
390 {
391 return this->Find (key);
392 }
393 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<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>)
397 {
398 size_t bucketIndex = get<0> (it.GetUnderlyingIteratorRep ());
399 size_t intraBucketIndex = get<1> (it.GetUnderlyingIteratorRep ());
400 this->fBuckets_[bucketIndex].fElements[intraBucketIndex].fValue = newValue;
401 }
402 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
403 constexpr void HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::Invariant () const noexcept
404 {
405#if qStroika_Foundation_Debug_AssertionsChecked
406 Invariant_ ();
407#endif
408 }
409#if qStroika_Foundation_Debug_AssertionsChecked
410 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
411 void HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::Invariant_ () const noexcept
412 {
413 }
414#endif
415
416 /*
417 ********************************************************************************
418 ********* HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::ForwardIterator ************
419 ********************************************************************************
420 */
421 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<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)}
424 {
425 AdvanceOverEmptyBuckets_ ();
426 }
427 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
428 constexpr HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::ForwardIterator::ForwardIterator (const HashTable* data, UnderlyingIteratorRep startAt) noexcept
429 : fData_{data}
430 , fBucketIndex_{get<0> (startAt)}
431 , fIntraBucketIndex_{get<1> (startAt)}
432 {
433 RequireNotNull (data);
434 }
435#if qStroika_Foundation_Debug_AssertionsChecked
436 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
437 HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::ForwardIterator::~ForwardIterator ()
438 {
439 Invariant ();
440 }
441#endif
442 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
444 {
445 return not Done ();
446 }
447 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
449 {
450 Assert (fData_ == nullptr or fBucketIndex_ <= fData_->bucket_count ());
451 return fData_ == nullptr or fBucketIndex_ == fData_->bucket_count ();
452 }
453 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
454 inline auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::ForwardIterator::operator* () const -> const value_type&
455 {
456 Require (not Done ());
457 return fData_->fBuckets_[fBucketIndex_].fElements[fIntraBucketIndex_];
458 }
459 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
460 inline auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::ForwardIterator::operator->() const -> const value_type*
461 {
462 Require (not Done ());
463 return &fData_->fBuckets_[fBucketIndex_].fElements[fIntraBucketIndex_];
464 }
465 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
467 {
468 Require (fData_ == rhs.fData_ or fData_ == nullptr or rhs.fData_ == nullptr); // nullptr used for sentinal end else must refer to same container
469 bool done = Done ();
470 bool rDone = rhs.Done ();
471 if (done and rDone) {
472 return true;
473 }
474 if (done or rDone) {
475 return false;
476 }
477 // neither is done, nor special sentinel value, so this case is easy
478 return this->fBucketIndex_ == rhs.fBucketIndex_ and this->fIntraBucketIndex_ == rhs.fIntraBucketIndex_;
479 }
480 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
482 {
483 return make_tuple (fBucketIndex_, fIntraBucketIndex_);
484 }
485 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
487 {
488 fBucketIndex_ = get<0> (l);
489 fIntraBucketIndex_ = get<1> (l);
490 // @todo assert valid in range
491 }
492 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
493 inline auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::ForwardIterator::operator++ () -> ForwardIterator&
494 {
495 Require (not Done ());
496 RequireNotNull (fData_);
497 ++fIntraBucketIndex_;
498 Assert (fIntraBucketIndex_ <= fData_->bucket_size (fBucketIndex_));
499 if (fIntraBucketIndex_ == fData_->bucket_size (fBucketIndex_)) {
500 ++fBucketIndex_;
501 fIntraBucketIndex_ = 0;
502 }
503 AdvanceOverEmptyBuckets_ ();
504 return *this;
505 }
506 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
508 {
509 ForwardIterator result = *this;
510 this->operator++ ();
511 return result;
512 }
513 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
514 inline void HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::ForwardIterator::AdvanceOverEmptyBuckets_ ()
515 {
516 while (fBucketIndex_ < fData_->bucket_count () and fData_->bucket_size (fBucketIndex_) == 0) {
517 ++fBucketIndex_;
518 }
519 }
520 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
522 {
523#if qStroika_Foundation_Debug_AssertionsChecked
524 Require (data == fData_);
525#endif
526 }
527 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
529 {
530#if qStroika_Foundation_Debug_AssertionsChecked
531 Invariant_ ();
532#endif
533 }
534#if qStroika_Foundation_Debug_AssertionsChecked
535 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
537 {
538 }
539#endif
540}
#define RequireNotNull(p)
Definition Assertions.h:347
#define AssertNotReached()
Definition Assertions.h:355
#define Verify(c)
Definition Assertions.h:419
implement hash table support in a lightweight standard template library style. Use traits to describe...
Definition HashTable.h:132
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
Definition HashTable.inl:36
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++,...
HashTable(const KeyHasherType &hashFunction={}, const KeyEqualsComparerType &keyComparer={})
Definition HashTable.inl:19
nonvirtual void ReHash(size_t newBucketCount)
nonvirtual size_t bucket_size(size_t bucketIdx) const
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...
Definition HashTable.inl:70
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...
Definition Throw.inl:43