/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2004-2010, 2017-2019 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
| Copyright (C) 2011-2016 OpenFOAM Foundation
-------------------------------------------------------------------------------
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 .
\*---------------------------------------------------------------------------*/
#ifndef HashTable_C
#define HashTable_C
#include "HashTable.H"
#include "List.H"
#include "FixedList.H"
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
template
Foam::HashTable::HashTable()
:
HashTable(128)
{}
template
Foam::HashTable::HashTable(const label size)
:
HashTableCore(),
size_(0),
capacity_(HashTableCore::canonicalSize(size)),
table_(nullptr)
{
if (capacity_)
{
table_ = new node_type*[capacity_];
for (label i=0; i < capacity_; ++i)
{
table_[i] = nullptr;
}
}
}
template
Foam::HashTable::HashTable(const HashTable& ht)
:
HashTable(ht.capacity_)
{
for (const_iterator iter = ht.cbegin(); iter != ht.cend(); ++iter)
{
insert(iter.key(), iter.val());
}
}
template
Foam::HashTable::HashTable(HashTable&& rhs)
:
HashTableCore(),
size_(rhs.size_),
capacity_(rhs.capacity_),
table_(rhs.table_)
{
rhs.size_ = 0;
rhs.capacity_ = 0;
rhs.table_ = nullptr;
}
template
Foam::HashTable::HashTable
(
std::initializer_list> list
)
:
HashTable(2*list.size())
{
for (const auto& keyval : list)
{
insert(keyval.first, keyval.second);
}
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
template
Foam::HashTable::~HashTable()
{
if (table_)
{
clear();
delete[] table_;
}
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
template
Foam::List Foam::HashTable::toc() const
{
List list(size_);
label count = 0;
for (const_iterator iter = cbegin(); iter != cend(); ++iter)
{
list[count++] = iter.key();
}
return list;
}
template
Foam::List Foam::HashTable::sortedToc() const
{
List list(this->toc());
Foam::sort(list);
return list;
}
template
template
Foam::List Foam::HashTable::sortedToc
(
const Compare& comp
) const
{
List list(this->toc());
Foam::sort(list, comp);
return list;
}
template
template
Foam::List Foam::HashTable::tocKeys
(
const UnaryPredicate& pred,
const bool invert
) const
{
List list(size_);
label count = 0;
for (const_iterator iter = cbegin(); iter != cend(); ++iter)
{
if ((pred(iter.key()) ? !invert : invert))
{
list[count++] = iter.key();
}
}
list.resize(count);
Foam::sort(list);
return list;
}
template
template
Foam::List Foam::HashTable::tocValues
(
const UnaryPredicate& pred,
const bool invert
) const
{
List list(size_);
label count = 0;
for (const_iterator iter = cbegin(); iter != cend(); ++iter)
{
if ((pred(iter.val()) ? !invert : invert))
{
list[count++] = iter.key();
}
}
list.resize(count);
Foam::sort(list);
return list;
}
template
template
Foam::List Foam::HashTable::tocEntries
(
const BinaryPredicate& pred,
const bool invert
) const
{
List list(size_);
label count = 0;
for (const_iterator iter = cbegin(); iter != cend(); ++iter)
{
if ((pred(iter.key(), iter.val()) ? !invert : invert))
{
list[count++] = iter.key();
}
}
list.resize(count);
Foam::sort(list);
return list;
}
template
template
Foam::label Foam::HashTable::countKeys
(
const UnaryPredicate& pred,
const bool invert
) const
{
label count = 0;
for (const_iterator iter = cbegin(); iter != cend(); ++iter)
{
if ((pred(iter.key()) ? !invert : invert))
{
++count;
}
}
return count;
}
template
template
Foam::label Foam::HashTable::countValues
(
const UnaryPredicate& pred,
const bool invert
) const
{
label count = 0;
for (const_iterator iter = cbegin(); iter != cend(); ++iter)
{
if ((pred(iter.val()) ? !invert : invert))
{
++count;
}
}
return count;
}
template
template
Foam::label Foam::HashTable::countEntries
(
const BinaryPredicate& pred,
const bool invert
) const
{
label count = 0;
for (const_iterator iter = cbegin(); iter != cend(); ++iter)
{
if ((pred(iter.key(), iter.val()) ? !invert : invert))
{
++count;
}
}
return count;
}
template
template
bool Foam::HashTable::setEntry
(
const bool overwrite,
const Key& key,
Args&&... args
)
{
if (!capacity_)
{
resize(2);
}
const label index = hashKeyIndex(key);
node_type* curr = nullptr;
node_type* prev = nullptr;
for (node_type* ep = table_[index]; ep; ep = ep->next_)
{
if (key == ep->key())
{
curr = ep;
break;
}
prev = ep;
}
if (!curr)
{
// Not found, insert it at the head
table_[index] =
new node_type(table_[index], key, std::forward(args)...);
++size_;
if (double(size_)/capacity_ > 0.8 && capacity_ < maxTableSize)
{
#ifdef FULLDEBUG
DebugInFunction << "Doubling table size\n";
#endif
resize(2*capacity_);
}
}
else if (overwrite)
{
// Overwrite current entry (Perl convention).
// Can skip if the value is not stored anyhow (Eg, HashSet)
// - this avoids a useless delete/new
if (!node_type::stores_value())
{
return true;
}
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(ep, key, std::forward(args)...);
// 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
DebugInFunction << "Not inserting " << key << ": already in table\n";
#endif
return false;
}
return true;
}
template
bool Foam::HashTable::erase(const iterator& iter)
{
// 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.
iterator& it = const_cast(iter);
return iterator_erase(it.entry_, it.index_);
}
template
bool Foam::HashTable::erase(const Key& key)
{
auto iter = find(key);
return erase(iter);
}
template
template
inline Foam::label Foam::HashTable::erase
(
InputIter first,
InputIter last
)
{
label changed = 0;
for
(
const label nTotal = this->size();
changed < nTotal && first != last; // terminate early
++first
)
{
if (this->erase(*first))
{
++changed;
}
}
return changed;
}
template
inline Foam::label Foam::HashTable::erase
(
std::initializer_list keys
)
{
return erase(keys.begin(), keys.end());
}
template
template
inline Foam::label Foam::HashTable::erase
(
const FixedList& keys
)
{
return erase(keys.cbegin(), keys.cend());
}
template
inline Foam::label Foam::HashTable::erase
(
const UList& keys
)
{
return erase(keys.cbegin(), keys.cend());
}
template
template
Foam::label Foam::HashTable::erase
(
const HashTable& other
)
{
const label nTotal = this->size();
label changed = 0;
if (other.size() <= nTotal)
{
// The other is smaller/same-size, use its keys for removal
for
(
auto iter = other.cbegin();
changed < nTotal && iter != other.cend(); // Terminate early
++iter
)
{
if (erase(iter.key()))
{
++changed;
}
}
}
else
{
// We are smaller: remove if found in the other hash
for
(
iterator iter = begin();
changed < nTotal && iter != end(); // Terminate early
++iter
)
{
if (other.found(iter.key()) && erase(iter))
{
++changed;
}
}
}
return changed;
}
template
template
Foam::label Foam::HashTable::retain
(
const HashTable& other
)
{
const label nTotal = this->size();
label changed = 0;
if (other.empty())
{
// Trivial case
changed = nTotal;
this->clear();
}
else
{
// Inverted logic: remove if *not* found in the other hash
for (iterator iter = begin(); iter != end(); ++iter)
{
if (!other.found(iter.key()) && erase(iter))
{
++changed;
}
}
}
return changed;
}
template
void Foam::HashTable::resize(const label sz)
{
const label newCapacity = HashTableCore::canonicalSize(sz);
const label oldCapacity = capacity_;
if (newCapacity == oldCapacity)
{
#ifdef FULLDEBUG
DebugInFunction << "New table size == old table size\n";
#endif
return;
}
else if (!newCapacity)
{
// Special treatment for resize(0)
if (size_)
{
WarningInFunction
<< "HashTable contains " << size_ << " cannot resize(0)" << nl;
}
else
{
if (table_)
{
delete[] table_;
capacity_ = 0;
}
table_ = nullptr;
}
return;
}
// Swap primary table entries: size_ is left untouched
auto oldTable = table_;
capacity_ = newCapacity;
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()
{
for (label i=0; size_ && inext_;
delete ep;
ep = next; // continue in the linked-list
--size_; // note any early completion
}
table_[i] = nullptr;
}
}
template
void Foam::HashTable::clearStorage()
{
clear();
resize(0);
}
template
void Foam::HashTable::swap(HashTable& rhs)
{
Foam::Swap(size_, rhs.size_);
Foam::Swap(capacity_, rhs.capacity_);
Foam::Swap(table_, rhs.table_);
}
template
void Foam::HashTable::transfer(HashTable& rhs)
{
clear();
swap(rhs);
}
template
template
Foam::label Foam::HashTable::filterKeys
(
const UnaryPredicate& pred,
const bool pruning
)
{
label changed = 0;
for (iterator iter = begin(); iter != end(); ++iter)
{
// Matches? either prune (pruning) or keep (!pruning)
if
(
(pred(iter.key()) ? pruning : !pruning)
&& erase(iter)
)
{
++changed;
}
}
return changed;
}
template
template
Foam::label Foam::HashTable::filterValues
(
const UnaryPredicate& pred,
const bool pruning
)
{
label changed = 0;
for (iterator iter = begin(); iter != end(); ++iter)
{
// Matches? either prune (pruning) or keep (!pruning)
if
(
(pred(iter.val()) ? pruning : !pruning)
&& erase(iter)
)
{
++changed;
}
}
return changed;
}
template
template
Foam::label Foam::HashTable::filterEntries
(
const BinaryPredicate& pred,
const bool pruning
)
{
label changed = 0;
for (iterator iter = begin(); iter != end(); ++iter)
{
// Matches? either prune (pruning) or keep (!pruning)
if
(
(pred(iter.key(), iter.val()) ? pruning : !pruning)
&& erase(iter)
)
{
++changed;
}
}
return changed;
}
// * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
template
void Foam::HashTable::operator=
(
const HashTable& rhs
)
{
// Check for assignment to self
if (this == &rhs)
{
FatalErrorInFunction
<< "attempted assignment to self"
<< abort(FatalError);
}
if (!capacity_)
{
// Zero-sized from a previous transfer()?
resize(rhs.capacity_);
}
else
{
clear();
}
for (const_iterator iter = rhs.cbegin(); iter != rhs.cend(); ++iter)
{
insert(iter.key(), iter.val());
}
}
template
void Foam::HashTable::operator=
(
std::initializer_list> rhs
)
{
if (!capacity_)
{
// Zero-sized from a previous transfer()?
resize(2*rhs.size());
}
else
{
clear();
}
for (const auto& keyval : rhs)
{
insert(keyval.first, keyval.second);
}
}
template
void Foam::HashTable::operator=
(
HashTable&& rhs
)
{
// Check for assignment to self
if (this == &rhs)
{
FatalErrorInFunction
<< "attempted assignment to self"
<< abort(FatalError);
}
transfer(rhs);
}
template
bool Foam::HashTable::operator==
(
const HashTable& rhs
) const
{
// Sizes (number of keys) must match
if (size() != rhs.size())
{
return false;
}
for (const_iterator iter = rhs.cbegin(); iter != rhs.cend(); ++iter)
{
const const_iterator other(this->cfind(iter.key()));
if (!other.good() || other.val() != iter.val())
{
return false;
}
}
return true;
}
template
bool Foam::HashTable::operator!=
(
const HashTable& rhs
) const
{
return !operator==(rhs);
}
template
Foam::HashTable& Foam::HashTable::operator+=
(
const HashTable& rhs
)
{
// Avoid no-ops:
if (rhs.size() || this != &rhs)
{
if (this->size())
{
for (const_iterator iter = rhs.cbegin(); iter != rhs.cend(); ++iter)
{
insert(iter.key(), iter.val());
}
}
else
{
(*this) = rhs;
}
}
return *this;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Iterators, Friend Operators
#include "HashTableIter.C"
#include "HashTableIO.C"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //