Arcane  v3.14.10.0
Documentation utilisateur
Chargement...
Recherche...
Aucune correspondance
HashTableMap.h
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2024 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/* Tableau associatif utilisant une table de hachage. */
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>
29class HashTableMapEnumeratorT;
30
31/*---------------------------------------------------------------------------*/
32/*---------------------------------------------------------------------------*/
33/*!
34 * \internal
35 * \brief Table de hachage pour tableaux associatifs.
36
37 Cette table permet de stocker une valeur en fonction d'une clé. La
38 clé est de type \a KeyType et la valeur \a ValueType.
39
40 Cette table permet pour l'instant uniquement d'ajouter des valeurs.
41 La mémoire associée à chaque entrée du tableau est gérée par
42 un MultiBufferT.
43
44 Il est possible de spécifier une fonction de hachage différente de
45 la fonction par défaut en spécifiant le troisième paramètre
46 template \a KeyTraitsType.
47
48 Pour des raisons de performance, il est préférable que la taille
49 de la table (buckets) soit un nombre premier.
50*/
51template <typename KeyType, typename ValueType, typename KeyTraitsType = HashTraitsT<KeyType>>
53: public HashTableBase
54{
55 public:
56
57 typedef typename KeyTraitsType::KeyTypeConstRef KeyTypeConstRef;
58 typedef typename KeyTraitsType::KeyTypeValue KeyTypeValue;
59 typedef typename KeyTraitsType::Printable Printable;
60 typedef typename KeyTraitsType::HashValueType HashValueType;
63
64 public:
65
66 struct Data
67 {
68 public:
69
70 Data()
71 : m_key(KeyTypeValue())
72 , m_value(ValueType())
73 {}
74
75 public:
76
77 Data* next() { return m_next; }
78 void setNext(Data* anext) { this->m_next = anext; }
79 KeyTypeConstRef key() { return m_key; }
80 const ValueType& value() const { return m_value; }
81 ValueType& value() { return m_value; }
82 //! Modifie la valeur de l'instance.
83 void setValue(const ValueType& avalue) { m_value = avalue; }
84 /*!
85 * \brief Change la valeur de la clé.
86 *
87 * Après avoir changé la valeur d'une ou plusieurs clés, il faut faire un rehash().
88 */
89 void setKey(const KeyType& new_key)
90 {
91 m_key = new_key;
92 }
93
94 public:
95
96 KeyTypeValue m_key; //!< Clé de recherche
97 ValueType m_value; //!< Valeur de l'élément
98 Data* m_next = nullptr; //! Elément suivant dans la table de hachage
99 };
100
101 public:
102
103 /*! \brief Crée une table de taille \a table_size
104 *
105 Si \a use_prime est vrai, utilise la fonction nearestPrimeNumber()
106 pour avoir une taille de taille qui est un nombre premier.
107 */
108 HashTableMapT(Integer table_size, bool use_prime)
109 : HashTableBase(table_size, use_prime)
110 , m_first_free(0)
111 , m_nb_collision(0)
112 , m_nb_direct(0)
113 , m_max_count(0)
114 {
115 m_buffer = new MultiBufferT<Data>(m_nb_bucket);
116 m_buckets.resize(m_nb_bucket);
117 m_buckets.fill(0);
118 _computeMaxCount();
119 }
120
121 /*! \brief Crée une table de taille \a table_size
122 *
123 Si \a use_prime est vrai, utilise la fonction nearestPrimeNumber()
124 pour avoir une taille de taille qui est un nombre premier.
125 */
126 HashTableMapT(Integer table_size, bool use_prime, Integer buffer_size)
127 : HashTableBase(table_size, use_prime)
128 , m_first_free(0)
129 , m_nb_collision(0)
130 , m_nb_direct(0)
131 {
132 m_buffer = new MultiBufferT<Data>(buffer_size);
133 m_buckets.resize(m_nb_bucket);
134 m_buckets.fill(0);
135 _computeMaxCount();
136 }
137
139 {
140 delete m_buffer;
141 }
142
143 //! Opérateur de recopie
144 ThatClass& operator=(const ThatClass& from)
145 {
146 if (&from == this)
147 return *this;
148 //cout << "** OPERATOR= this=" << this << '\n';
149 Integer nb_bucket = from.m_nb_bucket;
150 m_first_free = 0;
151 // Remet à zéro le compteur.
152 m_count = 0;
153 m_buckets.resize(nb_bucket);
154 m_buckets.fill(0);
155 _computeMaxCount();
156 delete m_buffer;
157 m_buffer = new MultiBufferT<Data>(nb_bucket);
158 ConstArrayView<Data*> from_buckets(from.buckets());
159 for (Integer i = 0; i < nb_bucket; ++i)
160 for (Data* data = from_buckets[i]; data; data = data->next())
161 _add(i, data->key(), data->value());
162 this->m_nb_bucket = nb_bucket;
163 return *this;
164 }
165
166 public:
167
168 //! \a true si une valeur avec la clé \a id est présente
169 bool hasKey(KeyTypeConstRef id)
170 {
171 Integer hf = _keyToBucket(id);
172 for (Data* i = m_buckets[hf]; i; i = i->m_next) {
173 if (i->key() == id)
174 return true;
175 }
176 return false;
177 }
178
179 //! Supprime tous les éléments de la table
180 void clear()
181 {
182 m_buckets.fill(0);
183 m_count = 0;
184 }
185
186 /*!
187 * \brief Recherche la valeur correspondant à la clé \a id.
188 *
189 * \return la structure associé à la clé \a id (0 si aucune)
190 */
191 Data* lookup(KeyTypeConstRef id)
192 {
193 return _lookup(id);
194 }
195
196 /*!
197 * \brief Recherche la valeur correspondant à la clé \a id.
198 *
199 * \return la structure associé à la clé \a id (0 si aucune)
200 */
201 const Data* lookup(KeyTypeConstRef id) const
202 {
203 return _lookup(id);
204 }
205
206 /*!
207 * \brief Recherche la valeur correspondant à la clé \a id.
208 *
209 * Une exception est générée si la valeur n'est pas trouvé.
210 */
211 ValueType& lookupValue(KeyTypeConstRef id)
212 {
213 Data* ht = _lookup(id);
214 if (!ht) {
215 this->_throwNotFound(id, Printable());
216 }
217 return ht->value();
218 }
219
220 /*!
221 * \brief Recherche la valeur correspondant à la clé \a id.
222 *
223 * Une exception est générée si la valeur n'est pas trouvé.
224 */
225 ValueType& operator[](KeyTypeConstRef id)
226 {
227 return lookupValue(id);
228 }
229
230 /*!
231 * \brief Recherche la valeur correspondant à la clé \a id.
232 *
233 * Une exception est générée si la valeur n'est pas trouvé.
234 */
235 const ValueType& lookupValue(KeyTypeConstRef id) const
236 {
237 const Data* ht = _lookup(id);
238 if (!ht) {
239 this->_throwNotFound(id, Printable());
240 }
241 return ht->m_value;
242 }
243
244 /*!
245 * \brief Recherche la valeur correspondant à la clé \a id.
246 *
247 * Une exception est générée si la valeur n'est pas trouvé.
248 */
249 const ValueType& operator[](KeyTypeConstRef id) const
250 {
251 return lookupValue(id);
252 }
253
254 /*!
255 * \brief Ajoute la valeur \a value correspondant à la clé \a id
256 *
257 * Si une valeur correspondant à \a id existe déjà, elle est remplacée.
258 *
259 * \retval true si la clé est ajoutée
260 * \retval false si la clé existe déjà et est remplacée
261 */
262 bool add(KeyTypeConstRef id, const ValueType& value)
263 {
264 Integer hf = _keyToBucket(id);
265 Data* ht = _lookupBucket(hf, id);
266 if (ht) {
267 ht->m_value = value;
268 return false;
269 }
270 _add(hf, id, value);
271 _checkResize();
272 return true;
273 }
274
275 /*!
276 * \brief Supprime la valeur associée à la clé \a id
277 */
278 void remove(KeyTypeConstRef id)
279 {
280 Integer hf = _keyToBucket(id);
281 Data* ht = _removeBucket(hf, id);
282 ht->setNext(m_first_free);
283 m_first_free = ht;
284 }
285
286 /*!
287 * \brief Recherche ou ajoute la valeur correspondant à la clé \a id.
288 *
289 * Si la clé \a id est déjà dans la table, retourne une référence sur cette
290 * valeur et positionne \a is_add à \c false. Sinon, ajoute la clé \a id
291 * avec pour valeur \a value et positionne \a is_add à \c true.
292 *
293 * La structure retournée n'est jamais nul et peut être conservée car elle
294 * ne change pas d'adresse tant que cette instance de la table de hachage existe
295 */
296 Data* lookupAdd(KeyTypeConstRef id, const ValueType& value, bool& is_add)
297 {
298 HashValueType hf = _applyHash(id);
299 Data* ht = _lookupBucket(_hashValueToBucket(hf), id);
300 if (ht) {
301 is_add = false;
302 return ht;
303 }
304 is_add = true;
305 // Toujours faire le resize avant de retourner le add
306 // car cela peut invalider le Data*
307 _checkResize();
308 ht = _add(_hashValueToBucket(hf), id, value);
309 return ht;
310 }
311
312 /*!
313 * \brief Recherche ou ajoute la valeur correspondant à la clé \a id.
314 *
315 * Si la clé \a id est déjà dans la table, retourne une référence sur cette
316 * valeur et positionne \a is_add à \c false. Sinon, ajoute la clé \a id
317 * avec pour valeur \a ValueType() (qui doit exister).
318 *
319 * La structure retournée n'est jamais nul et peut être conservée car elle
320 * ne change pas d'adresse tant que cette instance de la table de hachage existe
321 */
322 Data* lookupAdd(KeyTypeConstRef id)
323 {
324 HashValueType hf = _applyHash(id);
325 Data* ht = _lookupBucket(_hashValueToBucket(hf), id);
326 if (!ht) {
327 // Toujours faire le resize avant de retourner le add
328 // car cela peut invalider le Data*
329 _checkResize();
330 // Le resize provoque change le bucket associé à une clé
331 ht = _add(_hashValueToBucket(hf), id, ValueType());
332 }
333 return ht;
334 }
335
336 /*!
337 * \brief Ajoute la valeur \a value correspondant à la clé \a id
338 *
339 * Si une valeur correspondant à \a id existe déjà, le résultat est
340 * indéfini.
341 */
342 void nocheckAdd(KeyTypeConstRef id, const ValueType& value)
343 {
344 _checkResize();
345 Integer hf = _keyToBucket(id);
346 _add(hf, id, value);
347 }
348
349 ArrayView<Data*> buckets()
350 {
351 return m_buckets;
352 }
353
354 ConstArrayView<Data*> buckets() const
355 {
356 return m_buckets;
357 }
358
359 //! Redimensionne la table de hachage
360 void resize(Integer new_size, bool use_prime = false)
361 {
362 if (use_prime)
363 new_size = this->nearestPrimeNumber(new_size);
364 if (new_size == 0) {
365 m_nb_bucket = new_size;
366 clear();
367 return;
368 }
369 if (new_size == m_nb_bucket)
370 return;
371 _rehash(new_size);
372 }
373
374 //! Repositionne les données après changement de valeur des clés
375 void rehash()
376 {
377 _rehash(m_nb_bucket);
378 }
379
380 public:
381
382 //! Applique le fonctor \a f à tous les éléments de la collection
383 template <class Lambda> void
384 each(const Lambda& lambda)
385 {
386 for (Integer k = 0, n = m_buckets.size(); k < n; ++k) {
387 Data* nbid = m_buckets[k];
388 for (; nbid; nbid = nbid->next()) {
389 lambda(nbid);
390 }
391 }
392 }
393
394 /*!
395 * \brief Applique le fonctor \a f à tous les éléments de la collection
396 * et utilise x->value() (de type ValueType) comme argument.
397 */
398 template <class Lambda> void
399 eachValue(const Lambda& lambda)
400 {
401 for (Integer k = 0, n = m_buckets.size(); k < n; ++k) {
402 Data* nbid = m_buckets[k];
403 for (; nbid; nbid = nbid->next()) {
404 lambda(nbid->value());
405 }
406 }
407 }
408
409 private:
410
411 //! Repositionne les données après changement de valeur des clés
412 void _rehash(Integer new_size)
413 {
414 //todo: supprimer l'allocation de ce tableau
415 UniqueArray<Data*> old_buckets(m_buckets);
416 m_count = 0;
417 m_nb_bucket = new_size;
418 m_buckets.resize(new_size);
419 m_buckets.fill(0);
420 MultiBufferT<Data>* old_buffer = m_buffer;
421 m_first_free = 0;
422 m_buffer = new MultiBufferT<Data>(m_nb_bucket);
423 for (Integer z = 0, zs = old_buckets.size(); z < zs; ++z) {
424 for (Data* i = old_buckets[z]; i; i = i->next()) {
425 Data* current = i;
426 {
427 _add(_keyToBucket(current->key()), current->key(), current->value());
428 //Data* new_data = m_buffer->allocOne();
429 //new_data->setValue(current->value());
430 //_baseAdd(_hash(current->key()),current->key(),new_data);
431 }
432 }
433 }
434 delete old_buffer;
435 _computeMaxCount();
436 }
437
438 private:
439
440 MultiBufferT<Data>* m_buffer; //!< Tampon d'allocation des valeurs
441 Data* m_first_free = nullptr; //!< Pointeur vers le premier Data utilisable
442
443 public:
444
445 mutable Int64 m_nb_collision = 0;
446 mutable Int64 m_nb_direct = 0;
447
448 private:
449
450 Data* _add(Integer bucket, KeyTypeConstRef key, const ValueType& value)
451 {
452 Data* hd = 0;
453 if (m_first_free) {
454 hd = m_first_free;
455 m_first_free = m_first_free->next();
456 }
457 else
458 hd = m_buffer->allocOne();
459 hd->setValue(value);
460 _baseAdd(bucket, key, hd);
461 return hd;
462 }
463
464 HashValueType _applyHash(KeyTypeConstRef id) const
465 {
466 //return (Integer)(KeyTraitsType::hashFunction(id) % m_nb_bucket);
467 return KeyTraitsType::hashFunction(id);
468 }
469
470 Integer _keyToBucket(KeyTypeConstRef id) const
471 {
472 return (Integer)(_applyHash(id) % m_nb_bucket);
473 }
474
475 Integer _hashValueToBucket(KeyTypeValue id) const
476 {
477 return (Integer)(id % m_nb_bucket);
478 }
479
480 Data* _baseLookupBucket(Integer bucket, KeyTypeConstRef id) const
481 {
482 for (Data* i = m_buckets[bucket]; i; i = i->next()) {
483 if (!(i->key() == id)) {
484 ++m_nb_collision;
485 continue;
486 }
487 ++m_nb_direct;
488 return i;
489 }
490 return 0;
491 }
492
493 Data* _baseRemoveBucket(Integer bucket, KeyTypeConstRef id)
494 {
495 Data* i = m_buckets[bucket];
496 if (i) {
497 if (i->m_key == id) {
498 m_buckets[bucket] = i->next();
499 --m_count;
500 return i;
501 }
502 for (; i->next(); i = i->next()) {
503 if (i->next()->key() == id) {
504 Data* r = i->next();
505 i->setNext(i->next()->next());
506 --m_count;
507 return r;
508 }
509 }
510 }
511 this->_throwNotFound(id, Printable());
512 return 0;
513 }
514
515 inline Data* _baseLookup(KeyTypeConstRef id) const
516 {
517 return _baseLookupBucket(_keyToBucket(id), id);
518 }
519
520 inline Data* _baseRemove(KeyTypeConstRef id)
521 {
522 return _baseRemoveBucket(_keyToBucket(id), id);
523 }
524
525 void _baseAdd(Integer bucket, KeyTypeConstRef id, Data* hd)
526 {
527 Data* buck = m_buckets[bucket];
528 hd->m_key = id;
529 hd->m_next = buck;
530 m_buckets[bucket] = hd;
531 ++m_count;
532 }
533
534 Data* _lookup(KeyTypeConstRef id)
535 {
536 return _baseLookup(id);
537 }
538
539 const Data* _lookup(KeyTypeConstRef id) const
540 {
541 return _baseLookup(id);
542 }
543
544 Data* _lookupBucket(Integer bucket, KeyTypeConstRef id) const
545 {
546 return _baseLookupBucket(bucket, id);
547 }
548
549 Data* _removeBucket(Integer bucket, KeyTypeConstRef id)
550 {
551 return _baseRemoveBucket(bucket, id);
552 }
553
554 void _checkResize()
555 {
556 // Retaille si besoin.
557 if (m_count > m_max_count) {
558 //cout << "** BEFORE BUCKET RESIZE this=" << this << " count=" << m_count
559 // << " bucket=" << m_nb_bucket << " m_max_count=" << m_max_count
560 // << " memory=" << (m_buckets.capacity()*sizeof(Data*)) << '\n';
561 //_print(Printable());
562 // Pour les grosses tables, augmente moins vite pour limiter la
563 // consommation memoire
564 if (m_nb_bucket > 200000) {
565 resize((Integer)(1.3 * m_nb_bucket), true);
566 }
567 else if (m_nb_bucket > 10000) {
568 resize((Integer)(1.5 * m_nb_bucket), true);
569 }
570 else
571 resize(2 * m_nb_bucket, true);
572 //cout << "** AFTER BUCKET RESIZE this=" << this << " count=" << m_count
573 // << " bucket=" << m_nb_bucket << " m_max_count=" << m_max_count
574 // << " memory=" << (m_buckets.capacity()*sizeof(Data*)) << '\n';
575 //_print(Printable());
576 std::cout.flush();
577 }
578 }
579
580 void _print(FalseType)
581 {
582 }
583
584 void _print(TrueType)
585 {
586 for (Integer z = 0, zs = m_buckets.size(); z < zs; ++z) {
587 for (Data* i = m_buckets[z]; i; i = i->next()) {
588 cout << "* KEY=" << i->key() << " bucket=" << z << '\n';
589 }
590 }
591 }
592
593 void _throwNotFound ARCANE_NORETURN(KeyTypeConstRef, FalseType) const
594 {
595 HashTableBase::_throwNotFound();
596 }
597
598 void _throwNotFound ARCANE_NORETURN(KeyTypeConstRef id, TrueType) const
599 {
600 std::cout << "ERROR: can not find key=" << id << " bucket=" << _keyToBucket(id) << "\n";
601 std::cout.flush();
602 HashTableBase::_throwNotFound();
603 }
604
605 void _computeMaxCount()
606 {
607 m_max_count = (Integer)(m_nb_bucket * 0.85);
608 }
609
610 private:
611
612 //! Nombre maximal d'élément avant retaillage
613 Integer m_max_count = 0;
614 UniqueArray<Data*> m_buckets; //! Tableau des buckets
615};
616
617/*---------------------------------------------------------------------------*/
618/*---------------------------------------------------------------------------*/
619/*!
620 * \internal
621 * \brief Enumerateur sur un HashTableMap.
622 */
623template <typename KeyType, typename ValueType>
625{
626 typedef HashTableMapT<KeyType, ValueType> HashType;
627 typedef typename HashType::Data Data;
628
629 public:
630
631 HashTableMapEnumeratorT(const HashType& rhs)
632 : m_buckets(rhs.buckets())
633 , m_current_data(0)
634 , m_current_bucket(-1)
635 {}
636
637 public:
638
639 bool operator++()
640 {
641 if (m_current_data)
642 m_current_data = m_current_data->next();
643 if (!m_current_data) {
644 while (m_current_data == 0 && (m_current_bucket + 1) < m_buckets.size()) {
645 ++m_current_bucket;
646 m_current_data = m_buckets[m_current_bucket];
647 }
648 }
649 return m_current_data != 0;
650 }
651 ValueType& operator*() { return m_current_data->value(); }
652 const ValueType& operator*() const { return m_current_data->value(); }
653 Data* data() { return m_current_data; }
654 const Data* data() const { return m_current_data; }
655
656 public:
657
658 ConstArrayView<Data*> m_buckets;
659 Data* m_current_data;
660 Integer m_current_bucket;
661};
662
663/*---------------------------------------------------------------------------*/
664/*---------------------------------------------------------------------------*/
665
666} // namespace Arcane
667
668/*---------------------------------------------------------------------------*/
669/*---------------------------------------------------------------------------*/
670
671#endif
Integer m_nb_bucket
Nombre de buckets.
Definition HashTable.h:75
Integer nearestPrimeNumber(Integer n)
Retourne le nombre premier le plus proche de n par excès. Le nombre premier le plus proche et supérie...
Definition HashTable.cc:57
Integer m_count
Nombre d'éléments.
Definition HashTable.h:74
const ValueType & operator[](KeyTypeConstRef id) const
Recherche la valeur correspondant à la clé id.
void rehash()
Repositionne les données après changement de valeur des clés.
HashTableMapT(Integer table_size, bool use_prime, Integer buffer_size)
Crée une table de taille table_size.
void nocheckAdd(KeyTypeConstRef id, const ValueType &value)
Ajoute la valeur value correspondant à la clé id.
const Data * lookup(KeyTypeConstRef id) const
Recherche la valeur correspondant à la clé id.
Data * lookup(KeyTypeConstRef id)
Recherche la valeur correspondant à la clé id.
Data * lookupAdd(KeyTypeConstRef id, const ValueType &value, bool &is_add)
Recherche ou ajoute la valeur correspondant à la clé id.
void remove(KeyTypeConstRef id)
Supprime la valeur associée à la clé id.
void resize(Integer new_size, bool use_prime=false)
Redimensionne la table de hachage.
ValueType & lookupValue(KeyTypeConstRef id)
Recherche la valeur correspondant à la clé id.
ThatClass & operator=(const ThatClass &from)
Opérateur de recopie.
ValueType & operator[](KeyTypeConstRef id)
Recherche la valeur correspondant à la clé id.
bool add(KeyTypeConstRef id, const ValueType &value)
Ajoute la valeur value correspondant à la clé id.
void eachValue(const Lambda &lambda)
Applique le fonctor f à tous les éléments de la collection et utilise x->value() (de type ValueType) ...
void clear()
Supprime tous les éléments de la table.
void each(const Lambda &lambda)
Applique le fonctor f à tous les éléments de la collection.
const ValueType & lookupValue(KeyTypeConstRef id) const
Recherche la valeur correspondant à la clé id.
HashTableMapT(Integer table_size, bool use_prime)
Crée une table de taille table_size.
Data * lookupAdd(KeyTypeConstRef id)
Recherche ou ajoute la valeur correspondant à la clé id.
bool hasKey(KeyTypeConstRef id)
true si une valeur avec la clé id est présente
Vue modifiable d'un tableau d'un type T.
Vue constante d'un tableau de type T.
Vecteur 1D de données avec sémantique par valeur (style STL).
-*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
Int32 Integer
Type représentant un entier.
void setValue(const ValueType &avalue)
Modifie la valeur de l'instance.
ValueType m_value
Valeur de l'élément.
KeyTypeValue m_key
Clé de recherche.
void setKey(const KeyType &new_key)
Change la valeur de la clé.