Arcane  4.1.12.0
User documentation
Loading...
Searching...
No Matches
HashTableMap.h
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* HashTableMap.h (C) 2000-2024 */
9/* */
10/* Associative array using a hash table. */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCANE_UTILS_HASHTABLEMAP_H
13#define ARCANE_UTILS_HASHTABLEMAP_H
14/*---------------------------------------------------------------------------*/
15/*---------------------------------------------------------------------------*/
16
17#include "arcane/utils/HashTable.h"
18
19/*---------------------------------------------------------------------------*/
20/*---------------------------------------------------------------------------*/
21
22namespace Arcane
23{
24
25/*---------------------------------------------------------------------------*/
26/*---------------------------------------------------------------------------*/
27
28template <typename KeyType, typename ValueType>
30
31/*---------------------------------------------------------------------------*/
32/*---------------------------------------------------------------------------*/
33/*!
34 * \internal
35 * \brief Hash table for associative arrays.
36
37 This table allows storing a value based on a key. The key is of type \a KeyType and the value is \a ValueType.
38
39 For now, this table only allows adding values.
40 The memory associated with each entry in the array is managed by
41 a MultiBufferT.
42
43 It is possible to specify a hash function different from
44 the default function by specifying the third template parameter \a KeyTraitsType.
45
46 For performance reasons, it is preferable that the size
47 of the table (buckets) is a prime number.
48*/
49template <typename KeyType, typename ValueType, typename KeyTraitsType = HashTraitsT<KeyType>>
51: public HashTableBase
52{
53 public:
54
55 typedef typename KeyTraitsType::KeyTypeConstRef KeyTypeConstRef;
56 typedef typename KeyTraitsType::KeyTypeValue KeyTypeValue;
57 typedef typename KeyTraitsType::Printable Printable;
58 typedef typename KeyTraitsType::HashValueType HashValueType;
61
62 public:
63
64 struct Data
65 {
66 public:
67
68 Data()
69 : m_key(KeyTypeValue())
71 {}
72
73 public:
74
75 Data* next() { return m_next; }
76 void setNext(Data* anext) { this->m_next = anext; }
77 KeyTypeConstRef key() { return m_key; }
78 const ValueType& value() const { return m_value; }
79 ValueType& value() { return m_value; }
80 //! Modifies the value of the instance.
81 void setValue(const ValueType& avalue) { m_value = avalue; }
82 /*!
83 * \brief Changes the value of the key.
84 *
85 * After changing the value of one or more keys, a rehash() must be performed.
86 */
87 void setKey(const KeyType& new_key)
88 {
89 m_key = new_key;
90 }
91
92 public:
93
94 KeyTypeValue m_key; //!< Search key
95 ValueType m_value; //!< Element value
96 Data* m_next = nullptr; //! Next element in the hash table
97 };
98
99 public:
100
101 /*! \brief Creates a table of size \a table_size
102 *
103 If \a use_prime is true, it uses the nearestPrimeNumber() function
104 to have a size that is a prime number.
105 */
106 HashTableMapT(Integer table_size, bool use_prime)
107 : HashTableBase(table_size, use_prime)
108 , m_first_free(0)
109 , m_nb_collision(0)
110 , m_nb_direct(0)
111 , m_max_count(0)
112 {
113 m_buffer = new MultiBufferT<Data>(m_nb_bucket);
114 m_buckets.resize(m_nb_bucket);
115 m_buckets.fill(0);
116 _computeMaxCount();
117 }
118
119 /*! \brief Creates a table of size \a table_size
120 *
121 If \a use_prime is true, it uses the nearestPrimeNumber() function
122 to have a size that is a prime number.
123 */
124 HashTableMapT(Integer table_size, bool use_prime, Integer buffer_size)
125 : HashTableBase(table_size, use_prime)
126 , m_first_free(0)
127 , m_nb_collision(0)
128 , m_nb_direct(0)
129 {
130 m_buffer = new MultiBufferT<Data>(buffer_size);
131 m_buckets.resize(m_nb_bucket);
132 m_buckets.fill(0);
133 _computeMaxCount();
134 }
135
137 {
138 delete m_buffer;
139 }
140
141 //! Copy assignment operator
142 ThatClass& operator=(const ThatClass& from)
143 {
144 if (&from == this)
145 return *this;
146 //cout << "** OPERATOR= this=" << this << '\n';
147 Integer nb_bucket = from.m_nb_bucket;
148 m_first_free = 0;
149 // Resets the counter.
150 m_count = 0;
151 m_buckets.resize(nb_bucket);
152 m_buckets.fill(0);
153 _computeMaxCount();
154 delete m_buffer;
155 m_buffer = new MultiBufferT<Data>(nb_bucket);
156 ConstArrayView<Data*> from_buckets(from.buckets());
157 for (Integer i = 0; i < nb_bucket; ++i)
158 for (Data* data = from_buckets[i]; data; data = data->next())
159 _add(i, data->key(), data->value());
160 this->m_nb_bucket = nb_bucket;
161 return *this;
162 }
163
164 public:
165
166 //! \a true if a value with key \a id is present
167 bool hasKey(KeyTypeConstRef id)
168 {
169 Integer hf = _keyToBucket(id);
170 for (Data* i = m_buckets[hf]; i; i = i->m_next) {
171 if (i->key() == id)
172 return true;
173 }
174 return false;
175 }
176
177 //! Deletes all elements from the table
178 void clear()
179 {
180 m_buckets.fill(0);
181 m_count = 0;
182 }
183
184 /*!
185 * \brief Searches for the value corresponding to key \a id.
186 *
187 * \return the structure associated with key \a id (0 if none)
188 */
189 Data* lookup(KeyTypeConstRef id)
190 {
191 return _lookup(id);
192 }
193
194 /*!
195 * \brief Searches for the value corresponding to key \a id.
196 *
197 * \return the structure associated with key \a id (0 if none)
198 */
199 const Data* lookup(KeyTypeConstRef id) const
200 {
201 return _lookup(id);
202 }
203
204 /*!
205 * \brief Searches for the value corresponding to key \a id.
206 *
207 * An exception is generated if the value is not found.
208 */
209 ValueType& lookupValue(KeyTypeConstRef id)
210 {
211 Data* ht = _lookup(id);
212 if (!ht) {
213 this->_throwNotFound(id, Printable());
214 }
215 return ht->value();
216 }
217
218 /*!
219 * \brief Searches for the value corresponding to key \a id.
220 *
221 * An exception is generated if the value is not found.
222 */
223 ValueType& operator[](KeyTypeConstRef id)
224 {
225 return lookupValue(id);
226 }
227
228 /*!
229 * \brief Searches for the value corresponding to key \a id.
230 *
231 * An exception is generated if the value is not found.
232 */
233 const ValueType& lookupValue(KeyTypeConstRef id) const
234 {
235 const Data* ht = _lookup(id);
236 if (!ht) {
237 this->_throwNotFound(id, Printable());
238 }
239 return ht->m_value;
240 }
241
242 /*!
243 * \brief Searches for the value corresponding to key \a id.
244 *
245 * An exception is generated if the value is not found.
246 */
247 const ValueType& operator[](KeyTypeConstRef id) const
248 {
249 return lookupValue(id);
250 }
251
252 /*!
253 * \brief Adds the value \a value corresponding to key \a id
254 *
255 * If a value corresponding to \a id already exists, it is replaced.
256 *
257 * \retval true if the key is added
258 * \retval false if the key already exists and is replaced
259 */
260 bool add(KeyTypeConstRef id, const ValueType& value)
261 {
262 Integer hf = _keyToBucket(id);
263 Data* ht = _lookupBucket(hf, id);
264 if (ht) {
265 ht->m_value = value;
266 return false;
267 }
268 _add(hf, id, value);
269 _checkResize();
270 return true;
271 }
272
273 /*!
274 * \brief Removes the value associated with key \a id
275 */
276 void remove(KeyTypeConstRef id)
277 {
278 Integer hf = _keyToBucket(id);
279 Data* ht = _removeBucket(hf, id);
280 ht->setNext(m_first_free);
281 m_first_free = ht;
282 }
283
284 /*!
285 * \brief Searches for or adds the value corresponding to key \a id.
286 *
287 * If key \a id is already in the table, returns a reference to this
288 * value and sets \a is_add to \c false. Otherwise, adds key \a id
289 * with value \a value and sets \a is_add to \c true.
290 *
291 * The returned structure is never null and can be kept because it
292 * does not change address as long as this hash table instance exists
293 */
294 Data* lookupAdd(KeyTypeConstRef id, const ValueType& value, bool& is_add)
295 {
296 HashValueType hf = _applyHash(id);
297 Data* ht = _lookupBucket(_hashValueToBucket(hf), id);
298 if (ht) {
299 is_add = false;
300 return ht;
301 }
302 is_add = true;
303 // Always perform the resize before returning the add
304 // because it may invalidate the Data*
305 _checkResize();
306 ht = _add(_hashValueToBucket(hf), id, value);
307 return ht;
308 }
309
310 /*!
311 * \brief Searches for or adds the value corresponding to key \a id.
312 *
313 * If key \a id is already in the table, returns a reference to this
314 * value and sets \a is_add to \c false. Otherwise, adds key \a id
315 * with value \a ValueType() (which must exist).
316 *
317 * The returned structure is never null and can be kept because it
318 * does not change address as long as this hash table instance exists
319 */
320 Data* lookupAdd(KeyTypeConstRef id)
321 {
322 HashValueType hf = _applyHash(id);
323 Data* ht = _lookupBucket(_hashValueToBucket(hf), id);
324 if (!ht) {
325 // Always perform the resize before returning the add
326 // because it may invalidate the Data*
327 _checkResize();
328 // The resize changes the bucket associated with a key
329 ht = _add(_hashValueToBucket(hf), id, ValueType());
330 }
331 return ht;
332 }
333
334 /*!
335 * \brief Adds the value \a value corresponding to the key \a id
336 *
337 * If a value corresponding to \a id already exists, the result is
338 * undefined.
339 */
340 void nocheckAdd(KeyTypeConstRef id, const ValueType& value)
341 {
342 _checkResize();
343 Integer hf = _keyToBucket(id);
344 _add(hf, id, value);
345 }
346
347 ArrayView<Data*> buckets()
348 {
349 return m_buckets;
350 }
351
352 ConstArrayView<Data*> buckets() const
353 {
354 return m_buckets;
355 }
356
357 //! Resizes the hash table
358 void resize(Integer new_size, bool use_prime = false)
359 {
360 if (use_prime)
361 new_size = this->nearestPrimeNumber(new_size);
362 if (new_size == 0) {
363 m_nb_bucket = new_size;
364 clear();
365 return;
366 }
367 if (new_size == m_nb_bucket)
368 return;
369 _rehash(new_size);
370 }
371
372 //! Rehashes the data after changing key values
373 void rehash()
374 {
375 _rehash(m_nb_bucket);
376 }
377
378 public:
379
380 //! Applies the functor \a f to all elements of the collection
381 template <class Lambda> void
382 each(const Lambda& lambda)
383 {
384 for (Integer k = 0, n = m_buckets.size(); k < n; ++k) {
385 Data* nbid = m_buckets[k];
386 for (; nbid; nbid = nbid->next()) {
387 lambda(nbid);
388 }
389 }
390 }
391
392 /*!
393 * \brief Applies the functor \a f to all elements of the collection
394 * and uses x->value() (of type ValueType) as an argument.
395 */
396 template <class Lambda> void
397 eachValue(const Lambda& lambda)
398 {
399 for (Integer k = 0, n = m_buckets.size(); k < n; ++k) {
400 Data* nbid = m_buckets[k];
401 for (; nbid; nbid = nbid->next()) {
402 lambda(nbid->value());
403 }
404 }
405 }
406
407 private:
408
409 //! Rehashes the data after changing key values
410 void _rehash(Integer new_size)
411 {
412 //todo: delete the allocation of this array
413 UniqueArray<Data*> old_buckets(m_buckets);
414 m_count = 0;
415 m_nb_bucket = new_size;
416 m_buckets.resize(new_size);
417 m_buckets.fill(0);
418 MultiBufferT<Data>* old_buffer = m_buffer;
419 m_first_free = 0;
420 m_buffer = new MultiBufferT<Data>(m_nb_bucket);
421 for (Integer z = 0, zs = old_buckets.size(); z < zs; ++z) {
422 for (Data* i = old_buckets[z]; i; i = i->next()) {
423 Data* current = i;
424 {
425 _add(_keyToBucket(current->key()), current->key(), current->value());
426 //Data* new_data = m_buffer->allocOne();
427 //new_data->setValue(current->value());
428 //_baseAdd(_hash(current->key()),current->key(),new_data);
429 }
430 }
431 }
432 delete old_buffer;
433 _computeMaxCount();
434 }
435
436 private:
437
438 MultiBufferT<Data>* m_buffer; //!< Value allocation buffer
439 Data* m_first_free = nullptr; //!< Pointer to the first usable Data
440
441 public:
442
443 mutable Int64 m_nb_collision = 0;
444 mutable Int64 m_nb_direct = 0;
445
446 private:
447
448 Data* _add(Integer bucket, KeyTypeConstRef key, const ValueType& value)
449 {
450 Data* hd = 0;
451 if (m_first_free) {
452 hd = m_first_free;
453 m_first_free = m_first_free->next();
454 }
455 else
456 hd = m_buffer->allocOne();
457 hd->setValue(value);
458 _baseAdd(bucket, key, hd);
459 return hd;
460 }
461
462 HashValueType _applyHash(KeyTypeConstRef id) const
463 {
464 //return (Integer)(KeyTraitsType::hashFunction(id) % m_nb_bucket);
465 return KeyTraitsType::hashFunction(id);
466 }
467
468 Integer _keyToBucket(KeyTypeConstRef id) const
469 {
470 return (Integer)(_applyHash(id) % m_nb_bucket);
471 }
472
473 Integer _hashValueToBucket(KeyTypeValue id) const
474 {
475 return (Integer)(id % m_nb_bucket);
476 }
477
478 Data* _baseLookupBucket(Integer bucket, KeyTypeConstRef id) const
479 {
480 for (Data* i = m_buckets[bucket]; i; i = i->next()) {
481 if (!(i->key() == id)) {
482 ++m_nb_collision;
483 continue;
484 }
485 ++m_nb_direct;
486 return i;
487 }
488 return 0;
489 }
490
491 Data* _baseRemoveBucket(Integer bucket, KeyTypeConstRef id)
492 {
493 Data* i = m_buckets[bucket];
494 if (i) {
495 if (i->m_key == id) {
496 m_buckets[bucket] = i->next();
497 --m_count;
498 return i;
499 }
500 for (; i->next(); i = i->next()) {
501 if (i->next()->key() == id) {
502 Data* r = i->next();
503 i->setNext(i->next()->next());
504 --m_count;
505 return r;
506 }
507 }
508 }
509 this->_throwNotFound(id, Printable());
510 return 0;
511 }
512
513 inline Data* _baseLookup(KeyTypeConstRef id) const
514 {
515 return _baseLookupBucket(_keyToBucket(id), id);
516 }
517
518 inline Data* _baseRemove(KeyTypeConstRef id)
519 {
520 return _baseRemoveBucket(_keyToBucket(id), id);
521 }
522
523 void _baseAdd(Integer bucket, KeyTypeConstRef id, Data* hd)
524 {
525 Data* buck = m_buckets[bucket];
526 hd->m_key = id;
527 hd->m_next = buck;
528 m_buckets[bucket] = hd;
529 ++m_count;
530 }
531
532 Data* _lookup(KeyTypeConstRef id)
533 {
534 return _baseLookup(id);
535 }
536
537 const Data* _lookup(KeyTypeConstRef id) const
538 {
539 return _baseLookup(id);
540 }
541
542 Data* _lookupBucket(Integer bucket, KeyTypeConstRef id) const
543 {
544 return _baseLookupBucket(bucket, id);
545 }
546
547 Data* _removeBucket(Integer bucket, KeyTypeConstRef id)
548 {
549 return _baseRemoveBucket(bucket, id);
550 }
551
552 void _checkResize()
553 {
554 // Resize if necessary.
555 if (m_count > m_max_count) {
556 //cout << "** BEFORE BUCKET RESIZE this=" << this << " count=" << m_count
557 // << " bucket=" << m_nb_bucket << " m_max_count=" << m_max_count
558 // << " memory=" << (m_buckets.capacity()*sizeof(Data*)) << '\n';
559 //_print(Printable());
560 // For large tables, increase less quickly to limit
561 // memory consumption
562 if (m_nb_bucket > 200000) {
563 resize((Integer)(1.3 * m_nb_bucket), true);
564 }
565 else if (m_nb_bucket > 10000) {
566 resize((Integer)(1.5 * m_nb_bucket), true);
567 }
568 else
569 resize(2 * m_nb_bucket, true);
570 //cout << "** AFTER BUCKET RESIZE this=" << this << " count=" << m_count
571 // << " bucket=" << m_nb_bucket << " m_max_count=" << m_max_count
572 // << " memory=" << (m_buckets.capacity()*sizeof(Data*)) << '\n';
573 //_print(Printable());
574 std::cout.flush();
575 }
576 }
577
578 void _print(FalseType)
579 {
580 }
581
582 void _print(TrueType)
583 {
584 for (Integer z = 0, zs = m_buckets.size(); z < zs; ++z) {
585 for (Data* i = m_buckets[z]; i; i = i->next()) {
586 cout << "* KEY=" << i->key() << " bucket=" << z << '\n';
587 }
588 }
589 }
590
591 void _throwNotFound ARCANE_NORETURN(KeyTypeConstRef, FalseType) const
592 {
593 HashTableBase::_throwNotFound();
594 }
595
596 void _throwNotFound ARCANE_NORETURN(KeyTypeConstRef id, TrueType) const
597 {
598 std::cout << "ERROR: can not find key=" << id << " bucket=" << _keyToBucket(id) << "\n";
599 std::cout.flush();
600 HashTableBase::_throwNotFound();
601 }
602
603 void _computeMaxCount()
604 {
605 m_max_count = (Integer)(m_nb_bucket * 0.85);
606 }
607
608 private:
609
610 //! Maximum number of elements before resizing
611 Integer m_max_count = 0;
612 UniqueArray<Data*> m_buckets; //! Array of buckets
613};
614
615/*---------------------------------------------------------------------------*/
616/*---------------------------------------------------------------------------*/
617
618/*!
619 * \internal
620 * \brief Enumerator for a HashTableMap.
621 */
622template <typename KeyType, typename ValueType>
623class HashTableMapEnumeratorT
624{
625 typedef HashTableMapT<KeyType, ValueType> HashType;
626 typedef typename HashType::Data Data;
627
628 public:
629
630 HashTableMapEnumeratorT(const HashType& rhs)
631 : m_buckets(rhs.buckets())
632 , m_current_data(0)
633 , m_current_bucket(-1)
634 {}
635
636 public:
637
638 bool operator++()
639 {
640 if (m_current_data)
641 m_current_data = m_current_data->next();
642 if (!m_current_data) {
643 while (m_current_data == 0 && (m_current_bucket + 1) < m_buckets.size()) {
644 ++m_current_bucket;
645 m_current_data = m_buckets[m_current_bucket];
646 }
647 }
648 return m_current_data != 0;
649 }
650 ValueType& operator*() { return m_current_data->value(); }
651 const ValueType& operator*() const { return m_current_data->value(); }
652 Data* data() { return m_current_data; }
653 const Data* data() const { return m_current_data; }
654
655 public:
656
657 ConstArrayView<Data*> m_buckets;
658 Data* m_current_data;
659 Integer m_current_bucket;
660};
661
662/*---------------------------------------------------------------------------*/
663/*---------------------------------------------------------------------------*/
664
665} // namespace Arcane
666
667/*---------------------------------------------------------------------------*/
668/*---------------------------------------------------------------------------*/
669
670#endif
Modifiable view of an array of type T.
Constant view of an array of type T.
HashTableBase(Integer table_size, bool use_prime)
Creates a table of size table_size.
Definition HashTable.h:46
Integer m_nb_bucket
Number of buckets.
Definition HashTable.h:77
Integer nearestPrimeNumber(Integer n)
Returns the nearest prime number greater than n. The nearest prime number greater than n is returned ...
Definition HashTable.cc:57
Integer m_count
Number of elements.
Definition HashTable.h:76
const ValueType & operator[](KeyTypeConstRef id) const
Searches for the value corresponding to key id.
void rehash()
Rehashes the data after changing key values.
HashTableMapT(Integer table_size, bool use_prime, Integer buffer_size)
Creates a table of size table_size.
void nocheckAdd(KeyTypeConstRef id, const ValueType &value)
Adds the value value corresponding to the key id.
const Data * lookup(KeyTypeConstRef id) const
Searches for the value corresponding to key id.
Data * lookup(KeyTypeConstRef id)
Searches for the value corresponding to key id.
Data * lookupAdd(KeyTypeConstRef id, const ValueType &value, bool &is_add)
Searches for or adds the value corresponding to key id.
void remove(KeyTypeConstRef id)
Removes the value associated with key id.
void resize(Integer new_size, bool use_prime=false)
Resizes the hash table.
ValueType & lookupValue(KeyTypeConstRef id)
Searches for the value corresponding to key id.
ThatClass & operator=(const ThatClass &from)
Copy assignment operator.
ValueType & operator[](KeyTypeConstRef id)
Searches for the value corresponding to key id.
bool add(KeyTypeConstRef id, const ValueType &value)
Adds the value value corresponding to key id.
void eachValue(const Lambda &lambda)
Applies the functor f to all elements of the collection and uses x->value() (of type ValueType) as an...
void clear()
Deletes all elements from the table.
void each(const Lambda &lambda)
Applies the functor f to all elements of the collection.
const ValueType & lookupValue(KeyTypeConstRef id) const
Searches for the value corresponding to key id.
HashTableMapT(Integer table_size, bool use_prime)
Creates a table of size table_size.
Data * lookupAdd(KeyTypeConstRef id)
Searches for or adds the value corresponding to key id.
bool hasKey(KeyTypeConstRef id)
true if a value with key id is present
1D data vector with value semantics (STL style).
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
std::int64_t Int64
Signed integer type of 64 bits.
Int32 Integer
Type representing an integer.
void setValue(const ValueType &avalue)
Modifies the value of the instance.
ValueType m_value
Element value.
KeyTypeValue m_key
Search key.
void setKey(const KeyType &new_key)
Changes the value of the key.