ENH: improvements to HashTable internals

- make single-parameter construct (label) explicit
- consolidate iterators
- slightly reduced overhead for some HashSet types
- improved resizing behaviour
- compact output for empty Ptr hashes
This commit is contained in:
Mark Olesen
2017-10-31 08:46:47 +01:00
parent db552fb751
commit 053a648ee6
23 changed files with 1484 additions and 1020 deletions

View File

@ -1,3 +0,0 @@
Test-hashTable.C
EXE = $(FOAM_USER_APPBIN)/Test-hashTable

View File

@ -0,0 +1,3 @@
Test-HashTable1.C
EXE = $(FOAM_USER_APPBIN)/Test-HashTable1

View File

@ -0,0 +1,3 @@
Test-HashTable4.C
EXE = $(FOAM_USER_APPBIN)/Test-HashTable4

View File

@ -0,0 +1,2 @@
/* EXE_INC = -I$(LIB_SRC)/cfdTools/include */
/* EXE_LIBS = -lfiniteVolume */

View File

@ -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 <http://www.gnu.org/licenses/>.
Description
Test HashTable resizing
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "HashSet.H"
#include "HashTable.H"
#include "Map.H"
#include "cpuTime.H"
#include "memInfo.H"
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
// #undef ORDERED
// #define ORDERED
using namespace Foam;
template<class T>
Ostream& printInfo(Ostream& os, const HashTable<T, T, Hash<T>>& ht)
{
os << " (size " << ht.size() << " capacity " << ht.capacity() << ") ";
return os;
}
template<class K, class V>
inline void insertElem
(
#ifdef ORDERED
std::set<K>& container,
#else
std::unordered_set<K, Hash<K>>& container,
#endif
K k,
V v
)
{
container.insert(k);
}
template<class K, class V>
inline void insertElem
(
#ifdef ORDERED
std::map<K, V>& container,
#else
std::unordered_map<K, V, Hash<K>>& container,
#endif
K k,
V v
)
{
container.insert(std::make_pair(k, v));
}
template<class K, class V>
inline void insertElem
(
HashSet<K, Hash<K>>& container,
K k,
V v
)
{
container.insert(k);
}
template<class K, class V>
inline void insertElem
(
HashTable<K, V, Hash<K>>& container,
K k,
V v
)
{
container.insert(k, v);
}
template<class Container>
inline void loopInsert(Container& container, const label n)
{
for (label i = 0; i < n; i++)
{
insertElem(container, i, i);
}
}
template<class Container>
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<label, label, Hash<label>> 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<label> map;
#else
Info<< "using stl::unordered_set" << endl;
std::unordered_set<label, Hash<label>> 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<label> map;
#else
std::unordered_set<label, Hash<label>> 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<label, label> map;
#else
std::unordered_map<label, label, Hash<label>> map(32);
#endif
loopInsert(map, nElem);
}
}
}
else
{
if (optFnd)
{
Info<< "using HashSet" << endl;
HashSet<label, Hash<label>> 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<label, Hash<label>> map(32);
loopInsert(map, nElem);
}
}
else
{
Info<< "using HashTable" << endl;
for (label loopi = 0; loopi < nLoops; ++loopi)
{
HashTable<label, label, Hash<label>> map(32);
loopInsert(map, nElem);
}
}
}
Info<< timer.cpuTimeIncrement() << " s\n";
Info<< "mem info: " << mem.update() << endl;
return 0;
}
// ************************************************************************* //

View File

@ -28,6 +28,13 @@ License
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
template<class T, class Key, class Hash>
Foam::HashPtrTable<T, Key, Hash>::HashPtrTable()
:
parent_type()
{}
template<class T, class Key, class Hash>
Foam::HashPtrTable<T, Key, Hash>::HashPtrTable(const label size)
:
@ -157,6 +164,7 @@ void Foam::HashPtrTable<T, Key, Hash>::operator=
}
}
// * * * * * * * * * * * * * * * Friend Operators * * * * * * * * * * * * * //
#include "HashPtrTableIO.C"

View File

@ -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<class INew>
@ -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

View File

@ -41,7 +41,7 @@ void Foam::HashPtrTable<T, Key, Hash>::read(Istream& is, const INew& inewt)
is.fatalCheck
(
"HashPtrTable<T, Key, Hash>::read(Istream&, const INew&) : "
"HashPtrTable::read(Istream&, const INew&) : "
"reading first token"
);
@ -50,7 +50,7 @@ void Foam::HashPtrTable<T, Key, Hash>::read(Istream& is, const INew& inewt)
const label s = firstToken.labelToken();
// Read beginning of contents
const char delimiter = is.readBeginList("HashPtrTable<T, Key, Hash>");
const char delimiter = is.readBeginList("HashPtrTable");
if (s)
{
@ -69,8 +69,8 @@ void Foam::HashPtrTable<T, Key, Hash>::read(Istream& is, const INew& inewt)
is.fatalCheck
(
"HashPtrTable<T, Key, Hash>::"
"read(Istream&, const INew&) : reading entry"
"HashPtrTable::read(Istream&, const INew&) : "
"reading entry"
);
}
}
@ -114,7 +114,7 @@ void Foam::HashPtrTable<T, Key, Hash>::read(Istream& is, const INew& inewt)
is.fatalCheck
(
"HashPtrTable<T, Key, Hash>::read(Istream&, const INew&) : "
"HashPtrTable::read(Istream&, const INew&) : "
"reading entry"
);
@ -155,7 +155,7 @@ void Foam::HashPtrTable<T, Key, Hash>::read
template<class T, class Key, class Hash>
void Foam::HashPtrTable<T, Key, Hash>::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<T, Key, Hash>& tbl
)
{
using const_iterator = typename HashPtrTable<T, Key, Hash>::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;
}

