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 | www.openfoam.com
6 \\/ M anipulation |
7-------------------------------------------------------------------------------
8 Copyright (C) 2011-2016 OpenFOAM Foundation
9 Copyright (C) 2017-2022 OpenCFD Ltd.
10-------------------------------------------------------------------------------
11License
12 This file is part of OpenFOAM.
13
14 OpenFOAM is free software: you can redistribute it and/or modify it
15 under the terms of the GNU General Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26
27\*---------------------------------------------------------------------------*/
28
29#ifndef Foam_HashTable_C
30#define Foam_HashTable_C
31
32#include "HashTable.H"
33#include "List.H"
34#include "FixedList.H"
35#include "UPtrList.H"
36
37// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
38
39template<class T, class Key, class Hash>
41:
42 HashTable<T, Key, Hash>(128)
43{}
44
45
46template<class T, class Key, class Hash>
48:
50 size_(0),
51 capacity_(HashTableCore::canonicalSize(size)),
52 table_(nullptr)
53{
54 if (capacity_)
55 {
56 table_ = new node_type*[capacity_];
57 for (label i=0; i < capacity_; ++i)
58 {
59 table_[i] = nullptr;
60 }
61 }
62}
63
64
65template<class T, class Key, class Hash>
67:
68 HashTable<T, Key, Hash>(ht.capacity_)
69{
70 for (const_iterator iter = ht.cbegin(); iter != ht.cend(); ++iter)
71 {
72 insert(iter.key(), iter.val());
73 }
74}
75
76
77template<class T, class Key, class Hash>
78Foam::HashTable<T, Key, Hash>::HashTable(HashTable<T, Key, Hash>&& rhs)
79:
80 HashTableCore(),
81 size_(rhs.size_),
82 capacity_(rhs.capacity_),
83 table_(rhs.table_)
84{
85 rhs.size_ = 0;
86 rhs.capacity_ = 0;
87 rhs.table_ = nullptr;
88}
89
90
91template<class T, class Key, class Hash>
93(
94 std::initializer_list<std::pair<Key, T>> list
95)
96:
97 HashTable<T, Key, Hash>(2*list.size())
98{
99 for (const auto& keyval : list)
100 {
101 set(keyval.first, keyval.second);
102 }
103}
104
105
106// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
107
108template<class T, class Key, class Hash>
110{
111 if (table_)
112 {
113 clear();
114 delete[] table_;
115 }
116}
117
118
119// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
120
121template<class T, class Key, class Hash>
123{
124 List<Key> list(size_);
125 label count = 0;
126
127 for (const_iterator iter = cbegin(); iter != cend(); ++iter)
128 {
129 list[count++] = iter.key();
130 }
131
132 return list;
133}
134
135
136template<class T, class Key, class Hash>
138{
139 List<Key> list(this->toc());
140 Foam::sort(list);
141
142 return list;
143}
144
145
146template<class T, class Key, class Hash>
147template<class Compare>
149(
150 const Compare& comp
151) const
152{
153 List<Key> list(this->toc());
154 Foam::sort(list, comp);
155
156 return list;
157}
158
159
160template<class T, class Key, class Hash>
163{
164 UPtrList<const node_type> result(size_);
165
166 label count = 0;
167
168 for (const_iterator iter = cbegin(); iter != cend(); ++iter)
169 {
170 result.set(count++, iter.node());
171 }
172
173 Foam::sort(result);
174
175 return result;
176}
177
178
179template<class T, class Key, class Hash>
182{
183 return csorted();
184}
185
186
187template<class T, class Key, class Hash>
190{
191 UPtrList<node_type> result(size_);
192
193 label count = 0;
194
195 for (iterator iter = begin(); iter != end(); ++iter)
196 {
197 result.set(count++, iter.node());
198 }
199
200 Foam::sort(result);
201
202 return result;
203}
204
205
206
207template<class T, class Key, class Hash>
208template<class UnaryPredicate>
210(
211 const UnaryPredicate& pred,
212 const bool invert
213) const
214{
215 List<Key> list(size_);
216 label count = 0;
217
218 for (const_iterator iter = cbegin(); iter != cend(); ++iter)
219 {
220 if ((pred(iter.key()) ? !invert : invert))
221 {
222 list[count++] = iter.key();
223 }
224 }
225
226 list.resize(count);
227 Foam::sort(list);
228
229 return list;
230}
231
232
233template<class T, class Key, class Hash>
234template<class UnaryPredicate>
237 const UnaryPredicate& pred,
238 const bool invert
239) const
240{
241 List<Key> list(size_);
242 label count = 0;
243
244 for (const_iterator iter = cbegin(); iter != cend(); ++iter)
245 {
246 if ((pred(iter.val()) ? !invert : invert))
247 {
248 list[count++] = iter.key();
250 }
251
252 list.resize(count);
254
255 return list;
256}
257
258
259template<class T, class Key, class Hash>
260template<class BinaryPredicate>
262(
263 const BinaryPredicate& pred,
264 const bool invert
265) const
266{
267 List<Key> list(size_);
268 label count = 0;
269
270 for (const_iterator iter = cbegin(); iter != cend(); ++iter)
271 {
272 if ((pred(iter.key(), iter.val()) ? !invert : invert))
273 {
274 list[count++] = iter.key();
275 }
276 }
277
278 list.resize(count);
279 Foam::sort(list);
280
281 return list;
282}
283
284
285template<class T, class Key, class Hash>
286template<class UnaryPredicate>
288(
289 const UnaryPredicate& pred,
290 const bool invert
291) const
292{
293 label count = 0;
294
295 for (const_iterator iter = cbegin(); iter != cend(); ++iter)
296 {
297 if ((pred(iter.key()) ? !invert : invert))
298 {
299 ++count;
301 }
302
303 return count;
304}
305
306
307template<class T, class Key, class Hash>
308template<class UnaryPredicate>
310(
311 const UnaryPredicate& pred,
312 const bool invert
313) const
314{
315 label count = 0;
316
317 for (const_iterator iter = cbegin(); iter != cend(); ++iter)
318 {
319 if ((pred(iter.val()) ? !invert : invert))
320 {
321 ++count;
322 }
323 }
324
325 return count;
326}
327
328
329template<class T, class Key, class Hash>
330template<class BinaryPredicate>
332(
333 const BinaryPredicate& pred,
334 const bool invert
335) const
336{
337 label count = 0;
338
339 for (const_iterator iter = cbegin(); iter != cend(); ++iter)
340 {
341 if ((pred(iter.key(), iter.val()) ? !invert : invert))
342 {
343 ++count;
344 }
345 }
346
347 return count;
348}
350
351template<class T, class Key, class Hash>
352template<class... Args>
355 const bool overwrite,
356 const Key& key,
357 Args&&... args
358)
360 if (!capacity_)
361 {
362 resize(2);
363 }
364
365 const label index = hashKeyIndex(key);
366
367 node_type* curr = nullptr;
368 node_type* prev = nullptr;
369
370 for (node_type* ep = table_[index]; ep; ep = ep->next_)
371 {
372 if (key == ep->key())
373 {
374 curr = ep;
375 break;
376 }
377 prev = ep;
378 }
379
380 if (!curr)
381 {
382 // Not found, insert it at the head
383 table_[index] =
384 new node_type(table_[index], key, std::forward<Args>(args)...);
385
386 ++size_;
387 if (double(size_)/capacity_ > 0.8 && capacity_ < maxTableSize)
388 {
389 #ifdef FULLDEBUG
390 DebugInFunction << "Doubling table size\n";
391 #endif
392
393 resize(2*capacity_);
394 }
395 }
396 else if (overwrite)
397 {
398 // Overwrite current entry (Perl convention).
399
400 // Can skip if the value is not stored anyhow (Eg, HashSet)
401 // - this avoids a useless delete/new
402 if (!node_type::stores_value())
403 {
404 return true;
405 }
406
407 node_type* ep = curr->next_; // next in the linked list
408
409 // In some cases the delete/new could be avoided in favour of move
410 // assignment, but cannot be certain that all objects support this
411 // or that it behaves the same as a copy construct.
412
413 delete curr;
414 ep = new node_type(ep, key, std::forward<Args>(args)...);
415
416 // Replace current element - within list or insert at the head
417 if (prev)
418 {
419 prev->next_ = ep;
420 }
421 else
422 {
423 table_[index] = ep;
424 }
425 }
426 else
427 {
428 // Do not overwrite existing entry (STL 'insert' convention)
429 #ifdef FULLDEBUG
430 DebugInFunction << "Not inserting " << key << ": already in table\n";
431 #endif
432 return false;
433 }
434
435 return true;
436}
437
438
439template<class T, class Key, class Hash>
441{
442 // NOTE: we use (const iterator&) here, but treat its contents as mutable.
443 //
444 // The parameter should be (iterator&), but then the compiler doesn't find
445 // it correctly and tries to call as (iterator) instead.
446
447 iterator& it = const_cast<iterator&>(iter);
448
449 return iterator_erase(it.entry_, it.index_);
450}
451
453template<class T, class Key, class Hash>
455{
456 iterator iter(find(key));
457 return erase(iter);
458}
459
460
461template<class T, class Key, class Hash>
462template<class InputIter>
464(
465 InputIter first,
466 InputIter last
467)
468{
469 label changed = 0;
470
471 for
472 (
473 const label nTotal = this->size();
474 changed < nTotal && first != last; // terminate early
475 ++first
476 )
477 {
478 if (this->erase(*first))
479 {
480 ++changed;
481 }
482 }
483
484 return changed;
485}
486
487
488template<class T, class Key, class Hash>
490(
491 std::initializer_list<Key> keys
492)
493{
494 return erase(keys.begin(), keys.end());
495}
496
497
498template<class T, class Key, class Hash>
499template<unsigned N>
501(
502 const FixedList<Key, N>& keys
503)
504{
505 return erase(keys.cbegin(), keys.cend());
506}
507
508
509template<class T, class Key, class Hash>
511(
512 const UList<Key>& keys
513)
514{
515 return erase(keys.cbegin(), keys.cend());
516}
517
518
519template<class T, class Key, class Hash>
520template<class AnyType, class AnyHash>
522(
524)
525{
526 const label nTotal = this->size();
527 label changed = 0;
528
529 if (other.size() <= nTotal)
530 {
531 // The other is smaller/same-size, use its keys for removal
533 for
534 (
535 auto iter = other.cbegin();
536 changed < nTotal && iter != other.cend(); // Terminate early
537 ++iter
538 )
540 if (erase(iter.key()))
541 {
542 ++changed;
543 }
544 }
546 else
547 {
548 // We are smaller: remove if found in the other hash
549 for
550 (
551 iterator iter = begin();
552 changed < nTotal && iter != end(); // Terminate early
553 ++iter
554 )
555 {
556 if (other.found(iter.key()) && erase(iter))
557 {
558 ++changed;
559 }
560 }
561 }
562
563 return changed;
564}
565
566
567template<class T, class Key, class Hash>
568template<class AnyType, class AnyHash>
570(
572)
573{
574 const label nTotal = this->size();
575 label changed = 0;
577 if (other.empty())
578 {
579 // Trivial case
580 changed = nTotal;
581 this->clear();
583 else
584 {
585 // Inverted logic: remove if *not* found in the other hash
586
587 for (iterator iter = begin(); iter != end(); ++iter)
588 {
589 if (!other.found(iter.key()) && erase(iter))
590 {
591 ++changed;
592 }
593 }
594 }
595
596 return changed;
597}
598
599
600template<class T, class Key, class Hash>
602{
603 const label newCapacity = HashTableCore::canonicalSize(sz);
604 const label oldCapacity = capacity_;
605
606 if (newCapacity == oldCapacity)
607 {
608 #ifdef FULLDEBUG
609 DebugInFunction << "New table size == old table size\n";
610 #endif
611
612 return;
613 }
614 else if (!newCapacity)
615 {
616 // Special treatment for resize(0)
617 if (size_)
618 {
620 << "HashTable contains " << size_ << " cannot resize(0)" << nl;
621 }
622 else
623 {
624 if (table_)
625 {
626 delete[] table_;
627 capacity_ = 0;
628 }
629
630 table_ = nullptr;
631 }
632
633 return;
634 }
635
636 // Swap primary table entries: size_ is left untouched
637
638 auto oldTable = table_;
639 capacity_ = newCapacity;
640
641 table_ = new node_type*[capacity_];
642 for (label i=0; i < capacity_; ++i)
643 {
644 table_[i] = nullptr;
645 }
646
647 // Move to new table[] but with new chaining.
648
649 label nMove = size_; // Allow early completion
650 for (label i=0; nMove && i < oldCapacity; ++i)
651 {
652 for (node_type* ep = oldTable[i]; ep; /*nil*/)
653 {
654 node_type* next = ep->next_;
655
656 // Move to new location
657 {
658 const label newIdx = hashKeyIndex(ep->key());
659
660 ep->next_ = table_[newIdx]; // add to head
661 table_[newIdx] = ep;
662 }
663
664 ep = next; // continue in the linked-list
665 --nMove; // note any early completion
666 }
667 oldTable[i] = nullptr;
668 }
669
670 if (oldTable)
671 {
672 delete[] oldTable;
673 }
674}
675
676
677template<class T, class Key, class Hash>
679{
680 for (label i=0; size_ && i<capacity_; ++i)
681 {
682 for (node_type* ep = table_[i]; ep; /*nil*/)
683 {
684 node_type* next = ep->next_;
685
686 delete ep;
687
688 ep = next; // continue in the linked-list
689 --size_; // note any early completion
690 }
691 table_[i] = nullptr;
692 }
693}
694
695
696template<class T, class Key, class Hash>
698{
699 clear();
700 resize(0);
701}
702
703
704template<class T, class Key, class Hash>
706{
707 if (this == &rhs)
708 {
709 return; // Self-swap is a no-op
710 }
711
712 std::swap(size_, rhs.size_);
713 std::swap(capacity_, rhs.capacity_);
714 std::swap(table_, rhs.table_);
715}
716
717
718template<class T, class Key, class Hash>
720{
721 if (this == &rhs)
722 {
723 return; // Self-assignment is a no-op
724 }
725
726 clear();
727 swap(rhs);
728}
729
730
731template<class T, class Key, class Hash>
732template<class UnaryPredicate>
734(
735 const UnaryPredicate& pred,
736 const bool pruning
737)
738{
739 label changed = 0;
740
741 for (iterator iter = begin(); iter != end(); ++iter)
742 {
743 // Matches? either prune (pruning) or keep (!pruning)
744 if
745 (
746 (pred(iter.key()) ? pruning : !pruning)
747 && erase(iter)
748 )
749 {
750 ++changed;
751 }
752 }
753
754 return changed;
755}
756
757
758template<class T, class Key, class Hash>
759template<class UnaryPredicate>
761(
762 const UnaryPredicate& pred,
763 const bool pruning
764)
765{
766 label changed = 0;
767
768 for (iterator iter = begin(); iter != end(); ++iter)
769 {
770 // Matches? either prune (pruning) or keep (!pruning)
771 if
772 (
773 (pred(iter.val()) ? pruning : !pruning)
774 && erase(iter)
775 )
776 {
777 ++changed;
778 }
779 }
780
781 return changed;
782}
783
784
785template<class T, class Key, class Hash>
786template<class BinaryPredicate>
788(
789 const BinaryPredicate& pred,
790 const bool pruning
791)
792{
793 label changed = 0;
794
795 for (iterator iter = begin(); iter != end(); ++iter)
796 {
797 // Matches? either prune (pruning) or keep (!pruning)
798 if
799 (
800 (pred(iter.key(), iter.val()) ? pruning : !pruning)
801 && erase(iter)
802 )
803 {
804 ++changed;
805 }
806 }
807
808 return changed;
809}
810
811
812// * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
813
814template<class T, class Key, class Hash>
816(
817 const HashTable<T, Key, Hash>& rhs
818)
819{
820 if (this == &rhs)
821 {
822 return; // Self-assignment is a no-op
823 }
824
825 if (!capacity_)
826 {
827 // Zero-sized from a previous transfer()?
828 resize(rhs.capacity_);
829 }
830 else
831 {
832 clear();
833 }
834
835 for (const_iterator iter = rhs.cbegin(); iter != rhs.cend(); ++iter)
836 {
837 insert(iter.key(), iter.val());
838 }
839}
840
841
842template<class T, class Key, class Hash>
844(
845 std::initializer_list<std::pair<Key, T>> rhs
846)
847{
848 if (!capacity_)
849 {
850 // Zero-sized from a previous transfer()?
851 resize(2*rhs.size());
852 }
853 else
854 {
855 clear();
856 }
857
858 for (const auto& keyval : rhs)
859 {
860 set(keyval.first, keyval.second);
861 }
862}
863
864
865template<class T, class Key, class Hash>
867(
869)
870{
871 if (this == &rhs)
872 {
873 return; // Self-assignment is a no-op
874 }
875
876 transfer(rhs);
877}
878
879
880template<class T, class Key, class Hash>
882(
883 const HashTable<T, Key, Hash>& rhs
884) const
885{
886 // Sizes (number of keys) must match
887 if (size() != rhs.size())
888 {
889 return false;
890 }
891
892 for (const_iterator iter = rhs.cbegin(); iter != rhs.cend(); ++iter)
893 {
894 const const_iterator other(this->cfind(iter.key()));
895
896 if (!other.good() || other.val() != iter.val())
897 {
898 return false;
899 }
900 }
901
902 return true;
903}
904
905
906template<class T, class Key, class Hash>
908(
909 const HashTable<T, Key, Hash>& rhs
910) const
911{
912 return !operator==(rhs);
913}
914
915
916template<class T, class Key, class Hash>
918(
919 const HashTable<T, Key, Hash>& rhs
920)
921{
922 // Avoid no-ops:
923 if (rhs.size() || this != &rhs)
924 {
925 if (this->size())
926 {
927 for (const_iterator iter = rhs.cbegin(); iter != rhs.cend(); ++iter)
928 {
929 insert(iter.key(), iter.val());
930 }
931 }
932 else
933 {
934 (*this) = rhs;
935 }
936 }
937
938 return *this;
939}
940
941
942// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
943
944// Iterators, Friend Operators
945
946#include "HashTableIter.C"
947#include "HashTableIO.C"
948
949// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
950
951#endif
952
953// ************************************************************************* //
virtual bool resize()
Resize the ODE solver.
Definition: Euler.C:53
A 1D vector of objects of type <T> with a fixed length <N>.
Definition: FixedList.H:81
const_iterator cend() const noexcept
Return const_iterator to end traversing the constant FixedList.
Definition: FixedListI.H:564
const_iterator cbegin() const noexcept
Return const_iterator to begin traversing the constant FixedList.
Definition: FixedListI.H:540
bool good() const noexcept
True if iterator points to an entry.
node_type * entry_
The selected entry.
Definition: HashTable.H:671
label index_
Index within the hash-table data.
Definition: HashTable.H:680
Forward iterator with const access.
Definition: HashTable.H:794
reference val() const
Const access to referenced object (value)
Definition: HashTable.H:846
Forward iterator with non-const access.
Definition: HashTable.H:720
A HashTable similar to std::unordered_map.
Definition: HashTable.H:123
List< Key > tocEntries(const BinaryPredicate &pred, const bool invert=false) const
List< Key > sortedToc() const
The table of contents (the keys) in sorted order.
Definition: HashTable.C:137
label countEntries(const BinaryPredicate &pred, const bool invert=false) const
Count the number of entries that satisfy the binary predicate.
label countKeys(const UnaryPredicate &pred, const bool invert=false) const
Count the number of keys that satisfy the unary predicate.
List< Key > toc() const
The table of contents (the keys) in unsorted order.
Definition: HashTable.C:122
bool set(const Key &key, const T &obj)
Copy assign a new entry, overwriting existing entries.
Definition: HashTableI.H:202
label retain(const HashTable< AnyType, Key, AnyHash > &other)
Retain table entries given by keys of the other hash-table.
bool empty() const noexcept
True if the hash table is empty.
Definition: HashTableI.H:59
const_iterator cbegin() const
const_iterator set to the beginning of the HashTable
bool found(const Key &key) const
Return true if hashed entry is found in table.
Definition: HashTableI.H:100
void clearStorage()
Clear the table entries and the table itself.
Definition: HashTable.C:697
bool insert(const Key &key, const T &obj)
Copy insert a new entry, not overwriting existing entries.
Definition: HashTableI.H:180
label filterValues(const UnaryPredicate &pred, const bool pruning=false)
Generalized means to filter table entries based on their values.
List< Key > tocValues(const UnaryPredicate &pred, const bool invert=false) const
std::conditional< std::is_same< zero::null, typenamestd::remove_cv< T >::type >::value, Detail::HashTableSingle< Key >, Detail::HashTablePair< Key, T > >::type node_type
Definition: HashTable.H:138
label size() const noexcept
The number of elements in table.
Definition: HashTableI.H:52
void swap(HashTable< T, Key, Hash > &rhs)
Swap contents into this table.
Definition: HashTable.C:705
label filterEntries(const BinaryPredicate &pred, const bool pruning=false)
Generalized means to filter table entries based on their key/value.
label filterKeys(const UnaryPredicate &pred, const bool pruning=false)
Generalized means to filter table entries based on their keys.
~HashTable()
Destructor.
Definition: HashTable.C:109
label countValues(const UnaryPredicate &pred, const bool invert=false) const
Count the number of values that satisfy the unary predicate.
UPtrList< const node_type > sorted() const
Definition: HashTable.C:181
HashTable()
Default construct with default (128) table capacity.
Definition: HashTable.C:40
UPtrList< const node_type > csorted() const
Definition: HashTable.C:162
bool erase(const iterator &iter)
Erase an entry specified by given iterator.
Definition: HashTable.C:440
void clear()
Clear all entries from table.
Definition: HashTable.C:678
List< Key > tocKeys(const UnaryPredicate &pred, const bool invert=false) const
constexpr const_iterator cend() const noexcept
const_iterator to signal the end (for any HashTable)
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: List.H:77
void resize(const label len)
Adjust allocated size of list.
Definition: ListI.H:139
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:94
const_iterator cend() const noexcept
Return const_iterator to end traversing the constant UList.
Definition: UListI.H:364
const_iterator cbegin() const noexcept
Return const_iterator to begin traversing the constant UList.
Definition: UListI.H:343
A list of pointers to objects of type <T>, without allocation/deallocation management of the pointers...
Definition: UPtrList.H:71
const T * set(const label i) const
Definition: UPtrList.H:248
const volScalarField & T
patchWriters resize(patchIds.size())
patchWriters clear()
#define WarningInFunction
Report a warning using Foam::Warning.
#define DebugInFunction
Report an information message using Foam::Info.
tmp< faMatrix< Type > > operator==(const faMatrix< Type > &, const faMatrix< Type > &)
void sort(UList< T > &list)
Sort the list.
Definition: UList.C:342
labelList invert(const label len, const labelUList &map)
Create an inverse one-to-one mapping.
Definition: ListOps.C:36
constexpr char nl
The newline '\n' character (0x0a)
Definition: Ostream.H:53
srcOptions insert("case", fileName(rootDirSource/caseDirSource))
srcOptions erase("case")
Foam::argList args(argc, argv)
Bits that are independent of HashTable template parameters.
Definition: HashTableCore.H:57
static label canonicalSize(const label requested_size)
Return a canonical (power-of-two) of the requested size.
Definition: HashTableCore.C:45
Hash function class. The default definition is for primitives. Non-primitives used to hash entries on...
Definition: Hash.H:54