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