diff --git a/applications/test/HashTable/Make/files b/applications/test/HashTable/Make/files
deleted file mode 100644
index 53d1e07699..0000000000
--- a/applications/test/HashTable/Make/files
+++ /dev/null
@@ -1,3 +0,0 @@
-Test-hashTable.C
-
-EXE = $(FOAM_USER_APPBIN)/Test-hashTable
diff --git a/applications/test/HashTable1/Make/files b/applications/test/HashTable1/Make/files
new file mode 100644
index 0000000000..05c776d65c
--- /dev/null
+++ b/applications/test/HashTable1/Make/files
@@ -0,0 +1,3 @@
+Test-HashTable1.C
+
+EXE = $(FOAM_USER_APPBIN)/Test-HashTable1
diff --git a/applications/test/HashTable/Make/options b/applications/test/HashTable1/Make/options
similarity index 100%
rename from applications/test/HashTable/Make/options
rename to applications/test/HashTable1/Make/options
diff --git a/applications/test/HashTable/Test-hashTable.C b/applications/test/HashTable1/Test-HashTable1.C
similarity index 100%
rename from applications/test/HashTable/Test-hashTable.C
rename to applications/test/HashTable1/Test-HashTable1.C
diff --git a/applications/test/HashTable4/Make/files b/applications/test/HashTable4/Make/files
new file mode 100644
index 0000000000..fcc01c49bd
--- /dev/null
+++ b/applications/test/HashTable4/Make/files
@@ -0,0 +1,3 @@
+Test-HashTable4.C
+
+EXE = $(FOAM_USER_APPBIN)/Test-HashTable4
diff --git a/applications/test/HashTable4/Make/options b/applications/test/HashTable4/Make/options
new file mode 100644
index 0000000000..6a9e9810b3
--- /dev/null
+++ b/applications/test/HashTable4/Make/options
@@ -0,0 +1,2 @@
+/* EXE_INC = -I$(LIB_SRC)/cfdTools/include */
+/* EXE_LIBS = -lfiniteVolume */
diff --git a/applications/test/HashTable4/Test-HashTable4.C b/applications/test/HashTable4/Test-HashTable4.C
new file mode 100644
index 0000000000..b9333f320d
--- /dev/null
+++ b/applications/test/HashTable4/Test-HashTable4.C
@@ -0,0 +1,312 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | Copyright (C) 2017 OpenCFD Ltd.
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+License
+ This file is part of OpenFOAM.
+
+ OpenFOAM is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with OpenFOAM. If not, see .
+
+Description
+ Test HashTable resizing
+
+\*---------------------------------------------------------------------------*/
+
+#include "argList.H"
+#include "HashSet.H"
+#include "HashTable.H"
+#include "Map.H"
+#include "cpuTime.H"
+#include "memInfo.H"
+
+#include
+#include
+#include
+#include
+
+// #undef ORDERED
+// #define ORDERED
+
+using namespace Foam;
+
+template
+Ostream& printInfo(Ostream& os, const HashTable>& ht)
+{
+ os << " (size " << ht.size() << " capacity " << ht.capacity() << ") ";
+ return os;
+}
+
+
+template
+inline void insertElem
+(
+ #ifdef ORDERED
+ std::set& container,
+ #else
+ std::unordered_set>& container,
+ #endif
+ K k,
+ V v
+)
+{
+ container.insert(k);
+}
+
+
+template
+inline void insertElem
+(
+ #ifdef ORDERED
+ std::map& container,
+ #else
+ std::unordered_map>& container,
+ #endif
+ K k,
+ V v
+)
+{
+ container.insert(std::make_pair(k, v));
+}
+
+
+template
+inline void insertElem
+(
+ HashSet>& container,
+ K k,
+ V v
+)
+{
+ container.insert(k);
+}
+
+
+template
+inline void insertElem
+(
+ HashTable>& container,
+ K k,
+ V v
+)
+{
+ container.insert(k, v);
+}
+
+
+template
+inline void loopInsert(Container& container, const label n)
+{
+ for (label i = 0; i < n; i++)
+ {
+ insertElem(container, i, i);
+ }
+}
+
+
+template
+inline unsigned long loopFind(const Container& container, const label n)
+{
+ const auto endIter = container.end();
+ unsigned long sum = 0;
+
+ for (label i = 0; i < n; i++)
+ {
+ if (container.find(i) != endIter)
+ {
+ ++sum;
+ }
+ }
+
+ return sum;
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+// Main program:
+
+int main(int argc, char *argv[])
+{
+ const label nLoops = 200;
+ const label nFind = 10;
+ const label nElem = 1000000;
+
+ argList::noBanner();
+ argList::addBoolOption("std", "use std::unordered_map or std::set");
+ argList::addBoolOption("set", "test HashSet");
+ argList::addBoolOption("find", "test find");
+
+ argList args(argc, argv);
+
+ const bool optStd = args.optionFound("std");
+ const bool optSet = args.optionFound("set");
+ const bool optFnd = args.optionFound("find");
+
+
+ cpuTime timer;
+ memInfo mem;
+
+ Info<< "insert " << nElem << " (int) elements";
+ if (optFnd)
+ {
+ Info<< ", then find " << (nFind*nLoops) << " times\n";
+ }
+ else
+ {
+ Info<< " repeated " << nLoops << " times " << endl;
+ }
+
+ if (false)
+ {
+ // verify that resizing around (0) doesn't fail
+ HashTable> map(32);
+ printInfo(Info, map) << endl;
+
+ map.insert(10, 1000);
+
+ map.resize(0);
+ printInfo(Info, map) << endl;
+
+ map.resize(10);
+ printInfo(Info, map) << endl;
+
+ map.clear();
+ printInfo(Info, map) << endl;
+
+ map.resize(0);
+ printInfo(Info, map) << endl;
+ return 0;
+ }
+
+
+ if (optStd)
+ {
+ if (optFnd)
+ {
+ #ifdef ORDERED
+ Info<< "using stl::set" << endl;
+ std::set map;
+ #else
+ Info<< "using stl::unordered_set" << endl;
+ std::unordered_set> map(32);
+ #endif
+
+ loopInsert(map, nElem);
+ (void)timer.cpuTimeIncrement();
+
+ unsigned long sum = 0;
+ for (label loopi = 0; loopi < nFind*nLoops; ++loopi)
+ {
+ sum += loopFind(map, nElem);
+ }
+
+ // check result (suppress compiler optimizations?)
+ if (sum == 0)
+ {
+ Info<<"sum=0\n";
+ }
+ }
+ else if (optSet)
+ {
+ #ifdef ORDERED
+ Info<< "using stl::set" << endl;
+ #else
+ Info<< "using stl::unordered_set" << endl;
+ #endif
+
+ for (label loopi = 0; loopi < nLoops; ++loopi)
+ {
+ #ifdef ORDERED
+ std::set map;
+ #else
+ std::unordered_set> map(32);
+ #endif
+
+ loopInsert(map, nElem);
+ }
+ }
+ else
+ {
+ #ifdef ORDERED
+ Info<< "using stl::map" << endl;
+ #else
+ Info<< "using stl::unordered_set" << endl;
+ #endif
+
+ for (label loopi = 0; loopi < nLoops; ++loopi)
+ {
+ #ifdef ORDERED
+ std::map map;
+ #else
+ std::unordered_map> map(32);
+ #endif
+
+ loopInsert(map, nElem);
+ }
+ }
+ }
+ else
+ {
+ if (optFnd)
+ {
+ Info<< "using HashSet" << endl;
+
+ HashSet> map(32);
+
+ loopInsert(map, nElem);
+ (void)timer.cpuTimeIncrement();
+
+ unsigned long sum = 0;
+ for (label loopi = 0; loopi < nFind*nLoops; ++loopi)
+ {
+ sum += loopFind(map, nElem);
+ }
+
+ // check result (suppress compiler optimizations?)
+ if (sum == 0)
+ {
+ Info<<"sum=0\n";
+ }
+ }
+ else if (optSet)
+ {
+ Info<< "using HashSet" << endl;
+ for (label loopi = 0; loopi < nLoops; ++loopi)
+ {
+ HashSet> map(32);
+
+ loopInsert(map, nElem);
+ }
+ }
+ else
+ {
+ Info<< "using HashTable" << endl;
+ for (label loopi = 0; loopi < nLoops; ++loopi)
+ {
+ HashTable> map(32);
+
+ loopInsert(map, nElem);
+ }
+ }
+ }
+
+ Info<< timer.cpuTimeIncrement() << " s\n";
+ Info<< "mem info: " << mem.update() << endl;
+
+ return 0;
+}
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/containers/HashTables/HashPtrTable/HashPtrTable.C b/src/OpenFOAM/containers/HashTables/HashPtrTable/HashPtrTable.C
index 9370dd26ed..e7ad07ec2f 100644
--- a/src/OpenFOAM/containers/HashTables/HashPtrTable/HashPtrTable.C
+++ b/src/OpenFOAM/containers/HashTables/HashPtrTable/HashPtrTable.C
@@ -28,6 +28,13 @@ License
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
+template
+Foam::HashPtrTable::HashPtrTable()
+:
+ parent_type()
+{}
+
+
template
Foam::HashPtrTable::HashPtrTable(const label size)
:
@@ -157,6 +164,7 @@ void Foam::HashPtrTable::operator=
}
}
+
// * * * * * * * * * * * * * * * Friend Operators * * * * * * * * * * * * * //
#include "HashPtrTableIO.C"
diff --git a/src/OpenFOAM/containers/HashTables/HashPtrTable/HashPtrTable.H b/src/OpenFOAM/containers/HashTables/HashPtrTable/HashPtrTable.H
index f83c04e313..daf19328d0 100644
--- a/src/OpenFOAM/containers/HashTables/HashPtrTable/HashPtrTable.H
+++ b/src/OpenFOAM/containers/HashTables/HashPtrTable/HashPtrTable.H
@@ -91,8 +91,11 @@ public:
// Constructors
+ //- Construct null with default table size
+ HashPtrTable();
+
//- Construct given initial table size
- HashPtrTable(const label size = 128);
+ explicit HashPtrTable(const label size);
//- Construct from Istream using given Istream constructor class
template
@@ -102,7 +105,7 @@ public:
HashPtrTable(Istream& is);
//- Construct from dictionary with default dictionary constructor class
- HashPtrTable(const dictionary& dict);
+ explicit HashPtrTable(const dictionary& dict);
//- Construct as copy
HashPtrTable(const this_type& ht);
@@ -127,7 +130,7 @@ public:
//- Erase an entry specified by the given key
bool erase(const Key& key);
- //- Clear all entries from table
+ //- Clear all entries from table and deleting any allocated pointers
void clear();
//- Write
diff --git a/src/OpenFOAM/containers/HashTables/HashPtrTable/HashPtrTableIO.C b/src/OpenFOAM/containers/HashTables/HashPtrTable/HashPtrTableIO.C
index 55004189fe..bbc7351857 100644
--- a/src/OpenFOAM/containers/HashTables/HashPtrTable/HashPtrTableIO.C
+++ b/src/OpenFOAM/containers/HashTables/HashPtrTable/HashPtrTableIO.C
@@ -41,7 +41,7 @@ void Foam::HashPtrTable::read(Istream& is, const INew& inewt)
is.fatalCheck
(
- "HashPtrTable::read(Istream&, const INew&) : "
+ "HashPtrTable::read(Istream&, const INew&) : "
"reading first token"
);
@@ -50,7 +50,7 @@ void Foam::HashPtrTable::read(Istream& is, const INew& inewt)
const label s = firstToken.labelToken();
// Read beginning of contents
- const char delimiter = is.readBeginList("HashPtrTable");
+ const char delimiter = is.readBeginList("HashPtrTable");
if (s)
{
@@ -69,8 +69,8 @@ void Foam::HashPtrTable::read(Istream& is, const INew& inewt)
is.fatalCheck
(
- "HashPtrTable::"
- "read(Istream&, const INew&) : reading entry"
+ "HashPtrTable::read(Istream&, const INew&) : "
+ "reading entry"
);
}
}
@@ -114,7 +114,7 @@ void Foam::HashPtrTable::read(Istream& is, const INew& inewt)
is.fatalCheck
(
- "HashPtrTable::read(Istream&, const INew&) : "
+ "HashPtrTable::read(Istream&, const INew&) : "
"reading entry"
);
@@ -155,7 +155,7 @@ void Foam::HashPtrTable::read
template
void Foam::HashPtrTable::write(Ostream& os) const
{
- for (const_iterator iter = this->begin(); iter != this->end(); ++iter)
+ for (const_iterator iter = this->cbegin(); iter != this->cend(); ++iter)
{
const T* ptr = iter.object();
if (ptr)
@@ -209,29 +209,34 @@ Foam::Ostream& Foam::operator<<
const HashPtrTable& tbl
)
{
- using const_iterator = typename HashPtrTable::const_iterator;
+ const label sz = tbl.size();
- // Write size and start delimiter
- os << nl << tbl.size() << nl << token::BEGIN_LIST << nl;
-
- // Write contents
- for (const_iterator iter = tbl.cbegin(); iter != tbl.cend(); ++iter)
+ if (sz)
{
- const T* ptr = iter.object();
+ // Size and start list delimiter
+ os << nl << sz << nl << token::BEGIN_LIST << nl;
- os << iter.key();
- if (ptr)
+ // Contents
+ for (auto iter = tbl.cbegin(); iter != tbl.cend(); ++iter)
{
- os << token::SPACE << *ptr;
+ const T* ptr = iter.object();
+
+ os << iter.key();
+ if (ptr)
+ {
+ os << token::SPACE << *ptr;
+ }
+ os << nl;
}
- os << nl;
+ os << token::END_LIST; // End list delimiter
+ }
+ else
+ {
+ // Empty hash table
+ os << sz << token::BEGIN_LIST << token::END_LIST;
}
- // Write end delimiter
- os << token::END_LIST;
-
os.check(FUNCTION_NAME);
-
return os;
}
diff --git a/src/OpenFOAM/containers/HashTables/HashSet/HashSet.C b/src/OpenFOAM/containers/HashTables/HashSet/HashSet.C
index 020d3e36d9..3070831f26 100644
--- a/src/OpenFOAM/containers/HashTables/HashSet/HashSet.C
+++ b/src/OpenFOAM/containers/HashTables/HashSet/HashSet.C
@@ -79,7 +79,7 @@ inline Foam::label Foam::HashSet::assignMultiple
template
Foam::HashSet::HashSet(const UList& lst)
:
- HashTable(2*lst.size())
+ parent_type(2*lst.size())
{
for (const auto& k : lst)
{
@@ -92,7 +92,7 @@ template
template
Foam::HashSet::HashSet(const FixedList& lst)
:
- HashTable(2*lst.size())
+ parent_type(2*lst.size())
{
for (const auto& k : lst)
{
@@ -104,7 +104,7 @@ Foam::HashSet::HashSet(const FixedList& lst)
template
Foam::HashSet::HashSet(std::initializer_list lst)
:
- HashTable(2*lst.size())
+ parent_type(2*lst.size())
{
for (const auto& k : lst)
{
@@ -120,7 +120,7 @@ Foam::HashSet::HashSet
const HashTable& tbl
)
:
- HashTable(tbl.capacity())
+ parent_type(tbl.capacity())
{
using other_iter =
typename HashTable::const_iterator;
@@ -349,7 +349,7 @@ template
inline typename Foam::HashSet::const_iterator
Foam::HashSet::begin() const
{
- return HashTableCore::iterator_begin
+ return HashTableCore::iterator_cbegin
(
static_cast(*this)
);
@@ -360,7 +360,7 @@ template
inline typename Foam::HashSet::const_iterator
Foam::HashSet::cbegin() const
{
- return HashTableCore::iterator_begin
+ return HashTableCore::iterator_cbegin
(
static_cast(*this)
);
@@ -379,7 +379,7 @@ template
inline const typename Foam::HashSet::const_iterator&
Foam::HashSet::end() const
{
- return HashTableCore::iterator_end();
+ return HashTableCore::iterator_cend();
}
diff --git a/src/OpenFOAM/containers/HashTables/HashSet/HashSet.H b/src/OpenFOAM/containers/HashTables/HashSet/HashSet.H
index ace2ca6d7a..f20e905920 100644
--- a/src/OpenFOAM/containers/HashTables/HashSet/HashSet.H
+++ b/src/OpenFOAM/containers/HashTables/HashSet/HashSet.H
@@ -25,7 +25,24 @@ Class
Foam::HashSet
Description
- A HashTable with keys but without contents.
+ A HashTable with keys but without contents that is similar to
+ \c std::unordered_set.
+ The entries are considered \a unordered since their placement
+ depends on the method used to generate the hash key index, the
+ table capacity, insertion order etc. When the key order is
+ important, use the sortedToc() method to obtain a list of sorted
+ keys and use that for further access.
+
+Note
+ The HashSet iterator dereferences to the key, so the following
+ range-for works as expected:
+ \code
+ HashSet someLabels{10, 20, 30, 40, ...};
+ for (const label i : someLabels)
+ {
+ Info<< "val:" << i << nl;
+ }
+ \endcode
Typedef
Foam::wordHashSet
@@ -45,7 +62,6 @@ Description
#define HashSet_H
#include "HashTable.H"
-#include "nil.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@@ -66,7 +82,7 @@ Ostream& operator<<(Ostream& os, const HashSet& tbl);
template
class HashSet
:
- public HashTable
+ public HashTable
{
// Private Member Functions
@@ -94,7 +110,7 @@ public:
typedef HashSet this_type;
//- The template instance used for the parent HashTable
- typedef HashTable parent_type;
+ typedef HashTable parent_type;
//- An iterator, returning reference to the key
using iterator = typename parent_type::key_iterator;
@@ -105,13 +121,19 @@ public:
// Constructors
+ //- Construct null with default (128) table size
+ HashSet()
+ :
+ parent_type()
+ {}
+
//- Construct given initial size
- HashSet(const label size = 128)
+ explicit HashSet(const label size)
:
parent_type(size)
{}
- //- Construct from Istream
+ //- Construct from Istream with default table size
HashSet(Istream& is)
:
parent_type(is)
@@ -140,13 +162,13 @@ public:
{}
//- Construct by transferring the parameter contents
- HashSet(const Xfer>& hs)
+ HashSet(const Xfer& hs)
:
parent_type(hs)
{}
//- Construct by transferring the parameter contents
- HashSet(const Xfer>& hs)
+ HashSet(const Xfer& hs)
:
parent_type(hs)
{}
@@ -166,7 +188,7 @@ public:
// not previously exist in the set.
bool insert(const Key& key)
{
- return this->parent_type::insert(key, nil());
+ return this->parent_type::insert(key, zero::null());
}
//- Insert keys from the list of Key
@@ -182,26 +204,26 @@ public:
// \return The number of new elements inserted
label insert(std::initializer_list lst);
- //- Same as insert (cannot overwrite nil content)
+ //- Same as insert (no value to overwrite)
bool set(const Key& key)
{
return insert(key);
}
- //- Same as insert (cannot overwrite nil content)
+ //- Same as insert (no value to overwrite)
label set(const UList& lst)
{
return insert(lst);
}
- //- Same as insert (cannot overwrite nil content)
+ //- Same as insert (no value to overwrite)
template
label set(const FixedList& lst)
{
return insert(lst);
}
- //- Same as insert (cannot overwrite nil content)
+ //- Same as insert (no value to overwrite)
label set(std::initializer_list lst)
{
return insert(lst);
@@ -331,22 +353,22 @@ public:
// Logical operations
//- Combine entries from HashSets
- void operator|=(const HashSet& rhs);
+ void operator|=(const this_type& rhs);
//- Only retain entries found in both HashSets
- inline void operator&=(const HashSet& rhs);
+ inline void operator&=(const this_type& rhs);
//- Only retain unique entries (xor)
- void operator^=(const HashSet& rhs);
+ void operator^=(const this_type& rhs);
//- Add entries listed in the given HashSet to this HashSet
- inline void operator+=(const HashSet& rhs)
+ inline void operator+=(const this_type& rhs)
{
this->operator|=(rhs);
}
//- Remove entries listed in the given HashSet from this HashSet
- inline void operator-=(const HashSet& rhs);
+ inline void operator-=(const this_type& rhs);
// IOstream Operator
diff --git a/src/OpenFOAM/containers/HashTables/HashTable/HashTable.C b/src/OpenFOAM/containers/HashTables/HashTable/HashTable.C
index 1c1d7b4360..68faaafb49 100644
--- a/src/OpenFOAM/containers/HashTables/HashTable/HashTable.C
+++ b/src/OpenFOAM/containers/HashTables/HashTable/HashTable.C
@@ -61,21 +61,27 @@ Foam::label Foam::HashTable::eraseMultiple
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
+template
+Foam::HashTable::HashTable()
+:
+ HashTable(128)
+{}
+
+
template
Foam::HashTable::HashTable(const label size)
:
HashTableCore(),
- nElmts_(0),
- tableSize_(HashTableCore::canonicalSize(size)),
+ size_(0),
+ capacity_(HashTableCore::canonicalSize(size)),
table_(nullptr)
{
- if (tableSize_)
+ if (capacity_)
{
- table_ = new hashedEntry*[tableSize_];
-
- for (label hashIdx = 0; hashIdx < tableSize_; ++hashIdx)
+ table_ = new node_type*[capacity_];
+ for (label i=0; i < capacity_; ++i)
{
- table_[hashIdx] = nullptr;
+ table_[i] = nullptr;
}
}
}
@@ -84,7 +90,7 @@ Foam::HashTable::HashTable(const label size)
template
Foam::HashTable::HashTable(const HashTable& ht)
:
- HashTable(ht.tableSize_)
+ HashTable(ht.capacity_)
{
for (const_iterator iter = ht.cbegin(); iter != ht.cend(); ++iter)
{
@@ -96,10 +102,7 @@ Foam::HashTable::HashTable(const HashTable& ht)
template
Foam::HashTable::HashTable(HashTable&& ht)
:
- HashTableCore(),
- nElmts_(0),
- tableSize_(0),
- table_(nullptr)
+ HashTable(0)
{
transfer(ht);
}
@@ -111,10 +114,7 @@ Foam::HashTable::HashTable
const Xfer>& ht
)
:
- HashTableCore(),
- nElmts_(0),
- tableSize_(0),
- table_(nullptr)
+ HashTable(0)
{
transfer(ht());
}
@@ -150,110 +150,10 @@ Foam::HashTable::~HashTable()
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
-template
-bool Foam::HashTable::found(const Key& key) const
-{
- if (nElmts_)
- {
- const label hashIdx = hashKeyIndex(key);
-
- for (hashedEntry* ep = table_[hashIdx]; ep; ep = ep->next_)
- {
- if (key == ep->key_)
- {
- return true;
- }
- }
- }
-
- #ifdef FULLDEBUG
- if (debug)
- {
- InfoInFunction << "Entry " << key << " not found in hash table\n";
- }
- #endif
-
- return false;
-}
-
-
-template
-typename Foam::HashTable::iterator
-Foam::HashTable::find
-(
- const Key& key
-)
-{
- if (nElmts_)
- {
- const label hashIdx = hashKeyIndex(key);
-
- for (hashedEntry* ep = table_[hashIdx]; ep; ep = ep->next_)
- {
- if (key == ep->key_)
- {
- return iterator(this, ep, hashIdx);
- }
- }
- }
-
- #ifdef FULLDEBUG
- if (debug)
- {
- InfoInFunction << "Entry " << key << " not found in hash table\n";
- }
- #endif
-
- return iterator();
-}
-
-
-template
-typename Foam::HashTable::const_iterator
-Foam::HashTable::find
-(
- const Key& key
-) const
-{
- return this->cfind(key);
-}
-
-
-template
-typename Foam::HashTable::const_iterator
-Foam::HashTable::cfind
-(
- const Key& key
-) const
-{
- if (nElmts_)
- {
- const label hashIdx = hashKeyIndex(key);
-
- for (hashedEntry* ep = table_[hashIdx]; ep; ep = ep->next_)
- {
- if (key == ep->key_)
- {
- return const_iterator(this, ep, hashIdx);
- }
- }
- }
-
- #ifdef FULLDEBUG
- if (debug)
- {
- InfoInFunction << "Entry " << key << " not found in hash table\n";
- }
- #endif
-
- return const_iterator();
-}
-
-
template
Foam::List Foam::HashTable::toc() const
{
- List keyLst(nElmts_);
+ List keyLst(size_);
label count = 0;
for (const_iterator iter = cbegin(); iter != cend(); ++iter)
@@ -297,7 +197,7 @@ Foam::List Foam::HashTable::tocKeys
const bool invert
) const
{
- List keyLst(nElmts_);
+ List keyLst(size_);
label count = 0;
for (const_iterator iter = cbegin(); iter != cend(); ++iter)
@@ -323,7 +223,7 @@ Foam::List Foam::HashTable::tocValues
const bool invert
) const
{
- List keyLst(nElmts_);
+ List keyLst(size_);
label count = 0;
for (const_iterator iter = cbegin(); iter != cend(); ++iter)
@@ -349,7 +249,7 @@ Foam::List Foam::HashTable::tocEntries
const bool invert
) const
{
- List keyLst(nElmts_);
+ List keyLst(size_);
label count = 0;
for (const_iterator iter = cbegin(); iter != cend(); ++iter)
@@ -438,36 +338,36 @@ bool Foam::HashTable::set
(
const Key& key,
const T& obj,
- const bool protect
+ const bool overwrite
)
{
- if (!tableSize_)
+ if (!capacity_)
{
resize(2);
}
- const label hashIdx = hashKeyIndex(key);
+ const label index = hashKeyIndex(key);
- hashedEntry* existing = nullptr;
- hashedEntry* prev = nullptr;
+ node_type* curr = nullptr;
+ node_type* prev = nullptr;
- for (hashedEntry* ep = table_[hashIdx]; ep; ep = ep->next_)
+ for (node_type* ep = table_[index]; ep; ep = ep->next_)
{
- if (key == ep->key_)
+ if (key == ep->key())
{
- existing = ep;
+ curr = ep;
break;
}
prev = ep;
}
- if (!existing)
+ if (!curr)
{
// Not found, insert it at the head
- table_[hashIdx] = new hashedEntry(key, obj, table_[hashIdx]);
- nElmts_++;
+ table_[index] = new node_type(key, obj, table_[index]);
+ ++size_;
- if (double(nElmts_)/tableSize_ > 0.8 && tableSize_ < maxTableSize)
+ if (double(size_)/capacity_ > 0.8 && capacity_ < maxTableSize)
{
#ifdef FULLDEBUG
if (debug)
@@ -476,13 +376,35 @@ bool Foam::HashTable::set
}
#endif
- resize(2*tableSize_);
+ resize(2*capacity_);
}
}
- else if (protect)
+ else if (overwrite)
{
- // Found - but protected from overwriting
- // this corresponds to the STL 'insert' convention
+ // Overwrite current entry (Perl convention).
+
+ node_type* ep = curr->next_; // next in the linked list
+
+ // In some cases the delete/new could be avoided in favour of move
+ // assignment, but cannot be certain that all objects support this
+ // or that it behaves the same as a copy construct.
+
+ delete curr;
+ ep = new node_type(key, obj, ep);
+
+ // Replace current element - within list or insert at the head
+ if (prev)
+ {
+ prev->next_ = ep;
+ }
+ else
+ {
+ table_[index] = ep;
+ }
+ }
+ else
+ {
+ // Do not overwrite existing entry (STL 'insert' convention)
#ifdef FULLDEBUG
if (debug)
{
@@ -492,99 +414,22 @@ bool Foam::HashTable::set
#endif
return false;
}
- else
- {
- // Found - overwrite existing entry
- // this corresponds to the Perl convention
- hashedEntry* ep = new hashedEntry(key, obj, existing->next_);
-
- // Replace existing element - within list or insert at the head
- if (prev)
- {
- prev->next_ = ep;
- }
- else
- {
- table_[hashIdx] = ep;
- }
-
- delete existing;
- }
return true;
}
-template
-bool Foam::HashTable::iterator_base::erase()
-{
- // Note: entryPtr_ is nullptr for end(), so this catches that too
- if (entryPtr_)
- {
- // Search element before entryPtr_
- entry_type* prev = nullptr;
-
- for
- (
- entry_type* ep = hashTable_->table_[hashIndex_];
- ep;
- ep = ep->next_
- )
- {
- if (ep == entryPtr_)
- {
- break;
- }
- prev = ep;
- }
-
- if (prev)
- {
- // Has an element before entryPtr - reposition to there
- prev->next_ = entryPtr_->next_;
- delete entryPtr_;
- entryPtr_ = prev;
- }
- else
- {
- // entryPtr was first element on SLList
- hashTable_->table_[hashIndex_] = entryPtr_->next_;
- delete entryPtr_;
-
- // Assign any non-nullptr value so it doesn't look like end()
- entryPtr_ = reinterpret_cast(this);
-
- // Mark with special hashIndex value to signal it has been rewound.
- // The next increment will bring it back to the present location.
- //
- // From the current position 'curPos', we wish to continue at
- // prevPos='curPos-1', which we mark as markPos='-curPos-1'.
- // The negative lets us notice it is special, the extra '-1'
- // is needed to avoid ambiguity for position '0'.
- // To retrieve prevPos, we would later use '-(markPos+1) - 1'
- hashIndex_ = -hashIndex_ - 1;
- }
-
- hashTable_->nElmts_--;
-
- return true;
- }
- else
- {
- return false;
- }
-}
-
-
template
bool Foam::HashTable::erase(const iterator& iter)
{
- // NOTE: We use (const iterator&) here, but manipulate its contents anyhow.
+ // NOTE: we use (const iterator&) here, but treat its contents as mutable.
+ //
// The parameter should be (iterator&), but then the compiler doesn't find
// it correctly and tries to call as (iterator) instead.
- //
- // Adjust iterator after erase
- return const_cast(iter).erase();
+
+ iterator& it = const_cast(iter);
+
+ return iterator_erase(it.entry_, it.index_);
}
@@ -599,7 +444,7 @@ bool Foam::HashTable::erase(const Key& key)
template
Foam::label Foam::HashTable::erase(const UList& keys)
{
- return eraseMultiple(keys.begin(), keys.end());
+ return eraseMultiple(keys.cbegin(), keys.cend());
}
@@ -610,7 +455,7 @@ Foam::label Foam::HashTable::erase
const FixedList& keys
)
{
- return eraseMultiple(keys.begin(), keys.end());
+ return eraseMultiple(keys.cbegin(), keys.cend());
}
@@ -634,17 +479,14 @@ Foam::label Foam::HashTable::erase
const label nTotal = this->size();
label changed = 0;
- using other_iter =
- typename HashTable::const_iterator;
-
if (other.size() <= nTotal)
{
// The other is smaller/same-size, use its keys for removal
for
(
- other_iter iter = other.begin();
- changed < nTotal && iter != other.end(); // terminate early
+ auto iter = other.cbegin();
+ changed < nTotal && iter != other.cend(); // Terminate early
++iter
)
{
@@ -660,7 +502,7 @@ Foam::label Foam::HashTable::erase
for
(
iterator iter = begin();
- changed < nTotal && iter != end(); // terminate early
+ changed < nTotal && iter != end(); // Terminate early
++iter
)
{
@@ -711,9 +553,10 @@ Foam::label Foam::HashTable::retain
template
void Foam::HashTable::resize(const label sz)
{
- const label newSize = HashTableCore::canonicalSize(sz);
+ const label newCapacity = HashTableCore::canonicalSize(sz);
+ const label oldCapacity = capacity_;
- if (newSize == tableSize_)
+ if (newCapacity == oldCapacity)
{
#ifdef FULLDEBUG
if (debug)
@@ -724,46 +567,85 @@ void Foam::HashTable::resize(const label sz)
return;
}
-
- HashTable* tmpTable = new HashTable(newSize);
-
- for (const_iterator iter = cbegin(); iter != cend(); ++iter)
+ else if (!newCapacity)
{
- tmpTable->insert(iter.key(), iter.object());
+ // Special treatment for resize(0)
+ if (size_)
+ {
+ WarningInFunction
+ << "HashTable contains " << size_ << " cannot resize(0)"
+ << endl;
+ }
+ else
+ {
+ if (table_)
+ {
+ delete[] table_;
+ capacity_ = 0;
+ }
+
+ table_ = nullptr;
+ }
+
+ return;
}
- const label oldSize = tableSize_;
- tableSize_ = tmpTable->tableSize_;
- tmpTable->tableSize_ = oldSize;
+ // Swap primary table entries: size_ is left untouched
- hashedEntry** oldTable = table_;
- table_ = tmpTable->table_;
- tmpTable->table_ = oldTable;
+ auto oldTable = table_;
+ capacity_ = newCapacity;
- delete tmpTable;
+ table_ = new node_type*[capacity_];
+ for (label i=0; i < capacity_; ++i)
+ {
+ table_[i] = nullptr;
+ }
+
+ // Move to new table[] but with new chaining.
+
+ label nMove = size_; // Allow early completion
+ for (label i=0; nMove && i < oldCapacity; ++i)
+ {
+ for (node_type* ep = oldTable[i]; ep; /*nil*/)
+ {
+ node_type* next = ep->next_;
+
+ // Move to new location
+ {
+ const label newIdx = hashKeyIndex(ep->key());
+
+ ep->next_ = table_[newIdx]; // add to head
+ table_[newIdx] = ep;
+ }
+
+ ep = next; // continue in the linked-list
+ --nMove; // note any early completion
+ }
+ oldTable[i] = nullptr;
+ }
+
+ if (oldTable)
+ {
+ delete[] oldTable;
+ }
}
template
void Foam::HashTable::clear()
{
- if (nElmts_)
+ for (label i=0; size_ && inext_)
- {
- delete ep;
- ep = next;
- }
- delete ep;
- table_[hashIdx] = nullptr;
- }
+ node_type* next = ep->next_;
+
+ delete ep;
+
+ ep = next; // continue in the linked-list
+ --size_; // note any early completion
}
- nElmts_ = 0;
+ table_[i] = nullptr;
}
}
@@ -779,30 +661,31 @@ void Foam::HashTable::clearStorage()
template
void Foam::HashTable::swap(HashTable& ht)
{
- Foam::Swap(table_, ht.table_);
- Foam::Swap(tableSize_, ht.tableSize_);
- Foam::Swap(nElmts_, ht.nElmts_);
+ Foam::Swap(size_, ht.size_);
+ Foam::Swap(capacity_, ht.capacity_);
+ Foam::Swap(table_, ht.table_);
}
template
void Foam::HashTable::transfer(HashTable& ht)
{
- // As per the Destructor
+ // As per destructor
if (table_)
{
clear();
delete[] table_;
}
- tableSize_ = ht.tableSize_;
- ht.tableSize_ = 0;
+ size_ = ht.size_;
+ ht.size_ = 0;
+
+ capacity_ = ht.capacity_;
+ ht.capacity_ = 0;
table_ = ht.table_;
ht.table_ = nullptr;
- nElmts_ = ht.nElmts_;
- ht.nElmts_ = 0;
}
@@ -904,9 +787,9 @@ void Foam::HashTable::operator=
}
// Could be zero-sized from a previous transfer()
- if (!tableSize_)
+ if (!capacity_)
{
- resize(rhs.tableSize_);
+ resize(rhs.capacity_);
}
else
{
@@ -927,7 +810,7 @@ void Foam::HashTable::operator=
)
{
// Could be zero-sized from a previous transfer()
- if (!tableSize_)
+ if (!capacity_)
{
resize(2*lst.size());
}
@@ -975,7 +858,7 @@ bool Foam::HashTable::operator==
for (const_iterator iter = rhs.cbegin(); iter != rhs.cend(); ++iter)
{
- const_iterator other = this->cfind(iter.key());
+ const const_iterator other(this->cfind(iter.key()));
if (!other.found() || other.object() != iter.object())
{
@@ -997,8 +880,11 @@ bool Foam::HashTable::operator!=
}
-// * * * * * * * * * * * * * * * Friend Operators * * * * * * * * * * * * * //
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+// Iterators, Friend Operators
+
+#include "HashTableIter.C"
#include "HashTableIO.C"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
diff --git a/src/OpenFOAM/containers/HashTables/HashTable/HashTable.H b/src/OpenFOAM/containers/HashTables/HashTable/HashTable.H
index 1c42c9426a..a496b6b630 100644
--- a/src/OpenFOAM/containers/HashTables/HashTable/HashTable.H
+++ b/src/OpenFOAM/containers/HashTables/HashTable/HashTable.H
@@ -25,14 +25,15 @@ Class
Foam::HashTable
Description
- An STL-conforming hash table.
+ A HashTable similar to \c std::unordered_map.
+ The entries are considered \a unordered since their placement
+ depends on the method used to generate the hash key index, the
+ table capacity, insertion order etc. When the key order is
+ important, use the sortedToc() method to obtain a list of sorted
+ keys and use that for further access.
-Note
- Hashing index collisions are handled via chaining using a singly-linked
- list with the colliding entry being added to the head of the linked
- list. Thus copying the hash table (or indeed even resizing it) will
- often result in a different hash order. Use a sorted table-of-contents
- when the hash order is important.
+ Internally the table uses closed addressing into a flat storage space
+ with collisions handled by linked-list chaining.
The end iterator of all hash-tables has a nullptr to the hash entry.
Thus avoid separate allocation for each table and use a single one with
@@ -41,10 +42,38 @@ Note
a nullptr as its first data member.
The nullObject is such an item (with a nullptr data member).
+Note
+ For historical reasons, dereferencing the table iterator
+ (eg, \a *iter) returns a reference to the stored object value
+ value rather than the stored key/object value like std::unordered_map
+ does.
+
+ The HashTable iterator:
+ \code
+ forAllConstIters(table, iter)
+ {
+ Info<< "val:" << *iter << nl
+ << "key:" << iter.key() << nl;
+ << "val:" << iter.object() << nl;
+ }
+ \endcode
+ whereas for the \c std::unordered_map iterator:
+ \code
+ forAllConstIters(stdmap, iter)
+ {
+ Info<< "key/val:" << *iter << nl
+ << "key:" << iter->first << nl
+ << "val:" << iter->second << nl;
+ }
+ \endcode
+ This difference is most evident when using range-for syntax.
+
SourceFiles
HashTableI.H
+ HashTableIterI.H
HashTable.C
HashTableIO.C
+ HashTableIter.C
\*---------------------------------------------------------------------------*/
@@ -52,6 +81,7 @@ SourceFiles
#define HashTable_H
#include "word.H"
+#include "zero.H"
#include "Xfer.H"
#include "Hash.H"
#include "HashTableCore.H"
@@ -80,7 +110,7 @@ Ostream& operator<<(Ostream& os, const HashTable& tbl);
/*---------------------------------------------------------------------------*\
- Class HashTable Declaration
+ Class HashTable Declaration
\*---------------------------------------------------------------------------*/
template
@@ -88,6 +118,140 @@ class HashTable
:
public HashTableCore
{
+ // Private types for table entries
+
+ //- Structure with a (K,V) tuple and a linked-list for collisions
+ // Could store key/object as std::pair, but no particular advantage
+ // unless the iterator dereference type changes.
+ struct pair_entry
+ {
+ //- Type of key
+ typedef Key key_type;
+
+ //- Object content type
+ typedef T mapped_type;
+
+ //- The lookup key
+ key_type key_;
+
+ //- The data object
+ mapped_type obj_;
+
+ //- Addressing (next in collision list)
+ pair_entry* next_;
+
+ //- Construct from key, object, next pointer
+ pair_entry(const Key& key, const T& obj, pair_entry* next)
+ :
+ key_(key),
+ obj_(obj),
+ next_(next)
+ {}
+
+ //- The key
+ const key_type& key() const
+ {
+ return key_;
+ }
+
+ //- The mapped object
+ const mapped_type& mapped() const
+ {
+ return obj_;
+ }
+ mapped_type& mapped()
+ {
+ return obj_;
+ }
+
+ private:
+ //- Disallow default bitwise copy construct / assignment
+ pair_entry(const pair_entry&) = delete;
+ void operator=(const pair_entry&) = delete;
+ };
+
+
+ //- Structure with a single (K) value and a linked-list for collisions
+ struct unary_entry
+ {
+ //- Type of key
+ typedef Key key_type;
+
+ //- Object content type
+ typedef zero::null mapped_type;
+
+ //- Content storage type to the entry
+ typedef key_type value_type;
+
+ //- The lookup key == content
+ key_type key_;
+
+ //- Addressing (next in collision list)
+ unary_entry* next_;
+
+ //- Construct from key, (ununsed) object, next pointer
+ unary_entry(const Key& key, const T&, unary_entry* next)
+ :
+ key_(key),
+ next_(next)
+ {}
+
+ //- The key
+ const key_type& key() const
+ {
+ return key_;
+ }
+
+ //- Dummy mapped object
+ const mapped_type& mapped() const
+ {
+ return zeroNullElement;
+ }
+ mapped_type& mapped()
+ {
+ return zeroNullElement;
+ }
+
+
+ private:
+ //- Disallow default bitwise copy construct / assignment
+ unary_entry(const unary_entry&) = delete;
+ void operator=(const unary_entry&) = delete;
+ };
+
+
+ //- Hashed node with a linked-list for collisions
+ typedef typename std::conditional
+ <
+ std::is_same::type>::value,
+ unary_entry,
+ pair_entry
+ >::type node_type;
+
+
+ // Private Data
+
+ //- The number of nodes currently stored in table
+ label size_;
+
+ //- Number of nodes allocated in table
+ label capacity_;
+
+ //- The table of primary nodes
+ node_type** table_;
+
+
+ // Private Member Functions
+
+ //- Return the hash index of the Key within the current table size.
+ // No checks for zero-sized tables.
+ inline label hashKeyIndex(const Key& key) const;
+
+ //- Assign a new hash-entry to a possibly already existing key.
+ // \return True if the new entry was set.
+ bool set(const Key& key, const T& obj, const bool overwrite);
+
+
public:
//- The template instance used for this HashTable
@@ -96,18 +260,25 @@ public:
// STL type definitions
- //- Type of keys that the HashTable uses.
+ //- The second template parameter, type of keys used.
typedef Key key_type;
- //- Type of values that the HashTable contains.
+ //- The first template parameter, type of objects contained.
+ typedef T mapped_type;
+
+ //- Same as mapped_type for OpenFOAM HashTables
+ // Note that this is different than the std::map definition.
typedef T value_type;
+ //- The third template parameter, the hash index method.
+ typedef Hash hasher;
+
//- The type used for storing into value_type objects.
- // This type is usually value_type&.
+ // This type is usually 'value_type*'.
typedef T* pointer;
//- The type used for storing into value_type objects.
- // This type is usually value_type&.
+ // This type is usually 'value_type&'.
typedef T& reference;
//- The type used for reading from constant value_type objects.
@@ -129,65 +300,14 @@ public:
//- Forward iterator with const access
class const_iterator;
-
-private:
-
- // Private data type for table entries
-
- //- Structure to hold a hashed entry, with a linked-list for collisions
- struct hashedEntry
- {
- //- The lookup key
- Key key_;
-
- //- The data object
- T obj_;
-
- //- Pointer to next hashedEntry in sub-list
- hashedEntry* next_;
-
- //- Construct from key, object, next pointer
- inline hashedEntry(const Key& key, const T& obj, hashedEntry* next);
-
- private:
- //- Disallow default bitwise copy construct
- hashedEntry(const hashedEntry&) = delete;
-
- //- Disallow default bitwise assignment
- void operator=(const hashedEntry&) = delete;
- };
-
-
- // Private data: size of table, the table and current number of elements
-
- //- The current number of elements in table
- label nElmts_;
-
- //- Number of primary entries allocated in table
- label tableSize_;
-
- //- The table of primary entries
- hashedEntry** table_;
-
-
- // Private Member Functions
-
- //- Return the hash index of the Key within the current table size.
- // No checks for zero-sized tables.
- inline label hashKeyIndex(const Key& key) const;
-
- //- Assign a new hash-entry to a possibly already existing key.
- // \return True if the new entry was set.
- bool set(const Key& key, const T& obj, const bool protect);
-
-
protected:
//- Internally used base for iterator and const_iterator
- class iterator_base;
+ template class Iterator;
- //- Friendship with the iterator_base is required.
- friend class iterator_base;
+ //- Friendship with the base iterator is required.
+ friend class Iterator;
+ friend class Iterator;
// Protected Member Functions
@@ -205,20 +325,23 @@ public:
// Constructors
- //- Construct given initial table size
- HashTable(const label size = 128);
+ //- Construct null with default (128) table size
+ HashTable();
- //- Construct from Istream
+ //- Construct given initial table size
+ explicit HashTable(const label size);
+
+ //- Construct from Istream with default table size
HashTable(Istream& is, const label size = 128);
//- Construct as copy
- HashTable(const HashTable& ht);
+ HashTable(const this_type& ht);
//- Move construct
- HashTable(HashTable&& ht);
+ HashTable(this_type&& ht);
//- Construct by transferring the parameter contents
- HashTable(const Xfer>& ht);
+ HashTable(const Xfer& ht);
//- Construct from an initializer list
HashTable(std::initializer_list> lst);
@@ -242,19 +365,19 @@ public:
inline bool empty() const;
//- Return true if hashed entry is found in table
- bool found(const Key& key) const;
+ inline bool found(const Key& key) const;
//- Find and return an iterator set at the hashed entry
// If not found iterator = end()
- iterator find(const Key& key);
+ inline iterator find(const Key& key);
//- Find and return an const_iterator set at the hashed entry
// If not found iterator = end()
- const_iterator find(const Key& key) const;
+ inline const_iterator find(const Key& key) const;
//- Find and return an const_iterator set at the hashed entry
// If not found iterator = end()
- const_iterator cfind(const Key& key) const;
+ inline const_iterator cfind(const Key& key) const;
//- Return hashed entry if it exists, or return the given default
inline const T& lookup(const Key& key, const T& deflt) const;
@@ -512,149 +635,157 @@ protected:
// and prevent most external usage.
// iterator and const_iterator have the same size, allowing
// us to reinterpret_cast between them (if desired)
- class iterator_base
+
+ template
+ class Iterator
{
public:
- // Public typedefs
- using table_type = this_type;
- using key_type = this_type::key_type;
+ // Typedefs
using iterator_category = std::forward_iterator_tag;
- using difference_type = this_type::difference_type;
+ using difference_type = this_type::difference_type;
- private:
- using entry_type = hashedEntry;
+ //- The HashTable container type
+ using table_type = typename std::conditional
+ <
+ Const,
+ const this_type,
+ this_type
+ >::type;
- // Private Data
+ //- The node-type being addressed
+ using node_type = typename std::conditional
+ <
+ Const,
+ const this_type::node_type,
+ this_type::node_type
+ >::type;
- //- Currently selected entry.
- // MUST be the first member for easy comparison between iterators
- // and for reinterpret_cast from nullObject
- entry_type* entryPtr_;
+ //- The key type
+ using key_type = this_type::key_type;
- //- Pointer to the hash-table for which this is an iterator
- // This allows use of the default bitwise copy/assignment
- table_type* hashTable_;
+ //- The object type being addressed
+ using mapped_type = typename std::conditional
+ <
+ Const,
+ const this_type::mapped_type,
+ this_type::mapped_type
+ >::type;
+
+
+ // Member Functions
+
+ //- True if iterator points to an entry
+ // This can be used directly instead of comparing to end()
+ inline bool found() const;
+
+ //- The key associated with the iterator
+ inline const Key& key() const;
+
+ // Member Operators
+
+ //- Compare hash-entry element pointers.
+ // Independent of const/non-const access
+ inline bool operator==(const Iterator& iter) const;
+ inline bool operator!=(const Iterator& iter) const;
+
+ inline bool operator==(const Iterator& iter) const;
+ inline bool operator!=(const Iterator& iter) const;
- //- Current hash index within the hash-table data.
- // A signed value, since erase() uses a negative value to signal
- // the erasure state.
- label hashIndex_;
protected:
+ friend class HashTable; // For begin/find constructors
+
+ // Protected Data
+
+ //- The selected entry.
+ // MUST be the first member for easy comparison between iterators
+ // and for reinterpret_cast from nullObject
+ node_type* entry_;
+
+ //- The hash-table container being iterated on.
+ // Using a pointer allows default bitwise copy/assignment
+ table_type* container_;
+
+ //- Index within the hash-table data.
+ // A signed value, since iterator_erase() needs a negative value
+ // to mark the position.
+ label index_;
+
+
+ // Protected Constructors
+
+ //- Construct null (end iterator)
+ inline Iterator();
+
+ //- Construct from begin of hash-table
+ inline Iterator(bool, table_type* tbl);
+
+ //- Construct by finding key in hash table
+ inline Iterator(table_type* tbl, const Key& key);
+
// Protected Member Functions
//- Increment to the next position
inline void increment();
- //- The referenced object/value element
- inline T& element() const;
+ //- The object associated with the iterator
+ inline mapped_type& object() const
+ {
+ return entry_->mapped();
+ }
- //- Erase the entry at the current position
- bool erase();
-
-
- public:
-
- // Constructors
-
- //- Construct null (end iterator)
- inline iterator_base();
-
- //- Construct from begin of hash-table
- inline explicit iterator_base(const table_type* hashTbl);
-
- //- Construct from hash table, element and hash index
- inline iterator_base
- (
- const table_type* hashTbl,
- const entry_type* elmt,
- const label hashIndex
- );
-
- // Member functions/operators
-
- //- True if iterator points to an entry
- // This can be used directly instead of comparing to end()
- inline bool found() const;
-
- //- Return the Key corresponding to the iterator
- inline const Key& key() const;
-
- //- Compare hash-entry element pointers
- inline bool operator==(const iterator_base& iter) const;
- inline bool operator!=(const iterator_base& iter) const;
+ //- Permit an explicit cast to the other (const/non-const) searcher
+ inline explicit operator const Iterator&() const
+ {
+ return *reinterpret_cast*>(this);
+ }
};
+ //- Low-level entry erasure using iterator internals.
+ // This invalidates the iterator until the next ++ operation.
+ // \return True if the corresponding entry existed and was removed
+ bool iterator_erase(node_type*& entry, label& index);
+
public:
- //- An iterator wrapper for returning a reference to the key
- template
- class key_iterator_base
- :
- public WrappedIterator
- {
- public:
- using value_type = this_type::key_type;
- using pointer = const Key*;
- using reference = const Key&;
-
- //- Implicit conversion
- inline key_iterator_base(const WrappedIterator& iter);
-
- //- Return the key
- inline reference operator*() const;
- inline reference operator()() const;
-
- inline key_iterator_base& operator++();
- inline key_iterator_base operator++(int);
- };
-
-
- // STL iterator
-
//- Forward iterator with non-const access
class iterator
:
- public iterator_base
+ public Iterator
{
- friend class HashTable; // Uses iterator::erase() method
- using entry_type = hashedEntry;
-
public:
+ // Typedefs
+ using iterator_category = std::forward_iterator_tag;
+ using difference_type = this_type::difference_type;
- // Public typedefs
- using table_type = this_type;
- using value_type = this_type::value_type;
- using pointer = this_type::pointer;
- using reference = this_type::reference;
+ using key_type = this_type::key_type;
+ using mapped_type = this_type::mapped_type;
+ using value_type = this_type::value_type;
+ using pointer = this_type::pointer;
+ using reference = this_type::reference;
// Constructors
//- Construct null (end iterator)
- inline iterator();
+ inline iterator() {}
- //- Construct from begin of hash-table
- inline explicit iterator(table_type* hashTbl);
+ //- Copy construct from similar access type
+ inline explicit iterator(const Iterator& iter)
+ :
+ Iterator(iter)
+ {}
- //- Construct from hash table, element and hash index
- // Used by the hash-table find() method.
- inline iterator
- (
- table_type* hashTbl,
- entry_type* elmt,
- const label hashIndex
- );
// Member functions/operators
- //- Return non-const access to referenced object
- inline reference object() const;
+ //- Non-const access to referenced object
+ using Iterator::object;
- //- Return non-const access to referenced object
- inline reference operator*() const;
- inline reference operator()() const;
+ //- Non-const access to referenced object
+ inline reference operator*() const { return this->object(); }
+ inline reference operator()() const { return this->object(); }
inline iterator& operator++();
inline iterator operator++(int);
@@ -666,54 +797,111 @@ public:
//- Forward iterator with const access
class const_iterator
:
- public iterator_base
+ public Iterator
{
- using entry_type = const hashedEntry;
-
public:
+ // Typedefs
+ using iterator_category = std::forward_iterator_tag;
+ using difference_type = this_type::difference_type;
- // Public typedefs
- using table_type = const this_type;
- using value_type = const this_type::value_type;
- using pointer = this_type::const_pointer;
- using reference = this_type::const_reference;
+ using key_type = this_type::key_type;
+ using mapped_type = const this_type::mapped_type;
+ using value_type = const this_type::value_type;
+ using pointer = this_type::const_pointer;
+ using reference = this_type::const_reference;
// Constructors
//- Construct null (end iterator)
- inline const_iterator();
+ inline const_iterator() {}
- //- Construct from begin of hash-table
- inline explicit const_iterator(table_type* hashTbl);
+ //- Copy construct from similar access type
+ inline explicit const_iterator(const Iterator& iter)
+ :
+ Iterator(iter)
+ {}
- //- Construct from hash table, element and hash index.
- // Used by the hash-table find() method.
- inline const_iterator
- (
- table_type* hashTbl,
- entry_type* elmt,
- const label hashIndex
- );
+ //- Copy construct from dissimilar access type
+ inline explicit const_iterator(const Iterator& iter)
+ :
+ Iterator
+ (
+ static_cast&>(iter)
+ )
+ {}
+
+ //- Implicit conversion from dissimilar access type
+ inline const_iterator(const iterator& iter)
+ :
+ const_iterator(reinterpret_cast(iter))
+ {}
- //- Copy construct from iterator
- inline const_iterator(const iterator& iter);
// Member functions/operators
- //- Return const access to referenced object
- inline reference object() const;
+ //- Const access to referenced object
+ using Iterator::object;
- //- Return const access to referenced object
- inline reference operator*() const;
- inline reference operator()() const;
+ //- Const access to referenced object
+ inline reference operator*() const { return this->object(); }
+ inline reference operator()() const { return this->object(); }
inline const_iterator& operator++();
inline const_iterator operator++(int);
+
+ // Assignment
+
+ const_iterator& operator=(const const_iterator&) = default;
+
+ // Allow assign from iterator to const_iterator
+ const_iterator& operator=(const iterator& iter)
+ {
+ return this->operator=
+ (
+ reinterpret_cast(iter)
+ );
+ }
};
//- Iterating over keys only
+ //- An iterator wrapper for returning a reference to the key
+ template
+ class key_iterator_base
+ :
+ public Iter
+ {
+ public:
+ using value_type = this_type::key_type;
+ using pointer = const Key*;
+ using reference = const Key&;
+
+ //- Implicit conversion
+ inline key_iterator_base(const Iter& iter)
+ :
+ Iter(iter)
+ {}
+
+ //- Return the key
+ inline reference operator*() const { return this->key(); }
+ inline reference operator()() const { return this->key(); }
+
+ inline key_iterator_base& operator++()
+ {
+ this->increment();
+ return *this;
+ }
+
+ inline key_iterator_base operator++(int)
+ {
+ key_iterator_base iter(*this);
+ this->increment();
+ return iter;
+ }
+ };
+
+
//- Forward iterator returning the key
using key_iterator = key_iterator_base;
@@ -793,6 +981,7 @@ inline void Swap
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#include "HashTableI.H"
+#include "HashTableIterI.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
diff --git a/src/OpenFOAM/containers/HashTables/HashTable/HashTableCore.C b/src/OpenFOAM/containers/HashTables/HashTable/HashTableCore.C
index d6d7bef31d..059be27e3e 100644
--- a/src/OpenFOAM/containers/HashTables/HashTable/HashTableCore.C
+++ b/src/OpenFOAM/containers/HashTables/HashTable/HashTableCore.C
@@ -36,6 +36,8 @@ defineTypeNameAndDebug(HashTableCore, 0);
// Approximately labelMax/4
const Foam::label Foam::HashTableCore::maxTableSize(1L << (sizeof(label)*8-3));
+Foam::zero::null Foam::HashTableCore::zeroNullElement;
+
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
@@ -50,7 +52,7 @@ Foam::label Foam::HashTableCore::canonicalSize(const label requested_size)
return maxTableSize;
}
- // Enforce power of two - makes for a very fast modulus.
+ // Enforce power of two for fast modulus in hash index calculations.
// Use unsigned for these calculations.
//
// - The lower limit (8) is somewhat arbitrary, but if the hash table
@@ -65,7 +67,8 @@ Foam::label Foam::HashTableCore::canonicalSize(const label requested_size)
{
return powerOfTwo;
}
- else if (size & (size-1)) // <- Modulus of i^2
+
+ if (size & (size-1)) // <- Modulus of i^2
{
// Determine power-of-two. Brute-force is fast enough.
while (powerOfTwo < size)
@@ -75,10 +78,8 @@ Foam::label Foam::HashTableCore::canonicalSize(const label requested_size)
return powerOfTwo;
}
- else
- {
- return size;
- }
+
+ return size;
}
diff --git a/src/OpenFOAM/containers/HashTables/HashTable/HashTableCore.H b/src/OpenFOAM/containers/HashTables/HashTable/HashTableCore.H
index 46cc0c3f46..76feeb2105 100644
--- a/src/OpenFOAM/containers/HashTables/HashTable/HashTableCore.H
+++ b/src/OpenFOAM/containers/HashTables/HashTable/HashTableCore.H
@@ -40,6 +40,7 @@ SourceFiles
#include "uLabel.H"
#include "className.H"
#include "nullObject.H"
+#include "zero.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@@ -77,20 +78,16 @@ struct HashTableCore
template
inline static IteratorType iterator_begin(TableType& table);
- //- Factory method to create a const iterator begin
- template
- inline static IteratorType iterator_begin(const TableType& table);
-
//- Factory method to create a const iterator begin
template
inline static IteratorType iterator_cbegin(const TableType& table);
- //- Factory method to create a non-const iterator end
+ //- Factory method to return an iterator end
// Simply reinterprets a NullObject as a hash-table iterator.
template
inline static const IteratorType& iterator_end();
- //- Factory method to create a const iterator cend
+ //- Factory method to return an iterator cend
// Simply reinterprets a NullObject as a hash-table iterator.
template
inline static const IteratorType& iterator_cend();
@@ -116,6 +113,13 @@ struct HashTableCore
inline const IteratorType& end() const;
inline const IteratorType& cend() const;
};
+
+
+protected:
+
+ //- A static zero::null for dereferencing as a dummy HashSet element
+ static zero::null zeroNullElement;
+
};
diff --git a/src/OpenFOAM/containers/HashTables/HashTable/HashTableCoreI.H b/src/OpenFOAM/containers/HashTables/HashTable/HashTableCoreI.H
index 8198e2e4c3..1e59a0baa5 100644
--- a/src/OpenFOAM/containers/HashTables/HashTable/HashTableCoreI.H
+++ b/src/OpenFOAM/containers/HashTables/HashTable/HashTableCoreI.H
@@ -35,16 +35,6 @@ inline IteratorType Foam::HashTableCore::iterator_begin
}
-template
-inline IteratorType Foam::HashTableCore::iterator_begin
-(
- const TableType& table
-)
-{
- return IteratorType(table.begin());
-}
-
-
template
inline IteratorType Foam::HashTableCore::iterator_cbegin
(
diff --git a/src/OpenFOAM/containers/HashTables/HashTable/HashTableI.H b/src/OpenFOAM/containers/HashTables/HashTable/HashTableI.H
index 50f0c42d1e..27ac134006 100644
--- a/src/OpenFOAM/containers/HashTables/HashTable/HashTableI.H
+++ b/src/OpenFOAM/containers/HashTables/HashTable/HashTableI.H
@@ -25,30 +25,14 @@ License
#include "error.H"
-// * * * * * * * * * * * * * Private Member Classes * * * * * * * * * * * * //
-
-template
-inline Foam::HashTable::hashedEntry::hashedEntry
-(
- const Key& key,
- const T& obj,
- hashedEntry* next
-)
-:
- key_(key),
- obj_(obj),
- next_(next)
-{}
-
-
// * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * //
template
inline Foam::label
Foam::HashTable::hashKeyIndex(const Key& key) const
{
- // size is power of two - this is the modulus
- return Hash()(key) & (tableSize_ - 1);
+ // capacity is always a power of two - this is the modulus
+ return Hash()(key) & (capacity_ - 1);
}
@@ -57,21 +41,76 @@ Foam::HashTable::hashKeyIndex(const Key& key) const
template
inline Foam::label Foam::HashTable::capacity() const
{
- return tableSize_;
+ return capacity_;
}
template
inline Foam::label Foam::HashTable::size() const
{
- return nElmts_;
+ return size_;
}
template
inline bool Foam::HashTable::empty() const
{
- return !nElmts_;
+ return !size_;
+}
+
+
+template
+bool Foam::HashTable::found(const Key& key) const
+{
+ if (size_)
+ {
+ return Iterator(this, key).found();
+ }
+
+ return false;
+}
+
+
+template
+inline typename Foam::HashTable::iterator
+Foam::HashTable::find
+(
+ const Key& key
+)
+{
+ if (size_)
+ {
+ return iterator(Iterator(this, key));
+ }
+
+ return iterator();
+}
+
+
+template
+inline typename Foam::HashTable