HashTable.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2011-2024 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #ifndef HashTable_C
27 #define HashTable_C
28 
29 #include "HashTable.H"
30 #include "List.H"
31 #include "Tuple2.H"
32 
33 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
34 
35 template<class T, class Key, class Hash>
37 :
38  HashTableCore(),
39  nElmts_(0),
40  tableSize_(HashTableCore::canonicalSize(size)),
41  table_(nullptr)
42 {
43  if (tableSize_)
44  {
45  table_ = new hashedEntry*[tableSize_];
46 
47  for (label hashIdx = 0; hashIdx < tableSize_; hashIdx++)
48  {
49  table_[hashIdx] = 0;
50  }
51  }
52 }
53 
54 
55 template<class T, class Key, class Hash>
57 :
58  HashTable<T, Key, Hash>(ht.tableSize_)
59 {
60  for (const_iterator iter = ht.cbegin(); iter != ht.cend(); ++iter)
61  {
62  insert(iter.key(), *iter);
63  }
64 }
65 
66 
67 template<class T, class Key, class Hash>
69 (
71 )
72 :
73  HashTableCore(),
74  nElmts_(0),
75  tableSize_(0),
76  table_(nullptr)
77 {
78  transfer(ht);
79 }
80 
81 
82 template<class T, class Key, class Hash>
84 (
85  const UList<Key>& keyList,
86  const UList<T>& elmtList
87 )
88 :
89  HashTable<T, Key, Hash>(keyList.size())
90 {
91  if (keyList.size() != elmtList.size())
92  {
94  << "Lists of keys and elements have different sizes" << nl
95  << " number of keys: " << keyList.size()
96  << ", number of elements: " << elmtList.size()
97  << abort(FatalError);
98  }
99 
100  forAll(keyList, i)
101  {
102  insert(keyList[i], elmtList[i]);
103  }
104 }
105 
106 
107 template<class T, class Key, class Hash>
109 (
110  std::initializer_list<Tuple2<Key, T>> lst
111 )
112 :
113  HashTable<T, Key, Hash>(lst.size())
114 {
115  for (const Tuple2<Key, T>& pair : lst)
116  {
117  insert(pair.first(), pair.second());
118  }
119 }
120 
121 
122 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
123 
124 template<class T, class Key, class Hash>
126 {
127  if (table_)
128  {
129  clear();
130  delete[] table_;
131  }
132 }
133 
134 
135 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
136 
137 template<class T, class Key, class Hash>
138 bool Foam::HashTable<T, Key, Hash>::found(const Key& key) const
139 {
140  if (nElmts_)
141  {
142  const label hashIdx = hashKeyIndex(key);
143 
144  for (hashedEntry* ep = table_[hashIdx]; ep; ep = ep->next_)
145  {
146  if (key == ep->key_)
147  {
148  return true;
149  }
150  }
151  }
152 
153  #ifdef FULLDEBUG
154  if (debug)
155  {
156  InfoInFunction << "Entry " << key << " not found in hash table\n";
157  }
158  #endif
159 
160  return false;
161 }
162 
163 
164 template<class T, class Key, class Hash>
167 (
168  const Key& key
169 )
170 {
171  if (nElmts_)
172  {
173  const label hashIdx = hashKeyIndex(key);
174 
175  for (hashedEntry* ep = table_[hashIdx]; ep; ep = ep->next_)
176  {
177  if (key == ep->key_)
178  {
179  return iterator(this, ep, hashIdx);
180  }
181  }
182  }
183 
184  #ifdef FULLDEBUG
185  if (debug)
186  {
187  InfoInFunction << "Entry " << key << " not found in hash table\n";
188  }
189  #endif
190 
191  return iterator();
192 }
193 
194 
195 template<class T, class Key, class Hash>
198 (
199  const Key& key
200 ) const
201 {
202  if (nElmts_)
203  {
204  const label hashIdx = hashKeyIndex(key);
205 
206  for (hashedEntry* ep = table_[hashIdx]; ep; ep = ep->next_)
207  {
208  if (key == ep->key_)
209  {
210  return const_iterator(this, ep, hashIdx);
211  }
212  }
213  }
214 
215  #ifdef FULLDEBUG
216  if (debug)
217  {
218  InfoInFunction << "Entry " << key << " not found in hash table\n";
219  }
220  #endif
221 
222  return const_iterator();
223 }
224 
225 
226 template<class T, class Key, class Hash>
228 {
229  List<Key> keys(nElmts_);
230  label keyI = 0;
231 
232  for (const_iterator iter = cbegin(); iter != cend(); ++iter)
233  {
234  keys[keyI++] = iter.key();
235  }
236 
237  return keys;
238 }
239 
240 
241 template<class T, class Key, class Hash>
243 {
244  List<Key> sortedLst = this->toc();
245  sort(sortedLst);
246 
247  return sortedLst;
248 }
249 
250 
251 template<class T, class Key, class Hash>
254 {
255  List<const_iterator> sortedLst(size());
256 
257  label i = 0;
258  for (const_iterator iter = cbegin(); iter != cend(); ++iter)
259  {
260  sortedLst[i++] = iter;
261  }
262 
263  sort
264  (
265  sortedLst,
266  []
267  (
268  const const_iterator& a,
269  const const_iterator& b
270  )
271  {
272  return a.key() < b.key();
273  }
274  );
275 
276  return sortedLst;
277 }
278 
279 
280 template<class T, class Key, class Hash>
282 (
283  const Key& key,
284  const T& newEntry,
285  const bool protect
286 )
287 {
288  if (!tableSize_)
289  {
290  resize(2);
291  }
292 
293  const label hashIdx = hashKeyIndex(key);
294 
295  hashedEntry* existing = 0;
296  hashedEntry* prev = 0;
297 
298  for (hashedEntry* ep = table_[hashIdx]; ep; ep = ep->next_)
299  {
300  if (key == ep->key_)
301  {
302  existing = ep;
303  break;
304  }
305  prev = ep;
306  }
307 
308  // Not found, insert it at the head
309  if (!existing)
310  {
311  table_[hashIdx] = new hashedEntry(key, table_[hashIdx], newEntry);
312  nElmts_++;
313 
314  if (double(nElmts_)/tableSize_ > 0.8 && tableSize_ < maxTableSize)
315  {
316  #ifdef FULLDEBUG
317  if (debug)
318  {
319  InfoInFunction << "Doubling table size\n";
320  }
321  #endif
322 
323  resize(2*tableSize_);
324  }
325  }
326  else if (protect)
327  {
328  // Found - but protected from overwriting
329  // this corresponds to the STL 'insert' convention
330  #ifdef FULLDEBUG
331  if (debug)
332  {
334  << "Cannot insert " << key << " already in hash table\n";
335  }
336  #endif
337  return false;
338  }
339  else
340  {
341  // Found - overwrite existing entry
342  // this corresponds to the Perl convention
343  hashedEntry* ep = new hashedEntry(key, existing->next_, newEntry);
344 
345  // Replace existing element - within list or insert at the head
346  if (prev)
347  {
348  prev->next_ = ep;
349  }
350  else
351  {
352  table_[hashIdx] = ep;
353  }
354 
355  delete existing;
356  }
357 
358  return true;
359 }
360 
361 
362 template<class T, class Key, class Hash>
364 {
365  for (const_iterator iter = ht.cbegin(); iter != ht.cend(); ++iter)
366  {
367  insert(iter.key(), *iter);
368  }
369 }
370 
371 
372 template<class T, class Key, class Hash>
374 {
375  for (const_iterator iter = ht.cbegin(); iter != ht.cend(); ++iter)
376  {
377  set(iter.key(), *iter);
378  }
379 }
380 
381 
382 template<class T, class Key, class Hash>
384 {
385  // Note: entryPtr_ is nullptr for end(), so this catches that too
386  if (entryPtr_)
387  {
388  // Search element before entryPtr_
389  hashedEntry* prev = 0;
390 
391  for
392  (
393  hashedEntry* ep = hashTable_->table_[hashIndex_];
394  ep;
395  ep = ep->next_
396  )
397  {
398  if (ep == entryPtr_)
399  {
400  break;
401  }
402  prev = ep;
403  }
404 
405  if (prev)
406  {
407  // Has an element before entryPtr - reposition to there
408  prev->next_ = entryPtr_->next_;
409  delete entryPtr_;
410  entryPtr_ = prev;
411  }
412  else
413  {
414  // entryPtr was first element on SLList
415  hashTable_->table_[hashIndex_] = entryPtr_->next_;
416  delete entryPtr_;
417 
418  // Assign any non-nullptr value so it doesn't look
419  // like end()/cend()
420  entryPtr_ = reinterpret_cast<hashedEntry*>(this);
421 
422  // Mark with special hashIndex value to signal it has been rewound.
423  // The next increment will bring it back to the present location.
424  //
425  // From the current position 'curPos', we wish to continue at
426  // prevPos='curPos-1', which we mark as markPos='-curPos-1'.
427  // The negative lets us notice it is special, the extra '-1'
428  // is needed to avoid ambiguity for position '0'.
429  // To retrieve prevPos, we would later use '-(markPos+1) - 1'
430  hashIndex_ = -hashIndex_ - 1;
431  }
432 
433  hashTable_->nElmts_--;
434 
435  return true;
436  }
437  else
438  {
439  return false;
440  }
441 }
442 
443 
444 template<class T, class Key, class Hash>
446 {
447  // NOTE: We use (const iterator&) here, but manipulate its contents anyhow.
448  // The parameter should be (iterator&), but then the compiler doesn't find
449  // it correctly and tries to call as (iterator) instead.
450  //
451  // Adjust iterator after erase
452  return const_cast<iterator&>(iter).erase();
453 }
454 
455 
456 template<class T, class Key, class Hash>
458 {
459  return erase(find(key));
460 }
461 
462 
463 template<class T, class Key, class Hash>
465 {
466  const label nTotal = nElmts_;
467  label count = 0;
468 
469  // Remove listed keys from this table - terminates early if possible
470  for (label keyI = 0; count < nTotal && keyI < keys.size(); ++keyI)
471  {
472  if (erase(keys[keyI]))
473  {
474  count++;
475  }
476  }
477 
478  return count;
479 }
480 
481 
482 template<class T, class Key, class Hash>
483 template<class AnyType, class AnyHash>
485 (
487 )
488 {
489  label count = 0;
490 
491  // Remove rhs keys from this table - terminates early if possible
492  // Could optimise depending on which hash is smaller ...
493  for (iterator iter = begin(); iter != end(); ++iter)
494  {
495  if (rhs.found(iter.key()) && erase(iter))
496  {
497  count++;
498  }
499  }
500 
501  return count;
502 }
503 
504 
505 template<class T, class Key, class Hash>
507 {
508  label newSize = HashTableCore::canonicalSize(sz);
509 
510  if (newSize == tableSize_)
511  {
512  #ifdef FULLDEBUG
513  if (debug)
514  {
515  InfoInFunction << "New table size == old table size\n";
516  }
517  #endif
518 
519  return;
520  }
521 
522  HashTable<T, Key, Hash>* tmpTable = new HashTable<T, Key, Hash>(newSize);
523 
524  for (const_iterator iter = cbegin(); iter != cend(); ++iter)
525  {
526  tmpTable->insert(iter.key(), *iter);
527  }
528 
529  label oldSize = tableSize_;
530  tableSize_ = tmpTable->tableSize_;
531  tmpTable->tableSize_ = oldSize;
532 
533  hashedEntry** oldTable = table_;
534  table_ = tmpTable->table_;
535  tmpTable->table_ = oldTable;
536 
537  delete tmpTable;
538 }
539 
540 
541 template<class T, class Key, class Hash>
543 {
544  if (nElmts_)
545  {
546  for (label hashIdx = 0; hashIdx < tableSize_; hashIdx++)
547  {
548  if (table_[hashIdx])
549  {
550  hashedEntry* ep = table_[hashIdx];
551  while (hashedEntry* next = ep->next_)
552  {
553  delete ep;
554  ep = next;
555  }
556  delete ep;
557  table_[hashIdx] = 0;
558  }
559  }
560  nElmts_ = 0;
561  }
562 }
563 
564 
565 template<class T, class Key, class Hash>
567 {
568  clear();
569  resize(0);
570 }
571 
572 
573 template<class T, class Key, class Hash>
575 {
576  const label newSize = HashTableCore::canonicalSize(nElmts_);
577 
578  if (newSize < tableSize_)
579  {
580  // Avoid having the table disappear on us
581  resize(newSize ? newSize : 2);
582  }
583 }
584 
585 
586 template<class T, class Key, class Hash>
588 {
589  // As per the Destructor
590  if (table_)
591  {
592  clear();
593  delete[] table_;
594  }
595 
596  tableSize_ = ht.tableSize_;
597  ht.tableSize_ = 0;
598 
599  table_ = ht.table_;
600  ht.table_ = nullptr;
601 
602  nElmts_ = ht.nElmts_;
603  ht.nElmts_ = 0;
604 }
605 
606 
607 // * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
608 
609 template<class T, class Key, class Hash>
611 (
612  const HashTable<T, Key, Hash>& rhs
613 )
614 {
615  // Check for assignment to self
616  if (this == &rhs)
617  {
619  << "attempted assignment to self"
620  << abort(FatalError);
621  }
622 
623  // Could be zero-sized from a previous transfer()
624  if (!tableSize_)
625  {
626  resize(rhs.tableSize_);
627  }
628  else
629  {
630  clear();
631  }
632 
633  for (const_iterator iter = rhs.cbegin(); iter != rhs.cend(); ++iter)
634  {
635  insert(iter.key(), *iter);
636  }
637 }
638 
639 
640 template<class T, class Key, class Hash>
642 (
644 )
645 {
646  // Check for assignment to self
647  if (this == &rhs)
648  {
650  << "attempted assignment to self"
651  << abort(FatalError);
652  }
653 
654  transfer(rhs);
655 }
656 
657 
658 template<class T, class Key, class Hash>
660 (
661  std::initializer_list<Tuple2<Key, T>> lst
662 )
663 {
664  // Could be zero-sized from a previous transfer()
665  if (!tableSize_)
666  {
667  resize(lst.size());
668  }
669  else
670  {
671  clear();
672  }
673 
674  for (const Tuple2<Key, T>& pair : lst)
675  {
676  insert(pair.first(), pair.second());
677  }
678 }
679 
680 
681 template<class T, class Key, class Hash>
683 (
684  const HashTable<T, Key, Hash>& rhs
685 ) const
686 {
687  // Sizes (number of keys) must match
688  if (size() != rhs.size())
689  {
690  return false;
691  }
692 
693  for (const_iterator iter = rhs.cbegin(); iter != rhs.cend(); ++iter)
694  {
695  const_iterator fnd = find(iter.key());
696 
697  if (fnd == cend() || fnd() != iter())
698  {
699  return false;
700  }
701  }
702 
703  return true;
704 }
705 
706 
707 template<class T, class Key, class Hash>
709 (
710  const HashTable<T, Key, Hash>& rhs
711 ) const
712 {
713  return !(operator==(rhs));
714 }
715 
716 
717 // * * * * * * * * * * * * * * * Friend Operators * * * * * * * * * * * * * //
718 
719 #include "HashTableIO.C"
720 
721 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
722 
723 #endif
724 
725 // ************************************************************************* //
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
An STL-conforming const_iterator.
Definition: HashTable.H:498
bool erase()
Erase the HashTable element at the current position.
Definition: HashTable.C:383
An STL-conforming iterator.
Definition: HashTable.H:443
An STL-conforming hash table.
Definition: HashTable.H:127
List< const_iterator > sorted() const
Return a sorted list of constant iterators.
Definition: HashTable.C:253
List< Key > sortedToc() const
Return the table of contents as a sorted list.
Definition: HashTable.C:242
bool erase(const iterator &)
Erase a hashedEntry specified by given iterator.
Definition: HashTable.C:445
void shrink()
Shrink the allocated table to approx. twice number of elements.
Definition: HashTable.C:574
List< Key > toc() const
Return the table of contents.
Definition: HashTable.C:227
void transfer(HashTable< T, Key, Hash > &)
Transfer the contents of the argument table into this table.
Definition: HashTable.C:587
const_iterator cbegin() const
const_iterator set to the beginning of the HashTable
Definition: HashTableI.H:506
bool insert(const Key &, const T &newElmt)
Insert a new hashedEntry.
Definition: HashTableI.H:80
iterator find(const Key &)
Find and return an iterator set at the hashedEntry.
Definition: HashTable.C:167
void clearStorage()
Clear the table entries and the table itself.
Definition: HashTable.C:566
bool found(const Key &) const
Return true if hashedEntry is found in table.
Definition: HashTable.C:138
~HashTable()
Destructor.
Definition: HashTable.C:125
void clear()
Clear all entries from table.
Definition: HashTable.C:542
HashTable(const label size=128)
Construct given initial table size.
Definition: HashTable.C:36
void resize(const label newSize)
Resize the hash table for efficiency.
Definition: HashTable.C:506
Hash function class for primitives. All non-primitives used to hash entries on hash tables likely nee...
Definition: Hash.H:53
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: List.H:91
A 2-tuple for storing two objects of different types.
Definition: Tuple2.H:66
A 1D vector of objects of type <T>, where the size of the vector is known and can be used for subscri...
Definition: UList.H:74
label size() const
Return the number of elements in the UList.
Definition: UListI.H:311
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:334
volScalarField & b
Definition: createFields.H:25
tUEqn clear()
srcOptions erase("case")
#define InfoInFunction
Report an information message using Foam::Info.
void insert(const scalar, DynamicList< floatScalar > &)
Append scalar to given DynamicList.
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
tmp< fvMatrix< Type > > operator==(const fvMatrix< Type > &, const fvMatrix< Type > &)
errorManip< error > abort(error &err)
Definition: errorManip.H:131
void sort(UList< T > &)
Definition: UList.C:115
error FatalError
label count(const ListType &l, typename ListType::const_reference x)
Count the number of occurrences of a value in a list.
static const char nl
Definition: Ostream.H:266
void T(FieldField< Field, Type > &f1, const FieldField< Field, Type > &f2)
triSurfaceToAgglom resize(surfacesMesh.size())
Template-invariant bits for HashTable.
Definition: HashTable.H:83
static label canonicalSize(const label)
Return a canonical (power-of-two) size.
Definition: HashTableCore.C:47
static iteratorEnd cend()
iteratorEnd set to beyond the end of any HashTable
Definition: HashTable.H:106