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