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