ALib C++ Library
Library Version: 2412 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
hashtablebase.inl
Go to the documentation of this file.
1//==================================================================================================
2/// \file
3/// This header file is part of module \alib_containers of the \aliblong.
4///
5/// \emoji :copyright: 2013-2024 A-Worx GmbH, Germany.
6/// Published under \ref mainpage_license "Boost Software License".
7//==================================================================================================
8#ifndef HPP_ALIB_MONOMEM_DETAIL_HASHTABLEBASE
9#define HPP_ALIB_MONOMEM_DETAIL_HASHTABLEBASE 1
10#pragma once
11#if !defined(HPP_ALIB_MONOMEM_CONTAINERS_HASHTABLE)
12# error "ALib sources with ending '.inl' must not be included from outside."
13#endif
14#include "alib/lang/tmp.hpp"
16#include <iterator>
17
18namespace alib::containers {
19
20
21// forward declaration of HashTable
22template< typename TAllocator,
23 typename TValueDescriptor,
24 typename THash,
25 typename TEqual,
26 lang::Caching THashCaching,
27 Recycling TRecycling > class HashTable;
28
29
30namespace detail {
31
32
33#if ALIB_SIZEOF_INTEGER == 4
34 static constexpr int PRIME_TABLE_SIZE = 26;
35#elif ALIB_SIZEOF_INTEGER == 8
36 /// The size of the static table of prime numbers. Depends on the platform.
37 static constexpr int PRIME_TABLE_SIZE = 58;
38#else
39 #error "Unknown size of integer"
40#endif
41
42
43/// Table of prime numbers. The effective bucket size is chosen to be the first value found in
44/// this table that is equal or higher than the requested size.
46
47/// A dummy bucket used for nulled hash tables to avoid otherwise necessary checks.
48ALIB_API extern void* DUMMY_BUCKET;
49
50/// HashTable element type if hash codes are cached.
51/// @tparam TStored The custom data stored.
52template<typename TStored>
53struct HTElementCached : public lang::SidiNodeBase<HTElementCached<TStored>>
54{
55 /// TMP constant that denotes that hash codes are cached.
56 static constexpr bool CachedHashCodes = 1;
57
58 /// The custom data stored in nodes of this table.
59 TStored value;
60
61 size_t hashCode; ///< The cached hash code.
62
63 /// Stores the given hash code when an element is recycled or extracted and changed.
64 /// @param pHashCode The new hash code to set for this (recycled) element.
65 void fixHashCode( size_t pHashCode ) { *const_cast<size_t*>(&hashCode)= pHashCode; }
66
67 /// Returns the cached hash code.
68 /// @return The hash code of this element.
69 size_t getCached() { return hashCode; }
70
71};
72
73/// HashTable element type if hash codes are not cached.
74/// @tparam TStored The custom data stored.
75template<typename TStored>
76struct HTElementUncached : public lang::SidiNodeBase<HTElementUncached<TStored>>
77{
78 /// TMP constant that denotes that hash codes are not cached.
79 static constexpr bool CachedHashCodes = 0;
80
81 /// The custom data stored in nodes of this table.
82 TStored value;
83
84 /// Does nothing, parameter ignored.
85 /// @param pHashCode Ignored
86 void fixHashCode( size_t pHashCode ) { (void) pHashCode; }
87
88 /// Never called.
89 /// @return Undefined.
90 size_t getCached() { return 0; }
91};
92
93/// Node types used with \alib{containers;HashTable}.
94/// @tparam TValueDescriptor The descriptor of contained types provided with a template parameter
95/// of the \b HashTable.
96/// @tparam THashCaching A template parameter of the \b HashTable that determines whether
97/// hash values are cached or not.
98template<typename TValueDescriptor, lang::Caching THashCaching>
100{
101 /// @return \c true, if the selected #Type caches hash values, otherwise \c false.
102 static constexpr bool IsCachingHashes()
103 {
104 return THashCaching == lang::Caching::Enabled
105 || ( THashCaching == lang::Caching::Auto
106 && !std::is_arithmetic<typename TValueDescriptor::KeyType>::value );
107 }
108
109 /// The type stored in a hash table's bucket list.
110 using Type= ATMP_IF_T_F( IsCachingHashes(), HTElementCached <typename TValueDescriptor::StoredType>,
112};
113
114//==================================================================================================
115/// Base struct of \alib{containers;HashTable} providing internals.
116/// \note
117/// The separation of the internals of class \b HashTable to this type in namespace \b detail
118/// has no benefit on compilation speed or other positive "technical" effect, nor is it a matter
119/// of software design.<br>
120/// A user of derived class \alib{containers;HashTable} finds all interface methods and types
121/// in one place, which is not cluttered by the documentation of the internals found here.
122/// Otherwise, the separation is exclusively supporting source code organization.
123///
124/// @see For a description of the template parameters and a general introduction to
125/// this module's hash table implementation, see the
126/// \ref alib_ns_containers_hashtable_referencedoc "reference documentation"
127/// of class \b HashTable.
128//==================================================================================================
129template< typename TAllocator,
130 typename TValueDescriptor,
131 typename THash,
132 typename TEqual,
133 lang::Caching THashCaching,
134 Recycling TRecycling >
136: RecyclingSelector<TRecycling>:: template Type< TAllocator,
137 typename HTElementSelector<TValueDescriptor, THashCaching>::Type >
138{
139 /// Our base type.
140 using base= typename RecyclingSelector<TRecycling>:: template Type< TAllocator,
142
143 /// Type definition publishing template parameter \p{T}.
144 using T = typename TValueDescriptor::StoredType;
145
146 /// Type definition publishing template parameter \p{TKey}.
147 using TKey = typename TValueDescriptor::KeyType;
148
149 /// Type definition publishing template parameter \p{TKey}.
150 using TMapped = typename TValueDescriptor::MappedType;
151
152
153 /// Determines whether hash codes are stored with the elements.
154 /// It is done in case the given template parameter \p{THashCashing} equals
155 /// \alib{lang;Caching;Caching::Enabled} or if it equals \alib{lang;Caching;Caching::Auto}
156 /// and the key type is an arithmetic type.
157 /// @return \c true if hash codes are stored for reuse, \c false if not.
160
161 /// The type stored in the bucket of class \b HashTable.
163
164
165 // ###############################################################################################
166 // ### Types and Constants
167 // ###############################################################################################
168
169 /// Type of a single linked node list.
170 using recyclerType= typename RecyclingSelector<TRecycling>:: template Type< TAllocator,
172
173 /// This type definition may be used to define an externally managed shared recycler,
174 /// which can be passed to the alternative constructor of this class when template
175 /// parameter \p{TRecycling} equals \alib{containers;Recycling;Shared}.
176 /// \see
177 /// Chapter \ref alib_contmono_containers_recycling_shared of the Programmer's Manual
178 /// for this \alibmod.
180 :: template HookType<TAllocator,
182
183
184 /// Type of a single linked node list.
186
187 /// Type of a node in the \b List.
189
190
191 // ###############################################################################################
192 // ### internal helpers
193 // ###############################################################################################
194
195 /// Either returns the cached hash code or calculates it.
196 /// @param elem The element to receive the hashcode for.
197 /// @return The hash code of the given element.
198 static size_t getHashCode(Element* elem)
199 {
200 if constexpr ( Element::CachedHashCodes )
201 return elem->getCached();
202 else
203 return THash{}( TValueDescriptor().Key( elem->value ) );
204 }
205
206 /// Returns either a recycled or newly allocated element.
207 /// @param hashCode The hashCode of the new element. Used only in cached case.
208 /// @return A pointer to the element created or recycled.
209 Element* allocElement( const size_t hashCode )
210 {
211 Element* elem= recyclerType::Get();
212 elem->fixHashCode( hashCode );
213 return elem;
214 }
215
216 // ###############################################################################################
217 // std::iterator_traits types.
218 // ###############################################################################################
219
220 //==============================================================================================
221 /// Templated implementation of \c std::iterator_traits.
222 /// Will be exposed by derived class's definitions \alib{containers::HashTable;Iterator} and
223 /// \alib{containers::HashTable;ConstIterator}.
224 ///
225 /// As the name of the class indicates, this iterator satisfies the C++ standard library concept
226 /// \https{ForwardIterator,en.cppreference.com/w/cpp/named_req/ForwardIterator}.
227 ///
228 /// @tparam TConstOrMutable A constant or mutable version of the parent's template type
229 /// \p{TMapped}.
230 //==============================================================================================
231 template<typename TConstOrMutable>
233 {
234 #if !DOXYGEN
235 friend struct HashTableBase;
236 friend class containers::HashTable<TAllocator, TValueDescriptor,THash,TEqual,THashCaching,TRecycling>;
237 #endif
238
239 /// Const or mutable version of HashTableBase.
241 const HashTableBase );
242 using iterator_category = std::forward_iterator_tag; ///< Implementation of <c>std::iterator_traits</c>.
243 using value_type = TMapped ; ///< Implementation of <c>std::iterator_traits</c>.
244 using difference_type = integer ; ///< Implementation of <c>std::iterator_traits</c>.
245 using pointer = TConstOrMutable* ; ///< Implementation of <c>std::iterator_traits</c>.
246 using reference = TConstOrMutable& ; ///< Implementation of <c>std::iterator_traits</c>.
247
248 protected:
249 /// The pointer to the hash table.
251
252 /// The actual bucket index.
254
255 /// The pointer to the actual element.
257
258
259 /// Internal constructor. Searches the first element, starting with given bucket
260 /// number.
261 /// @param pTable Pointer to the hash table.
262 /// @param pBbucketIx The bucket index.
263 TIterator( THashtable* pTable, uinteger pBbucketIx )
264 : table(pTable)
265 {
266 while( pBbucketIx < pTable->bucketCount )
267 {
269 if( !pTable->buckets[pBbucketIx].isEmpty() )
270 {
271 bucketIdx= pBbucketIx;
272 element = pTable->buckets[pBbucketIx].first();
273 return;
274 }
275 ++pBbucketIx;
277 }
278 bucketIdx= pBbucketIx;
279 element = nullptr;
280 }
281
282 /// Internal constructor creating a specific iterator.
283 /// @param pTable Pointer to the hash table.
284 /// @param pBbucketIx The bucket index.
285 /// @param pElement Pointer to the current element.
286 TIterator( THashtable* pTable, uinteger pBbucketIx, Element* pElement)
287 : table ( pTable )
288 , bucketIdx( pBbucketIx )
289 , element ( pElement )
290 {}
291
292 /// Moves an iterator with a nulled element pointer to the next element.
293 void repair()
294 {
297 if( !table->buckets[bucketIdx].isEmpty() )
298 {
299 element= table->buckets[bucketIdx].first();
300 return;
301 }
303 }
304
305
306 public:
307 /// Default constructor. Keeps everything uninitialized.
308 TIterator() = default;
309
310 /// Copy constructor (default).
311 /// @param other The iterator to assign from.
312 TIterator( const TIterator& other) = default;
313
314
315 /// Copy assignment (default).
316 /// @param other The iterator to assign from.
317 /// @return A reference to this object.
318 TIterator& operator=( const TIterator& other ) = default;
319
320
321 #if DOXYGEN
322 /// Copy constructor accepting a mutable iterator.
323 /// Available only for the constant version of this iterator.
324 /// @tparam TMutable The type of this constructor's argument.
325 /// @param mutableIt Mutable iterator to copy from.
326 TMutable TIterator( const TMutable& mutableIt );
327 #else
328 ATMP_SELECT_IF_1TP( typename TMutable, ATMP_EQ( TMutable, TIterator<T> ) )
329 TIterator( const TMutable& mutableIt )
330 : table ( mutableIt.table )
331 , bucketIdx( mutableIt.bucketIdx )
332 , element ( mutableIt.element )
333 {}
334 #endif
335
336 //#################### To satisfy concept of InputIterator ####################
337
338 /// Prefix increment operator.
339 /// @return A reference to this object.
341 {
342 if(element->hasNext())
343 {
344 element= element->next();
345 return *this;
346 }
347
349 {
351 if( !table->buckets[bucketIdx].isEmpty() )
352 {
353 element= table->buckets[bucketIdx].first();
354 return *this;
355 }
357 }
358
359 element= nullptr;
360 return *this;
361 }
362
363 /// Postfix increment operator.
364 /// @return An iterator value that is not increased, yet.
366 {
367 auto result= TIterator( *this);
368 ++(*this);
369 return result;
370 }
371
372 /// Comparison operator.
373 /// @param other The iterator to compare ourselves to.
374 /// @return \c true if this and the given iterator are pointing to the same element,
375 /// \c false otherwise.
376 bool operator==( TIterator other) const
377 {
378 return element == other.element;
379 }
380
381 /// Comparison operator.
382 /// @param other The iterator to compare ourselves to.
383 /// @return \c true if this and given iterator are not equal, \c false otherwise.
384 bool operator!=( TIterator other) const
385 {
386 return !(*this == other);
387 }
388
389
390 // ############ access to templated members ############
391
392 /// Retrieves the stored object that this iterator references.
393 /// @return A reference to the stored object.
394 TConstOrMutable& operator*() const
395 {
396 ALIB_ASSERT_ERROR( element != nullptr, "MONOMEM/HASHTABLE", "Illegal iterator." )
397 return element->value;
398 }
399
400 /// Retrieves a pointer to the stored object that this iterator references.
401 /// @return A pointer to the stored object.
402 TConstOrMutable* operator->() const
403 {
404 ALIB_ASSERT_ERROR( element != nullptr, "MONOMEM/HASHTABLE", "Illegal iterator." )
405 return &element->value;
406 }
407
408 /// Retrieves the stored object that this iterator references.
409 /// @return A reference to the stored object.
410 TConstOrMutable& Value() const
411 {
412 ALIB_ASSERT_ERROR( element != nullptr, "MONOMEM/HASHTABLE", "Illegal iterator." )
413 return element->value;
414 }
415
416 /// Retrieves the key-portion of the stored object that this iterator references.
417 /// @return A reference to the key-portion of the stored object.
418 const TKey& Key() const
419 {
420 ALIB_ASSERT_ERROR( element != nullptr, "MONOMEM/HASHTABLE", "Illegal iterator." )
421 return TValueDescriptor().Key( element->value );
422 }
423
424 /// Retrieves the mapped-portion of the stored object that this iterator references.
425 /// This method is an alias to <c>operator*</c>
426 /// @return A reference to the mapped-portion of the stored object.
428 {
429 ALIB_ASSERT_ERROR( element != nullptr, "MONOMEM/HASHTABLE", "Illegal iterator." )
430 return TValueDescriptor().Mapped( element->value );
431 }
432
433 }; // class TIterator
434
435
436 //==============================================================================================
437 /// Templated implementation of \c std::iterator_traits.
438 /// Will be exposed by derived class's definitions
439 /// \alib{containers::HashTable;LocalIterator} and
440 /// \alib{containers::HashTable;ConstLocalIterator}.
441 ///
442 /// As the name of the class indicates, this iterator satisfies the C++ standard library concept
443 /// \https{ForwardIterator,en.cppreference.com/w/cpp/named_req/ForwardIterator}.
444 ///
445 /// @tparam TConstOrMutable A constant or mutable version of template parameter \p{T}.
446 //==============================================================================================
447 template<typename TConstOrMutable>
449 {
450 #if !DOXYGEN
451 friend struct HashTableBase;
452 friend class containers::HashTable<TAllocator,TValueDescriptor,THash,TEqual,THashCaching,TRecycling>;
453 #endif
454
455 using iterator_category = std::forward_iterator_tag; ///< Implementation of <c>std::iterator_traits</c>.
456 using value_type = TConstOrMutable ; ///< Implementation of <c>std::iterator_traits</c>.
457 using difference_type = integer ; ///< Implementation of <c>std::iterator_traits</c>.
458 using pointer = TConstOrMutable* ; ///< Implementation of <c>std::iterator_traits</c>.
459 using reference = TConstOrMutable& ; ///< Implementation of <c>std::iterator_traits</c>.
460
461 protected:
462 /// The pointer to the actual element.
464
465 /// The index of the bucket that this iterator works on.
467
468 public:
469 /// Default constructor.
472
473 /// Copy constructor (default).
474 /// @param other The iterator to assign from.
475 TLocalIterator( const TLocalIterator& other ) = default;
476
477 #if DOXYGEN
478 /// Copy constructor accepting a mutable iterator.
479 /// Available only for the constant version of this iterator.
480 /// @tparam TMutable The type of this constructor's argument.
481 /// @param mutableIt Mutable iterator to copy from.
482 TMutable TLocalIterator( const TMutable& mutableIt );
483 #else
484 ATMP_SELECT_IF_1TP( typename TMutable, ATMP_EQ( TMutable, TLocalIterator<T> ) )
485 TLocalIterator( const TMutable& mutableIt )
486 : element ( mutableIt.element )
487 , bucketIdx( mutableIt.bucketIdx )
488 {}
489 #endif
490
491 /// Constructor.
492 /// @param pBucketIdx Index of the bucket this iterator works on.
493 /// @param pElement Pointer to current element.
494 explicit TLocalIterator( uinteger pBucketIdx, Element* pElement )
495 : element (pElement )
496 , bucketIdx(pBucketIdx)
497 {}
498
499 /// Copy assignment (default).
500 /// @param other The iterator to assign from.
501 /// @return A reference to this object.
502 TLocalIterator& operator=( const TLocalIterator& other) = default;
503
504 // ################### To satisfy concept of InputIterator ####################
505
506 /// Prefix increment operator.
507 /// @return A reference to this object.
509 {
510 ALIB_ASSERT_ERROR( element != nullptr, "MONOMEM/HASHTABLE", "Illegal iterator." )
511 element= element->next();
512 return *this;
513 }
514
515 /// Postfix increment operator.
516 /// @return An iterator value that is not increased, yet.
518 {
519 auto result= TLocalIterator( *this);
520 ++(*this);
521 return result;
522 }
523
524 /// Comparison operator.
525 /// @param other The iterator to compare ourselves to.
526 /// @return \c true if this and the given iterator are pointing to the same element,
527 /// \c false otherwise.
528 bool operator==( TLocalIterator other) const
529 {
530 return element == other.element
531 && bucketIdx == other.bucketIdx;
532 }
533
534 /// Comparison operator.
535 /// @param other The iterator to compare ourselves to.
536 /// @return \c true if this and given iterator are not equal, \c false otherwise.
537 bool operator!=( TLocalIterator other) const
538 {
539 return !(*this == other);
540 }
541
542 // ############ access to templated members ############
543
544 /// Retrieves the stored object that this iterator references.
545 /// @return A reference to the stored object.
546 TConstOrMutable& operator*() const
547 {
548 ALIB_ASSERT_ERROR( element != nullptr, "MONOMEM/HASHTABLE", "Illegal iterator." )
549 return element->value;
550 }
551
552 /// Retrieves a pointer to the stored object that this iterator references.
553 /// @return A pointer to the stored object.
554 TConstOrMutable* operator->() const
555 {
556 ALIB_ASSERT_ERROR( element != nullptr, "MONOMEM/HASHTABLE", "Illegal iterator." )
557 return &element->value;
558 }
559
560 /// Retrieves the stored object that this iterator references.
561 /// @return A reference to the stored object.
562 TConstOrMutable& Value() const
563 {
564 ALIB_ASSERT_ERROR( element != nullptr, "MONOMEM/HASHTABLE", "Illegal iterator." )
565 return element->value;
566 }
567
568 /// Retrieves the key-portion of the stored object that this iterator references.
569 /// @return A reference to the key-portion of the stored object.
570 const TKey& Key() const
571 {
572 ALIB_ASSERT_ERROR( element != nullptr, "MONOMEM/HASHTABLE", "Illegal iterator." )
573 return TValueDescriptor().Key( element->value );
574 }
575
576 /// Retrieves the stored object that this iterator references.<br>
577 /// This method is an alias to <c>operator*</c>
578 /// @return A reference to the mapped-portion of the stored object.
580 {
581 ALIB_ASSERT_ERROR( element != nullptr, "MONOMEM/HASHTABLE", "Illegal iterator." )
582 return TValueDescriptor().Mapped( element->value );
583 }
584 }; // class TLocalIterator
585
586
587 // ###############################################################################################
588 // Fields
589 // ###############################################################################################
590 /// The number of buckets managed by this tree.
592
593 /// The list of buckets.
595
596 /// The load factor that is set when the table is rehashed automatically.
598
599 /// The maximum quotient of #size and #bucketCount that triggers a rehash.
601
602 /// The number of elements stored.
604
605 /// Calculated once with rehash. Product of #maxLoadFactor and #bucketCount.
607
608
609 // ###############################################################################################
610 // ### mini helpers
611 // ###############################################################################################
612
613 /// Compares two elements. If cached mode, the hash code is compared before the keys.
614 /// @param lhs The first node to compare.
615 /// @param rhs The second node to compare.
616 /// @return The result of the comparison.
617 bool areEqual( Element* lhs, Element* rhs ) const
618 {
619 return ( !Element::CachedHashCodes || ( getHashCode(lhs) == getHashCode(rhs) ) )
620 && TEqual{}( TValueDescriptor().Key( lhs->value ),
621 TValueDescriptor().Key( rhs->value ) );
622 }
623
624 /// Compares a key and a node. If cached mode, the hash codes are compared before the
625 /// keys.
626 /// @param elem The element to compare.
627 /// @param key The key to compare.
628 /// @param keyHashCode The hash code of \p{key}.
629 /// @return The result of the comparison.
630 bool areEqual( Element* elem, const TKey& key, size_t keyHashCode ) const
631 {
632 return ( !Element::CachedHashCodes || ( keyHashCode == getHashCode(elem) ) )
633 && TEqual{}( TValueDescriptor().Key( elem->value ), key );
634 }
635
636 /// Searches the first element equal to \p{key} in bucket \p{bucketIdx}.
637 ///
638 /// @param bucketIdx The bucket number to search in (hash code divided by #bucketCount).
639 /// @param key The <em>key-portion</em> of the element to search.
640 /// @param keyHashCode The hash code of \p{key}.
641 /// @return A pointer to the element searched, respectively \c nullptr if not found.
642 Element* findElement( uinteger bucketIdx, const TKey& key, size_t keyHashCode ) const
643 {
645 Node* result= buckets[bucketIdx].first();
646 while( result)
647 {
648 if( areEqual( static_cast<Element*>(result), key, keyHashCode ) )
649 return static_cast<Element*>(result);
650
651 result= result->next();
652 }
653 return nullptr;
655 }
656
657 /// Searches the predecessor of the first element equal to \p{key} in bucket \p{bucketIdx}.
658 ///
659 /// @param bucketIdx The bucket number to search in (hash code divided by #bucketCount).
660 /// @param key The <em>key-portion</em> of the element to search.
661 /// @param keyHashCode The hash code of \p{key}.
662 /// @return A pointer to the element before the searched one, respectively \c nullptr if not
663 /// found.
664 Node* findElementBefore( uinteger bucketIdx, size_t keyHashCode, const TKey& key ) const
665 {
667 Node* result= &buckets[bucketIdx];
669
670 while( result->hasNext() && !areEqual( result->next(), key, keyHashCode ) )
671 result= result->next();
672
673 return result->hasNext() ? result : nullptr;
674 }
675
676 /// Inserts the given element into the bucket.
677 /// If an element of the same key exists, then the element is put in front of that element.
678 /// Otherwise it is added to the start of the bucket.
679 /// @param element The element to insert to its bucket.
680 /// @param hashCode The hash code of parameter \p{element}.
681 /// @return The bucket number that the element was inserted to.
682 uinteger insertInBucket( Element* element, const size_t hashCode )
683 {
684 auto bucketIdx= hashCode % bucketCount;
685 Node* previous= findElementBefore( bucketIdx, hashCode, TValueDescriptor().Key( element->value ) );
687 if( previous == nullptr )
688 previous= &buckets[bucketIdx];
690
691 previous->addBehind( element );
692 return bucketIdx;
693 }
694
695 /// Increases field #size and checks for a rehash.
696 ///
697 /// @param increase The amount to increase.
698 /// @param hashCode A hashCode that caller might want to have converted into a new
699 /// actual bucket index.
700 /// @return The bucket index of \p{hashCode}.
701 size_t increaseSize( integer increase, const size_t hashCode= 0 )
702 {
703 size+= increase;
704 if( size >=sizeLimitToRehash )
705 rehash( (std::max)( uinteger( float(size) / baseLoadFactor ), bucketCount + 1 ) );
706
707 return hashCode % bucketCount;
708 }
709
710 //==============================================================================================
711 /// Constructor.
712 /// @param pAllocator The allocator to use.
713 /// @param pBaseLoadFactor The base load factor.
714 /// @param pMaxLoadFactor The maximum load factor.
715 //==============================================================================================
716 HashTableBase( TAllocator& pAllocator,
717 float pBaseLoadFactor,
718 float pMaxLoadFactor )
719 : recyclerType ( pAllocator )
720 , baseLoadFactor( pBaseLoadFactor )
721 , maxLoadFactor ( pMaxLoadFactor )
722 , size ( 0 )
723 {
724 buckets = reinterpret_cast<FwdList*>(&detail::DUMMY_BUCKET);
725 bucketCount = 1;
726 size = 0;
728 }
729
730 //==============================================================================================
731 /// Constructor using \alib{lang;HeapAllocator}.
732 /// @param pBaseLoadFactor The base load factor.
733 /// @param pMaxLoadFactor The maximum load factor.
734 //==============================================================================================
735 HashTableBase( float pBaseLoadFactor,
736 float pMaxLoadFactor )
737 : baseLoadFactor( pBaseLoadFactor )
738 , maxLoadFactor ( pMaxLoadFactor )
739 , size ( 0 )
740 {
741 buckets = reinterpret_cast<FwdList*>(&detail::DUMMY_BUCKET);
742 bucketCount = 1;
743 size = 0;
745 }
746
747 //==============================================================================================
748 /// Constructor taking a shared recycler.
749 /// @param pSharedRecycler The shared recycler hook.
750 /// @param pBaseLoadFactor The base load factor.
751 /// @param pMaxLoadFactor The maximum load factor.
752 //==============================================================================================
753 template<typename TSharedRecycler= SharedRecyclerType,
754 ATMP_T_IF(int, !ATMP_EQ(TSharedRecycler , void)) = 0 >
755 HashTableBase( TSharedRecycler& pSharedRecycler,
756 float pBaseLoadFactor = 1.0,
757 float pMaxLoadFactor = 2.0 )
758 : recyclerType ( pSharedRecycler )
759 , baseLoadFactor( pBaseLoadFactor )
760 , maxLoadFactor ( pMaxLoadFactor )
761 , size ( 0 )
762 {
763 buckets = reinterpret_cast<FwdList*>(&detail::DUMMY_BUCKET);
764 bucketCount = 1;
765 size = 0;
767 }
768
769 //==============================================================================================
770 /// Destructor. Destructs all elements in this object.
771 /// Deletes recyclables, buckets and bucket array.
772 //==============================================================================================
774 {
775 if ( buckets == reinterpret_cast<FwdList*>( &detail::DUMMY_BUCKET ) )
776 return;
777
778 // destruct entry data and delete entry objects
779 for( uinteger bucketIdx= 0 ; bucketIdx < bucketCount ; ++bucketIdx )
780 {
782 Element* first= buckets[bucketIdx].first();
783 if ( first != nullptr )
784 recyclerType::DisposeList(first);
786 }
787
788 // free bucket array
789 recyclerType::template DisposeChunk<FwdList>(buckets, bucketCount );
790 }
791
792 // ###############################################################################################
793 // ### method implementations
794 // ###############################################################################################
795
796 //==============================================================================================
797 /// Destructs and removes all entries from this hash table.
798 //==============================================================================================
799 void clear()
800 {
801 if( size == 0 )
802 return;
803
804 // destruct and recycle entries
805 for( uinteger bucketIdx= 0 ; bucketIdx < bucketCount ; ++bucketIdx )
806 if ( buckets[bucketIdx].first() )
807 {
808 recyclerType::RecycleList( buckets[bucketIdx].first() );
809 buckets[bucketIdx].reset();
810 }
811
812 size= 0;
813 }
814
815
816 //==============================================================================================
817 /// Changes the maximum load factor value and invokes #rehash providing the actual bucket
818 /// count as the minimum bucket count that is to be chosen.
819 /// @param pMaxLoadFactor The maximum load factor used to determine the need of re-hashing.
820 //==============================================================================================
821 void setMaxLoadFactor( float pMaxLoadFactor )
822 {
823 maxLoadFactor= pMaxLoadFactor;
824 if( bucketCount > 1 )
826 }
827
828 //==============================================================================================
829 /// Changes the number of buckets to be at least the higher value of
830 /// a) the given \p{newMinBucketCount}, and<br>
831 /// b) the quotient of the current size and the maximum load factor.
832 ///
833 /// The result of the above, is increased to the next higher prime number.
834 /// Rehash is only performed if bucket size increases. It never is decreased.
835 ///
836 /// @param newMinBucketCount The minimum new bucket count requested.
837 //==============================================================================================
838 void rehash( uinteger newMinBucketCount )
839 {
840 // smaller than before?
841 if( newMinBucketCount <= bucketCount )
842 return;
843
844 const auto oldBucketCount= bucketCount;
845
846 // adjust requested bucket count to the maximum load factor
847 newMinBucketCount= (std::max)( newMinBucketCount,
848 static_cast<uinteger>( static_cast<float>(size) / maxLoadFactor ) );
849
850 // adjust requested bucket count to next higher prime value
851 {
853 int idx= 0;
854 while( detail::PRIME_NUMBERS[idx] < newMinBucketCount )
855 ++idx;
858 }
859
860 ALIB_ASSERT_ERROR( bucketCount > oldBucketCount, "MONOMEM/HASHTABLE",
861 "Internal error: Rehashing to equal or smaller bucket count." )
862
863 // store new rehash trigger
865
866 // collect elements
867 FwdList elements;
868 for( uinteger bucketIdx= 0 ; bucketIdx < oldBucketCount ; ++bucketIdx )
869 {
871 Element* first= buckets[bucketIdx].first();
872 if( first != nullptr )
873 elements.pushFront( first, buckets[bucketIdx].findLast() );
875 }
876
877
878 // create a new data array
879 FwdList* oldData = buckets;
880
881 buckets= base::AI().template NewArray<FwdList>(bucketCount);
882
883 // re-insert objects
884 Element* actual= elements.first();
885 while( actual != nullptr )
886 {
887 Element* next= actual->next();
888 insertInBucket( actual, getHashCode(actual) );
889 actual= next;
890 }
891
892 // recycle old array and data (make future nodes elements out of it)
893 // But this must only be done with MonoAllocator and if ALIB_DEBUG_MONOMEM is not set.
894 // (This is ensured by 'TAllocator::allowsMemSplit()')
895 if ( oldData != reinterpret_cast<FwdList*>( &detail::DUMMY_BUCKET ) )
896 recyclerType::template RecycleChunk<FwdList>( oldData, oldBucketCount );
897 }
898
899 //==============================================================================================
900 /// Searches the first and last element stored according to given \p{key}.
901 /// Returns a pare of iterators that define a range containing all elements with key
902 /// \p{key} of the container.<br>
903 /// Parameter \p{resultStart} is set to the first element of the matching range and
904 /// parameter \p{resultEnd} is set to point past the last element of the range.
905 ///
906 /// If no element with key \p{key} is found, both iterators are set to \b end.
907 ///
908 /// @param key The <em>key-portion</em> of the elements to find.
909 /// @return A pair of iterators defining the range of elements with key \p{key}.
910 //==============================================================================================
911 std::pair<TIterator<T>,TIterator<T>> findRange( const TKey& key )
912 {
913 const auto hashCode= THash{}(key);
914 auto bucketIdx= hashCode % bucketCount;
915 Element* element= findElement( bucketIdx, key, hashCode );
916 if( element == nullptr )
917 return std::make_pair( TIterator<T>( this, bucketCount, nullptr ),
918 TIterator<T>( this, bucketCount, nullptr ) );
919
920 auto result= std::make_pair( TIterator<T>( this, bucketIdx, element ),
921 TIterator<T>( this, bucketIdx, element ) );
922 for(;;)
923 {
924 ++result.second;
925 if( result.second.element == nullptr
926 || !areEqual( result.second.element, key, hashCode ) )
927 return result;
928 }
929
930 }
931
932 //==============================================================================================
933 /// Searches (a first) element with the given key.
934 /// If not found, an invalid iterator to the bucket is returned with a nulled element pointer.
935 /// Before this counter #size is increased and, if a load limit is reached, a re-hash is
936 /// performed.
937 ///
938 /// If otherwise an element was found, its valid iterator is returned.
939 ///
940 /// Note: Used by \alib{containers;HashMap::EmplaceIfNotExistent}.
941 ///
942 /// @param key The key to the (first) element to find.
943 /// @param hashCode The hash code of parameter \p{key}.
944 /// @return A pair of an iterator referring to the found or inserted element and a boolean
945 /// that is \c true if the insertion took place and \c false nothing was changed.
946 //==============================================================================================
947 std::pair<TIterator<T>, bool> insertIfNotExists( const TKey& key, size_t hashCode )
948 {
949 auto bucketIdx= hashCode % bucketCount;
950 Element* element = findElement( bucketIdx, key, hashCode );
951 if (element != nullptr )
952 return std::make_pair(TIterator<T>( this, bucketIdx, element ), false);
953
955 bucketIdx= increaseSize( 1, hashCode );
956
957 Element* newElement= allocElement( hashCode );
958 buckets[bucketIdx].pushFront( newElement );
959 return std::make_pair(TIterator<T>( this, bucketIdx, newElement ) , true);
961 }
962
963 //==============================================================================================
964 /// Inserts the topmost recyclable element if no element with the same key-portion of its value
965 /// exists.
966 ///
967 /// @param key Pointer to the key to search elements for deletion.
968 /// @param hashCode The hash code of parameter \p{key}.
969 /// @return A pair containing an iterator referring to the element added.
970 /// The bool component is \c true if the insertion took place and \c false if a new
971 /// element was created.
972 //==============================================================================================
973 std::pair<TIterator<T>, bool> insertOrGet( const TKey& key, size_t hashCode )
974 {
975 // find (first) element with same key in bucket
976 auto bucketIdx= hashCode % bucketCount;
977 auto* elem= findElement( bucketIdx, key, hashCode );
978 if( elem != nullptr )
979 return std::make_pair( TIterator<T>( this, bucketIdx, elem ), false);
980
981 // create new element
982 bucketIdx= increaseSize( 1, hashCode );
983 Element* newElem= allocElement( hashCode );
985 buckets[bucketIdx].pushFront( newElem );
987 return std::make_pair(TIterator<T>( this, bucketIdx, newElem ), true);
988 }
989
990}; // HashTableBase
991
992}} // namespace [alib::containers::detail]
993
994
995#endif // HPP_ALIB_MONOMEM_DETAIL_HASHTABLEBASE
996
TIterator(THashtable *pTable, uinteger pBbucketIx, Element *pElement)
ATMP_IF_T_F(!ATMP_IS_CONST(TConstOrMutable), HashTableBase, const HashTableBase) THashtable
Const or mutable version of HashTableBase.
TIterator(THashtable *pTable, uinteger pBbucketIx)
TMapped value_type
Implementation of std::iterator_traits.
std::forward_iterator_tag iterator_category
Implementation of std::iterator_traits.
TIterator & operator=(const TIterator &other)=default
void repair()
Moves an iterator with a nulled element pointer to the next element.
TIterator(const TIterator &other)=default
THashtable * table
The pointer to the hash table.
TMutable TIterator(const TMutable &mutableIt)
Element * element
The pointer to the actual element.
TConstOrMutable * pointer
Implementation of std::iterator_traits.
integer difference_type
Implementation of std::iterator_traits.
TConstOrMutable & reference
Implementation of std::iterator_traits.
TIterator()=default
Default constructor. Keeps everything uninitialized.
TLocalIterator(const TLocalIterator &other)=default
TConstOrMutable value_type
Implementation of std::iterator_traits.
TLocalIterator & operator=(const TLocalIterator &other)=default
TLocalIterator(uinteger pBucketIdx, Element *pElement)
std::forward_iterator_tag iterator_category
Implementation of std::iterator_traits.
Element * element
The pointer to the actual element.
TConstOrMutable * pointer
Implementation of std::iterator_traits.
TMutable TLocalIterator(const TMutable &mutableIt)
integer difference_type
Implementation of std::iterator_traits.
TConstOrMutable & reference
Implementation of std::iterator_traits.
uinteger bucketIdx
The index of the bucket that this iterator works on.
#define ATMP_IF_T_F( Cond, T, F)
Definition tmp.hpp:50
#define ATMP_IS_CONST(T)
Definition tmp.hpp:20
#define ALIB_WARNINGS_RESTORE
Definition alib.hpp:849
#define ALIB_API
Definition alib.hpp:639
#define ATMP_EQ( T, TEqual)
Definition tmp.hpp:27
#define ALIB_ASSERT_ERROR(cond,...)
Definition alib.hpp:1271
#define ALIB_WARNINGS_ALLOW_UNSAFE_BUFFER_USAGE
Definition alib.hpp:760
#define ATMP_SELECT_IF_1TP(TParam, ...)
Definition tmp.hpp:58
#define ATMP_T_IF(T, Cond)
Definition tmp.hpp:49
ALIB_API const uinteger PRIME_NUMBERS[PRIME_TABLE_SIZE]
static constexpr int PRIME_TABLE_SIZE
The size of the static table of prime numbers. Depends on the platform.
ALIB_API void * DUMMY_BUCKET
A dummy bucket used for nulled hash tables to avoid otherwise necessary checks.
Caching
Denotes if a cache mechanism is enabled or disabled.
@ Enabled
Caching is enabled.
@ Auto
Auto/default mode.
lang::uinteger uinteger
Type alias in namespace alib.
Definition integers.hpp:276
containers::HashTable< TAllocator, TValueDescriptor, THash, TEqual, THashCaching, TRecycling > HashTable
Type alias in namespace alib. See type definition alib::containers::HashSet.
lang::integer integer
Type alias in namespace alib.
Definition integers.hpp:273
TStored value
The custom data stored in nodes of this table.
static constexpr bool CachedHashCodes
TMP constant that denotes that hash codes are cached.
ATMP_IF_T_F( IsCachingHashes(), HTElementCached< typename TValueDescriptor::StoredType >, HTElementUncached< typename TValueDescriptor::StoredType >) Type
The type stored in a hash table's bucket list.
TStored value
The custom data stored in nodes of this table.
static constexpr bool CachedHashCodes
TMP constant that denotes that hash codes are not cached.
integer sizeLimitToRehash
Calculated once with rehash. Product of maxLoadFactor and bucketCount.
Element * findElement(uinteger bucketIdx, const TKey &key, size_t keyHashCode) const
uinteger bucketCount
The number of buckets managed by this tree.
bool areEqual(Element *lhs, Element *rhs) const
std::pair< TIterator< T >, bool > insertIfNotExists(const TKey &key, size_t hashCode)
size_t increaseSize(integer increase, const size_t hashCode=0)
typename HTElementSelector< TValueDescriptor, THashCaching >::Type Element
The type stored in the bucket of class HashTable.
typename TValueDescriptor::MappedType TMapped
Type definition publishing template parameter TKey.
integer size
The number of elements stored.
void rehash(uinteger newMinBucketCount)
typename RecyclingSelector< TRecycling >::template Type< TAllocator, typename HTElementSelector< TValueDescriptor, THashCaching >::Type > recyclerType
Type of a single linked node list.
std::pair< TIterator< T >, TIterator< T > > findRange(const TKey &key)
HashTableBase(float pBaseLoadFactor, float pMaxLoadFactor)
Element * allocElement(const size_t hashCode)
typename detail::RecyclingSelector< TRecycling > ::template HookType< TAllocator, typename HTElementSelector< TValueDescriptor, THashCaching >::Type > SharedRecyclerType
FwdList * buckets
The list of buckets.
HashTableBase(TSharedRecycler &pSharedRecycler, float pBaseLoadFactor=1.0, float pMaxLoadFactor=2.0)
std::pair< TIterator< T >, bool > insertOrGet(const TKey &key, size_t hashCode)
typename RecyclingSelector< TRecycling >::template Type< TAllocator, typename HTElementSelector< TValueDescriptor, THashCaching >::Type > base
Our base type.
HashTableBase(TAllocator &pAllocator, float pBaseLoadFactor, float pMaxLoadFactor)
float baseLoadFactor
The load factor that is set when the table is rehashed automatically.
static size_t getHashCode(Element *elem)
float maxLoadFactor
The maximum quotient of size and bucketCount that triggers a rehash.
uinteger insertInBucket(Element *element, const size_t hashCode)
typename TValueDescriptor::StoredType T
Type definition publishing template parameter T.
void clear()
Destructs and removes all entries from this hash table.
void setMaxLoadFactor(float pMaxLoadFactor)
typename TValueDescriptor::KeyType TKey
Type definition publishing template parameter TKey.
Node * findElementBefore(uinteger bucketIdx, size_t keyHashCode, const TKey &key) const
bool areEqual(Element *elem, const TKey &key, size_t keyHashCode) const
void reset() noexcept
Resets this list to zero elements.
Definition sidilist.hpp:224
void pushFront(TElement *elem) noexcept
Definition sidilist.hpp:233
TElement * first() const noexcept
Definition sidilist.hpp:229
void next(SidiNodeBase *p)
Definition sidilist.hpp:101
TElement * addBehind(TElement *elem) noexcept
Definition sidilist.hpp:150