View File

@ -79,7 +79,7 @@ inline Foam::label Foam::HashSet<Key, Hash>::assignMultiple
template<class Key, class Hash>
Foam::HashSet<Key, Hash>::HashSet(const UList<Key>& lst)
:
HashTable<nil, Key, Hash>(2*lst.size())
parent_type(2*lst.size())
{
for (const auto& k : lst)
{
@ -92,7 +92,7 @@ template<class Key, class Hash>
template<unsigned Size>
Foam::HashSet<Key, Hash>::HashSet(const FixedList<Key, Size>& lst)
:
HashTable<nil, Key, Hash>(2*lst.size())
parent_type(2*lst.size())
{
for (const auto& k : lst)
{
@ -104,7 +104,7 @@ Foam::HashSet<Key, Hash>::HashSet(const FixedList<Key, Size>& lst)
template<class Key, class Hash>
Foam::HashSet<Key, Hash>::HashSet(std::initializer_list<Key> lst)
:
HashTable<nil, Key, Hash>(2*lst.size())
parent_type(2*lst.size())
{
for (const auto& k : lst)
{
@ -120,7 +120,7 @@ Foam::HashSet<Key, Hash>::HashSet
const HashTable<AnyType, Key, AnyHash>& tbl
)
:
HashTable<nil, Key, Hash>(tbl.capacity())
parent_type(tbl.capacity())
{
using other_iter =
typename HashTable<AnyType, Key, AnyHash>::const_iterator;
@ -349,7 +349,7 @@ template<class Key, class Hash>
inline typename Foam::HashSet<Key, Hash>::const_iterator
Foam::HashSet<Key, Hash>::begin() const
{
return HashTableCore::iterator_begin<const_iterator>
return HashTableCore::iterator_cbegin<const_iterator>
(
static_cast<const parent_type&>(*this)
);
@ -360,7 +360,7 @@ template<class Key, class Hash>
inline typename Foam::HashSet<Key, Hash>::const_iterator
Foam::HashSet<Key, Hash>::cbegin() const
{
return HashTableCore::iterator_begin<const_iterator>
return HashTableCore::iterator_cbegin<const_iterator>
(
static_cast<const parent_type&>(*this)
);
@ -379,7 +379,7 @@ template<class Key, class Hash>
inline const typename Foam::HashSet<Key, Hash>::const_iterator&
Foam::HashSet<Key, Hash>::end() const
{
return HashTableCore::iterator_end<const_iterator>();
return HashTableCore::iterator_cend<const_iterator>();
}

View File

@ -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<label> 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<Key, Hash>& tbl);
template<class Key=word, class Hash=string::hash>
class HashSet
:
public HashTable<nil, Key, Hash>
public HashTable<zero::null, Key, Hash>
{
// Private Member Functions
@ -94,7 +110,7 @@ public:
typedef HashSet<Key, Hash> this_type;
//- The template instance used for the parent HashTable
typedef HashTable<nil, Key, Hash> parent_type;
typedef HashTable<zero::null, Key, Hash> 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<HashSet<Key, Hash>>& hs)
HashSet(const Xfer<this_type>& hs)
:
parent_type(hs)
{}
//- Construct by transferring the parameter contents
HashSet(const Xfer<HashTable<nil, Key, Hash>>& hs)
HashSet(const Xfer<parent_type>& 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<Key> 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<Key>& lst)
{
return insert(lst);
}
//- Same as insert (cannot overwrite nil content)
//- Same as insert (no value to overwrite)
template<unsigned Size>
label set(const FixedList<Key, Size>& lst)
{
return insert(lst);
}
//- Same as insert (cannot overwrite nil content)
//- Same as insert (no value to overwrite)
label set(std::initializer_list<Key> lst)
{
return insert(lst);
@ -331,22 +353,22 @@ public:
// Logical operations
//- Combine entries from HashSets
void operator|=(const HashSet<Key, Hash>& rhs);
void operator|=(const this_type& rhs);
//- Only retain entries found in both HashSets
inline void operator&=(const HashSet<Key, Hash>& rhs);
inline void operator&=(const this_type& rhs);
//- Only retain unique entries (xor)
void operator^=(const HashSet<Key, Hash>& rhs);
void operator^=(const this_type& rhs);
//- Add entries listed in the given HashSet to this HashSet
inline void operator+=(const HashSet<Key, Hash>& 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<Key, Hash>& rhs);
inline void operator-=(const this_type& rhs);
// IOstream Operator

View File

@ -61,21 +61,27 @@ Foam::label Foam::HashTable<T, Key, Hash>::eraseMultiple
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
template<class T, class Key, class Hash>
Foam::HashTable<T, Key, Hash>::HashTable()
:
HashTable<T, Key, Hash>(128)
{}
template<class T, class Key, class Hash>
Foam::HashTable<T, Key, Hash>::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<T, Key, Hash>::HashTable(const label size)
template<class T, class Key, class Hash>
Foam::HashTable<T, Key, Hash>::HashTable(const HashTable<T, Key, Hash>& ht)
:
HashTable<T, Key, Hash>(ht.tableSize_)
HashTable<T, Key, Hash>(ht.capacity_)
{
for (const_iterator iter = ht.cbegin(); iter != ht.cend(); ++iter)
{
@ -96,10 +102,7 @@ Foam::HashTable<T, Key, Hash>::HashTable(const HashTable<T, Key, Hash>& ht)
template<class T, class Key, class Hash>
Foam::HashTable<T, Key, Hash>::HashTable(HashTable<T, Key, Hash>&& ht)
:
HashTableCore(),
nElmts_(0),
tableSize_(0),
table_(nullptr)
HashTable<T, Key, Hash>(0)
{
transfer(ht);
}
@ -111,10 +114,7 @@ Foam::HashTable<T, Key, Hash>::HashTable
const Xfer<HashTable<T, Key, Hash>>& ht
)
:
HashTableCore(),
nElmts_(0),
tableSize_(0),
table_(nullptr)
HashTable<T, Key, Hash>(0)
{
transfer(ht());
}
@ -150,110 +150,10 @@ Foam::HashTable<T, Key, Hash>::~HashTable()
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
template<class T, class Key, class Hash>
bool Foam::HashTable<T, Key, Hash>::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<class T, class Key, class Hash>
typename Foam::HashTable<T, Key, Hash>::iterator
Foam::HashTable<T, Key, Hash>::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<class T, class Key, class Hash>
typename Foam::HashTable<T, Key, Hash>::const_iterator
Foam::HashTable<T, Key, Hash>::find
(
const Key& key
) const
{
return this->cfind(key);
}
template<class T, class Key, class Hash>
typename Foam::HashTable<T, Key, Hash>::const_iterator
Foam::HashTable<T, Key, Hash>::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<class T, class Key, class Hash>
Foam::List<Key> Foam::HashTable<T, Key, Hash>::toc() const
{
List<Key> keyLst(nElmts_);
List<Key> keyLst(size_);
label count = 0;
for (const_iterator iter = cbegin(); iter != cend(); ++iter)
@ -297,7 +197,7 @@ Foam::List<Key> Foam::HashTable<T, Key, Hash>::tocKeys
const bool invert
) const
{
List<Key> keyLst(nElmts_);
List<Key> keyLst(size_);
label count = 0;
for (const_iterator iter = cbegin(); iter != cend(); ++iter)
@ -323,7 +223,7 @@ Foam::List<Key> Foam::HashTable<T, Key, Hash>::tocValues
const bool invert
) const
{
List<Key> keyLst(nElmts_);
List<Key> keyLst(size_);
label count = 0;
for (const_iterator iter = cbegin(); iter != cend(); ++iter)
@ -349,7 +249,7 @@ Foam::List<Key> Foam::HashTable<T, Key, Hash>::tocEntries
const bool invert
) const
{
List<Key> keyLst(nElmts_);
List<Key> keyLst(size_);
label count = 0;
for (const_iterator iter = cbegin(); iter != cend(); ++iter)
@ -438,36 +338,36 @@ bool Foam::HashTable<T, Key, Hash>::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<T, Key, Hash>::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<T, Key, Hash>::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<class T, class Key, class Hash>
bool Foam::HashTable<T, Key, Hash>::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<hashedEntry*>(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<class T, class Key, class Hash>
bool Foam::HashTable<T, Key, Hash>::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<iterator&>(iter).erase();
iterator& it = const_cast<iterator&>(iter);
return iterator_erase(it.entry_, it.index_);
}
@ -599,7 +444,7 @@ bool Foam::HashTable<T, Key, Hash>::erase(const Key& key)
template<class T, class Key, class Hash>
Foam::label Foam::HashTable<T, Key, Hash>::erase(const UList<Key>& keys)
{
return eraseMultiple(keys.begin(), keys.end());
return eraseMultiple(keys.cbegin(), keys.cend());
}
@ -610,7 +455,7 @@ Foam::label Foam::HashTable<T, Key, Hash>::erase
const FixedList<Key, Size>& keys
)
{
return eraseMultiple(keys.begin(), keys.end());
return eraseMultiple(keys.cbegin(), keys.cend());
}
@ -634,17 +479,14 @@ Foam::label Foam::HashTable<T, Key, Hash>::erase
const label nTotal = this->size();
label changed = 0;
using other_iter =
typename HashTable<AnyType, Key, AnyHash>::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<T, Key, Hash>::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<T, Key, Hash>::retain
template<class T, class Key, class Hash>
void Foam::HashTable<T, Key, Hash>::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<T, Key, Hash>::resize(const label sz)
return;
}
HashTable<T, Key, Hash>* tmpTable = new HashTable<T, Key, Hash>(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<class T, class Key, class Hash>
void Foam::HashTable<T, Key, Hash>::clear()
{
if (nElmts_)
for (label i=0; size_ && i<capacity_; ++i)
{
for (label hashIdx = 0; hashIdx < tableSize_; hashIdx++)
for (node_type* ep = table_[i]; ep; /*nil*/)
{
if (table_[hashIdx])
{
hashedEntry* ep = table_[hashIdx];
while (hashedEntry* next = ep->next_)
{
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<T, Key, Hash>::clearStorage()
template<class T, class Key, class Hash>
void Foam::HashTable<T, Key, Hash>::swap(HashTable<T, Key, Hash>& 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<class T, class Key, class Hash>
void Foam::HashTable<T, Key, Hash>::transfer(HashTable<T, Key, Hash>& 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<T, Key, Hash>::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<T, Key, Hash>::operator=
)
{
// Could be zero-sized from a previous transfer()
if (!tableSize_)
if (!capacity_)
{
resize(2*lst.size());
}
@ -975,7 +858,7 @@ bool Foam::HashTable<T, Key, Hash>::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<T, Key, Hash>::operator!=
}
// * * * * * * * * * * * * * * * Friend Operators * * * * * * * * * * * * * //
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Iterators, Friend Operators
#include "HashTableIter.C"
#include "HashTableIO.C"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

View File

@ -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<T, Key, Hash>& tbl);
/*---------------------------------------------------------------------------*\
Class HashTable Declaration
Class HashTable Declaration
\*---------------------------------------------------------------------------*/
template<class T, class Key=word, class Hash=string::hash>
@ -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<zero::null, typename std::remove_cv<T>::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<bool Const> class Iterator;
//- Friendship with the iterator_base is required.
friend class iterator_base;
//- Friendship with the base iterator is required.
friend class Iterator<true>;
friend class Iterator<false>;
// 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<T, Key, Hash>& ht);
HashTable(const this_type& ht);
//- Move construct
HashTable(HashTable<T, Key, Hash>&& ht);
HashTable(this_type&& ht);
//- Construct by transferring the parameter contents
HashTable(const Xfer<HashTable<T, Key, Hash>>& ht);
HashTable(const Xfer<this_type>& ht);
//- Construct from an initializer list
HashTable(std::initializer_list<std::pair<Key, T>> 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<bool Const>
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<true>& iter) const;
inline bool operator!=(const Iterator<true>& iter) const;
inline bool operator==(const Iterator<false>& iter) const;
inline bool operator!=(const Iterator<false>& 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>&() const
{
return *reinterpret_cast<const Iterator<!Const>*>(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 WrappedIterator>
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<false>
{
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<false>& iter)
:
Iterator<false>(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<false>::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<true>
{
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<true>& iter)
:
Iterator<true>(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<false>& iter)
:
Iterator<true>
(
static_cast<const Iterator<true>&>(iter)
)
{}
//- Implicit conversion from dissimilar access type
inline const_iterator(const iterator& iter)
:
const_iterator(reinterpret_cast<const const_iterator&>(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<true>::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<const const_iterator&>(iter)
);
}
};
//- Iterating over keys only
//- An iterator wrapper for returning a reference to the key
template<class Iter>
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<iterator>;
@ -793,6 +981,7 @@ inline void Swap
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#include "HashTableI.H"
#include "HashTableIterI.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

View File

@ -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;
}

View File

@ -40,6 +40,7 @@ SourceFiles
#include "uLabel.H"
#include "className.H"
#include "nullObject.H"
#include "zero.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -77,20 +78,16 @@ struct HashTableCore
template<class IteratorType, class TableType>
inline static IteratorType iterator_begin(TableType& table);
//- Factory method to create a const iterator begin
template<class IteratorType, class TableType>
inline static IteratorType iterator_begin(const TableType& table);
//- Factory method to create a const iterator begin
template<class IteratorType, class TableType>
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<class IteratorType>
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<class IteratorType>
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;
};

View File

@ -35,16 +35,6 @@ inline IteratorType Foam::HashTableCore::iterator_begin
}
template<class IteratorType, class TableType>
inline IteratorType Foam::HashTableCore::iterator_begin
(
const TableType& table
)
{
return IteratorType(table.begin());
}
template<class IteratorType, class TableType>
inline IteratorType Foam::HashTableCore::iterator_cbegin
(

View File

@ -25,30 +25,14 @@ License
#include "error.H"
// * * * * * * * * * * * * * Private Member Classes * * * * * * * * * * * * //
template<class T, class Key, class Hash>
inline Foam::HashTable<T, Key, Hash>::hashedEntry::hashedEntry
(
const Key& key,
const T& obj,
hashedEntry* next
)
:
key_(key),
obj_(obj),
next_(next)
{}
// * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * //
template<class T, class Key, class Hash>
inline Foam::label
Foam::HashTable<T, Key, Hash>::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<T, Key, Hash>::hashKeyIndex(const Key& key) const
template<class T, class Key, class Hash>
inline Foam::label Foam::HashTable<T, Key, Hash>::capacity() const
{
return tableSize_;
return capacity_;
}
template<class T, class Key, class Hash>
inline Foam::label Foam::HashTable<T, Key, Hash>::size() const
{
return nElmts_;
return size_;
}
template<class T, class Key, class Hash>
inline bool Foam::HashTable<T, Key, Hash>::empty() const
{
return !nElmts_;
return !size_;
}
template<class T, class Key, class Hash>
bool Foam::HashTable<T, Key, Hash>::found(const Key& key) const
{
if (size_)
{
return Iterator<true>(this, key).found();
}
return false;
}
template<class T, class Key, class Hash>
inline typename Foam::HashTable<T, Key, Hash>::iterator
Foam::HashTable<T, Key, Hash>::find
(
const Key& key
)
{
if (size_)
{
return iterator(Iterator<false>(this, key));
}
return iterator();
}
template<class T, class Key, class Hash>
inline typename Foam::HashTable<T, Key, Hash>::const_iterator
Foam::HashTable<T, Key, Hash>::find
(
const Key& key
) const
{
return this->cfind(key);
}
template<class T, class Key, class Hash>
inline typename Foam::HashTable<T, Key, Hash>::const_iterator
Foam::HashTable<T, Key, Hash>::cfind
(
const Key& key
) const
{
if (size_)
{
return const_iterator(Iterator<true>(this, key));
}
return const_iterator();
}
@ -82,7 +121,7 @@ inline bool Foam::HashTable<T, Key, Hash>::insert
const T& obj
)
{
return this->set(key, obj, true);
return this->set(key, obj, false); // No overwrite
}
@ -93,7 +132,7 @@ inline bool Foam::HashTable<T, Key, Hash>::set
const T& obj
)
{
return this->set(key, obj, false);
return this->set(key, obj, true); // Overwrite
}
@ -112,7 +151,7 @@ inline const T& Foam::HashTable<T, Key, Hash>::lookup
const T& deflt
) const
{
const_iterator iter = this->find(key);
const const_iterator iter(this->cfind(key));
return iter.found() ? iter.object() : deflt;
}
@ -122,7 +161,7 @@ inline const T& Foam::HashTable<T, Key, Hash>::lookup
template<class T, class Key, class Hash>
inline T& Foam::HashTable<T, Key, Hash>::operator[](const Key& key)
{
iterator iter = this->find(key);
const iterator iter(this->find(key));
if (!iter.found())
{
@ -139,7 +178,7 @@ inline T& Foam::HashTable<T, Key, Hash>::operator[](const Key& key)
template<class T, class Key, class Hash>
inline const T& Foam::HashTable<T, Key, Hash>::operator[](const Key& key) const
{
const_iterator iter = this->find(key);
const const_iterator iter(this->cfind(key));
if (!iter.found())
{
@ -156,14 +195,14 @@ inline const T& Foam::HashTable<T, Key, Hash>::operator[](const Key& key) const
template<class T, class Key, class Hash>
inline T& Foam::HashTable<T, Key, Hash>::operator()(const Key& key)
{
iterator iter = this->find(key);
const iterator iter(this->find(key));
if (iter.found())
{
return iter.object();
}
this->insert(key, T());
this->insert(key, mapped_type());
return find(key).object();
}
@ -175,7 +214,7 @@ inline T& Foam::HashTable<T, Key, Hash>::operator()
const T& deflt
)
{
iterator iter = this->find(key);
const iterator iter(this->find(key));
if (iter.found())
{
@ -198,412 +237,6 @@ inline const T& Foam::HashTable<T, Key, Hash>::operator()
}
// * * * * * * * * * * * * * * * iterator base * * * * * * * * * * * * * * * //
template<class T, class Key, class Hash>
inline Foam::HashTable<T, Key, Hash>::iterator_base::iterator_base()
:
entryPtr_(nullptr),
hashTable_(nullptr),
hashIndex_(0)
{}
template<class T, class Key, class Hash>
inline Foam::HashTable<T, Key, Hash>::iterator_base::iterator_base
(
const table_type* hashTbl,
const entry_type* elmt,
const label hashIndex
)
:
entryPtr_(const_cast<entry_type*>(elmt)),
hashTable_(const_cast<table_type*>(hashTbl)),
hashIndex_(hashIndex)
{}
template<class T, class Key, class Hash>
inline Foam::HashTable<T, Key, Hash>::iterator_base::iterator_base
(
const table_type* hashTbl
)
:
entryPtr_(nullptr),
hashTable_(const_cast<table_type*>(hashTbl)),
hashIndex_(0)
{
if (hashTable_ && hashTable_->nElmts_)
{
// find first non-nullptr table entry
while
(
!(entryPtr_ = hashTable_->table_[hashIndex_])
&& ++hashIndex_ < hashTable_->tableSize_
)
{}
if (hashIndex_ >= hashTable_->tableSize_)
{
// make into an end iterator
entryPtr_ = nullptr;
hashIndex_ = 0;
}
}
}
template<class T, class Key, class Hash>
inline void
Foam::HashTable<T, Key, Hash>::iterator_base::increment()
{
// A negative index is a special value from erase
if (hashIndex_ < 0)
{
// the markPos='-curPos-1', but we wish to continue at 'curPos-1'
// thus use '-(markPos+1) -1'
hashIndex_ = -(hashIndex_+1) - 1;
}
else if (entryPtr_)
{
if (entryPtr_->next_)
{
// Move to next element on the SLList
entryPtr_ = entryPtr_->next_;
return;
}
}
// else
// {
// // if we reach here (entryPtr_ is nullptr) it is already at the end()
// // we should probably stop
// }
// Step to the next table entry
while
(
++hashIndex_ < hashTable_->tableSize_
&& !(entryPtr_ = hashTable_->table_[hashIndex_])
)
{}
if (hashIndex_ >= hashTable_->tableSize_)
{
// make into an end iterator
entryPtr_ = nullptr;
hashIndex_ = 0;
}
}
template<class T, class Key, class Hash>
inline bool
Foam::HashTable<T, Key, Hash>::iterator_base::found() const
{
return entryPtr_;
}
template<class T, class Key, class Hash>
inline const Key& Foam::HashTable<T, Key, Hash>::iterator_base::key() const
{
return entryPtr_->key_;
}
template<class T, class Key, class Hash>
inline T& Foam::HashTable<T, Key, Hash>::iterator_base::element() const
{
return entryPtr_->obj_;
}
template<class T, class Key, class Hash>
inline bool Foam::HashTable<T, Key, Hash>::iterator_base::operator==
(
const iterator_base& iter
) const
{
return entryPtr_ == iter.entryPtr_;
}
template<class T, class Key, class Hash>
inline bool Foam::HashTable<T, Key, Hash>::iterator_base::operator!=
(
const iterator_base& iter
) const
{
return entryPtr_ != iter.entryPtr_;
}
// * * * * * * * * * * * * * * key iterator base * * * * * * * * * * * * * * //
template<class T, class Key, class Hash>
template<class WrappedIterator>
inline Foam::HashTable<T, Key, Hash>::key_iterator_base<WrappedIterator>
::key_iterator_base
(
const WrappedIterator& iter
)
:
WrappedIterator(iter)
{}
template<class T, class Key, class Hash>
template<class WrappedIterator>
inline const Key&
Foam::HashTable<T, Key, Hash>::key_iterator_base<WrappedIterator>
::operator*() const
{
return this->key();
}
template<class T, class Key, class Hash>
template<class WrappedIterator>
inline const Key&
Foam::HashTable<T, Key, Hash>::key_iterator_base<WrappedIterator>
::operator()() const
{
return this->key();
}
template<class T, class Key, class Hash>
template<class WrappedIterator>
inline Foam::HashTable<T, Key, Hash>::key_iterator_base<WrappedIterator>&
Foam::HashTable<T, Key, Hash>::key_iterator_base<WrappedIterator>
::operator++()
{
this->increment();
return *this;
}
template<class T, class Key, class Hash>
template<class WrappedIterator>
inline Foam::HashTable<T, Key, Hash>::key_iterator_base<WrappedIterator>
Foam::HashTable<T, Key, Hash>::key_iterator_base<WrappedIterator>
::operator++(int)
{
key_iterator_base old = *this;
this->increment();
return old;
}
// * * * * * * * * * * * * * * * * STL iterator * * * * * * * * * * * * * * //
template<class T, class Key, class Hash>
inline Foam::HashTable<T, Key, Hash>::iterator::iterator()
:
iterator_base()
{}
template<class T, class Key, class Hash>
inline Foam::HashTable<T, Key, Hash>::iterator::iterator
(
table_type* hashTbl
)
:
iterator_base(hashTbl)
{}
template<class T, class Key, class Hash>
inline Foam::HashTable<T, Key, Hash>::iterator::iterator
(
table_type* hashTbl,
entry_type* elmt,
const label hashIndex
)
:
iterator_base(hashTbl, elmt, hashIndex)
{}
template<class T, class Key, class Hash>
inline T&
Foam::HashTable<T, Key, Hash>::iterator::object() const
{
return this->element();
}
template<class T, class Key, class Hash>
inline T&
Foam::HashTable<T, Key, Hash>::iterator::operator*() const
{
return this->object();
}
template<class T, class Key, class Hash>
inline T&
Foam::HashTable<T, Key, Hash>::iterator::operator()() const
{
return this->object();
}
template<class T, class Key, class Hash>
inline typename Foam::HashTable<T, Key, Hash>::iterator&
Foam::HashTable<T, Key, Hash>::iterator::operator++()
{
this->increment();
return *this;
}
template<class T, class Key, class Hash>
inline typename Foam::HashTable<T, Key, Hash>::iterator
Foam::HashTable<T, Key, Hash>::iterator::operator++(int)
{
iterator old = *this;
this->increment();
return old;
}
// * * * * * * * * * * * * * * * STL const_iterator * * * * * * * * * * * * //
template<class T, class Key, class Hash>
inline Foam::HashTable<T, Key, Hash>::const_iterator::const_iterator()
:
iterator_base()
{}
template<class T, class Key, class Hash>
inline Foam::HashTable<T, Key, Hash>::const_iterator::const_iterator
(
const HashTable<T, Key, Hash>::iterator& iter
)
:
iterator_base(iter)
{}
template<class T, class Key, class Hash>
inline Foam::HashTable<T, Key, Hash>::const_iterator::const_iterator
(
table_type* hashTbl
)
:
iterator_base(hashTbl)
{}
template<class T, class Key, class Hash>
inline Foam::HashTable<T, Key, Hash>::const_iterator::const_iterator
(
table_type* hashTbl,
entry_type* elmt,
const label hashIndex
)
:
iterator_base(hashTbl, elmt, hashIndex)
{}
template<class T, class Key, class Hash>
inline const T&
Foam::HashTable<T, Key, Hash>::const_iterator::object() const
{
return this->element();
}
template<class T, class Key, class Hash>
inline const T&
Foam::HashTable<T, Key, Hash>::const_iterator::operator*() const
{
return this->object();
}
template<class T, class Key, class Hash>
inline const T&
Foam::HashTable<T, Key, Hash>::const_iterator::operator()() const
{
return this->object();
}
template<class T, class Key, class Hash>
inline typename Foam::HashTable<T, Key, Hash>::const_iterator&
Foam::HashTable<T, Key, Hash>::const_iterator::operator++()
{
this->increment();
return *this;
}
template<class T, class Key, class Hash>
inline typename Foam::HashTable<T, Key, Hash>::const_iterator
Foam::HashTable<T, Key, Hash>::const_iterator::operator++(int)
{
const_iterator old = *this;
this->increment();
return old;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
template<class T, class Key, class Hash>
inline typename Foam::HashTable<T, Key, Hash>::iterator
Foam::HashTable<T, Key, Hash>::begin()
{
return iterator(this);
}
template<class T, class Key, class Hash>
inline typename Foam::HashTable<T, Key, Hash>::const_iterator
Foam::HashTable<T, Key, Hash>::begin() const
{
return const_iterator(this);
}
template<class T, class Key, class Hash>
inline typename Foam::HashTable<T, Key, Hash>::const_iterator
Foam::HashTable<T, Key, Hash>::cbegin() const
{
return const_iterator(this);
}
template<class T, class Key, class Hash>
inline const typename Foam::HashTable<T, Key, Hash>::iterator&
Foam::HashTable<T, Key, Hash>::end()
{
return iterator_end<iterator>();
}
template<class T, class Key, class Hash>
inline const typename Foam::HashTable<T, Key, Hash>::const_iterator&
Foam::HashTable<T, Key, Hash>::end() const
{
return iterator_end<const_iterator>();
}
template<class T, class Key, class Hash>
inline const typename Foam::HashTable<T, Key, Hash>::const_iterator&
Foam::HashTable<T, Key, Hash>::cend() const
{
return iterator_cend<const_iterator>();
}
// * * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * //
template<class T, class Key, class Hash>

View File

@ -33,17 +33,17 @@ template<class T, class Key, class Hash>
Foam::HashTable<T, Key, Hash>::HashTable(Istream& is, 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_];
table_ = new node_type*[capacity_];
for (label hashIdx = 0; hashIdx < tableSize_; ++hashIdx)
for (label i=0; i < capacity_; ++i)
{
table_[hashIdx] = nullptr;
table_[i] = nullptr;
}
}
@ -60,10 +60,10 @@ Foam::Ostream& Foam::HashTable<T, Key, Hash>::printInfo(Ostream& os) const
label maxChain = 0;
unsigned avgChain = 0;
for (label hashIdx = 0; hashIdx < tableSize_; ++hashIdx)
for (label i=0; i < capacity_; ++i)
{
label count = 0;
for (hashedEntry* ep = table_[hashIdx]; ep; ep = ep->next_)
for (node_type* ep = table_[i]; ep; ep = ep->next_)
{
++count;
}
@ -81,7 +81,7 @@ Foam::Ostream& Foam::HashTable<T, Key, Hash>::printInfo(Ostream& os) const
}
os << "HashTable<T,Key,Hash>"
<< " elements:" << size() << " slots:" << used << "/" << tableSize_
<< " elements:" << size() << " slots:" << used << "/" << capacity_
<< " chaining(avg/max):" << (used ? (float(avgChain)/used) : 0)
<< "/" << maxChain << endl;
@ -145,7 +145,7 @@ Foam::Istream& Foam::operator>>
{
is.fatalCheck(FUNCTION_NAME);
// Anull list
// Anull existing table
L.clear();
is.fatalCheck(FUNCTION_NAME);
@ -154,7 +154,7 @@ Foam::Istream& Foam::operator>>
is.fatalCheck
(
"operator>>(Istream&, HashTable<T, Key, Hash>&) : "
"operator>>(Istream&, HashTable&) : "
"reading first token"
);
@ -163,11 +163,11 @@ Foam::Istream& Foam::operator>>
const label s = firstToken.labelToken();
// Read beginning of contents
const char delimiter = is.readBeginList("HashTable<T, Key, Hash>");
const char delimiter = is.readBeginList("HashTable");
if (s)
{
if (2*s > L.tableSize_)
if (2*s > L.capacity_)
{
L.resize(2*s);
}
@ -182,7 +182,7 @@ Foam::Istream& Foam::operator>>
is.fatalCheck
(
"operator>>(Istream&, HashTable<T, Key, Hash>&) : "
"operator>>(Istream&, HashTable&) : "
"reading entry"
);
}
@ -224,15 +224,11 @@ Foam::Istream& Foam::operator>>
Key key;
is >> key;
T element;
is >> element;
L.insert(key, element);
L.insert(key, pTraits<T>(is));
is.fatalCheck
(
"operator>>(Istream&, HashTable<T, Key, Hash>&) : "
"operator>>(Istream&, HashTable&) : "
"reading entry"
);
@ -262,19 +258,26 @@ Foam::Ostream& Foam::operator<<
const HashTable<T, Key, Hash>& tbl
)
{
using const_iterator = typename HashTable<T, Key, Hash>::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)
{
os << iter.key() << token::SPACE << iter.object() << nl;
}
// Size and start list delimiter
os << nl << sz << nl << token::BEGIN_LIST << nl;
// Write end delimiter
os << token::END_LIST;
// Contents
for (auto iter = tbl.cbegin(); iter != tbl.cend(); ++iter)
{
os << iter.key() << token::SPACE << iter.object() << nl;
}
os << token::END_LIST; // End list delimiter
}
else
{
// Empty hash table
os << sz << token::BEGIN_LIST << token::END_LIST;
}
os.check(FUNCTION_NAME);
return os;

View File

@ -0,0 +1,123 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / 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 <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
template<class T, class Key, class Hash>
template<bool Const>
Foam::HashTable<T, Key, Hash>::Iterator<Const>::Iterator
(
table_type* tbl,
const Key& key
)
:
entry_(nullptr),
container_(tbl),
index_(0)
{
if (tbl->size())
{
const label index = container_->hashKeyIndex(key);
for (node_type* ep = container_->table_[index]; ep; ep = ep->next_)
{
if (key == ep->key())
{
entry_ = ep;
index_ = index;
break;
}
}
}
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
//
// Any changes here may need changes in the iterator increment() method
//
template<class T, class Key, class Hash>
bool Foam::HashTable<T, Key, Hash>::iterator_erase
(
node_type*& entry,
label& index
)
{
// Safeguard against the following:
// - empty table
// - nullptr entry
// - end iterator (which is also a nullptr)
// - negative index from a previous erase. See comment below.
if (!size_ || !entry || index < 0)
{
return false;
}
// Decrease count
size_--;
// The previous element in the singly linked list
node_type* prev = nullptr;
for (node_type* ep = table_[index]; ep; ep = ep->next_)
{
if (ep == entry)
{
break;
}
prev = ep;
}
if (prev)
{
// Had previous element in linked list - reposition to there
prev->next_ = entry->next_;
delete entry;
entry = prev;
return true;
}
// Was first element on linked list
table_[index] = entry->next_;
delete entry;
// Assign any non-nullptr value so it doesn't look like end()
entry = reinterpret_cast<node_type*>(this);
// Mark the present index to continue and bring it back to the present
// location with the next index.
//
// Save: (-index-1), which has no ambiguity for index 0.
// Retrieve: (-(index+1))
index = (-index - 1);
return true;
}
// ************************************************************************* //

View File

@ -0,0 +1,263 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / 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 <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
// * * * * * * * * * * * * * * * iterator base * * * * * * * * * * * * * * * //
template<class T, class Key, class Hash>
template<bool Const>
inline Foam::HashTable<T, Key, Hash>::Iterator<Const>::Iterator()
:
entry_(nullptr),
container_(nullptr),
index_(0)
{}
template<class T, class Key, class Hash>
template<bool Const>
inline Foam::HashTable<T, Key, Hash>::Iterator<Const>::Iterator
(
bool, // Future use and to avoid implicit construct
table_type* tbl
)
:
entry_(nullptr),
container_(tbl),
index_(0)
{
if (container_ && container_->size_)
{
// Locate the first non-nullptr table entry
while
(
!(entry_ = container_->table_[index_])
&& ++index_ < container_->capacity_
)
{}
if (index_ >= container_->capacity_)
{
// Nothing found - make it an end iterator
entry_ = nullptr;
index_ = 0;
}
}
}
//
// Any changes here may need changes in iterator_erase() method too
//
template<class T, class Key, class Hash>
template<bool Const>
inline void
Foam::HashTable<T, Key, Hash>::Iterator<Const>::increment()
{
if (index_ < 0)
{
// Negative index is a special value from erase
//
// Saved as (-index-1), retrieved as (-(index-1)) but to with an
// extra (-1) to compensate for the ++ in the following while loop
index_ = -(index_+1) - 1;
}
else if (index_ < container_->capacity_ && entry_ && entry_->next_)
{
// Move to next element on the linked-list
entry_ = entry_->next_;
return;
}
// Move to the next non-nullptr table entry
while
(
++index_ < container_->capacity_
&& !(entry_ = container_->table_[index_])
)
{}
if (index_ >= container_->capacity_)
{
// Nothing found - make it an end iterator
entry_ = nullptr;
index_ = 0;
}
}
template<class T, class Key, class Hash>
template<bool Const>
inline bool
Foam::HashTable<T, Key, Hash>::Iterator<Const>::found() const
{
return entry_;
}
template<class T, class Key, class Hash>
template<bool Const>
inline const Key& Foam::HashTable<T, Key, Hash>::Iterator<Const>::key() const
{
return entry_->key();
}
template<class T, class Key, class Hash>
template<bool Const>
inline bool Foam::HashTable<T, Key, Hash>::Iterator<Const>::operator==
(
const Iterator<true>& iter
) const
{
return entry_ == iter.entry_;
}
template<class T, class Key, class Hash>
template<bool Const>
inline bool Foam::HashTable<T, Key, Hash>::Iterator<Const>::operator!=
(
const Iterator<true>& iter
) const
{
return entry_ != iter.entry_;
}
template<class T, class Key, class Hash>
template<bool Const>
inline bool Foam::HashTable<T, Key, Hash>::Iterator<Const>::operator==
(
const Iterator<false>& iter
) const
{
return entry_ == iter.entry_;
}
template<class T, class Key, class Hash>
template<bool Const>
inline bool Foam::HashTable<T, Key, Hash>::Iterator<Const>::operator!=
(
const Iterator<false>& iter
) const
{
return entry_ != iter.entry_;
}
// * * * * * * * * * * * * * * * * STL iterator * * * * * * * * * * * * * * //
template<class T, class Key, class Hash>
inline typename Foam::HashTable<T, Key, Hash>::iterator&
Foam::HashTable<T, Key, Hash>::iterator::operator++()
{
this->increment();
return *this;
}
template<class T, class Key, class Hash>
inline typename Foam::HashTable<T, Key, Hash>::iterator
Foam::HashTable<T, Key, Hash>::iterator::operator++(int)
{
iterator iter(*this);
this->increment();
return iter;
}
// * * * * * * * * * * * * * * * STL const_iterator * * * * * * * * * * * * //
template<class T, class Key, class Hash>
inline typename Foam::HashTable<T, Key, Hash>::const_iterator&
Foam::HashTable<T, Key, Hash>::const_iterator::operator++()
{
this->increment();
return *this;
}
template<class T, class Key, class Hash>
inline typename Foam::HashTable<T, Key, Hash>::const_iterator
Foam::HashTable<T, Key, Hash>::const_iterator::operator++(int)
{
const_iterator iter(*this);
this->increment();
return iter;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
template<class T, class Key, class Hash>
inline typename Foam::HashTable<T, Key, Hash>::iterator
Foam::HashTable<T, Key, Hash>::begin()
{
return iterator(Iterator<false>(true, this));
}
template<class T, class Key, class Hash>
inline typename Foam::HashTable<T, Key, Hash>::const_iterator
Foam::HashTable<T, Key, Hash>::begin() const
{
return const_iterator(Iterator<true>(true, this));
}
template<class T, class Key, class Hash>
inline typename Foam::HashTable<T, Key, Hash>::const_iterator
Foam::HashTable<T, Key, Hash>::cbegin() const
{
return const_iterator(Iterator<true>(true, this));
}
template<class T, class Key, class Hash>
inline const typename Foam::HashTable<T, Key, Hash>::iterator&
Foam::HashTable<T, Key, Hash>::end()
{
return iterator_end<iterator>();
}
template<class T, class Key, class Hash>
inline const typename Foam::HashTable<T, Key, Hash>::const_iterator&
Foam::HashTable<T, Key, Hash>::end() const
{
return iterator_cend<const_iterator>();
}
template<class T, class Key, class Hash>
inline const typename Foam::HashTable<T, Key, Hash>::const_iterator&
Foam::HashTable<T, Key, Hash>::cend() const
{
return iterator_cend<const_iterator>();
}
// ************************************************************************* //

View File

@ -27,6 +27,11 @@ Class
Description
A HashTable to objects of type \<T\> with a label key.
Note
The Map contents are unordered.
When the key order is important, use the sortedToc() method to obtain
a list of sorted keys and use that for further access.
See also
PtrMap
@ -65,13 +70,19 @@ public:
// Constructors
//- Construct given initial size
Map(const label size = 128)
//- Construct null with default table size
Map()
:
parent_type()
{}
//- Construct with given table size
explicit Map(const label size)
:
parent_type(size)
{}
//- Construct from Istream
//- Construct from Istream with default table size
Map(Istream& is)
:
parent_type(is)
@ -96,7 +107,7 @@ public:
{}
//- Construct by transferring the parameter contents
Map(const Xfer<HashTable<T, label, Hash<label>>>& map)
Map(const Xfer<parent_type>& map)
:
parent_type(map)
{}

View File

@ -62,8 +62,14 @@ public:
// Constructors
//- Construct null with default table size
PtrMap()
:
parent_type()
{}
//- Construct given initial map size
PtrMap(const label size = 128)
explicit PtrMap(const label size)
:
parent_type(size)
{}