ALib C++ Framework
by
Library Version: 2511 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
hashtable.inl
Go to the documentation of this file.
1
2//==================================================================================================
3/// \file
4/// This header-file is part of module \alib_containers of the \aliblong.
5///
6/// \emoji :copyright: 2013-2025 A-Worx GmbH, Germany.
7/// Published under #"mainpage_license".
8//==================================================================================================
9ALIB_EXPORT namespace alib { namespace containers {
10//==================================================================================================
11/// # Contents #
12/// #"alib_ns_containers_hashtable_intro" <br>
13/// #"alib_ns_containers_hashtable_setandmap" <br>
14/// #"alib_ns_containers_hashtable_single_multi" <br>
15/// #"alib_ns_containers_hashtable_rehashing" <br>
16/// #"alib_ns_containers_hashtable_iterators" <br>
17/// #"alib_ns_containers_hashtable_hashcodes" <br>
18/// &nbsp;&nbsp;#"alib_ns_containers_hashtable_caching" <br>
19/// &nbsp;&nbsp;#"alib_ns_containers_hashtable_hashprecalc" <br>
20/// &nbsp;&nbsp;#"alib_ns_containers_hashtable_hashquality" <br>
21/// #"alib_ns_containers_hashtable_memory" <br>
22/// #"alib_ns_containers_hashtable_comparison" <br>
23/// #"alib_ns_containers_hashtable_referencedoc" <br>
24///
25/// \I{#############################################################################################}
26/// # 1. Introduction # {#alib_ns_containers_hashtable_intro}
27/// This class implements a \https{hash table,en.wikipedia.org/wiki/Hash_table} that
28/// stores and retrieves objects very efficiently in respect to execution performance.
29/// All memory for the hash table and its entries are allocated using a template
30/// #"lang::Allocator;allocator type".
31///
32/// Two type definitions based on this class exist, which change the set of template parameters
33/// of this type by providing reasonable replacements. These types are:
34/// - #"containers::HashMap" and
35/// - #"containers::HashSet".
36///
37/// In many cases, the use of one of these definitions is more convenient than instantiating this
38/// type directly. The meaning of these types is discussed in the next section.
39///
40/// \I{#############################################################################################}
41/// # 2. Hash Sets vs. Hash Maps# {#alib_ns_containers_hashtable_setandmap}
42/// A <em>"hash set"</em> is commonly understood as a type that contains custom values of
43/// #".StoredType", which are also used as the key to finding such stored objects.
44/// <em>"Hash maps"</em> are hash tables that store objects of a custom #".MappedType" which are
45/// associated to a value of a key #".KeyType". In this "mode" the key is not contained in the custom
46/// value.<br>
47/// The template parameters and implementation of this class supports both concepts and they are
48/// covered by the two provided alternative type definitions mentioned in the previous section.
49/// Furthermore, those go along well with what is found in the standard C++ library:
50///
51/// 1. #"containers::HashSet"
52/// Removes template parameter \p{TValueDescriptor} (by presetting it to a built-in helper-type)
53/// and instead denotes all three types, namely #".StoredType", #".KeyType" and #".MappedType" to the
54/// same new template parameter \p{T}.
55/// Functors \p{THash} and \p{TEqual} consequently expect the \b %StoredType.<br>
56///
57/// This type definition is similar to what types <c>std::unordered_set</c> and
58/// <c>std::unordered_multiset</c> of the standard C++ class library provide.
59///
60/// 2. #"containers::HashMap"
61/// Removes template parameter \p{TValueDescriptor} (by presetting it to a built-in helper-type)
62/// and instead introduces template parameters \p{TKey} and \p{TMapped}.
63/// Here, only the <em>key-portion</em> is used for calculating hash values and testing elements
64/// for equality. The <em>mapped-portion</em> is the true custom type that is to be stored. <br>
65/// Consequently, \b %HashMap defines the #".StoredType" as <c>std::pair<TKey, TMapped></c>.<br>
66///
67/// The type definition is similar to what types <c>std::unordered_map</c> and
68/// <c>std::unordered_multimap</c> of the standard C++ class library provide.
69///
70/// To achieve this flexibility, template parameter \p{TValueDescriptor}, which is exposed as
71/// #".DescriptorType", is constrained to have two methods \b Key() and \b Mapped() to extract
72/// a reference to the corresponding portions out of the stored value.
73///
74/// One of the advantages of this approach is that this type supports a third "mode", besides
75/// implementing <em>"hash sets"</em> and <em>"hash maps"</em>: Custom value type #".StoredType"
76/// may have a <em>key-portion</em> embedded, instead of using the complete type as a search key.<br>
77/// While there is no specific type definition for this "key-embedded mode" available, the using
78/// code needs to create a custom version of template parameter \p{TValueDescriptor}
79/// which enables the extraction of the key and mapped portions of the stored type.
80/// The following table summarizes this:
81///
82/// Working Mode | Type to Use | Value Descriptor Type
83/// ---------------------------|-------------------------------------|-------------------------
84/// Hash Set |#"containers::HashSet" (A type definition on \b %HashTable) | Automaticly chosen as built-in #"TIdentDescriptor"
85/// Hash Map |#"containers::HashMap" (A type definition on \b %HashTable) | Automaticly chosen as built-in #"TPairDescriptor"
86/// Hash Set with embedded Key |#"HashTable" (The original type) | A <b>custom type</b> has to be provided
87///
88/// \note The hash-table implementations of the standard C++ library do not support a similar
89/// approach. While with the provision of customized hash- and comparison-functors, of course
90/// only a portion of a type stored in a set can be used for hashing and comparing, still the
91/// interface functions require to receive a "complete" instance, which then is often
92/// "nulled" or "empty" in respect to the fields that are not used for the key.<br>
93/// Because of this limitation, \alib introduces template parameter \p{TValueDescriptor} in
94/// addition to parameters \p{THash} and \p{TEqual}.
95///
96///
97/// As a sample for the advantage consider method
98/// #"EmplaceIfNotExistent(const KeyType& key, TArgs&&... args)".
99/// A corresponding method is found with type <c>std::unordered_map::try_emplace</c>, but not
100/// with <c>std::unordered_set</c>. In contrast with this implementation, the method
101/// is usable with <em>hash maps</em> as well as with <em>hash sets</em>!<br>
102/// The only precondition to the availability of this method, is that the #".StoredType" has to be
103/// constructible from the combined list of given arguments, hence the \p{KeyType} argument along
104/// with given arguments of variadic template types \p{TArgs}.
105/// The same is true for method #"EmplaceOrAssign(const" KeyType&, TArgs&&... args)
106///
107///
108/// The set of available interface methods slightly changes with the two operation modes:
109/// 1. <b>Hash Set Mode:</b><br>
110/// Method overload #"EmplaceIfNotExistent(TArgs&&... args)" is exclusively available with hash
111/// sets.<br>
112///
113/// 2. <b>Hash Map Mode:</b><br>
114/// The following method overloads are exclusively available with hash \b maps:
115/// - #"InsertOrAssign(const KeyType&, MappedType&&)"
116/// - #"InsertOrAssign(const KeyType&, const MappedType&)"
117/// - #"InsertIfNotExistent(const KeyType&, MappedType&&)"
118/// - #"InsertIfNotExistent(const KeyType&, const MappedType&)"
119/// <br><br>
120///
121/// In addition, the following method overloads of inner types are also exclusively available
122/// with hash \b maps.
123/// - #"TIterator::Mapped"
124/// - #"TLocalIterator::Mapped"
125/// - #"ElementHandle::Mapped"
126///
127/// A hint to restrictions is given with the documentation of each method of concern.
128/// Note that methods that expect a single #KeyType are usable with both operation modes, because
129/// with hash sets the key-type equals type #StoredType.
130///
131/// \note
132/// Technically, the availability of the methods is selected at compile-time.
133///
134/// \I{#############################################################################################}
135/// # 3. Single And Multiple Entries # {#alib_ns_containers_hashtable_single_multi}
136/// The standard C++ library differentiates between hashing containers that accept only one
137/// element with a specific <em>key-portion</em> of the value (see <c>std::unordered_set</c> and
138/// <c>std::unordered_map</c>) and those that accept multiple insertions of objects with the
139/// same <em>key-portion</em> (see <c>std::unordered_multiset</c> <c>std::unordered_multimap</c>).
140///
141/// This library does \b not provide separate types and any instantiation of this class allows
142/// multiple entries. But this is rather an extension of functionality, than a restriction
143/// and does not impose a penalty on performance.
144///
145/// If unique entries are to be achieved, the user of the type has to make sure for herself that
146/// no multiple entries are inserted. This can be guaranteed, with the exclusive use of the
147/// following set of (partly overloaded) methods, which prevent the creation of multiple entries:
148///
149/// - #InsertUnique / #EmplaceUnique
150/// - #InsertOrAssign / #EmplaceOrAssign
151/// - #InsertIfNotExistent / #EmplaceIfNotExistent
152///
153/// In contrast to this, methods #Insert and #Emplace (and their overloads) will insert
154/// an equal value without giving further notice (for example, by providing a special return value
155/// that indicates if the inserted key existed before).<br>
156/// Method #EraseUnique(const KeyType&) is more efficient than #erase(const KeyType&)
157/// and a further advantage is that it asserts (in debug-compilations) that not more than one
158/// element is found in the table.
159///
160/// \note
161/// If uniqueness of key values is needed, it might be seen by the reader to be a "disadvantage"
162/// that the burden is on the user of the class to guarantee such uniqueness.
163/// However, such burden exists with the set-types of the standard library likewise:
164/// There, result values have to be evaluated to determine if an insertion was successful or not.
165/// The various smaller changes and extensions of the <c>std</c>-types with C++ 14 and 17 reflect
166/// the design problems of the approach to provide "unique" and "multi" types.
167///
168/// \note
169/// The approach that \alib takes here has three small benefits:
170/// 1. A user is free to choose a "mixed mode" by allowing duplicates of certain values (keys)
171/// and not allowing duplicates for others.
172/// While this can be achieved with <c>std::unordered_multimap/set</c> as well, a performance
173/// penalty is given, unless the extended interface of C++17 standard is used with great care.
174/// 2. The code is better readable, due to explicit naming of the invocations, in contrast
175/// to the need to knowing a container's type with each invocation.
176/// 3. The use of the type and this documentation is less confusing, because only one type exists.
177/// In contrast, the two types found in <c>std</c> have equally named methods that act
178/// differently and return different result types.
179///
180/// \I{#############################################################################################}
181/// # 4. Re-Hashing # {#alib_ns_containers_hashtable_rehashing}
182/// A check for the need to perform re-hashing is made with every insertion of an element.
183/// The policy for re-hashing is described as follows:
184/// - With insertions, the new average bucket size is calculated as #Size divided by #BucketCount.
185/// - This value is compared with #MaxLoadFactor and if higher the number of buckets is increased.
186/// - When increased, the new minimum number of buckets is calculated as #Size divided by
187/// #BaseLoadFactor. Starting from this minimum, the effective number of buckets is chosen as the
188/// next higher prime number from a static table.
189/// - Automatic rehashes may be disabled by setting #MaxLoadFactor to a very high value, e.g.
190/// <c>std::numeric_limits<float>::max()</c>
191///
192/// The number of buckets is never decreased, unless the method #Reset is invoked.
193///
194/// Manual re-hashing is not supported by design. In our view, with monotonic growth (or stability
195/// in size) and hence the absence of dynamic increase/decrease scenarios, manual rehashing is not
196/// needed. Only the base and maximum load factors are of concern, which both can be
197/// specified with #"HashTable::HashTable(float, float);construction" and with the methods
198/// #".MaxLoadFactor()" respectively #".MaxLoadFactor(float)".<br>
199/// What can be done, however, is to use the method #".MaxLoadFactor" to "disable" rehashing
200/// temporarily and thus to allow an efficient mass insertion. Nevertheless, if the number of
201/// elements to be inserted is known upfront, the use of method #Reserve, respectively
202/// #ReserveRecyclables is the preferred approach.
203///
204/// \I{############################################################################################}
205/// # 5. Iterators # {#alib_ns_containers_hashtable_iterators}
206/// ## 5.1 Iterator Types ##
207/// There are two types of iterators provided:
208/// - #"HashTable::Iterator" and its constant sibling
209/// #"HashTable::ConstIterator", used to iterate over all elements of the
210/// hash table,
211/// - #"HashTable::LocalIterator" and its constant sibling
212/// #"HashTable::ConstLocalIterator" used to iterate over the elements
213/// of a certain bucket only.
214///
215/// Both types satisfy the C++ standard library concept
216/// \https{ForwardIterator,en.cppreference.com/w/cpp/named_req/ForwardIterator}.
217///
218/// ## 5.2 Validity Rules ##
219/// The following rules define the validity of existing iterator instances on table operations:
220/// - <b>Element \b Insertions:</b>
221/// - If no rehashing is performed (see previous section) all iterators remain valid.
222/// - If rehashing is performed, existing iterators become invalid in respect to allowance
223/// of increments and comparison. The elements that existing iterators referred to
224/// before the insertion remain valid. Consequently, existing iterators may still be used
225/// to access the custom element value and modify the mapped portion of it. They may just
226/// not be traversed then.
227/// - <b>Element \b Removals:</b> <br>
228/// All existing iterators that referred to elements that have been removed become invalid.
229/// All others remain fully intact.<br>
230/// The order of the elements that are not erased is preserved, which makes it possible to
231/// erase individual elements during iteration.
232///
233///\I{#############################################################################################}
234/// # 6. Hash Codes # {#alib_ns_containers_hashtable_hashcodes}
235///
236///\I{#############################################################################################}
237/// ## 6.1 Caching Hash Codes ## {#alib_ns_containers_hashtable_caching}
238///
239/// Template parameter \p{THashCaching} may be used to control if hash codes are cached.
240/// Caching the hash codes increases the memory consumption of this class by <c>sizeof(size_t)</c>
241/// per inserted element. While this is only a small amount and memory consumption should not
242/// be a great concern when monotonic allocation is used, caching the hash codes may impose a
243/// reasonable performance impact. This impact depends on the performance of functor \p{THash}
244/// working on the <c>key-portion</c> of the inserted value.
245///
246/// The cached hash code is used when the table is
247/// #"alib_ns_containers_hashtable_rehashing;re-hashed". In addition, the cached
248/// hash code is also used with almost all interface methods (insertion, deletion and search
249/// operations): If cached, any needed comparison of two elements will first compare the hash
250/// codes and only invoke templated functor \p{TEqual} if those match.
251///
252/// If template parameter \p{THashCaching} evaluates to <b>Caching::Auto</b> then this class
253/// defaults to use caching if type #KeyType is not arithmetic
254/// (using <c>std::is_arithmetic<TKey></c> for this check).<br>
255///
256/// The caching option of an instance of this class can be queried with enum #CachedHashCodes.
257///
258///
259///\I{#############################################################################################}
260/// ## 6.2 Hash Codes Pre-calculation ## {#alib_ns_containers_hashtable_hashprecalc}
261/// The following overloaded methods accept parameter \p{hashCode} in addition to the parameters
262/// accepted by their corresponding base version:
263/// - #Find(const KeyType&, size_t)
264/// - #Insert(const T&, size_t)
265/// - #Insert(T&&, size_t)
266/// - #InsertUnique(T&&, size_t)
267/// - #InsertOrAssign(const KeyType&, MappedType&& mapped, size_t)
268/// - #InsertIfNotExistent(const KeyType&, MappedType&& mapped, size_t)
269/// - #InsertIfNotExistent(T&&, size_t )
270/// - #Extract(const KeyType&, size_t )
271/// - #Erase(const KeyType&, size_t )
272/// - #EraseUnique( const KeyType&, size_t )
273///
274/// The rationale for the provision of these methods is to allow to "pre-calculate" a key's hash
275/// code before invoking the method. This is efficient in situations where one or more subsequent
276/// operations with the same key are performed. For example:
277/// - Insertions of multiple objects with the same key.
278/// - Insertions of an object into multiple hash tables.
279/// - Situations where the result of a find operation may lead to further operations with the
280/// same object.
281///
282///\I{#############################################################################################}
283/// ## 6.3 Hash Code Quality ## {#alib_ns_containers_hashtable_hashquality}
284/// To have hash tables perform in constant time <em>O(1)</em> in the average case, a
285/// well-implemented calculation of hash-codes has to be provided for template type \p{TKey} with
286/// template functor \p{THash}. This is an obligation of the user of this type.
287///
288/// This \alibmod_nl supports the configuration macro #"ALIB_DEBUG_CONTAINERS" which enables extra
289/// features.
290/// The entities relevant for this type are:
291/// - #"DbgGetHashTableDistribution",
292/// - #"DbgDumpDistribution" and
293/// - #"DbgDumpHashtable".
294///
295/// These methods may be used to assert the quality of custom hash algorithms.
296///
297///\I{#############################################################################################}
298/// # 7. Memory Use # {#alib_ns_containers_hashtable_memory}
299/// With template parameter \p{TRecycling} being either #"Recycling::Private"
300/// (the default) or #"Recycling::Shared", the internal
301/// <em>"node objects"</em> are remembered when deleted, and "recycled" with future insertions.
302/// The rationale for this lies in the simple fact that memory cannot be returned to
303/// the monotonic allocator. The recycling mechanism is very lightweight and does not cost measurable
304/// performance. If it is assured that no deletions are made during the life-cycle of an instance,
305/// type #"Recycling::None" may be used to eliminate this little overhead
306/// of recycling. This is why type-definition #"MonoAllocator" is recommended to be used
307/// with this container.
308///
309/// If the table is re-hashed, the former bucket list is likewise recycled and sliced into as many
310/// internal node objects as possible. The precondition for this is that the allocator
311/// interface method #"Allocator::allowsMemSplit" returns \c true. This is true for type
312/// \b MonoAllocator.
313///
314/// With these two mechanisms in place, the use of monotonic allocation with this container
315/// is very safe in respect to avoid memory exhaustion.
316/// The use of this class, even in scenarios where a lot of (theoretically an endless amount of)
317/// erase and insert operations are performed, will not increase memory consumption, as long it is
318/// guaranteed that the overall size of the table is limited.<br>
319///
320/// If a maximum number of possible insertions is known, method #ReserveRecyclables might be used
321/// to allocate all needed memory at once. With that, a
322/// #"TakeSnapshot;snapshot" of the allocator can be taken and
323/// later used to reset the allocator to the minimum that preserves all memory in the according
324/// hash table instance.<br>
325/// For advanced usage, it is advisable to fully understand the concept of monotonic allocation
326/// implemented with this module \alib_containers.
327///
328/// \I{############################################################################################}
329/// # 8. Comparison To Standard Library Hash-Types # {#alib_ns_containers_hashtable_comparison}
330/// In the previous sections, it was already referred several times to types
331/// - <c>std::unordered_map</c>,
332/// - <c>std::unordered_multimap,</c>
333/// - <c>std::unordered_set</c> and
334/// - <c>std::unordered_multiset</c>
335///
336/// of the C++ standard library. The use cases and features of these four compared to this type are
337/// generally the same and this type can be used to replace the standard tyes without general
338/// limitations and vice versa.
339///
340/// Then with C++17, the standard library was extended by a new set of types, namely
341/// - <c>std::pmr::unordered_map</c>,
342/// - <c>std::pmr::unordered_multimap,</c>
343/// - <c>std::pmr::unordered_set</c> and
344/// - <c>std::pmr::unordered_multiset</c>
345///
346/// which, likewise this type, may use monotonic allocation (for example, in combination with C++17
347/// type <c>std::pmr::monotonic_buffer_resource</c>).
348/// On the one hand, \alib in the past needed support of monotonic allocation already for
349/// C++ 11, and on the other hand, the C++17 library extensions follow slightly different design
350/// goals. Details of these were given in the previous section.
351///
352/// Besides these general differences, the specific similarities and differences can be summarized
353/// as follows:
354///
355/// - This \alib type does not distinguish sets and maps but provides a more flexible approach:
356/// Mapped types do not necessarily need to be build using a <c>std::pair</c> of key and value
357/// elements.
358/// For convenience, when the use of <c>std::pair</c> is suitable, type definition
359/// #"containers::HashMap" offers exactly this.
360/// - This \alib type does not distinguish sets/maps from multi-sets/maps.
361/// The rationale for this design decision has been described in section
362/// #"alib_ns_containers_hashtable_single_multi".
363/// - Almost all members of the four standard library types are implemented with this type
364/// in a very compatible fashion. The member names were translated from <em>lower_snake_case</em>
365/// to <em>UpperCamelCase</em> and then sometimes slightly changed.<br>
366/// C++17 extensions of the standard types were reflected.
367/// - Method #Find provides additional overloads that accept an externally (pre-) calculated
368/// hash code. This allows efficiently using the same key with a series of searches within
369/// different hash table instances. Note that C++20 offers a templated version of <c>find</c>,
370/// which allows performing the same optimization.
371/// - Erase methods that accept a #LocalIterator (bucket iterators) that are not available with
372/// the four standard library types, are provided with this type.
373/// - There is no <c>operator[]</c> defined. The rationale to omit this - at first sight convenient
374/// and intuitive - operator, is that it imposes insert operation even if used in r-value
375/// expressions. This is considered an unwanted side effect.
376/// - The caching of hash-codes can be controlled with this type, while it is an
377/// implementation-dependent feature with the standard library.
378/// - The automatic increase (re-hashing) of the bucket number can be tweaked with
379/// constructor parameter \p{pBaseLoadFactor} (and method #BaseLoadFactor(float)), which is not
380/// specified with the standard library types.
381/// - Assignment <c>operator=</c> is not available with this implementation.
382/// If needed, such has to be implemented by a user (full iteration with copying elements from one
383/// instance to another).
384///
385/// @see
386/// - Chapter #"alib_contmono_containers_types" of the joint Programmer's Manuals of modules
387/// \alib_containers and \alib_monomem.
388/// - Chapter #"alib_threads_intro_assert_entry" of the Programmer's Manual of module
389/// \alib_threads for information about debugging multithreaded access on instances of this
390/// type.
391///\I{#############################################################################################}
392/// # Reference Documentation # {#alib_ns_containers_hashtable_referencedoc}
393/// @tparam TAllocator The #"lang::Allocator;allocator type" to use.
394/// @tparam TValueDescriptor Defines the #StoredType, #KeyType and #MappedType. Furthermore has to
395/// proving methods that to extract key- and mapped values out of the
396/// stored type.<br>
397/// For details on required type definitions and method signatures, see
398/// provided implementations
399/// #"TIdentDescriptor" and
400/// #"TPairDescriptor" as a sample.<br>
401/// The type is published as
402/// #"HashTable::DescriptorType;*".
403/// @tparam THash The hash functor applicable to the key-type defined by
404/// \p{TValueDescriptor}.
405/// Defaults to <c>std::hash<typename TValueDescriptor::KeyType></c>
406/// and is published as #"HashTable::HashType;*".
407/// @tparam TEqual The comparison functor on the key-type defined by \p{TValueDescriptor}.
408/// Defaults to <c>std::equal_to<typename TValueDescriptor::KeyType></c>
409/// and is published as #"HashTable::EqualType;*".
410/// @tparam THashCaching Determines if hash codes are cached when elements are inserted.
411/// Defaults to <b>Caching::Auto</b>, which enables caching if
412/// <c>std::is_arithmetic<TKey>::value</c> evaluates to \c false.
413/// @tparam TRecycling Denotes the type of recycling that is to be performed. Possible values
414/// are
415/// #"Recycling::Private" (the default),
416/// #"Recycling::Shared" or
417/// #"Recycling::None".
418//==================================================================================================
419template< typename TAllocator,
420 typename TValueDescriptor,
421 typename THash = std::hash <typename TValueDescriptor::KeyType>,
422 typename TEqual = std::equal_to<typename TValueDescriptor::KeyType>,
423 lang::Caching THashCaching = lang::Caching::Auto,
424 Recycling TRecycling = Recycling::Private >
425class HashTable : protected detail::HashTableBase<TAllocator,TValueDescriptor,THash,TEqual,THashCaching,TRecycling>
426{
427 public:
428 #if !DOXYGEN
429 #if ALIB_DEBUG_CRITICAL_SECTIONS
430 mutable lang::DbgCriticalSections dcs;
431 #define DCS ALIB_DCS_WITH(dcs)
432 #define DCSSHRD ALIB_DCS_SHARED_WITH(dcs)
433 #else
434 #define DCS
435 #define DCSSHRD
436 #endif
437 #endif
438
439 protected:
440 // protected shortcuts to parent and its types we need
441 #if !DOXYGEN
443 using Element = typename base::Element;
444 using Node = typename base::Node;
445 #endif
446
447 /// The recycler type.
448 using recyclerType= typename base::recyclerType;
449
450
451 public:
452
453 //################################################################################################
454 // Types and Constants
455 //################################################################################################
456 /// Type definition publishing template parameter \p{TAllocator}.
457 using AllocatorType = TAllocator;
458
459 /// Type definition publishing template parameter \p{TValueDescriptor}.
460 using DescriptorType= TValueDescriptor;
461
462 /// Type definition publishing the stored type of this container as defined with template
463 /// parameter \p{TValueDescriptor}.
464 using StoredType = typename TValueDescriptor::StoredType;
465
466 /// Type definition publishing the key type of this container as defined with template
467 /// parameter \p{TValueDescriptor}.
468 using KeyType = typename TValueDescriptor::KeyType;
469
470 /// Type definition publishing the map type of this container as defined with template
471 /// parameter \p{TValueDescriptor}.
472 using MappedType = typename TValueDescriptor::MappedType;
473
474 /// Type definition publishing template parameter \p{THash}.
475 using HashType = THash;
476
477 /// Type definition publishing template parameter \p{TEqual}.
478 using EqualType = TEqual;
479
480 /// Determines whether hash codes are stored with the elements.
481 /// It is done in case the given template parameter \p{THashCashing} equals
482 /// #"Caching::Enabled" or if it equals #"Caching::Auto"
483 /// and the #KeyType type is an arithmetic type.
484 /// @return \c true if hash codes are stored for reuse, \c false if not.
485 static constexpr bool IsCachingHashes() { return base::IsCachingHashes(); }
486
487 /// @return The enum element value of template parameter \p{TRecycling}.
488 static constexpr Recycling RecyclingTag() { return TRecycling; }
489
490 /// This type definition may be used to define an externally managed shared recycler,
491 /// which can be passed to the alternative constructor of this class when template
492 /// parameter \p{TRecycling} equals #"Recycling::Shared".
493 /// \see
494 /// Chapter #"alib_contmono_containers_recycling_shared" of the Programmer's Manual
495 /// for this \alibmod.
496 using SharedRecyclerType= typename base::SharedRecyclerType;
497
498
499 /// Determines whether the used recycler type is in fact recycling elements.
500 /// @return \c false if template parameter \p{TRecycling} equals
501 /// #"Recycling::None", \c true otherwise.
502 static constexpr bool IsRecycling() { return recyclerType::IsRecycling(); }
503
504 /// Denotes whether hash codes are cached or not.
505 static constexpr bool CachedHashCodes = base::Element::CachedHashCodes;
506
507 /// The mutable iterator exposed by this container.
508 using Iterator = typename base::template TIterator < StoredType>;
509
510 /// The constant iterator exposed by this container.
511 using ConstIterator = typename base::template TIterator <const StoredType>;
512
513 /// The mutable iterator for a single bucket exposed by this container.
514 using LocalIterator = typename base::template TLocalIterator < StoredType>;
515
516 /// The constant iterator for a single bucket exposed by this container.
517 using ConstLocalIterator = typename base::template TLocalIterator <const StoredType>;
518
519 /// A value of this type is returned by the methods #Extract, which allow removing
520 /// an element from the hashtable, without deleting its allocated storage and destructing its
521 /// custom value.
522 ///
523 /// This handle allows writing access to the value of an extracted element.
524 /// In combination with methods #Insert(ElementHandle&) and #InsertIfNotExistent(ElementHandle&),
525 /// this supports changing parts of the element value, including the <em>key-portion</em> with
526 /// proper re-insertion.
527 ///
528 /// Objects of this type cannot be copied, but just moved.
530 {
531 #if !DOXYGEN
532 friend class HashTable;
533 #endif
534
535 private:
536 HashTable* table; ///< The table we belong to.
537 Element* element; ///< The extracted element.
538
539 /// Constructor setting fields #table and #element.
540 /// @param pTable The table we belong to.
541 /// @param pElement The extracted element.
542 ElementHandle( HashTable* pTable, Element* pElement )
543 : table ( pTable )
544 , element( pElement ) {}
545
546 public:
547 /// Move constructor setting the moved object to emtpy.
548 /// @param other The handle to move.
550 : table ( other.table )
551 , element( other.element ) { other.element= nullptr; }
552
553 /// Default constructor creating and empty handle.
555 : element( nullptr ) {}
556
557 /// Deleted copy constructor.
558 ElementHandle( ElementHandle& other ) =delete;
559
560 /// Deleted copy assignment operator.
561 ElementHandle& operator =( const ElementHandle& other ) =delete;
562
563 /// Move assignment. Disposes any current content, and moves \p{other} into this.
564 /// @param other The handle to move into this object.
565 /// @return A reference to <c>this</c>.
567 if( element != nullptr )
568 table->recyclerType::Recycle(element);
569 table = other.table;
570 element= other.element;
571 other.element= nullptr;
572
573 return *this;
574 }
575
576 /// Destructor. If this handle is not empty, the allocated storage of the
577 /// represented element is added to the list of recyclable objects.
578 ~ElementHandle() { if( element != nullptr ) table->recyclerType::Recycle(element); }
579
580 /// Determines if this is a "valid" handle.
581 /// @return Returns \c true if this objects represents a valid element, \c false
582 /// otherwise.
583 bool IsEmpty() const { return element == nullptr; }
584
585 /// Returns a mutable reference to this element's data.
586 /// Must not be invoked on empty instances.
587 /// @return Returns a mutable reference to value of the represented element.
588 StoredType& Value () const { return element->value; }
589
590 /// Returns a mutable reference to the <em>key-portion</em> of this element's data.
591 /// Must not be invoked on empty instances.
592 /// @return Returns a mutable reference to the <em>key-portion</em> of the represented
593 /// element.
594 KeyType& Key () const { return TValueDescriptor().Key( element->value ); }
595
596 /// Returns a mutable reference to the <em>mapped-portion</em> of this element's data.
597 /// Must not be invoked on empty instances.
598 /// @return Returns a mutable reference to the mapped object.
599 MappedType& Mapped () const { return TValueDescriptor().Mapped( element->value ); }
600
601 }; // class ElementHandle
602
603 //################################################################################################
604 // Construction/Destruction And Allocator Access
605 //################################################################################################
606
607 /// Constructor.
608 /// \note
609 /// This constructor is not available if the template argument \p{TRecycling} equals
610 /// #"Recycling::Shared".
611 ///
612 /// @tparam TRequires Defaulted template parameter. Must not be specified.
613 /// @param pAllocator The allocator to use.
614 /// @param pBaseLoadFactor The base load factor. Defaults to <c>1.0</c>.
615 /// @param pMaxLoadFactor The maximum load factor. Defaults to <c>2.0</c>.
616 template<bool TRequires= TRecycling!= Recycling::Shared>
617 requires TRequires
618 explicit
620 float pBaseLoadFactor = 1.0,
621 float pMaxLoadFactor = 2.0 )
622 : base( pAllocator, pBaseLoadFactor, pMaxLoadFactor )
624 , dcs("HashTable")
625 #endif
626 {}
627
628 /// Constructor.
629 /// \note
630 /// This constructor is not available if the template argument \p{TRecycling} equals
631 /// #"Recycling::Shared" and if the template argument \p{TAllocator} does not equal
632 /// #"HeapAllocator"
633 ///
634 ///
635 /// @tparam TRequires Defaulted template parameter. Must not be specified.
636 /// @param pBaseLoadFactor The base load factor. Defaults to <c>1.0</c>.
637 /// @param pMaxLoadFactor The maximum load factor. Defaults to <c>2.0</c>.
638 template<bool TRequires= TRecycling!= Recycling::Shared>
639 requires TRequires
640 explicit
641 HashTable( float pBaseLoadFactor = 1.0,
642 float pMaxLoadFactor = 2.0 )
643 : base( pBaseLoadFactor, pMaxLoadFactor )
645 , dcs("HashTable")
646 #endif
647 {}
648
649
650 /// Constructor taking a shared recycler.
651 /// @param pAllocator The allocator to use.
652 /// @param pSharedRecycler The shared recycler.
653 /// @param pBaseLoadFactor The base load factor. See method #BaseLoadFactor for details.
654 /// Defaults to <c>1.0</c>.
655 /// @param pMaxLoadFactor The maximum load factor. See method #MaxLoadFactor for details.
656 /// Defaults to <c>2.0</c>.
657 /// @tparam TSharedRecycler Used to select this constructor. Deduced by the compiler.
658 template<typename TSharedRecycler= SharedRecyclerType>
659 requires (!std::same_as<TSharedRecycler, void>)
661 TSharedRecycler& pSharedRecycler,
662 float pBaseLoadFactor = 1.0,
663 float pMaxLoadFactor = 2.0 )
664 : base( pAllocator, pSharedRecycler, pBaseLoadFactor, pMaxLoadFactor )
666 , dcs("HashTable")
667 #endif
668 {}
669
670 /// Constructor taking a shared recycler.
671 /// @param pSharedRecycler The shared recycler.
672 /// @param pBaseLoadFactor The base load factor. See method #BaseLoadFactor for details.
673 /// Defaults to <c>1.0</c>.
674 /// @param pMaxLoadFactor The maximum load factor. See method #MaxLoadFactor for details.
675 /// Defaults to <c>2.0</c>.
676 /// @tparam TSharedRecycler Used to select this constructor. Deduced by the compiler.
677 template<typename TSharedRecycler= SharedRecyclerType>
678 requires (!std::same_as<TSharedRecycler, void>)
679 HashTable( TSharedRecycler& pSharedRecycler,
680 float pBaseLoadFactor = 1.0,
681 float pMaxLoadFactor = 2.0 )
682 : base( pSharedRecycler, pBaseLoadFactor, pMaxLoadFactor )
684 , dcs("HashTable")
685 #endif
686 {}
687
688 /// Returns the allocator of this object. Usually the allocator might be used to perform
689 /// allocations in respect to data found in objects stored in this object.
690 /// However, such allowance is dependent on the use case and not part of this class's
691 /// contract.
692 ///
693 /// @return The allocator that was provided in the constructor.
694 AllocatorType& GetAllocator() noexcept { return base::base::GetAllocator(); }
695
696
697 //##############################################################################################
698 /// @name Size and Capacity
699 //##############################################################################################
700
701 /// Destructs and removes all elements from this hash table. The allocated space
702 /// of the elements will be preserved and "recycled" with future insertions.
703 void Clear() { DCS base::clear(); }
704
705 /// Same as clear, but does not recycle internal nodes. Furthermore, all recyclables
706 /// are deleted. The latter is done only if the template parameter \p{TRecycling} is not
707 /// #"Recycling::Shared". In this case, the elements are still recycled.
708 ///
709 /// This method is useful with monotonic allocators, that can be reset as well, after
710 /// this instance is reset.
711 /// But, because the life-cycle of the monotonic allocator(s) used for insertions is not
712 /// under control of this object, it is the obligation of the caller to ensure that the
713 /// monotonic allocator is kept in sync with this object.
714 /// The following recipe shows a valid use:
715 /// - Construct a \b HashTable passing a \b MonoAllocator.
716 /// - Create a snapshot of the \b MonoAllocator.
717 /// - Use the \b HashTable.
718 /// - Reset the \b HashTable and right afterwards the \b MonoAllocator to the snapeshot taken.
719 void Reset() {
720 recyclerType oldRecycler(*this);
721 auto baseLoadFactor = base::baseLoadFactor;
722 auto maxLoadFactor = base::maxLoadFactor ;
723 auto& allocator = base::base::GetAllocator();
724 lang::Destruct(*this);
725 if constexpr ( std::same_as< typename base::recyclerType,
727 new (this) HashTable( oldRecycler, baseLoadFactor, maxLoadFactor );
728 else
729 new (this) HashTable( allocator, baseLoadFactor, maxLoadFactor );
730 }
731
732 /// Returns the number of stored elements. Note that this method runs in constant time, as
733 /// the number of elements is kept counted during operation.
734 /// @return The number of elements stored in the hash table.
735 integer Size() const noexcept { return base::size; }
736
737 /// Invokes #Size and compares result with \c 0.
738 /// @return \c true if this list is empty, \c false otherwise.
739 bool IsEmpty() const noexcept { return base::size == 0; }
740
741 /// Reserves space for at least the given number of elements.
742 /// This might re-hash this table.
743 /// \see Method #ReserveRecyclables.
744 /// @param expected The expected number or increase of elements to be stored in the hash table.
745 /// @param reference Denotes whether \p{expected} is meant as an absolute size or an increase.
746 void Reserve( integer expected, lang::ValueReference reference ) {DCS
747 float expectedSize= float(expected + (reference==lang::ValueReference::Relative ? Size()
748 : 0 ) );
749 return base::rehash( uinteger(std::ceil( expectedSize / base::baseLoadFactor)) );
750 }
751
752 /// Same as #Reserve but in addition also already allocates the required space for the number
753 /// of additional elements expected.
754 ///
755 /// @see Chapter #"alib_contmono_containers_recycling_reserving" of the Programmer's
756 /// Manual.
757 ///
758 /// @param expected The expected resulting number (or increase) of elements to be stored in
759 /// this container.
760 /// @param reference Denotes whether \p{expected} is meant as an absolute size or an
761 /// increase.
762 void ReserveRecyclables( integer expected, lang::ValueReference reference ) {
763 Reserve( expected, reference );
764 {DCS
765 auto requiredRecyclables= expected- (reference==lang::ValueReference::Absolute ? Size()
766 : 0 )
767 - base::recyclerType::Count();
768 if( requiredRecyclables > 0 )
769 recyclerType::Reserve( requiredRecyclables );
770 } }
771
772 /// Counts the number of currently allocated but unused (not contained) element nodes
773 /// that will be recycled with upcoming insertions.
774 ///
775 /// \note
776 /// This method is provided for completeness and unit-testing. It should not be of
777 /// relevance for common usage.<br>
778 /// Furthermore, this method is not available (aka does not compile) with instantiations
779 /// that specify template parameter \p{TRecycling} as #"Recycling::None".
780 ///
781 /// @return The number of removed and not yet recycled elements.
782 integer RecyclablesCount() const noexcept
783 {DCSSHRD return base::recyclerType::Count(); }
784
785 //##############################################################################################
786 /// @name Hash Policy
787 //##############################################################################################
788
789 /// Sets a new value for the "base load factor" used with this container.
790 /// The base load factor determines the minimum number of buckets
791 /// when re-hashing is performed.
792 ///
793 /// The formula to determine the minimum number of buckets is #Size divided by this factor.
794 /// A static table of prime numbers is searched for the next higher number and this value
795 /// is then used to determine the number of buckets.
796 ///
797 /// The default value for this factor is defined as <c>1.0</c> by the default value
798 /// of parameter \p{pBaseLoadFactor} of the constructor.
799 ///
800 /// \note
801 /// Invoking this method never triggers rehashing.
802 ///
803 /// \see
804 /// Overloaded method #BaseLoadFactor() and this class's documentation section
805 /// #"alib_ns_containers_hashtable_rehashing".
806 ///
807 /// @param newBaseLoadFactor The new base load factor to use when a rehash is performed.
808 void BaseLoadFactor( float newBaseLoadFactor ) noexcept
809 { base::baseLoadFactor= newBaseLoadFactor; }
810
811 /// Returns the actual base load factor.
812 ///
813 /// \see
814 /// Overloaded method #BaseLoadFactor(float) and this class's documentation section
815 /// #"alib_ns_containers_hashtable_rehashing".
816 ///
817 /// @return The current value of the base load factor.
818 float BaseLoadFactor() const noexcept { return base::baseLoadFactor; }
819
820
821 /// Sets a new value for the "maximum load factor" which is the average number of elements
822 /// per bucket.
823 ///
824 /// The default value for this factor is defined as <c>2.0</c> by the default value
825 /// of parameter \p{pMaxLoadFactor} of the constructor.
826 ///
827 /// \note
828 /// Invoking this method triggers rehashing, in case the hash table is not empty and
829 /// the new maximum load factor is below the current load factor of this container, which
830 /// equals #Size divided by #BucketCount.<br>
831 /// This method may be used to temporarily disable re-hashing by providing
832 /// <c>std::numeric_limits<float>::max()</c>.
833 ///
834 /// \see
835 /// Overloaded method #MaxLoadFactor() and this class's documentation section
836 /// #"alib_ns_containers_hashtable_rehashing".
837 ///
838 /// @param newMaxLoadFactor The maximum load factor used to determine the need of re-hashing.
839 void MaxLoadFactor( float newMaxLoadFactor ) noexcept
840 { base::setMaxLoadFactor( newMaxLoadFactor ); }
841
842 /// Returns the actual maximum load factor.
843 ///
844 /// \see
845 /// Overloaded method #MaxLoadFactor(float) and this class's documentation section
846 /// #"alib_ns_containers_hashtable_rehashing".
847 ///
848 /// @return The current value of the maximum load factor.
849 float MaxLoadFactor() const noexcept { return base::maxLoadFactor; }
850
851 //##############################################################################################
852 /// @name Bucket Interface
853 //##############################################################################################
854
855 /// Returns the number of "buckets" that this hash table currently uses.
856 ///
857 /// @return The size of the array of hash buckets.
858 uinteger BucketCount() const noexcept { return base::bucketCount; }
859
860 /// Returns the number of entries stored in the bucket with the given number.
861 ///
862 /// @param bucketNumber The number of the bucket to receive the size for.
863 /// @return The number of entries in the specified bucket.
864 uinteger BucketSize( uinteger bucketNumber ) const noexcept {DCSSHRD
865 ALIB_ASSERT_ERROR( bucketNumber < base::bucketCount, "MONOMEM/HASHTABLE",
866 "Bucket number out of range. {}>={}", bucketNumber, base::bucketCount )
867 return uinteger(base::buckets[bucketNumber].count());
868 }
869
870 /// Returns the number of the bucket corresponding to \p{key}.
871 ///
872 /// @param key The key to evaluate the bucket number for.
873 /// @return The bucket number that \p{key} is assigned to.
874 uinteger BucketNumber( const KeyType& key ) const noexcept
875 { return THash{}(key) % base::bucketCount; }
876
877 //##############################################################################################
878 /// @name Element Insertion
879 //##############################################################################################
880 /// See #Insert(StoredType&&) which is invoked with a copy of \p{value}.
881 ///
882 /// @param value A value to copy and insert.
883 /// @return An iterator referring to the element added.
885 { return Insert(value, THash{}(TValueDescriptor().Key(reinterpret_cast<StoredType&>(value)))); }
886
887 /// Overloaded version of method #"Insert(const StoredType&)" which
888 /// accepts the \p{hashCode} of the given \p{value} as a second parameter.
889 ///
890 /// @see Use cases of this method are discussed in reference documentation section
891 /// #"alib_ns_containers_hashtable_hashprecalc".
892 ///
893 /// @param value A value to copy and insert.
894 /// @param hashCode Pre-calculated hash code of \p{value}.
895 /// @return An iterator referring to the element added.
896 Iterator Insert( const StoredType& value, size_t hashCode )
897 { return Insert(value, hashCode ); }
898
899 /// Moves the given value into this table.<br>
900 /// Existing iterators remain valid.
901 ///
902 /// \note
903 /// The use of this method may insert elements sharing the same key as already existing
904 /// elements.
905 /// For more information, see the section #"alib_ns_containers_hashtable_single_multi".
906 ///
907 /// @param value A rvalue reference of contained type \p{StoredType} to insert.
908 /// @return An iterator referring to the element added.
910 {
911 auto hashCode = THash{}( TValueDescriptor().Key( reinterpret_cast<StoredType&>(value)) );
912 return Insert( std::move(value), hashCode );
913 }
914
915 /// Overloaded version of method #"HashTable::Insert(StoredType&&)" which
916 /// accepts the \p{hashCode} of the given \p{value} as a second parameter.
917 ///
918 /// @see Use cases of this method are discussed in reference documentation section
919 /// #"alib_ns_containers_hashtable_hashprecalc".
920 ///
921 /// @param value An rvalue reference of contained type \p{StoredType} to insert.
922 /// @param hashCode Pre-calculated hash code of \p{value}.
923 /// @return An iterator referring to the element added.
924 Iterator Insert( StoredType&& value, size_t hashCode ) {DCS
925 // Recycle node or create a new one
926 Element* element= base::allocElement(hashCode);
927
928 // placement-new
929 new ( &element->value ) StoredType ( std::move(value) );
930
931 // insert to hash table
932 base::increaseSize( 1 );
933 auto bucketIdx= base::insertInBucket( element, hashCode );
934 return Iterator( this, bucketIdx, element);
935 }
936
937 /// Inserts the element contained in the given #"HashTable::ElementHandle"
938 /// into the hash table.
939 ///
940 /// \note
941 /// The use of this method may insert elements sharing the same key as already existing
942 /// elements.
943 /// For more information, see the section #"alib_ns_containers_hashtable_single_multi".
944 ///
945 /// <p>
946 /// Objects of type \b ElementHandle objects may be received using overloaded methods
947 /// #Extract. The combination of \b %Extract and this method (as well as method
948 /// #InsertIfNotExistent(ElementHandle&) are the only way to change the <em>key-portion</em> of an
949 /// element without element destruction/re-construction.
950 ///
951 /// @param handle A reference to a handle to an element, previously received with #Extract.
952 /// @return On success, returns an iterator that refers to the inserted element.
953 /// On failure (if parameter \p{handle} was empty), the returned iterator equals #end.
954 Iterator Insert( ElementHandle& handle ) {DCS
955 if( handle.IsEmpty() )
956 return end();
957
958 base::increaseSize( 1 );
959 Element* element= handle.element;
960 auto hashCode= THash{}( TValueDescriptor().Key( element->value ));
961 element->fixHashCode( hashCode );
962 auto bucketIdx= base::insertInBucket( element, hashCode );
963 handle.element= nullptr;
964 return Iterator( this, bucketIdx, element );
965 }
966
967 /// See #InsertUnique(StoredType&&) which is invoked with a copy of \p{value}.
968 ///
969 /// @param value An element to insert whose <em>key-portion</em> has to be different to
970 /// all currently contained elements.
971 /// @return An iterator referring to the element added.
973 { return InsertUnique(std::move(StoredType( value )) ); }
974
975 /// Overloaded version of method
976 /// #"InsertUnique(const StoredType&)" which accepts the
977 /// \p{hashCode} of the given \p{key} as a second parameter.
978 ///
979 /// @see Use cases of this method are discussed in reference documentation section
980 /// #"alib_ns_containers_hashtable_hashprecalc".
981 ///
982 /// @param value An element to insert whose <em>key-portion</em> has to be different to
983 /// all currently contained elements.
984 /// @param hashCode Pre-calculated hash code of \p{value}.
985 /// @return An iterator referring to the element added.
986 Iterator InsertUnique( const StoredType& value, size_t hashCode )
987 { return InsertUnique( StoredType( value ), hashCode ); }
988
989 /// Moves the given value into this table without checking to place it in the right
990 /// position in respect to existing elements with the same <em>key-portion</em>.
991 ///
992 /// \attention
993 /// This method must only be used if the caller guarantees that no other element is
994 /// currently stored in this container having an equal <em>key-portion</em>.
995 /// In such situations, this method is very efficient.<br>
996 /// If one exists already, this hash table is not considered in a consistent state
997 /// after the operation. I.e., method #EqualRange will discontinue functioning properly.
998 ///
999 /// \attention
1000 /// In debug-compilations an \alib_assertion is raised if an equal element exists.
1001 /// For this reason, performance differences to method #Insert will be seen only with
1002 /// release-compilations.
1003 ///
1004 /// \attention
1005 /// For more information, see the section #"alib_ns_containers_hashtable_single_multi".
1006 ///
1007 ///
1008 /// @param value An element to insert whose <em>key-portion</em> has to be different to
1009 /// all currently contained elements.
1010 /// @return An iterator referring to the element added.
1012 {
1013 auto hashCode = THash{}( TValueDescriptor().Key( reinterpret_cast<StoredType&>(value)) );
1014 return InsertUnique(std::move(value), hashCode );
1015 }
1016
1017 /// Overloaded version of method #"HashTable::InsertUnique(StoredType&&)"
1018 /// which accepts the \p{hashCode} of the given \p{key} as a second parameter.
1019 ///
1020 /// @see Use cases of this method are discussed in reference documentation section
1021 /// #"alib_ns_containers_hashtable_hashprecalc".
1022 ///
1023 /// @param value An element to insert whose <em>key-portion</em> has to be different to
1024 /// all currently contained elements.
1025 /// @param hashCode Pre-calculated hash code of \p{value}.
1026 /// @return An iterator referring to the element added.
1027 Iterator InsertUnique( StoredType&& value, size_t hashCode ) {DCS
1028 Element* element = base::allocElement( hashCode );
1029
1030 base::increaseSize( 1 );
1031 auto bucketIdx= hashCode % base::bucketCount;
1032 base::buckets[bucketIdx].pushFront( element );
1033
1034 new ( &element->value ) StoredType( std::move(value) );
1035
1036 #if ALIB_DEBUG
1037 //Check that this was the first of
1038 auto it= ConstLocalIterator( bucketIdx, base::buckets[bucketIdx].first() ); // cbegin(bucketIdx);
1039 ALIB_ASSERT( it.element == element, "MONOMEM/HASHTABLE" ) // has to be the first inserted
1040 while( ++it != cend(bucketIdx) ) {
1041 ALIB_ASSERT_ERROR( !base::areEqual(element, it.element ), "MONOMEM/HASHTABLE",
1042 "InsertUnique used while element with same key-portion existed!" )
1043 }
1044 #endif
1045
1046 return Iterator( this, bucketIdx, element);
1047 }
1048
1049
1050 /// See #InsertOrAssign(const KeyType&, MappedType&&) which is invoked with a copy of \p{mapped}.
1051 ///
1052 /// \par Availability
1053 /// This method is only available with
1054 /// #"alib_ns_containers_hashtable_setandmap;hash map mode".
1055 ///
1056 /// @tparam TRequires Used to disable this method for instantiations of this
1057 /// template type with <em>hash set mode</em>.<br>
1058 /// Defaulted and must not be specified with invocations.
1059 /// @param key The key to use for search and insertion.
1060 /// @param mapped The mapped value to copy and then insert or assign.
1061 /// @return A pair containing an iterator referencing the element added.
1062 /// The bool component is \c true if the insertion took place and \c false if the
1063 /// assignment took place.
1064 template<typename TRequires= MappedType>
1065 requires(!std::same_as<TRequires, StoredType>)
1066 std::pair<Iterator, bool> InsertOrAssign( const KeyType& key, const MappedType& mapped)
1067 { return InsertOrAssign( key, MappedType(mapped) ); }
1068
1069 /// Replaces an existing, respectively inserts a new element into this hash table.
1070 ///
1071 /// \note
1072 /// This method allows preventing the insertion of double entries.
1073 /// For more information, see the section #"alib_ns_containers_hashtable_single_multi".
1074 ///
1075 /// \par Availability
1076 /// This method is only available with
1077 /// #"alib_ns_containers_hashtable_setandmap;hash map mode".
1078 ///
1079 /// @tparam TRequires Used to disable this method for instantiations of this
1080 /// template type with <em>hash set mode</em>.<br>
1081 /// Defaulted and must not be specified with invocations.
1082 /// @param key The key to use for search and insertion.
1083 /// @param mapped The mapped value to insert or assign.
1084 /// @return A pair containing an iterator referring to the element added.
1085 /// The bool component is \c true if the insertion took place and \c false if the
1086 /// assignment took place.
1087 template<typename TRequires= MappedType>
1088 requires(!std::same_as<TRequires, StoredType>)
1089 std::pair<Iterator, bool> InsertOrAssign( const KeyType& key, MappedType&& mapped )
1090 { return InsertOrAssign( key, std::move(mapped), THash{}(key) ); }
1091
1092 /// Overloaded version of the method #"InsertOrAssign(const KeyType&, MappedType&&)" which
1093 /// accepts the \p{hashCode} of the given \p{key} as a third parameter.
1094 ///
1095 /// @see Use cases of this method are discussed in reference documentation section
1096 /// #"alib_ns_containers_hashtable_hashprecalc".
1097 ///
1098 /// \par Availability
1099 /// This method is only available with
1100 /// #"alib_ns_containers_hashtable_setandmap;hash map mode".
1101 ///
1102 /// @tparam TRequires Used to disable this method for instantiations of this
1103 /// template type with <em>hash set mode</em>.<br>
1104 /// Defaulted and must not be specified with invocations.
1105 /// @param key The key to use for search and insertion.
1106 /// @param mapped The mapped value to insert or assign.
1107 /// @param hashCode Pre-calculated hash code of \p{key}.
1108 /// @return A pair containing an iterator referring to the element added.
1109 /// The bool component is \c true if the insertion took place and \c false if the
1110 /// assignment took place.
1111 template<typename TRequires= MappedType>
1112 requires(!std::same_as<TRequires, StoredType>)
1113 std::pair<Iterator, bool> InsertOrAssign( const KeyType& key, MappedType&& mapped, size_t hashCode )
1114 {DCS
1115 std::pair<Iterator, bool> result= base::insertOrGet( key, hashCode );
1116
1117 // if an existing element was found, we have to destruct the mapped value
1118 if( result.second == false )
1119 lang::Destruct( TValueDescriptor().Mapped( result.first.element->value ) );
1120
1121 // if otherwise a new element was inserted, we have to copy the key
1122 else
1123 new (&TValueDescriptor().Key( result.first.element->value )) KeyType( key );
1124
1125 // placement-new for the mapped object
1126 new ( &TValueDescriptor().Mapped( result.first.element->value ) ) MappedType( std::move( mapped) );
1127
1128 return result;
1129 }
1130
1131 /// See #InsertIfNotExistent(const KeyType&, MappedType&&) which is invoked with a copy of
1132 /// \p{mapped}.
1133 ///
1134 /// \par Availability
1135 /// This method is only available with
1136 /// #"alib_ns_containers_hashtable_setandmap;hash map mode".
1137 ///
1138 /// @tparam TRequires Used to disable this method for instantiations of this
1139 /// template type with <em>hash set mode</em>.<br>
1140 /// Defaulted and must not be specified with invocations.
1141 /// @param key The key to use for search and insertion.
1142 /// @param mapped The mapped object to copy and insert if \p{key} is not existing.
1143 /// @return A pair containing an iterator referencing either the element found or the new
1144 /// element added. The bool component is \c true if the insertion took place and \c false
1145 /// if nothing was changed.
1146 template<typename TRequires= MappedType>
1147 requires(!std::same_as<TRequires, StoredType>)
1148 std::pair<Iterator, bool> InsertIfNotExistent( const KeyType& key, const MappedType& mapped)
1149 { return InsertIfNotExistent( key, MappedType(mapped) ); }
1150
1151 /// Overloaded version of method
1152 /// #"InsertIfNotExistent(const KeyType&,MappedType&&)" which
1153 /// accepts the \p{hashCode} of the given \p{key} as a third parameter.
1154 ///
1155 /// \par Availability
1156 /// This method is only available with
1157 /// #"alib_ns_containers_hashtable_setandmap;hash map mode".
1158 ///
1159 /// @see Use cases of this method are discussed in reference documentation section
1160 /// #"alib_ns_containers_hashtable_hashprecalc".
1161 ///
1162 /// @tparam TRequires Used to disable this method for instantiations of this
1163 /// template type with <em>hash set mode</em>.<br>
1164 /// Defaulted and must not be specified with invocations.
1165 /// @param key The key to use for search and insertion.
1166 /// @param hashCode Pre-calculated hash code of \p{key}.
1167 /// @param mapped The mapped value to insert if \p{key} is not existing.
1168 /// @return A pair containing an iterator referencing either the element found or the new
1169 /// element added. The bool component is \c true if the insertion took place and \c false
1170 /// if nothing was changed.
1171 template<typename TRequires= MappedType>
1172 requires(!std::same_as<TRequires, StoredType>)
1173 std::pair<Iterator, bool> InsertIfNotExistent( const KeyType& key, MappedType&& mapped, size_t hashCode)
1174 {DCS
1175 // search element
1176 std::pair<Iterator, bool> result= base::insertIfNotExists( key, hashCode );
1177
1178 // existed? Do nothing
1179 if( result.second == false )
1180 return result;
1181
1182 // placement-new for the key (using copy constructor)
1183 new (&TValueDescriptor().Key( result.first.element->value )) KeyType( key );
1184
1185 // placement-new for the mapped (using move constructor)
1186 new ( &TValueDescriptor().Mapped( result.first.element->value ) ) MappedType( std::move( mapped) );
1187
1188 return result;
1189 }
1190
1191 /// Inserts a new mapped object only if no other object is contained that is associated
1192 /// already with the same key as given \p{key}.
1193 ///
1194 /// \note
1195 /// This method allows preventing the insertion of double entries.
1196 /// For more information, see the section #"alib_ns_containers_hashtable_single_multi".
1197 ///
1198 /// \par Availability
1199 /// This method is only available with
1200 /// #"alib_ns_containers_hashtable_setandmap;hash map mode".
1201 ///
1202 /// @tparam TRequires Used to disable this method for instantiations of this
1203 /// template type with <em>hash set mode</em>.<br>
1204 /// Defaulted and must not be specified with invocations.
1205 /// @param key The key to use for search and insertion.
1206 /// @param mapped The mapped value to insert if \p{key} is not existing.
1207 /// @return A pair containing an iterator referencing either the element found or the new
1208 /// element added. The bool component is \c true if the insertion took place and \c false
1209 /// if nothing was changed.
1210 template<typename TRequires= MappedType>
1211 requires(!std::same_as<TRequires, StoredType>)
1212 std::pair<Iterator, bool> InsertIfNotExistent( const KeyType& key, MappedType&& mapped)
1213 { return InsertIfNotExistent( key, std::move(mapped), THash{}(key) ); }
1214
1215 /// See #InsertIfNotExistent(StoredType&&) which is invoked with a copy of \p{value}.
1216 ///
1217 /// @param value The value to copy and insert.
1218 /// @return A pair containing an iterator referencing either the element found or the new
1219 /// element added. The bool component is \c true if the insertion took place and \c false
1220 /// if nothing was changed.
1221 std::pair<Iterator, bool> InsertIfNotExistent( const StoredType& value )
1222 { return InsertIfNotExistent( StoredType(value) ); }
1223
1224 /// Inserts a new mapped object only if no other object is contained that is associated
1225 /// already with the same key as given \p{key}.
1226 ///
1227 /// \note
1228 /// This method allows preventing the insertion of double entries.
1229 /// For more information, see the section #"alib_ns_containers_hashtable_single_multi".
1230 ///
1231 /// @param value A rvalue reference of a \p{StoredType} to insert.
1232 /// @return A pair containing an iterator referencing either the element found or the new
1233 /// element added.
1234 /// The bool component is \c true if the insertion took place and \c false if nothing
1235 /// was changed.
1236 std::pair<Iterator, bool> InsertIfNotExistent( StoredType&& value )
1237 {
1238 auto hashCode= THash{}( TValueDescriptor().Key(value) );
1239 return InsertIfNotExistent(std::move(value), hashCode);
1240 }
1241
1242 /// Overloaded version of method
1243 /// #"HashTable::InsertIfNotExistent(StoredType&&)" which accepts the
1244 /// \p{hashCode} of the given \p{key} as a second parameter.
1245 ///
1246 /// @see Use cases of this method are discussed in reference documentation section
1247 /// #"alib_ns_containers_hashtable_hashprecalc".
1248 ///
1249 /// @param value A rvalue reference of a \p{StoredType} to insert.
1250 /// @param hashCode Pre-calculated hash code of \p{value}.
1251 /// @return A pair containing an iterator referencing either the element found or the new
1252 /// element added.
1253 /// The bool component is \c true if the insertion took place and \c false if nothing
1254 /// was changed.
1255 std::pair<Iterator, bool> InsertIfNotExistent( StoredType&& value, size_t hashCode ) {DCS
1256 // search element
1257 std::pair<Iterator, bool> result= base::insertIfNotExists( TValueDescriptor().Key(value), hashCode );
1258
1259 // existed? Do nothing
1260 if( result.second == false )
1261 return result;
1262
1263 // placement-new for the value(using move constructor)
1264 new ( &result.first.element->value ) StoredType( std::move(value) );
1265
1266 return result;
1267 }
1268
1269 /// Inserts the element contained in the given #"HashTable::ElementHandle" into
1270 /// this table, if no equal element exists. In the unsuccessful case, the given
1271 /// \b %ElementHandle remains set and can be reused.<br>
1272 ///
1273 /// Existing iterators remain valid.
1274 ///
1275 /// \note
1276 /// This method allows preventing the insertion of double entries.
1277 /// For more information, see the section #"alib_ns_containers_hashtable_single_multi".
1278 ///
1279 /// <p>
1280 /// \see
1281 /// Objects of type \b ElementHandle objects may be received using overloaded methods
1282 /// #Extract. The combination of \b %Extract and this method (as well as method
1283 /// #Insert(ElementHandle&) are the only way to change the <em>key-portion</em> of an element
1284 /// without element destruction/re-construction.
1285 ///
1286 /// @param handle A reference to a handle to an element, previously received with #Extract.
1287 /// @return If an empty handle is given, #end is returned.
1288 /// Otherwise, if no equal element existed, an iterator that refers to the inserted
1289 /// element is returned and the given \p{handle} is emptied.<br>
1290 /// If an equal element existed, the returned iterator refers to the existing element
1291 /// and the \p{handle} remains set (not empty).
1292 Iterator InsertIfNotExistent( ElementHandle& handle ) {DCS
1293 if( handle.IsEmpty() )
1294 return Iterator( this, base::bucketCount, nullptr ); //end();
1295
1296 Element* element = handle.element;
1297 auto hashCode = THash{}( TValueDescriptor().Key( handle.element->value ) );
1298 auto bucketIdx= hashCode % base::bucketCount;
1299
1300 Element* existing= base::findElement( hashCode, TValueDescriptor().Key( element->value ), hashCode );
1301 if ( existing != nullptr )
1302 return Iterator( this, bucketIdx, existing );
1303
1304 handle.element= nullptr;
1305 element->fixHashCode( hashCode ); // the key might have been changed outside
1306
1307 bucketIdx= base::increaseSize( 1, hashCode );
1308 base::buckets[bucketIdx].pushFront( element );
1309 return Iterator( this, bucketIdx, element);
1310 }
1311
1312 /// Constructs a new element within this container.
1313 ///
1314 /// \note
1315 /// The use of this method may insert elements sharing the same key as already existing
1316 /// elements.
1317 /// For more information, see the section #"alib_ns_containers_hashtable_single_multi".
1318 ///
1319 /// @tparam TArgs Types of variadic parameters given with parameter \p{args}.
1320 /// @param args Variadic parameters to be forwarded to the constructor of the inserted
1321 /// instance of type #StoredType.
1322 /// @return An iterator referring to the element added.
1323 template<typename... TArgs>
1324 Iterator Emplace( TArgs&&... args ) {DCS
1325 // Recycle node or create a new one
1326 Element* element= base::allocElement( 0 );
1327
1328 // placement-new
1329 new ( &element->value ) StoredType( std::forward<TArgs>(args)... );
1330
1331 // fix the hash code which was not available at allocation, yet.
1332 auto hashCode= THash{}( TValueDescriptor().Key( element->value ));
1333 element->fixHashCode( hashCode );
1334
1335 // insert to hash table
1336 base::increaseSize( 1 );
1337 auto bucketIdx= base::insertInBucket( element, hashCode );
1338 return Iterator( this, bucketIdx, element);
1339 }
1340
1341 /// Constructs a value within this container \b without checking to place it in the right
1342 /// position in respect to existing elements with the same <em>key-portion</em>.
1343 ///
1344 /// \attention
1345 /// This method must only be used if the caller guarantees that no other element is
1346 /// currently stored in this container having an equal <em>key-portion</em>.
1347 /// In such situations, this method is very efficient.<br>
1348 /// If one exists already, this hash table is not considered in a consistent state
1349 /// after the operation. I.e., method #EqualRange will discontinue functioning properly.
1350 ///
1351 /// \attention
1352 /// In debug-compilations, an \alib_assertion is raised if an equal element exists.
1353 /// For this reason, performance improvements to method #Insert will be seen only with
1354 /// release-compilations.
1355 ///
1356 /// \attention
1357 /// For more information, see the section #"alib_ns_containers_hashtable_single_multi".
1358 ///
1359 /// @tparam TArgs Types of variadic parameters given with parameter \p{args}.
1360 /// @param args Variadic parameters to be forwarded to the constructor of the
1361 /// element to insert whose <em>key-portion</em> has to be different to
1362 /// all currently contained elements.
1363 /// @return An iterator referring to the element added.
1364 template<typename... TArgs>
1365 Iterator EmplaceUnique( TArgs&&... args ) {DCS
1366 // Recycle node or create a new one
1367 Element* element= base::allocElement(0);
1368
1369 // placement-new
1370 new ( &element->value ) StoredType( std::forward<TArgs>(args)... );
1371
1372 // replace hash code (which is available only now)
1373 auto hashCode= THash{}( TValueDescriptor().Key( element->value ));
1374 element->fixHashCode( hashCode );
1375
1376
1377 // insert to hash table
1378 auto bucketIdx= base::increaseSize( 1, hashCode );
1379 base::buckets[bucketIdx].pushFront( element );
1380 auto result= Iterator( this, bucketIdx, element);
1381
1382 #if ALIB_DEBUG
1383 // Check that this was the first of
1384 auto it= ConstLocalIterator( result.bucketIdx, base::buckets[result.bucketIdx].first() ); // cbegin();
1385 ALIB_ASSERT( it.element == result.element, "MONOMEM/HASHTABLE" ) // has to be the first inserted
1386 while( ++it != ConstLocalIterator( result.bucketIdx, nullptr ) ) //cend(result.bucketIdx)
1387 ALIB_ASSERT_ERROR( !base::areEqual(result.element, it.element ), "MONOMEM/HASHTABLE",
1388 "EmplaceUnique used while element with same key-portion existed!" )
1389 #endif
1390
1391 return result;
1392 }
1393
1394#if DOXYGEN
1395 //==============================================================================================
1396 /// Replaces an existing, respectively constructs a new element within this container.
1397 ///
1398 /// \note
1399 /// This method allows preventing the insertion of double entries.
1400 /// For more information, see the section #"alib_ns_containers_hashtable_single_multi".
1401 ///
1402 /// \par Availability
1403 /// This method is available if this templated type is instantiated with
1404 /// #"alib_ns_containers_hashtable_setandmap;hash map mode"
1405 /// or for hash sets that only define a subset of \p{StoredType} as a key type \p{TKey} and
1406 /// whose stored type \p{StoredType} is constructible if given a key value and the variadic
1407 /// template arguments.
1408 ///
1409 ///
1410 /// @tparam TRequires Used to disable this method where not available.<br>
1411 /// Defaulted and must not be specified with invocations.
1412 /// @tparam TArgs Types of variadic parameters given with parameter \p{args}.
1413 /// @param key The key to use for search and insertion.
1414 /// @param args Variadic parameters to be forwarded to the constructor of the mapped object
1415 /// of type <c>\p{MappedType}</c>.
1416 /// @return A pair containing an iterator referring to the element added.
1417 /// The bool component is \c true if the insertion took place and \c false if the
1418 /// assignment took place.
1419 //==============================================================================================
1420 template<typename TRequires= MappedType, typename... TArgs>
1421 requires(!std::same_as<TRequires, StoredType>)
1422 std::pair<Iterator, bool> EmplaceOrAssign( const KeyType& key, TArgs&&... args);
1423#else
1424 template<typename TRequires= MappedType, typename... TArgs>
1425 requires(!std::same_as<TRequires, StoredType>)
1426 std::pair<Iterator, bool> EmplaceOrAssign( const KeyType& key, TArgs&&... args) {DCS
1427 // insert to hash table
1428 std::pair<Iterator, bool> result= base::insertOrGet( key, THash{}(key) );
1429
1430 // if an existing element was found, we have to destruct the currently mapped object
1431 if( result.second == false )
1432 lang::Destruct( TValueDescriptor().Mapped( result.first.element->value ) );
1433
1434 // if otherwise an insertion took place, we have to copy the key
1435 else
1436 new (&TValueDescriptor().Key( result.first.element->value )) KeyType( key );
1437
1438 // placement-new for the mapped object
1439 new ( &TValueDescriptor().Mapped( result.first.element->value )) MappedType( std::forward<TArgs>( args)... );
1440
1441 return result;
1442 }
1443
1444 // set mode
1445 template<typename TRequires= MappedType, typename... TArgs>
1446 requires( std::same_as<TRequires, StoredType>
1447 && std::is_constructible< StoredType,
1448 const KeyType&,
1449 TArgs&&... >::value )
1450 std::pair<Iterator, bool> EmplaceOrAssign( const KeyType& key, TArgs&&... args) {DCS
1451 // insert to hash table
1452 std::pair<Iterator, bool> result= base::insertOrGet( key, THash{}(key) );
1453
1454 // if an existing element was found, we have to destruct the whole object
1455 if( result.second == false )
1456 lang::Destruct( result.first.element->value );
1457
1458 // placement-new for the whole object
1459 new (&result.first.element->value) StoredType( key, std::forward<TArgs>( args)... );
1460
1461 return result;
1462 }
1463#endif
1464
1465 /// Inserts a new element only if no other element is contained equals to the one
1466 /// that is constructed by \p{args}.
1467 ///
1468 /// \note
1469 /// This method allows preventing the insertion of double entries.
1470 /// For more information, see the section #"alib_ns_containers_hashtable_single_multi".
1471 /// <p>
1472 /// \note
1473 /// For comparison, a local object of type #StoredType is constructed. In case an equal
1474 /// object exists, it is destructed. Otherwise it is moved into this container.
1475 ///
1476 /// \par Availability
1477 /// This method is available only if this templated type is instantiated with
1478 /// #"alib_ns_containers_hashtable_setandmap;hash set mode". For <em>hash map mode</em>,
1479 /// use overloaded version #EmplaceIfNotExistent(const KeyType& key, TArgs&&... args).
1480 /// \par
1481 /// Furthermore it is available only if custom type \p{StoredType} has a move constructor.
1482 ///
1483 /// \attention
1484 /// If a move constructor exists, but is not duly defined, the method produces undefined
1485 /// behavior.<br>
1486 /// An alternative version that does not require a move constructor is found with
1487 /// #EmplaceIfNotExistent(const KeyType& key, TArgs&&... args). This can be used for hash sets
1488 /// that define a subset of \p{StoredType} as a key type \p{TKey} and whose stored type
1489 /// is constructible from a constant <em>key-portion</em> and the variadic template arguments.
1490 ///
1491 /// @tparam TRequires Used to disable this method where not available.
1492 /// @tparam TArgs Types of variadic parameters given with parameter \p{args}.<br>
1493 /// Defaulted and must not be specified with invocations.
1494 /// @param args Variadic parameters to be forwarded to the constructor of \p{StoredType}.
1495 /// @return A pair containing an iterator referencing either the element found or the new
1496 /// element added.
1497 /// The bool component is \c true if the insertion took place and \c false nothing
1498 /// was changed.
1499 template<typename TRequires= MappedType, typename... TArgs>
1500 requires ( std::same_as<TRequires, StoredType>
1501 && std::is_move_constructible<StoredType>::value )
1502 std::pair<Iterator, bool> EmplaceIfNotExistent( TArgs&&... args) {DCS
1503 StoredType value( std::forward<TArgs>( args)... );
1504
1505 // search element
1506 std::pair<Iterator, bool> result= base::insertIfNotExists( TValueDescriptor().Key(value),
1507 THash{}(TValueDescriptor().Key(value)) );
1508
1509 // existed? Do nothing
1510 if( result.second == false )
1511 return result;
1512
1513 // placement-new moving the locally created object
1514 new ( &result.first.element->value ) StoredType( std::move(value) );
1515
1516 return result;
1517 }
1518
1519#if DOXYGEN
1520 //==============================================================================================
1521 /// Inserts a new mapped object only if no other object is contained that is associated
1522 /// already with a key that equals the given \p{key}.
1523 ///
1524 /// \note
1525 /// This method allows preventing the insertion of double entries.
1526 /// For more information, see the section #"alib_ns_containers_hashtable_single_multi".
1527 ///
1528 /// \par Availability
1529 /// This method is available if this templated type is instantiated with
1530 /// #"alib_ns_containers_hashtable_setandmap;hash map mode"
1531 /// or for hash sets that only define a subset of \p{StoredType} as a key type \p{TKey} and
1532 /// whose stored type is constructible if given a key value and the variadic template
1533 /// arguments.
1534 ///
1535 /// @tparam TArgs Types of variadic parameters given with parameter \p{args}.
1536 /// @param key The key to use for search and insertion.
1537 /// @param args Variadic parameters to be forwarded to the constructor of the mapped object
1538 /// of type <c>\p{MappedType}</c>.
1539 /// @return A pair containing an iterator referencing either the element found or the new
1540 /// element added.
1541 /// The bool component is \c true if the insertion took place and \c false nothing
1542 /// was changed.
1543 //==============================================================================================
1544 template<typename... TArgs>
1545 std::pair<Iterator, bool> EmplaceIfNotExistent( const KeyType& key, TArgs&&... args);
1546#else
1547 template<typename... TArgs>
1548 requires( !std::is_constructible< StoredType, const KeyType&, TArgs&&... >::value )
1549 std::pair<Iterator, bool> EmplaceIfNotExistent( const KeyType& key, TArgs&&... args) {DCS
1550 // search element
1551 std::pair<Iterator, bool> result= base::insertIfNotExists( key, THash{}(key) );
1552
1553 // existed? Do nothing
1554 if( result.second == false )
1555 return result;
1556
1557 // placement-new for the key (using copy constructor)
1558 new (&TValueDescriptor().Key(result.first.element->value)) KeyType( key );
1559
1560 // placement-new for the mapped object (using custom constructor)
1561 new (&TValueDescriptor().Mapped(result.first.element->value)) MappedType( std::forward<TArgs>( args)... );
1562
1563 return result;
1564 }
1565
1566 template<typename... TArgs>
1567 requires(std::is_constructible< StoredType, const KeyType&, TArgs&&...>::value )
1568 std::pair<Iterator, bool> EmplaceIfNotExistent( const KeyType& key, TArgs&&... args) {DCS
1569 // search element
1570 std::pair<Iterator, bool> result= base::insertIfNotExists( key, THash{}(key) );
1571
1572 // existed? Do nothing
1573 if( result.second == false )
1574 return result;
1575
1576 // placement-new for the element passing key and args together
1577 new (&result.first.element->value) StoredType( key, std::forward<TArgs>( args)... );
1578
1579 return result;
1580 }
1581#endif
1582
1583 //##############################################################################################
1584 /// @name Element Search
1585 //##############################################################################################
1586 /// Returns an iterator pointing to the first element of equal key value.
1587 ///
1588 /// \note
1589 /// The iterator returned may be incremented. It is ensured that further elements contained
1590 /// in this hash table with the same key are consecutively following this first element
1591 /// returned. However, the iterator does \b not end with the last element of that key.
1592 ///
1593 /// \see
1594 /// Method #EqualRange, which in addition returns an iterator pointing behind the last
1595 /// element with that key.
1596 ///
1597 ///
1598 /// @param key The key to search for.
1599 /// @return An iterator pointing to the first element with a matching <em>key-portion</em>
1600 /// found, respectively, one being equal to #end, if no element was found with \p{key}.
1601 Iterator Find( const KeyType& key ) {DCSSHRD
1602 auto hashCode = THash{}(key);
1603 auto bucketIdx= hashCode % base::bucketCount;
1604 Element* elem = base::findElement( bucketIdx, key, hashCode );
1605 return Iterator( this, elem == nullptr ? base::bucketCount : bucketIdx, elem );
1606 }
1607
1608 /// Searches an element.
1609 /// @param key The key to search for.
1610 /// @return An iterator pointing to the first element with a matching <em>key-portion</em>
1611 /// found, respectively, one being equal to #end, if no element was found with \p{key}.
1612 ConstIterator Find( const KeyType& key ) const {DCSSHRD
1613 auto hashCode = THash{}(key);
1614 auto bucketIdx= hashCode % base::bucketCount;
1615 Element* elem = base::findElement( bucketIdx, key, hashCode );
1616 return ConstIterator( this, elem == nullptr ? base::bucketCount : bucketIdx, elem );
1617 }
1618
1619 /// Overloaded version of method #"Find(const KeyType&)" which
1620 /// accepts the \p{hashCode} of the given \p{key} as a second parameter.
1621 ///
1622 /// @see Use cases of this method are discussed in reference documentation section
1623 /// #"alib_ns_containers_hashtable_hashprecalc".
1624 ///
1625 /// @param key The key to search for.
1626 /// @param hashCode Pre-calculated hash code of \p{key}.
1627 /// @return An iterator pointing to the first element with a matching <em>key-portion</em>
1628 /// found, respectively, one being equal to #end, if no element was found with \p{key}.
1629 Iterator Find( const KeyType& key, size_t hashCode ) {DCSSHRD
1630 auto bucketIdx= hashCode % base::bucketCount;
1631 Element* elem = base::findElement( bucketIdx, key, hashCode );
1632 return Iterator( this, elem == nullptr ? base::bucketCount : bucketIdx, elem );
1633 }
1634
1635 /// Overloaded version of method #"Find(const KeyType&)const" which
1636 /// accepts the \p{hashCode} of the given \p{key} as a second parameter.
1637 ///
1638 /// @see Use cases of this method are discussed in reference documentation section
1639 /// #"alib_ns_containers_hashtable_hashprecalc".
1640 ///
1641 /// @param key The key to search for.
1642 /// @param hashCode Pre-calculated hash code of \p{key}.
1643 /// @return An iterator pointing to the first element with a matching <em>key-portion</em>
1644 /// found, respectively, one being equal to #end, if no element was found with \p{key}.
1645 ConstIterator Find( const KeyType& key, size_t hashCode ) const {DCSSHRD
1646 auto bucketIdx= hashCode % base::bucketCount;
1647 Element* elem = base::findElement( bucketIdx, key, hashCode );
1648 return ConstIterator( this, elem == nullptr ? base::bucketCount : bucketIdx, elem );
1649 }
1650
1651 /// Tests if an element with given \p{key} is stored in this container.
1652 /// @param key The key to search for.
1653 /// @return \c true if this hash table contains at least one element with given
1654 /// <em>key-portion</em> \p{key}, \c false otherwise.
1655 bool Contains( const KeyType& key ) const {DCSSHRD
1656 auto hashCode= THash{}(key);
1657 return base::findElement(hashCode % base::bucketCount, key, hashCode )
1658 != nullptr;
1659 }
1660
1661 /// Searches a key and returns a pair of iterators. The first is pointing to the first
1662 /// element of the range, the second is pointing to the first element past the range.
1663 ///
1664 /// If both iterators are equal, the range is empty (both iterators will be equal to #end).
1665 ///
1666 /// @param key The key to search for.
1667 /// @return A pair of iterators defining the range of elements with key \p{key}.
1668 std::pair<Iterator,Iterator> EqualRange(const KeyType& key )
1669 {DCSSHRD return base::findRange( key ); }
1670
1671 /// Searches a key and returns a pair of iterators. The first is pointing to the first
1672 /// element of the range, the second is pointing to the first element past the range.
1673 ///
1674 /// If both iterators are equal, the range is empty (both iterators will be equal to #end).
1675 ///
1676 /// @param key The key to search for.
1677 /// @return A pair of iterators defining the range of elements with key \p{key}.
1678 std::pair<ConstIterator,ConstIterator> EqualRange(const KeyType& key ) const
1679 {DCSSHRD return base::findRange( key ); }
1680
1681 //##############################################################################################
1682 /// @name Element Removal
1683 //##############################################################################################
1684 /// Extracts the first element found with the given key from the hash table and returns a
1685 /// handle to it.<br>
1686 /// Extracting an element invalidates only the iterators to the extracted element and preserves
1687 /// the relative order of the elements that are not extracted.
1688 ///
1689 /// Extracting and re-inserting nodes is the only way to change a key of an element without
1690 /// performing reallocation and or destruction/construction.
1691 ///
1692 /// @param key The key to search a first element for.
1693 /// @return A handle to an element that allows changes of the formerly stored value.
1694 /// Changes may include the <em>key-portion</em> of the data stored.
1695 /// Handles may be passed to one of the overloaded insert methods.
1696 /// If no element was found, the returned handle is empty.
1697 ElementHandle Extract(const KeyType& key ) { return Extract( key, THash{}(key) ); }
1698
1699 /// Overloaded version of method #"Extract(const KeyType&)" which
1700 /// accepts the \p{hashCode} of the given \p{key} as a second parameter.
1701 ///
1702 /// @see Use cases of this method are discussed in reference documentation section
1703 /// #"alib_ns_containers_hashtable_hashprecalc".
1704 ///
1705 /// @param key The key to search a first element for.
1706 /// @param hashCode Pre-calculated hash code of \p{key}.
1707 /// @return A handle to an element that allows changes of the formerly stored value.
1708 /// Changes may include the <em>key-portion</em> of the data stored.
1709 /// Handles may be passed to one of the overloaded insert methods.
1710 /// If no element was found, the returned handle is empty.
1711 ElementHandle Extract(const KeyType& key, size_t hashCode ) {DCS
1712 Node* previous= base::findElementBefore( hashCode % base::bucketCount, hashCode, key );
1713 if( previous == nullptr )
1714 return ElementHandle(this, nullptr);
1715
1716 Element* element= previous->next();
1717 previous->removeNext();
1718 --base::size;
1719 return ElementHandle( this, element );
1720 }
1721
1722 /// Extracts the first element found with the given key from the hash table and returns a
1723 /// handle to it.<br>
1724 /// If the iterator was not valid (i.e., #end), the method has undefined behavior.
1725 /// With debug-builds an \alib_assertion is raised.
1726 ///
1727 /// Extracting a element invalidates only the iterators to the extracted element, and preserves
1728 /// the relative order of the elements that are not extracted.
1729 ///
1730 /// Extracting and re-inserting nodes is the only way to change a key of an element without
1731 /// performing reallocation and or destruction/construction.
1732 ///
1733 /// @param pos The position of the element to extract.
1734 /// @return A handle to an element that allows changes of the formerly stored value.
1735 /// Changes may include the <em>key-portion</em> of the data stored.
1736 /// Handles may be passed to one of the overloaded insert methods.
1737 /// If no element was found, the returned handle is empty.
1738 ElementHandle Extract( ConstIterator pos ) {DCS
1739 ALIB_ASSERT_ERROR( pos.element != nullptr
1740 && pos.table != nullptr ,
1741 "MONOMEM/HASHTABLE", "Illegal iterator." )
1742
1743 Node* previous= base::buckets[pos.bucketIdx].findLastBefore( pos.element );
1744 ALIB_ASSERT_ERROR( previous != nullptr, "MONOMEM/HASHTABLE",
1745 "Illegal iterator: Element not found." )
1746
1747 previous->removeNext();
1748 --base::size;
1749 return ElementHandle( this, pos.element );
1750 }
1751
1752 /// Erases all elements stored with the given key.
1753 ///
1754 /// @param key The key to search elements for deletion.
1755 /// @return The number of elements removed.
1756 integer erase(const KeyType& key ) { return Erase( key, THash{}(key) ); }
1757
1758 /// Overloaded version of the method #"erase(const KeyType&)"
1759 /// which accepts the \p{hashCode} of the given \p{key} as a second parameter.
1760 ///
1761 /// @see Use cases of this method are discussed in reference documentation section
1762 /// #"alib_ns_containers_hashtable_hashprecalc".
1763 ///
1764 /// @param key The key to search elements for deletion.
1765 /// @param hashCode Pre-calculated hash code of \p{key}.
1766 /// @return The number of elements removed.
1767 integer Erase(const KeyType& key, size_t hashCode ) {DCS
1768 // search start
1769 Node* beforeFirst= base::findElementBefore( hashCode % base::bucketCount, hashCode, key );
1770 if( beforeFirst == nullptr )
1771 return 0;
1772
1773 // search end
1774 Element* end= beforeFirst->next();
1775 while( end && base::areEqual( end, key, hashCode ) )
1776 end= end->next();
1777
1778 // erase and add to recycler
1779 auto result= base::recyclerType::RecycleList(beforeFirst->next(), end);
1780 beforeFirst->next( end );
1781
1782 base::size-= result.second;
1783 return result.second;
1784 }
1785
1786 /// Erases the unique element with the given key.
1787 ///
1788 /// \note
1789 /// This method is slightly more efficient than the method #erase (const KeyType&), as it will
1790 /// not search for the next element with an equal key.<br>
1791 /// In debug-compilations, the method asserts that no second element with the same \p{key}
1792 /// is available.<br>
1793 /// If this table is supposed to
1794 /// #"alib_ns_containers_hashtable_single_multi;store only unique elements", the
1795 /// use of this method is therefore recommended, as an assertion hints to an erroneous use
1796 /// of the insertion methods.
1797 ///
1798 /// @param key The key to search elements for deletion.
1799 /// @return \c true if an element was found and removed, \c false otherwise.
1800 bool EraseUnique( const KeyType& key ) { return EraseUnique( key, THash{}(key) ); }
1801
1802
1803 /// Overloaded version of method #"EraseUnique(const KeyType&)" which
1804 /// accepts the \p{hashCode} of the given \p{key} as a second parameter.
1805 ///
1806 /// @see Use cases of this method are discussed in reference documentation section
1807 /// #"alib_ns_containers_hashtable_hashprecalc".
1808 ///
1809 /// @param key The key to search elements for deletion.
1810 /// @param hashCode Pre-calculated hash code of \p{key}.
1811 /// @return \c true if an element was found and removed, \c false otherwise.
1812 bool EraseUnique( const KeyType& key, size_t hashCode ) {DCS
1813 Node* before= base::findElementBefore( hashCode % base::bucketCount, hashCode, key );
1814 if( before == nullptr )
1815 return false;
1816
1817 ALIB_ASSERT_ERROR( before->next()->next() == nullptr
1818 || !base::areEqual( before->next()->next(), key, hashCode ),
1819 "MONOMEM/HASHTABLE", "More than one element found matching the given key" )
1820
1821 Element* elem= before->removeNext();
1822 base::recyclerType::Recycle(elem);
1823
1824 --base::size;
1825 return true;
1826 }
1827
1828
1829 /// Removes an element specified by an iterator.<br>
1830 /// If the iterator was not valid (i.e #end), the method has undefined behavior.
1831 /// With debug-builds an \alib_assertion is raised.
1832 ///
1833 /// The order of the elements that are not erased is preserved, what makes it possible to
1834 /// erase individual elements while iterating through the container.
1835 ///
1836 /// @param pos The iterator to the element to remove.
1837 /// @return An iterator following the removed element.
1839 ALIB_ASSERT_ERROR( pos.element != nullptr
1840 && pos.table != nullptr ,
1841 "MONOMEM/HASHTABLE", "Illegal iterator." )
1842
1843 Iterator result( this, pos.bucketIdx, pos.element );
1844 ++result;
1845
1846 // search pointer to element before pos
1847 Node* previous= base::buckets[pos.bucketIdx].findLastBefore( pos.element );
1848 ALIB_ASSERT_ERROR( previous != nullptr, "MONOMEM/HASHTABLE",
1849 "Illegal iterator: Element not found." )
1850
1851
1852 Element* toDelete= previous->removeNext();
1853 base::recyclerType::Recycle(toDelete);
1854
1855 --base::size;
1856 return result;
1857 }
1858
1860 /// Removes all elements from the given position \p{start} to the element
1861 /// before given position \p{end}.
1862 ///
1863 /// The order of the elements that are not erased is preserved, what makes it possible to
1864 /// erase individual elements while iterating through the container.
1865 ///
1866 /// @param start The iterator to the element to remove.
1867 /// @param end The first element not to remove.
1868 /// @return An iterator following the last removed element.
1870 ALIB_ASSERT_ERROR( start.element != nullptr
1871 && start.table != nullptr ,
1872 "MONOMEM/HASHTABLE", "Illegal iterator." )
1873
1874 ALIB_ASSERT_ERROR( start.table == end.table, "MONOMEM/HASHTABLE",
1875 "Iterators are referring to different hash tables." )
1876
1877 if( start.element == end.element )
1878 return Iterator(this, start.bucketIdx, start.element );
1879
1880 // loop over all buckets in question
1881 for( uinteger bucketIdx= start.bucketIdx; bucketIdx <= end.bucketIdx; ++bucketIdx ) {
1882 // end of buckets? Return iterator that marks hashtable end
1883 if( bucketIdx == base::bucketCount )
1884 return HashTable::end();
1885
1886 // find the previous pointer to the start node:
1887 Node* previous;
1888 if( bucketIdx == start.bucketIdx ) { // With the first bucket in the loop, this has to be searched...
1889 // search pointer to element before start
1890 previous= base::buckets[start.bucketIdx].findLastBefore( start.element );
1891 ALIB_ASSERT_ERROR( previous != nullptr, "MONOMEM/HASHTABLE",
1892 "Illegal iterator: Element not found." )
1893 }
1894 else { // ...afterwards, its of course just the bucket that points to it
1895 if( base::buckets[bucketIdx].isEmpty() )
1896 continue;
1897 previous= &base::buckets[bucketIdx];
1898 }
1899
1900 // destruct either to end of list or to end-iterator element
1901 if ( bucketIdx < end.bucketIdx ) {
1902 base::size-= previous->count();
1903 base::recyclerType::RecycleList( previous->next() );
1904 previous->next( nullptr );
1905 } else {
1906 auto pair= base::recyclerType::RecycleList(previous->next(), end.element );
1907 previous->next( end.element );
1908 base::size-= pair.second;
1909
1910 return Iterator( this, bucketIdx, end.element );
1911 } } }
1913
1914
1915 /// Removes an element specified by a bucket iterator.
1916 /// Bucket iterators are receivable using overloaded methods #begin(uinteger) and
1917 /// #"cbegin(uinteger)const;cbegin(uinteger)".
1918 ///
1919 /// The order of the elements that are not erased is preserved, what makes it possible to
1920 /// erase individual elements while iterating through the container.
1921 ///
1922 /// @param pos The iterator to the element to remove.
1923 /// @return An iterator following the removed element.
1925 ALIB_ASSERT_ERROR( pos.element != nullptr, "MONOMEM/HASHTABLE", "Illegal iterator." )
1926
1927 LocalIterator result( pos.bucketIdx, pos.element->next() );
1928
1929 Element* element= pos.element;
1930 base::buckets[pos.bucketIdx].findAndRemove( element );
1931 base::recyclerType::Recycle( element);
1932 --base::size;
1933
1934 return result;
1935 }
1936
1937 /// Removes all element from the given bucket iterator position \p{start} to the element
1938 /// before given position \p{end}.
1939 ///
1940 /// The order of the elements that are not erased is preserved, what makes it possible to
1941 /// erase individual elements while iterating through the container.
1942 ///
1943 /// @param start The bucket iterator to the element to remove.
1944 /// @param end The bucket iterator to the first element not to remove.
1945 /// @return An iterator following the last removed element.
1947 ALIB_ASSERT_ERROR( start.element != nullptr, "MONOMEM/HASHTABLE", "Illegal iterator." )
1948
1949 Node* previous= base::buckets[start.bucketIdx].findLastBefore( start.element );
1950 ALIB_ASSERT_ERROR( previous != nullptr, "MONOMEM/HASHTABLE", "Illegal iterator." )
1951 if( start.element == end.element )
1952 return LocalIterator( start.bucketIdx, start.element );
1953
1954 previous->next( end.element );
1955 auto pair= base::recyclerType::RecycleList( start.element, end.element );
1956
1957 base::size-= pair.second;
1958
1959 return LocalIterator( start.bucketIdx, end.element );
1960 }
1961
1962 //##############################################################################################
1963 /// @name std::iterator_traits Interface
1964 //##############################################################################################
1965
1966 /// Returns an iterator referring to a mutable element at the start of this table.
1967 /// @return The first of element in this container.
1968 Iterator begin() { return Iterator( this, 0 ); }
1969
1970 /// Returns an iterator referring to a mutable, non-existing element.
1971 /// @return The end of the list of elements in this container.
1972 Iterator end() { DCSSHRD return Iterator( this, base::bucketCount, nullptr ); }
1973
1974 /// Returns an iterator referring to a constant element at the start of this container.
1975 /// @return The first of element in this container.
1976 ConstIterator begin() const { return ConstIterator( this, 0 ); }
1977
1978 /// Returns an iterator referring to a constant, non-existing element.
1979 /// @return The end of the list of elements in this container.
1980 ConstIterator end() const {DCSSHRD return ConstIterator(this,base::bucketCount,nullptr); }
1981
1982 /// Returns an iterator referring to a constant element at the start of this container.
1983 /// @return The first of element in this container.
1984 ConstIterator cbegin() const { return ConstIterator( this, 0 ); }
1985
1986 /// Returns an iterator referring to a constant, non-existing element.
1987 /// @return The end of the list of elements in this container.
1988 ConstIterator cend() const {DCSSHRD return ConstIterator(this,base::bucketCount,nullptr); }
1989
1990 /// Returns an iterator referring to a mutable element at the start of bucket of index
1991 /// \p{bucketNumber}.
1992 /// @param bucketNumber The bucket to iterate on.
1993 /// @return The first element in bucket \p{bucketNumber}.
1994 LocalIterator begin( uinteger bucketNumber ) {DCSSHRD
1995 ALIB_ASSERT_ERROR( bucketNumber < base::bucketCount, "MONOMEM/HASHTABLE",
1996 "Bucket number out of range: {}>={}.", bucketNumber, base::bucketCount )
1997 return LocalIterator( bucketNumber, base::buckets[bucketNumber].first() );
1998 }
1999
2000 /// Returns an iterator referring to a constant, non-existing element in bucket of index
2001 /// \p{bucketNumber}.
2002 /// @param bucketNumber The bucket to iterate on.
2003 /// @return The end of the list of elements in bucket \p{bucketNumber}.
2004 LocalIterator end( uinteger bucketNumber ) {DCSSHRD
2005 ALIB_ASSERT_ERROR( bucketNumber < base::bucketCount, "MONOMEM/HASHTABLE",
2006 "Bucket number out of range: {}>={}.", bucketNumber, base::bucketCount )
2007 return LocalIterator( bucketNumber, nullptr );
2008 }
2009
2010 /// Returns an iterator referring to a mutable element at the start of bucket of index
2011 /// \p{bucketNumber}.
2012 /// @param bucketNumber The bucket to iterate on.
2013 /// @return The first element in bucket \p{bucketNumber}.
2014 ConstLocalIterator begin( uinteger bucketNumber ) const {DCSSHRD
2015 ALIB_ASSERT_ERROR( bucketNumber < base::bucketCount, "MONOMEM/HASHTABLE",
2016 "Bucket number out of range: {}>={}.", bucketNumber, base::bucketCount )
2017 return ConstLocalIterator( bucketNumber, base::buckets[bucketNumber].first() );
2018 }
2019
2020 /// Returns an iterator referring to a constant, non-existing element in bucket of index
2021 /// \p{bucketNumber}.
2022 /// @param bucketNumber The bucket to iterate on.
2023 /// @return The end of the list of elements in bucket \p{bucketNumber}.
2024 ConstLocalIterator end( uinteger bucketNumber ) const {DCSSHRD
2025 ALIB_ASSERT_ERROR( bucketNumber < base::bucketCount, "MONOMEM/HASHTABLE",
2026 "Bucket number out of range: {}>={}.", bucketNumber, base::bucketCount )
2027 return ConstLocalIterator( bucketNumber, nullptr );
2028 }
2029
2030 /// Returns an iterator referring to a mutable element at the start of bucket of index
2031 /// \p{bucketNumber}.
2032 /// @param bucketNumber The bucket to iterate on.
2033 /// @return The first element in bucket \p{bucketNumber}.
2034 ConstLocalIterator cbegin( uinteger bucketNumber ) const {DCSSHRD
2035 ALIB_ASSERT_ERROR( bucketNumber < base::bucketCount, "MONOMEM/HASHTABLE",
2036 "Bucket number out of range: {}>={}.", bucketNumber, base::bucketCount )
2037 return ConstLocalIterator( bucketNumber, base::buckets[bucketNumber].first() );
2038 }
2039
2040 /// Returns an iterator referring to a constant, non-existing element in bucket of index
2041 /// \p{bucketNumber}.
2042 /// @param bucketNumber The bucket to iterate on.
2043 /// @return The end of the list of elements in bucket \p{bucketNumber}.
2044 ConstLocalIterator cend( uinteger bucketNumber ) const {DCSSHRD
2045 ALIB_ASSERT_ERROR( bucketNumber < base::bucketCount, "MONOMEM/HASHTABLE",
2046 "Bucket number out of range: {}>={}.", bucketNumber, base::bucketCount )
2047 return ConstLocalIterator( bucketNumber, nullptr );
2048 }
2049}; // class Hashtable
2050
2051
2052//##################################################################################################
2053//##################################################################################################
2054// Debug Functions
2055//##################################################################################################
2056//##################################################################################################
2057#if ALIB_DEBUG_CONTAINERS
2058#include "ALib.Lang.CIFunctions.H"
2059/// Generates statistics on the given hash table. The meanings of the returned tuple are:
2060/// 0. The expected average size of a bucket (simply table size divided by number of buckets).
2061/// 1. The <em>standard deviation</em> of the buckets. The lower this value, the better
2062/// is the hash algorithm used. A value of <c>1.0</c> denotes the <em>gaussian distribution</em>
2063/// which indicates perfect randomness. However, this value is unlikely (impossible) to be
2064/// achieved.
2065/// 2. The minimum number of elements found in a bucket.
2066/// 3. The maximum number of elements found in a bucket.
2067///
2068/// \par Availability
2069/// Available only if the configuration macro #"ALIB_DEBUG_CONTAINERS" is set.
2070///
2071/// \see
2072/// Sibling namespace functions #"DbgDumpDistribution" and
2073/// #"DbgDumpHashtable" provided for debugging and optimization.
2074///
2075/// @tparam THashtable A specification of templated type #"HashTable".
2076/// Deduced by the compiler by given parameter \p{hashtable}.
2077/// @param hashtable The hashtable to investigate on.
2078/// @return The tuple as described above.
2079template<typename THashtable>
2080inline
2081std::tuple<double,double,integer,integer>
2082DbgGetHashTableDistribution(const THashtable& hashtable )
2083{
2084 auto qtyBuckets = hashtable.BucketCount();
2085 double averageExpected = double(hashtable.Size()) / double(qtyBuckets);
2086 uinteger minimum = std::numeric_limits<uinteger>::max();
2087 uinteger maximum = std::numeric_limits<uinteger>::min();
2088 double diffs = 0.0;
2089 integer sumCheck = 0;
2090 for( uinteger i= 0 ; i < qtyBuckets ; ++i )
2091 {
2092 auto bucketSize= hashtable.BucketSize( i );
2093 sumCheck+= bucketSize;
2094 if( minimum > bucketSize ) minimum= bucketSize;
2095 if( maximum < bucketSize ) maximum= bucketSize;
2096
2097 double diff= averageExpected - double(bucketSize);
2098 diffs+= diff > 0 ? diff : - diff;
2099 }
2100
2101 ALIB_ASSERT_ERROR( sumCheck == hashtable.Size(), "MONOMEM/HASHTABLE",
2102 "Error: Hashtable::Size() and sum of bucket sizes differ: {}!={}", hashtable.Size(),sumCheck )
2103 double deviation= diffs / double(qtyBuckets);
2104
2105 return std::make_tuple( averageExpected, deviation, minimum, maximum );
2106}
2107
2108
2109#include "ALib.Lang.CIMethods.H"
2110#endif //ALIB_DEBUG_CONTAINERS
2111
2112//==================================================================================================
2113//============================================= HashSet ============================================
2114//==================================================================================================
2115
2116/// This type definition is a shortcut to #"HashTable", usable if the full
2117/// portion of the data stored in the container is used for the comparison of values.
2118///
2119/// \note
2120/// As with this definition template type \p{TKey} equals stored type \p{StoredType}, methods of
2121/// target type #"HashTable" that accept an object of template type
2122/// \b TKey expect an object of \p{StoredType} when this type is used.<br>
2123/// In case this is not wanted (or possible), and only the true key-portion should be expected
2124/// by interface functions such as #"HashTable::Find(const KeyType&);*", the original
2125/// type has to be used. Here, typically template parameter \p{TValueDescriptor} can be
2126/// set to #"TSubsetKeyDescriptor".
2127///
2128/// \see
2129/// For a detailed description of this type, see original type
2130/// #"HashTable", as well as alternative type definition
2131/// #"containers::HashMap".
2132///
2133/// @tparam TAllocator The #"lang::Allocator;allocator type" to use.
2134/// @tparam T The element type stored with this container.
2135/// This type is published as #"HashTable::StoredType;*"
2136/// and type definition #"HashTable::KeyType;*" becomes
2137/// a synonym.
2138/// @tparam THash The hash functor applicable to \p{T}.<br>
2139/// Defaults to <c>std::hash<T></c> and is published as
2140/// #"HashTable::HashType;*".
2141/// @tparam TEqual The comparison functor on \p{T}.<br>
2142/// Defaults to <c>std::equal_to<T></c> and is published as
2143/// #"HashTable::EqualType;*".
2144/// @tparam THashCaching Determines if hash codes are cached when elements are inserted.<br>
2145/// Defaults to <b>Caching::Auto</b>, which enables caching if
2146/// <c>std::is_arithmetic<StoredType>::value</c> evaluates to \c false.
2147/// @tparam TRecycling Denotes the type of recycling that is to be performed. Possible values are
2148/// #"Recycling::None",
2149/// #"Recycling::Private" (the default), or
2150/// #"Recycling::Shared".
2151template< typename TAllocator,
2152 typename T,
2153 typename THash = std::hash <T>,
2154 typename TEqual = std::equal_to<T>,
2155 lang::Caching THashCaching = lang::Caching::Auto,
2156 Recycling TRecycling = Recycling::Private >
2157using HashSet= HashTable< TAllocator,
2159 THash, TEqual,
2160 THashCaching,
2161 TRecycling >;
2162
2163//==================================================================================================
2164//============================================= HashMap ============================================
2165//==================================================================================================
2166
2167/// This type definition is a shortcut to #"HashTable", usable if data
2168/// stored in the container does not include a key-portion, and thus the key to the data
2169/// is to be separately defined.<br>
2170/// To achieve this, this type definition aggregates types \p{TKey} and \p{TMapped} into a
2171/// <c>std::pair<TKey,TMapped></c>. This is done using special value descriptor type
2172/// #"TPairDescriptor".
2173///
2174/// \see
2175/// For a detailed description of this type, see original type
2176/// #"HashTable", as well as alternative type definition
2177/// #"containers::HashSet".
2178///
2179/// @tparam TAllocator The #"lang::Allocator;allocator type" to use.
2180/// @tparam TKey The type of the <em>key-portion</em> of the inserted data.<br>
2181/// This type is published as #"HashTable::KeyType;*".
2182/// @tparam TMapped The type of the <em>mapped-portion</em> of the inserted data.<br>
2183/// This type is published as #"HashTable::MappedType;*".
2184/// @tparam THash The hash functor applicable to \p{TKey}.<br>
2185/// Defaults to <c>std::hash<TKey></c> and is published as
2186/// #"HashTable::HashType;*".
2187/// @tparam TEqual The comparison functor on \p{TKey}.<br>
2188/// Defaults to <c>std::equal_to<TKey></c> and is published as
2189/// #"HashTable::EqualType;*".
2190/// @tparam THashCaching Determines if hash codes are cached when elements are inserted.<br>
2191/// Defaults to <b>Caching::Auto</b>, which enables caching if
2192/// <c>std::is_arithmetic<TKey>::value</c> evaluates to \c false.
2193/// @tparam TRecycling Denotes the type of recycling that is to be performed. Possible values are
2194/// #"Recycling::None",
2195/// #"Recycling::Private" (the default), or
2196/// #"Recycling::Shared".
2197template< typename TAllocator,
2198 typename TKey,
2199 typename TMapped,
2200 typename THash = std::hash <TKey>,
2201 typename TEqual = std::equal_to<TKey>,
2202 lang::Caching THashCaching = lang::Caching::Auto,
2203 Recycling TRecycling = Recycling::Private >
2204using HashMap= HashTable<TAllocator,
2206 THash,TEqual,
2207 THashCaching,
2208 TRecycling >;
2209
2210} // namespace alib[::containers]
2211
2212/// Type alias in namespace \b alib. See type definition #"alib::containers::HashSet".
2213template< typename TAllocator,
2214 typename TValueDescriptor,
2215 typename THash = std::hash <typename TValueDescriptor::KeyType>,
2216 typename TEqual = std::equal_to<typename TValueDescriptor::KeyType>,
2217 lang::Caching THashCaching = lang::Caching::Auto,
2218 Recycling TRecycling = Recycling::Private >
2220
2221/// Type alias in namespace \b alib. See type definition #"alib::containers::HashSet".
2222template< typename TAllocator,
2223 typename T,
2224 typename THash = std::hash <T>,
2225 typename TEqual = std::equal_to<T>,
2226 lang::Caching THashCaching = lang::Caching::Auto,
2227 Recycling TRecycling = Recycling::Private >
2229
2230/// Type alias in namespace \b alib.
2231template< typename TAllocator,
2232 typename TKey,
2233 typename TMapped,
2234 typename THash = std::hash <TKey>,
2235 typename TEqual = std::equal_to<TKey>,
2236 lang::Caching THashCaching = lang::Caching::Auto,
2237 Recycling TRecycling = Recycling::Private >
2239
2240} // namespace [alib]
2241
2242#if ALIB_DEBUG_CRITICAL_SECTIONS
2243# undef DCS
2244# undef DCSSHRD
2245#endif
#define ALIB_ASSERT(cond, domain)
Definition alib.inl:1143
#define ALIB_DEBUG_CRITICAL_SECTIONS
Definition alib.inl:58
#define ALIB_POP_ALLOWANCE
Definition alib.inl:673
#define ALIB_EXPORT
Definition alib.inl:562
#define ALIB_ASSERT_ERROR(cond, domain,...)
Definition alib.inl:1144
#define ALIB_ALLOW_NOTHING_RETURNED
Definition alib.inl:652
ElementHandle & operator=(ElementHandle &&other)
HashTable * table
The table we belong to.
ElementHandle(ElementHandle &other)=delete
Deleted copy constructor.
ElementHandle(HashTable *pTable, Element *pElement)
ElementHandle()
Default constructor creating and empty handle.
Element * element
The extracted element.
ALIB_ALLOW_NOTHING_RETURNED Iterator erase(ConstIterator start, ConstIterator end)
std::pair< Iterator, bool > InsertOrAssign(const KeyType &key, MappedType &&mapped, size_t hashCode)
std::pair< Iterator, bool > InsertIfNotExistent(const KeyType &key, MappedType &&mapped)
std::pair< Iterator, bool > EmplaceIfNotExistent(const KeyType &key, TArgs &&... args)
ConstIterator cend() const
typename TValueDescriptor::MappedType MappedType
std::pair< Iterator, bool > InsertIfNotExistent(const KeyType &key, const MappedType &mapped)
std::pair< Iterator, bool > InsertOrAssign(const KeyType &key, const MappedType &mapped)
typename base::template TLocalIterator< const StoredType > ConstLocalIterator
The constant iterator for a single bucket exposed by this container.
typename TValueDescriptor::StoredType StoredType
TValueDescriptor DescriptorType
Type definition publishing template parameter TValueDescriptor.
integer Erase(const KeyType &key, size_t hashCode)
HashTable(AllocatorType &pAllocator, TSharedRecycler &pSharedRecycler, float pBaseLoadFactor=1.0, float pMaxLoadFactor=2.0)
TAllocator AllocatorType
Type definition publishing template parameter TAllocator.
HashTable(AllocatorType &pAllocator, float pBaseLoadFactor=1.0, float pMaxLoadFactor=2.0)
typename base::SharedRecyclerType SharedRecyclerType
THash HashType
Type definition publishing template parameter THash.
typename TValueDescriptor::KeyType KeyType
HashTable(TSharedRecycler &pSharedRecycler, float pBaseLoadFactor=1.0, float pMaxLoadFactor=2.0)
std::pair< Iterator, bool > InsertOrAssign(const KeyType &key, MappedType &&mapped)
typename base::template TIterator< StoredType > Iterator
The mutable iterator exposed by this container.
typename base::recyclerType recyclerType
The recycler type.
typename base::template TLocalIterator< StoredType > LocalIterator
The mutable iterator for a single bucket exposed by this container.
TEqual EqualType
Type definition publishing template parameter TEqual.
typename base::template TIterator< const StoredType > ConstIterator
The constant iterator exposed by this container.
std::pair< Iterator, bool > InsertIfNotExistent(const KeyType &key, MappedType &&mapped, size_t hashCode)
std::tuple< double, double, integer, integer > DbgGetHashTableDistribution(const THashtable &hashtable)
HashTable< TAllocator, TIdentDescriptor< T >, THash, TEqual, THashCaching, TRecycling > HashSet
HashTable< TAllocator, TPairDescriptor< TKey, TMapped >, THash, TEqual, THashCaching, TRecycling > HashMap
void Destruct(T &object)
Definition tmp.inl:82
Caching
Denotes if a cache mechanism is enabled or disabled.
@ Auto
Auto/default mode.
ValueReference
Denotes if a value is interpreted as an absolute or relative number.
@ Relative
Referring to a relative value.
@ Absolute
Referring to an absolute value.
containers::HashSet< TAllocator, T, THash, TEqual, THashCaching, TRecycling > HashSet
Type alias in namespace alib. See type definition #"alib::containers::HashSet".
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.inl:149
containers::HashMap< TAllocator, TKey, TMapped, THash, TEqual, THashCaching, TRecycling > HashMap
Type alias in namespace alib.
containers::Recycling Recycling
Type alias in namespace alib.
Definition recycling.inl:35
lang::uinteger uinteger
Type alias in namespace alib.
Definition integers.inl:152
typename HTElementSelector< TValueDescriptor, THashCaching >::Type Element
The type stored in the bucket of class HashTable.
float baseLoadFactor
The load factor that is set when the table is rehashed automatically.
typename RecyclingSelector< TRecycling >::template Type< TAllocator, typename HTElementSelector< TValueDescriptor, THashCaching >::Type > base
Our base type.
lang::SidiNodeBase< Element > Node
Type of a node in the List.
TElement * removeNext() noexcept
Definition sidilist.inl:116