HashTable.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | Copyright (C) 2011-2016 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 
32 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
33 
34 template<class T, class Key, class Hash>
36 :
37  HashTableCore(),
38  nElmts_(0),
39  tableSize_(HashTableCore::canonicalSize(size)),
40  table_(NULL)
41 {
42  if (tableSize_)
43  {
44  table_ = new hashedEntry*[tableSize_];
45 
46  for (label hashIdx = 0; hashIdx < tableSize_; hashIdx++)
47  {
48  table_[hashIdx] = 0;
49  }
50  }
51 }
52 
53 
54 template<class T, class Key, class Hash>
56 :
57  HashTableCore(),
58  nElmts_(0),
59  tableSize_(ht.tableSize_),
60  table_(NULL)
61 {
62  if (tableSize_)
63  {
64  table_ = new hashedEntry*[tableSize_];
65 
66  for (label hashIdx = 0; hashIdx < tableSize_; hashIdx++)
67  {
68  table_[hashIdx] = 0;
69  }
70 
71  for (const_iterator iter = ht.cbegin(); iter != ht.cend(); ++iter)
72  {
73  insert(iter.key(), *iter);
74  }
75  }
76 }
77 
78 template<class T, class Key, class Hash>
80 (
81  const Xfer<HashTable<T, Key, Hash>>& ht
82 )
83 :
84  HashTableCore(),
85  nElmts_(0),
86  tableSize_(0),
87  table_(NULL)
88 {
89  transfer(ht());
90 }
91 
92 
93 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
94 
95 template<class T, class Key, class Hash>
97 {
98  if (table_)
99  {
100  clear();
101  delete[] table_;
102  }
103 }
104 
105 
106 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
107 
108 template<class T, class Key, class Hash>
109 bool Foam::HashTable<T, Key, Hash>::found(const Key& key) const
110 {
111  if (nElmts_)
112  {
113  const label hashIdx = hashKeyIndex(key);
114 
115  for (hashedEntry* ep = table_[hashIdx]; ep; ep = ep->next_)
116  {
117  if (key == ep->key_)
118  {
119  return true;
120  }
121  }
122  }
123 
124  #ifdef FULLDEBUG
125  if (debug)
126  {
127  InfoInFunction << "Entry " << key << " not found in hash table\n";
128  }
129  #endif
130 
131  return false;
132 }
133 
134 
135 template<class T, class Key, class Hash>
138 (
139  const Key& key
140 )
141 {
142  if (nElmts_)
143  {
144  const label hashIdx = hashKeyIndex(key);
145 
146  for (hashedEntry* ep = table_[hashIdx]; ep; ep = ep->next_)
147  {
148  if (key == ep->key_)
149  {
150  return iterator(this, ep, hashIdx);
151  }
152  }
153  }
154 
155  #ifdef FULLDEBUG
156  if (debug)
157  {
158  InfoInFunction << "Entry " << key << " not found in hash table\n";
159  }
160  #endif
161 
162  return iterator();
163 }
164 
165 
166 template<class T, class Key, class Hash>
169 (
170  const Key& key
171 ) const
172 {
173  if (nElmts_)
174  {
175  const label hashIdx = hashKeyIndex(key);
176 
177  for (hashedEntry* ep = table_[hashIdx]; ep; ep = ep->next_)
178  {
179  if (key == ep->key_)
180  {
181  return const_iterator(this, ep, hashIdx);
182  }
183  }
184  }
185 
186  #ifdef FULLDEBUG
187  if (debug)
188  {
189  InfoInFunction << "Entry " << key << " not found in hash table\n";
190  }
191  #endif
192 
193  return const_iterator();
194 }
195 
196 
197 template<class T, class Key, class Hash>
199 {
200  List<Key> keys(nElmts_);
201  label keyI = 0;
202 
203  for (const_iterator iter = cbegin(); iter != cend(); ++iter)
204  {
205  keys[keyI++] = iter.key();
206  }
207 
208  return keys;
209 }
210 
211 
212 template<class T, class Key, class Hash>
214 {
215  List<Key> sortedLst = this->toc();
216  sort(sortedLst);
217 
218  return sortedLst;
219 }
220 
221 
222 template<class T, class Key, class Hash>
224 (
225  const Key& key,
226  const T& newEntry,
227  const bool protect
228 )
229 {
230  if (!tableSize_)
231  {
232  resize(2);
233  }
234 
235  const label hashIdx = hashKeyIndex(key);
236 
237  hashedEntry* existing = 0;
238  hashedEntry* prev = 0;
239 
240  for (hashedEntry* ep = table_[hashIdx]; ep; ep = ep->next_)
241  {
242  if (key == ep->key_)
243  {
244  existing = ep;
245  break;
246  }
247  prev = ep;
248  }
249 
250  // Not found, insert it at the head
251  if (!existing)
252  {
253  table_[hashIdx] = new hashedEntry(key, table_[hashIdx], newEntry);
254  nElmts_++;
255 
256  if (double(nElmts_)/tableSize_ > 0.8 && tableSize_ < maxTableSize)
257  {
258  #ifdef FULLDEBUG
259  if (debug)
260  {
261  InfoInFunction << "Doubling table size\n";
262  }
263  #endif
264 
265  resize(2*tableSize_);
266  }
267  }
268  else if (protect)
269  {
270  // Found - but protected from overwriting
271  // this corresponds to the STL 'insert' convention
272  #ifdef FULLDEBUG
273  if (debug)
274  {
276  << "Cannot insert " << key << " already in hash table\n";
277  }
278  #endif
279  return false;
280  }
281  else
282  {
283  // Found - overwrite existing entry
284  // this corresponds to the Perl convention
285  hashedEntry* ep = new hashedEntry(key, existing->next_, newEntry);
286 
287  // Replace existing element - within list or insert at the head
288  if (prev)
289  {
290  prev->next_ = ep;
291  }
292  else
293  {
294  table_[hashIdx] = ep;
295  }
296 
297  delete existing;
298  }
299 
300  return true;
301 }
302 
303 
304 template<class T, class Key, class Hash>
306 {
307  // Note: entryPtr_ is NULL for end(), so this catches that too
308  if (entryPtr_)
309  {
310  // Search element before entryPtr_
311  hashedEntry* prev = 0;
312 
313  for
314  (
315  hashedEntry* ep = hashTable_->table_[hashIndex_];
316  ep;
317  ep = ep->next_
318  )
319  {
320  if (ep == entryPtr_)
321  {
322  break;
323  }
324  prev = ep;
325  }
326 
327  if (prev)
328  {
329  // Has an element before entryPtr - reposition to there
330  prev->next_ = entryPtr_->next_;
331  delete entryPtr_;
332  entryPtr_ = prev;
333  }
334  else
335  {
336  // entryPtr was first element on SLList
337  hashTable_->table_[hashIndex_] = entryPtr_->next_;
338  delete entryPtr_;
339 
340  // Assign any non-NULL pointer value so it doesn't look
341  // like end()/cend()
342  entryPtr_ = reinterpret_cast<hashedEntry*>(this);
343 
344  // Mark with special hashIndex value to signal it has been rewound.
345  // The next increment will bring it back to the present location.
346  //
347  // From the current position 'curPos', we wish to continue at
348  // prevPos='curPos-1', which we mark as markPos='-curPos-1'.
349  // The negative lets us notice it is special, the extra '-1'
350  // is needed to avoid ambiguity for position '0'.
351  // To retrieve prevPos, we would later use '-(markPos+1) - 1'
352  hashIndex_ = -hashIndex_ - 1;
353  }
354 
355  hashTable_->nElmts_--;
356 
357  return true;
358  }
359  else
360  {
361  return false;
362  }
363 }
364 
365 
366 template<class T, class Key, class Hash>
368 {
369  // NOTE: We use (const iterator&) here, but manipulate its contents anyhow.
370  // The parameter should be (iterator&), but then the compiler doesn't find
371  // it correctly and tries to call as (iterator) instead.
372  //
373  // Adjust iterator after erase
374  return const_cast<iterator&>(iter).erase();
375 }
376 
377 
378 template<class T, class Key, class Hash>
380 {
381  return erase(find(key));
382 }
383 
384 
385 template<class T, class Key, class Hash>
387 {
388  const label nTotal = nElmts_;
389  label count = 0;
390 
391  // Remove listed keys from this table - terminates early if possible
392  for (label keyI = 0; count < nTotal && keyI < keys.size(); ++keyI)
393  {
394  if (erase(keys[keyI]))
395  {
396  count++;
397  }
398  }
399 
400  return count;
401 }
402 
403 
404 template<class T, class Key, class Hash>
405 template<class AnyType, class AnyHash>
407 (
409 )
410 {
411  label count = 0;
412 
413  // Remove rhs keys from this table - terminates early if possible
414  // Could optimize depending on which hash is smaller ...
415  for (iterator iter = begin(); iter != end(); ++iter)
416  {
417  if (rhs.found(iter.key()) && erase(iter))
418  {
419  count++;
420  }
421  }
422 
423  return count;
424 }
425 
426 
427 template<class T, class Key, class Hash>
429 {
430  label newSize = HashTableCore::canonicalSize(sz);
431 
432  if (newSize == tableSize_)
433  {
434  #ifdef FULLDEBUG
435  if (debug)
436  {
437  InfoInFunction << "New table size == old table size\n";
438  }
439  #endif
440 
441  return;
442  }
443 
444  HashTable<T, Key, Hash>* tmpTable = new HashTable<T, Key, Hash>(newSize);
445 
446  for (const_iterator iter = cbegin(); iter != cend(); ++iter)
447  {
448  tmpTable->insert(iter.key(), *iter);
449  }
450 
451  label oldSize = tableSize_;
452  tableSize_ = tmpTable->tableSize_;
453  tmpTable->tableSize_ = oldSize;
454 
455  hashedEntry** oldTable = table_;
456  table_ = tmpTable->table_;
457  tmpTable->table_ = oldTable;
458 
459  delete tmpTable;
460 }
461 
462 
463 template<class T, class Key, class Hash>
465 {
466  if (nElmts_)
467  {
468  for (label hashIdx = 0; hashIdx < tableSize_; hashIdx++)
469  {
470  if (table_[hashIdx])
471  {
472  hashedEntry* ep = table_[hashIdx];
473  while (hashedEntry* next = ep->next_)
474  {
475  delete ep;
476  ep = next;
477  }
478  delete ep;
479  table_[hashIdx] = 0;
480  }
481  }
482  nElmts_ = 0;
483  }
484 }
485 
486 
487 template<class T, class Key, class Hash>
489 {
490  clear();
491  resize(0);
492 }
493 
494 
495 template<class T, class Key, class Hash>
497 {
498  const label newSize = HashTableCore::canonicalSize(nElmts_);
499 
500  if (newSize < tableSize_)
501  {
502  // Avoid having the table disappear on us
503  resize(newSize ? newSize : 2);
504  }
505 }
506 
507 
508 template<class T, class Key, class Hash>
510 {
511  // As per the Destructor
512  if (table_)
513  {
514  clear();
515  delete[] table_;
516  }
517 
518  tableSize_ = ht.tableSize_;
519  ht.tableSize_ = 0;
520 
521  table_ = ht.table_;
522  ht.table_ = NULL;
523 
524  nElmts_ = ht.nElmts_;
525  ht.nElmts_ = 0;
526 }
527 
528 
529 // * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
530 
531 template<class T, class Key, class Hash>
532 void Foam::HashTable<T, Key, Hash>::operator=
533 (
534  const HashTable<T, Key, Hash>& rhs
535 )
536 {
537  // Check for assignment to self
538  if (this == &rhs)
539  {
541  << "attempted assignment to self"
542  << abort(FatalError);
543  }
544 
545  // Could be zero-sized from a previous transfer()
546  if (!tableSize_)
547  {
548  resize(rhs.tableSize_);
549  }
550  else
551  {
552  clear();
553  }
554 
555  for (const_iterator iter = rhs.cbegin(); iter != rhs.cend(); ++iter)
556  {
557  insert(iter.key(), *iter);
558  }
559 }
560 
561 
562 template<class T, class Key, class Hash>
563 bool Foam::HashTable<T, Key, Hash>::operator==
564 (
565  const HashTable<T, Key, Hash>& rhs
566 ) const
567 {
568  // Sizes (number of keys) must match
569  if (size() != rhs.size())
570  {
571  return false;
572  }
573 
574  for (const_iterator iter = rhs.cbegin(); iter != rhs.cend(); ++iter)
575  {
576  const_iterator fnd = find(iter.key());
577 
578  if (fnd == cend() || fnd() != iter())
579  {
580  return false;
581  }
582  }
583 
584  return true;
585 }
586 
587 
588 template<class T, class Key, class Hash>
589 bool Foam::HashTable<T, Key, Hash>::operator!=
590 (
591  const HashTable<T, Key, Hash>& rhs
592 ) const
593 {
594  return !(operator==(rhs));
595 }
596 
597 
598 // * * * * * * * * * * * * * * * Friend Operators * * * * * * * * * * * * * //
599 
600 #include "HashTableIO.C"
601 
602 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
603 
604 #endif
605 
606 // ************************************************************************* //
A simple container for copying or transferring objects of type <T>.
Definition: Xfer.H:85
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
An STL-conforming const_iterator.
Definition: HashTable.H:470
static iteratorEnd end()
iteratorEnd set to beyond the end of any HashTable
Definition: HashTable.H:106
error FatalError
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:319
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: HashTable.H:59
HashTable(const label size=128)
Construct given initial table size.
Definition: HashTable.C:35
label size() const
Return number of elements in table.
Definition: HashTableI.H:65
void transfer(HashTable< T, Key, Hash > &)
Transfer the contents of the argument table into this table.
Definition: HashTable.C:509
bool erase(const iterator &)
Erase a hashedEntry specified by given iterator.
Definition: HashTable.C:367
~HashTable()
Destructor.
Definition: HashTable.C:96
HashTableCore()
Construct null.
Definition: HashTable.H:85
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:138
An STL-conforming iterator.
Definition: HashTable.H:415
bool operator==(const HashTable< T, Key, Hash > &) const
Equality. Hash tables are equal if the keys and values are equal.
Definition: HashTable.C:564
friend class const_iterator
Declare friendship with the const_iterator.
Definition: HashTable.H:192
void clear()
Clear all entries from table.
Definition: HashTable.C:464
void sort(UList< T > &)
Definition: UList.C:115
static const label maxTableSize
Maximum allowable table size.
Definition: HashTable.H:82
iterator begin()
Iterator set to the beginning of the HashTable.
Definition: HashTableI.H:419
An STL-conforming hash table.
Definition: HashTable.H:61
errorManip< error > abort(error &err)
Definition: errorManip.H:131
List< Key > toc() const
Return the table of contents.
Definition: HashTable.C:198
A 1D vector of objects of type <T>, where the size of the vector is known and can be used for subscri...
Definition: HashTable.H:60
bool found(const Key &) const
Return true if hashedEntry is found in table.
Definition: HashTable.C:109
void T(FieldField< Field, Type > &f1, const FieldField< Field, Type > &f2)
label size() const
Return the number of elements in the UList.
Definition: UListI.H:299
Template-invariant bits for HashTable.
Definition: HashTable.H:76
static label canonicalSize(const label)
Return a canonical (power-of-two) size.
Definition: HashTableCore.C:47
List< Key > sortedToc() const
Return the table of contents as a sorted list.
Definition: HashTable.C:213
void resize(const label newSize)
Resize the hash table for efficiency.
Definition: HashTable.C:428
friend class iterator
Declare friendship with the iterator.
Definition: HashTable.H:189
bool erase()
Erase the HashTable element at the current position.
Definition: HashTable.C:305
static iteratorEnd cend()
iteratorEnd set to beyond the end of any HashTable
Definition: HashTable.H:100
const_iterator cbegin() const
const_iterator set to the beginning of the HashTable
Definition: HashTableI.H:514
void shrink()
Shrink the allocated table to approx. twice number of elements.
Definition: HashTable.C:496
void clearStorage()
Clear the table entries and the table itself.
Definition: HashTable.C:488
#define InfoInFunction
Report an information message using Foam::Info.