/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2017-2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
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 Foam_HashTable_C
#define Foam_HashTable_C
#include "HashTable.H"
#include "List.H"
#include "FixedList.H"
#include "UPtrList.H"
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
template
Foam::HashTable::HashTable(const Foam::zero) noexcept
:
HashTableCore(),
size_(0),
capacity_(0),
table_(nullptr)
{}
template
Foam::HashTable::HashTable()
:
HashTable(128)
{}
template
Foam::HashTable::HashTable(const label initialCapacity)
:
HashTableCore(),
size_(0),
capacity_(0),
table_(nullptr)
{
if (initialCapacity > 0)
{
// Like resize() but no initial content to transfer
capacity_ = HashTableCore::canonicalSize(initialCapacity);
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) noexcept
:
HashTableCore(),
size_(rhs.size_),
capacity_(rhs.capacity_),
table_(rhs.table_)
{
// Stole all contents
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)
{
set(keyval.first, keyval.second);
}
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
template
Foam::HashTable::~HashTable()
{
// Remove all entries from table
clear();
// Remove the table itself
capacity_ = 0;
delete[] table_;
table_ = nullptr;
}
// * * * * * * * * * * * * * * * 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
Foam::UPtrList::node_type>
Foam::HashTable::csorted() const
{
UPtrList result(size_);
label count = 0;
for (const_iterator iter = cbegin(); iter != cend(); ++iter)
{
result.set(count++, iter.node());
}
Foam::sort(result);
return result;
}
template
Foam::UPtrList::node_type>
Foam::HashTable::sorted()
{
UPtrList result(size_);
label count = 0;
for (iterator iter = begin(); iter != end(); ++iter)
{
result.set(count++, iter.node());
}
Foam::sort(result);
return result;
}
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_)
{
// Same as default sizing
resize(128);
}
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 (0.8*capacity_ < size_) // Resize after 80% fill factor
{
if (capacity_ < maxTableSize) 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
{
// Not overwriting existing entry
return false;
}
return true;
}
template
void Foam::HashTable::insert_node(node_type* entry)
{
if (!entry) return;
if (!capacity_)
{
// Same as default sizing
resize(128);
}
const label index = hashKeyIndex(entry->key());
node_type* curr = nullptr;
//node_type* prev = nullptr;
for (node_type* ep = table_[index]; ep; ep = ep->next_)
{
if (entry->key() == ep->key())
{
curr = ep;
break;
}
//prev = ep;
}
if (!curr)
{
// Not found, insert it at the head
table_[index] = entry;
++size_;
if (0.8*capacity_ < size_) // Resize after 80% fill factor
{
if (capacity_ < maxTableSize) resize(2*capacity_);
}
}
else
{
FatalErrorInFunction
<< "Not inserting " << entry->key() << ": already in table\n"
<< exit(FatalError);
}
}
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.
return iterator_erase(const_cast(iter));
}
template
bool Foam::HashTable::erase(const Key& key)
{
iterator iter(find(key));
return iterator_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.contains(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.contains(iter.key()) && erase(iter))
{
++changed;
}
}
}
return changed;
}
template
void Foam::HashTable::resize(const label requestedCapacity)
{
const label newCapacity = HashTableCore::canonicalSize(requestedCapacity);
const label oldCapacity = capacity_;
if (newCapacity == oldCapacity)
{
return;
}
else if (!newCapacity)
{
// Special treatment for resize(0)
if (size_)
{
WarningInFunction
<< "HashTable contains " << size_
<< " elements, cannot resize(0)" << nl;
}
else
{
capacity_ = 0;
delete[] table_;
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;
}
if (!oldTable)
{
return;
}
// Move to new table[] but with new chaining.
for (label i = 0, pending = size_; pending && 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
--pending; // note any early completion
}
oldTable[i] = nullptr;
}
delete[] oldTable;
}
template
void Foam::HashTable::clear()
{
if (!table_)
{
capacity_ = 0; // Paranoid
}
for (label i = 0, pending = size_; pending && i < capacity_; ++i)
{
for (node_type* ep = table_[i]; ep; /*nil*/)
{
node_type* next = ep->next_;
delete ep;
ep = next; // continue in the linked-list
--pending; // note any early completion
}
table_[i] = nullptr;
}
size_ = 0;
}
template
void Foam::HashTable::clearStorage()
{
// Remove all entries from table
clear();
// Remove the table itself
capacity_ = 0;
delete[] table_;
table_ = nullptr;
}
template
void Foam::HashTable::swap(HashTable& rhs) noexcept
{
if (this == &rhs)
{
return; // Self-swap is a no-op
}
std::swap(size_, rhs.size_);
std::swap(capacity_, rhs.capacity_);
std::swap(table_, rhs.table_);
}
template
void Foam::HashTable::transfer(HashTable& rhs)
{
if (this == &rhs)
{
return; // Self-assignment is a no-op
}
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;
}
template
void Foam::HashTable::merge(HashTable& source)
{
// Self-merge implicitly a no-op
if (node_type::stores_value())
{
// key/val pair
for (iterator iter = source.begin(); iter != source.end(); ++iter)
{
if (!contains(iter.key()))
{
node_type* entry = source.iterator_extract(iter);
this->insert_node(entry);
}
}
}
else
{
// key only, does not store a value.
// Since the key is const, juggling the node does not work
for (iterator iter = source.begin(); iter != source.end(); ++iter)
{
if (emplace(iter.key()))
{
source.erase(iter);
}
}
}
}
template
void Foam::HashTable::merge(HashTable&& source)
{
merge(source);
}
// * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
template
void Foam::HashTable::operator=
(
const HashTable& rhs
)
{
if (this == &rhs)
{
return; // Self-assignment is a no-op
}
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)
{
set(keyval.first, keyval.second);
}
}
template
void Foam::HashTable::operator=
(
HashTable&& rhs
)
{
transfer(rhs); // Includes self-assignment check
}
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
// ************************************************************************* //