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