Stroika Library 3.0d24
 
Loading...
Searching...
No Matches
HashTable.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. 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 =
233 Containers::Support::ReserveTweaks::GetScaledUpCapacity (static_cast<size_t> (targetLoadFactor * fCachedSize_ + 1));
234 ReHash (targetBucketCount);
235 return;
236 }
237 }
238 if (lf > fMaxLoadFactor_) {
239 float targetLoadFactor = fMaxLoadFactor_ * 1.5f; // NO IDEA how much to use here?
240 size_t targetBucketCount =
241 Containers::Support::ReserveTweaks::GetScaledUpCapacity (static_cast<size_t> (targetLoadFactor * fCachedSize_ + 1));
242 ReHash (targetBucketCount);
243 }
244 }
245 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
247 {
248 return fBuckets_.size ();
249 }
250 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
251 inline size_t HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::bucket_size (size_t bucketIdx) const
252 {
253 Require (bucketIdx < bucket_count ());
254 return fBuckets_[bucketIdx].fElements.size ();
255 }
256 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
258 {
259 return fCachedSize_;
260 }
261 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
263 {
264 return fCachedSize_ == 0;
266 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
268 {
269 return static_cast<float> (fCachedSize_) / fBuckets_.size ();
270 }
271 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
273 {
274 return fMaxLoadFactor_;
275 }
276 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
278 {
279 Require (mlf > 0.0);
280 fMaxLoadFactor_ = mlf;
281 }
282 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
284 {
285 if (fCachedSize_ != 0) {
286 for (auto& bi : fBuckets_) {
287 bi.fElements.clear ();
288 }
289 fCachedSize_ = 0;
290 }
291 }
292 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
293 inline bool HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::contains (ArgByValueType<key_type> key) const
294 {
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)) {
299 return true;
300 }
301 }
302 }
303 return false;
304 }
305 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
306 template <invocable<typename TRAITS::value_type> FUNCTION>
307 inline void HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::Apply (FUNCTION&& doToElement) const
308 {
309 // @todo measure the crossover and auto-choose the policy here - eSeq is a placeholder, not a decision
310 Apply (forward<FUNCTION> (doToElement), Execution::SequencePolicy::eSeq);
311 }
312 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
313 template <invocable<typename TRAITS::value_type> FUNCTION>
315 {
316 if constexpr (derived_from<LayoutType_, HashTable_Support::SeparateChainingTag>) {
317 // only ePar goes parallel; 'default' running sequentially is deliberate - see the dispatch
318 // note on Execution::SequencePolicy
319 switch (seq) {
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); });
325 });
326 break;
327 // @todo add other Execution::SequencePolicy cases
328#endif
329 default:
330 for (const auto& bi : this->fBuckets_) {
331 for (const auto& i : bi.fElements) {
332 forward<FUNCTION> (doToElement) (i);
333 }
334 }
335 break;
336 }
337 }
338 }
339 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
340 auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::Find (ArgByValueType<key_type> key) const -> ForwardIterator
341 {
342 size_t hashVal = Hash_ (key);
343 if constexpr (derived_from<LayoutType_, HashTable_Support::SeparateChainingTag>) {
344 size_t idx{0};
345 for (auto i : fBuckets_[hashVal].fElements) {
346 if (this->fKeyComparer_ (i.fKey, key)) {
347 return ForwardIterator{this, make_tuple (hashVal, idx)};
348 }
349 ++idx;
350 }
351 }
352 return ForwardIterator{};
353 }
354 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<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>)
358 {
359 size_t hashVal = Hash_ (key);
360 if constexpr (derived_from<LayoutType_, HashTable_Support::SeparateChainingTag>) {
361 size_t idx{0};
362 for (auto i : this->fBuckets_[hashVal].fElements) {
363 if (this->fKeyComparer_ (i.fKey, key)) {
364 return ForwardIterator{this, make_tuple (hashVal, idx)};
365 }
366 ++idx;
367 }
368 }
369 return ForwardIterator{};
370 }
371 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<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
374 {
375 if constexpr (derived_from<LayoutType_, HashTable_Support::SeparateChainingTag>) {
376 size_t hashVal{0};
377 for (const auto& bi : fBuckets_) {
378 size_t idx{0};
379 for (const auto& i : bi.fElements) {
380 if (forward<FUNCTION> (firstThat) (i)) {
381 return ForwardIterator{this, make_tuple (hashVal, idx)};
382 }
383 ++idx;
384 }
385 ++hashVal;
386 }
387 }
388 return ForwardIterator{};
389 }
390 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
391 inline auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::find (ArgByValueType<key_type> key) const -> ForwardIterator
392 {
393 return this->Find (key);
394 }
395 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
396 template <typename ARG_T>
397 inline auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::find (ARG_T key) const -> ForwardIterator
398 requires (not same_as<typename TRAITS::AlternateFindType, void> and same_as<remove_cvref_t<ARG_T>, typename TRAITS::AlternateFindType>)
399 {
400 return this->Find (key);
401 }
402 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
403 template <typename CHECKED_T>
404 void HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::Update (const ForwardIterator& it, ArgByValueType<CHECKED_T> newValue)
405 requires (not same_as<MAPPED_TYPE, void>)
406 {
407 size_t bucketIndex = get<0> (it.GetUnderlyingIteratorRep ());
408 size_t intraBucketIndex = get<1> (it.GetUnderlyingIteratorRep ());
409 this->fBuckets_[bucketIndex].fElements[intraBucketIndex].fValue = newValue;
410 }
411 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
412 constexpr void HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::Invariant () const noexcept
413 {
414#if qStroika_Foundation_Debug_AssertionsChecked
415 this->Invariant_ ();
416#endif
418#if qStroika_Foundation_Debug_AssertionsChecked
419 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
421 {
422 }
423#endif
424
425 /*
426 ********************************************************************************
427 ********* HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::ForwardIterator ************
428 ********************************************************************************
429 */
430 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<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)}
433 {
434 AdvanceOverEmptyBuckets_ ();
435 }
436 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
437 constexpr HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::ForwardIterator::ForwardIterator (const HashTable* data, UnderlyingIteratorRep startAt) noexcept
438 : fData_{data}
439 , fBucketIndex_{get<0> (startAt)}
440 , fIntraBucketIndex_{get<1> (startAt)}
441 {
442 RequireNotNull (data);
443 }
444#if qStroika_Foundation_Debug_AssertionsChecked
445 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
446 HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::ForwardIterator::~ForwardIterator ()
447 {
448 this->Invariant ();
449 }
450#endif
451 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
453 {
454 return not this->AtEnd ();
455 }
456 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
458 {
459 Assert (this->fData_ == nullptr or this->fBucketIndex_ <= this->fData_->bucket_count ());
460 return this->fData_ == nullptr or this->fBucketIndex_ == this->fData_->bucket_count ();
461 }
462 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
463 inline auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::ForwardIterator::operator* () const -> const value_type&
464 {
465 Require (not AtEnd ());
466 return fData_->fBuckets_[this->fBucketIndex_].fElements[this->fIntraBucketIndex_];
467 }
468 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
469 inline auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::ForwardIterator::operator->() const -> const value_type*
470 {
471 Require (not this->AtEnd ());
472 return &this->fData_->fBuckets_[this->fBucketIndex_].fElements[this->fIntraBucketIndex_];
473 }
474 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
476 {
477 Require (fData_ == rhs.fData_ or fData_ == nullptr or rhs.fData_ == nullptr); // nullptr used for sentinal end else must refer to same container
478 bool done = this->AtEnd ();
479 bool rDone = rhs.AtEnd ();
480 if (done and rDone) {
481 return true;
482 }
483 if (done or rDone) {
484 return false;
485 }
486 // neither is done, nor special sentinel value, so this case is easy
487 return this->fBucketIndex_ == rhs.fBucketIndex_ and this->fIntraBucketIndex_ == rhs.fIntraBucketIndex_;
488 }
489 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
491 {
492 return make_tuple (this->fBucketIndex_, this->fIntraBucketIndex_);
493 }
494 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
496 {
497 this->fBucketIndex_ = get<0> (l);
498 this->fIntraBucketIndex_ = get<1> (l);
499 // @todo assert valid in range
500 }
501 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
502 inline auto HashTable<KEY_TYPE, MAPPED_TYPE, TRAITS>::ForwardIterator::operator++ () -> ForwardIterator&
503 {
504 Require (not this->AtEnd ());
505 RequireNotNull (this->fData_);
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;
511 }
512 AdvanceOverEmptyBuckets_ ();
513 return *this;
514 }
515 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
517 {
518 ForwardIterator result = *this;
519 this->operator++ ();
520 return result;
521 }
522 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
524 {
525 while (this->fBucketIndex_ < this->fData_->bucket_count () and this->fData_->bucket_size (this->fBucketIndex_) == 0) {
526 ++this->fBucketIndex_;
527 }
528 }
529 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
531 {
532#if qStroika_Foundation_Debug_AssertionsChecked
533 Require (data == this->fData_);
534#endif
535 }
536 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
538 {
539#if qStroika_Foundation_Debug_AssertionsChecked
540 this->Invariant_ ();
541#endif
542 }
543#if qStroika_Foundation_Debug_AssertionsChecked
544 template <typename KEY_TYPE, typename MAPPED_TYPE, HashTable_Support::IValidTraits<KEY_TYPE, MAPPED_TYPE> TRAITS>
546 {
547 }
548#endif
549}
#define RequireNotNull(p)
Definition Assertions.h:348
#define AssertNotReached()
Definition Assertions.h:356
#define Verify(c)
Definition Assertions.h:420
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 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 ForwardIterator Find(ArgByValueType< key_type > key) const
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
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...
Definition Throw.inl:43