/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation \\/ M anipulation | Copyright (C) 2017 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 HashTable_C #define HashTable_C #include "HashTable.H" #include "List.H" #include "FixedList.H" #include "Tuple2.H" // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * // template template Foam::label Foam::HashTable::eraseMultiple ( const InputIter begIter, const InputIter endIter ) { const label nTotal = this->size(); label changed = 0; for ( InputIter iter = begIter; changed < nTotal && iter != endIter; // terminate early ++iter ) { if (this->erase(*iter)) { ++changed; } } return changed; } // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // template Foam::HashTable::HashTable(const label size) : HashTableCore(), nElmts_(0), tableSize_(HashTableCore::canonicalSize(size)), table_(nullptr) { if (tableSize_) { table_ = new hashedEntry*[tableSize_]; for (label hashIdx = 0; hashIdx < tableSize_; hashIdx++) { table_[hashIdx] = nullptr; } } } template Foam::HashTable::HashTable(const HashTable& ht) : HashTable(ht.tableSize_) { for (const_iterator iter = ht.cbegin(); iter != ht.cend(); ++iter) { insert(iter.key(), iter.object()); } } template Foam::HashTable::HashTable ( const Xfer>& ht ) : HashTableCore(), nElmts_(0), tableSize_(0), table_(nullptr) { transfer(ht()); } template Foam::HashTable::HashTable ( std::initializer_list> lst ) : HashTable(2*lst.size()) { for (const Tuple2& pair : lst) { insert(pair.first(), pair.second()); } } // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // template Foam::HashTable::~HashTable() { if (table_) { clear(); delete[] table_; } } // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // template bool Foam::HashTable::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 typename Foam::HashTable::iterator Foam::HashTable::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 typename Foam::HashTable::const_iterator Foam::HashTable::find ( 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 Foam::List Foam::HashTable::toc() const { List keys(nElmts_); label keyI = 0; for (const_iterator iter = cbegin(); iter != cend(); ++iter) { keys[keyI++] = iter.key(); } return keys; } template Foam::List Foam::HashTable::sortedToc() const { List sortedLst = this->toc(); sort(sortedLst); return sortedLst; } template bool Foam::HashTable::set ( const Key& key, const T& newEntry, const bool protect ) { if (!tableSize_) { resize(2); } const label hashIdx = hashKeyIndex(key); hashedEntry* existing = nullptr; hashedEntry* prev = nullptr; for (hashedEntry* ep = table_[hashIdx]; ep; ep = ep->next_) { if (key == ep->key_) { existing = ep; break; } prev = ep; } if (!existing) { // Not found, insert it at the head table_[hashIdx] = new hashedEntry(key, newEntry, table_[hashIdx]); nElmts_++; if (double(nElmts_)/tableSize_ > 0.8 && tableSize_ < maxTableSize) { #ifdef FULLDEBUG if (debug) { InfoInFunction << "Doubling table size\n"; } #endif resize(2*tableSize_); } } else if (protect) { // Found - but protected from overwriting // this corresponds to the STL 'insert' convention #ifdef FULLDEBUG if (debug) { InfoInFunction << "Cannot insert " << key << " already in hash table\n"; } #endif return false; } else { // Found - overwrite existing entry // this corresponds to the Perl convention hashedEntry* ep = new hashedEntry(key, newEntry, 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 bool Foam::HashTable::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(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 bool Foam::HashTable::erase(const iterator& iter) { // NOTE: We use (const iterator&) here, but manipulate its contents anyhow. // 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(iter).erase(); } template bool Foam::HashTable::erase(const Key& key) { return erase(find(key)); } template Foam::label Foam::HashTable::erase(const UList& keys) { return eraseMultiple(keys.begin(), keys.end()); } template template Foam::label Foam::HashTable::erase ( const FixedList& keys ) { return eraseMultiple(keys.begin(), keys.end()); } template Foam::label Foam::HashTable::erase ( std::initializer_list keys ) { return eraseMultiple(keys.begin(), keys.end()); } template template Foam::label Foam::HashTable::erase ( const HashTable& other ) { // Remove other keys from this table const label nTotal = this->size(); label changed = 0; if (other.size() < nTotal) { // other is smaller, use its keys for removal using other_iter = typename HashTable::const_iterator; for ( other_iter iter = other.begin(); changed < nTotal && iter != other.end(); // terminate early ++iter ) { if (erase(iter.key())) { ++changed; } } } else { // other is same/larger: iterate ourselves and check for key in other for ( iterator iter = begin(); changed < nTotal && iter != end(); // terminate early ++iter ) { if (other.found(iter.key()) && erase(iter)) { ++changed; } } } return changed; } template void Foam::HashTable::resize(const label sz) { const label newSize = HashTableCore::canonicalSize(sz); if (newSize == tableSize_) { #ifdef FULLDEBUG if (debug) { InfoInFunction << "New table size == old table size\n"; } #endif return; } HashTable* tmpTable = new HashTable(newSize); for (const_iterator iter = cbegin(); iter != cend(); ++iter) { tmpTable->insert(iter.key(), iter.object()); } const label oldSize = tableSize_; tableSize_ = tmpTable->tableSize_; tmpTable->tableSize_ = oldSize; hashedEntry** oldTable = table_; table_ = tmpTable->table_; tmpTable->table_ = oldTable; delete tmpTable; } template void Foam::HashTable::clear() { if (nElmts_) { for (label hashIdx = 0; hashIdx < tableSize_; hashIdx++) { if (table_[hashIdx]) { hashedEntry* ep = table_[hashIdx]; while (hashedEntry* next = ep->next_) { delete ep; ep = next; } delete ep; table_[hashIdx] = nullptr; } } nElmts_ = 0; } } template void Foam::HashTable::clearStorage() { clear(); resize(0); } template void Foam::HashTable::transfer(HashTable& ht) { // As per the Destructor if (table_) { clear(); delete[] table_; } tableSize_ = ht.tableSize_; ht.tableSize_ = 0; table_ = ht.table_; ht.table_ = nullptr; nElmts_ = ht.nElmts_; ht.nElmts_ = 0; } // * * * * * * * * * * * * * * * 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); } // Could be zero-sized from a previous transfer() if (!tableSize_) { resize(rhs.tableSize_); } else { clear(); } for (const_iterator iter = rhs.cbegin(); iter != rhs.cend(); ++iter) { insert(iter.key(), iter.object()); } } template void Foam::HashTable::operator= ( std::initializer_list> lst ) { // Could be zero-sized from a previous transfer() if (!tableSize_) { resize(2*lst.size()); } else { clear(); } for (const auto& pair : lst) { insert(pair.first(), pair.second()); } } 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_iterator other = find(iter.key()); if (!other.found() || other.object() != iter.object()) { return false; } } return true; } template bool Foam::HashTable::operator!= ( const HashTable& rhs ) const { return !operator==(rhs); } // * * * * * * * * * * * * * * * Friend Operators * * * * * * * * * * * * * // #include "HashTableIO.C" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // #endif // ************************************************************************* //