/*---------------------------------------------------------------------------*\ ========= | \\ / 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 // ************************************************************************* //