//----------------------------------------------------------------------------- // Copyright (c) 2013 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- #ifndef _HASHTABLE_H_ #define _HASHTABLE_H_ #include "vector.h" #include "platform/platform.h" #include "string/stringTable.h" namespace Hash { //-Mat this was copied from StringTable void initTolowerTable(); U32 hash(const char *str); //-Mat inline U32 hash(U32 data) { return data; } inline U32 hash(const void *data) { #ifdef TORQUE_CPU_X64 return (U32)((U64)data); #else return (U32)data; #endif } U32 nextPrime(U32); }; namespace tKeyCompare { template inline bool equals( Key keya, Key keyb ) { return ( keya == keyb ); } template<> inline bool equals<>( const char *keya, const char *keyb ) { //-Mat make sure this is an accurate compare (do we check case?) return ( dStricmp( keya, keyb ) == 0 ); } }; /// A HashTable template class. /// /// The hash table class maps between a key and an associated value. Access /// using the key is performed using a hash table. The class provides /// methods for both unique and equal keys. The global ::hash(Type) function /// is used for hashing, see util/hash.h /// @ingroup UtilContainers template class HashTable { public: struct Pair { Key key; Value value; Pair() {} Pair(Key k,Value v): key(k), value(v) {} }; private: struct Node { Node* mNext; Pair mPair; Node(): mNext(0) {} Node(Pair p,Node* n): mPair(p),mNext(n) {} }; Node** mTable; ///< Hash table S32 mTableSize; ///< Hash table size U32 mSize; ///< Number of keys in the table U32 _hash(const Key& key) const; U32 _index(const Key& key) const; Node* _next(U32 index) const; void _resize(U32 size); void _destroy(); public: // iterator support template class _Iterator { friend class HashTable; E* mLink; M* mtHashTable; operator E*(); public: typedef U ValueType; typedef U* Pointer; typedef U& Reference; _Iterator() { mtHashTable = 0; mLink = 0; } _Iterator(M* table,E* ptr) { mtHashTable = table; mLink = ptr; } _Iterator& operator++() { mLink = mLink->mNext? mLink->mNext : mtHashTable->_next(mtHashTable->_index(mLink->mPair.key) + 1); return *this; } _Iterator operator++(int) { _Iterator itr(*this); ++(*this); return itr; } //-Mat to check if mLink is bad Value getValue() { if( mLink ) { return mLink->mPair.value; } return (Value)(0); } bool operator==(const _Iterator& b) const { return mtHashTable == b.mtHashTable && mLink == b.mLink; } bool operator!=(const _Iterator& b) const { return !(*this == b); } U* operator->() const { return &mLink->mPair; } U& operator*() const { return mLink->mPair; } }; // Types typedef Pair ValueType; typedef Pair& Reference; typedef const Pair& ConstReference; typedef S32 DifferenceType; typedef U32 SizeType; typedef _Iterator iterator; typedef _Iterator const_iterator; // Initialization HashTable(); ~HashTable(); HashTable(const HashTable& p); // Management U32 size() const; ///< Return the number of elements U32 tableSize() const; ///< Return the size of the hash bucket table void clear(); ///< Empty the HashTable void resize(U32 size); bool isEmpty() const; ///< Returns true if the table is empty F32 collisions() const; ///< Returns the average number of nodes per bucket // Insert & erase elements iterator insertEqual(const Key& key, const Value&); iterator insertUnique(const Key& key, const Value&); void erase(iterator); ///< Erase the given entry void erase(const Key& key); ///< Erase all matching keys from the table // HashTable lookup iterator findOrInsert(const Key& key); iterator find(const Key&); ///< Find the first entry for the given key const_iterator find(const Key&) const; ///< Find the first entry for the given key S32 count(const Key&); ///< Count the number of matching keys in the table // Forward iterator access iterator begin(); ///< iterator to first element const_iterator begin() const; ///< iterator to first element iterator end(); ///< iterator to last element + 1 const_iterator end() const; ///< iterator to last element + 1 void operator=(const HashTable& p); }; template HashTable::HashTable() { mTableSize = 0; mTable = 0; mSize = 0; } template HashTable::HashTable(const HashTable& p) { mSize = 0; mTableSize = 0; mTable = 0; *this = p; } template HashTable::~HashTable() { _destroy(); } //----------------------------------------------------------------------------- template inline U32 HashTable::_hash(const Key& key) const { return Hash::hash(key); } template inline U32 HashTable::_index(const Key& key) const { return _hash(key) % mTableSize; } template typename HashTable::Node* HashTable::_next(U32 index) const { for (; index < (U32)mTableSize; index++) if (Node* node = mTable[index]) return node; return 0; } template void HashTable::_resize(U32 size) { S32 currentSize = mTableSize; mTableSize = Hash::nextPrime(size); Node** table = new Node*[mTableSize]; dMemset(table,0,mTableSize * sizeof(Node*)); for (S32 i = 0; i < currentSize; i++) for (Node* node = mTable[i]; node; ) { // Get groups of matching keys Node* last = node; while (last->mNext && last->mNext->mPair.key == node->mPair.key) last = last->mNext; // Move the chain to the new table Node** link = &table[_index(node->mPair.key)]; Node* tmp = last->mNext; last->mNext = *link; *link = node; node = tmp; } delete[] mTable; mTable = table; } template void HashTable::_destroy() { for (S32 i = 0; i < mTableSize; i++) for (Node* ptr = mTable[i]; ptr; ) { Node *tmp = ptr; ptr = ptr->mNext; delete tmp; } delete[] mTable; mTable = NULL; } //----------------------------------------------------------------------------- // management template inline U32 HashTable::size() const { return mSize; } template inline U32 HashTable::tableSize() const { return mTableSize; } template inline void HashTable::clear() { _destroy(); mTableSize = 0; mSize = 0; } /// Resize the bucket table for an estimated number of elements. /// This method will optimize the hash bucket table size to hold the given /// number of elements. The size argument is a hint, and will not be the /// exact size of the table. If the table is sized down below it's optimal /// size, the next insert will cause it to be resized again. Normally this /// function is used to avoid resizes when the number of elements that will /// be inserted is known in advance. template inline void HashTable::resize(U32 size) { _resize(size); } template inline bool HashTable::isEmpty() const { return mSize == 0; } template inline F32 HashTable::collisions() const { S32 chains = 0; for (S32 i = 0; i < mTableSize; i++) if (mTable[i]) chains++; return F32(mSize) / chains; } //----------------------------------------------------------------------------- // add & remove elements /// Insert the key value pair but don't insert duplicates. /// This insert method does not insert duplicate keys. If the key already exists in /// the table the function will fail and end() is returned. template typename HashTable::iterator HashTable::insertUnique(const Key& key, const Value& x) { if (mSize >= (U32)mTableSize) _resize(mSize + 1); Node** table = &mTable[_index(key)]; for (Node* itr = *table; itr; itr = itr->mNext) if ( tKeyCompare::equals( itr->mPair.key, key) ) return end(); mSize++; *table = new Node(Pair(key,x),*table); return iterator(this,*table); } /// Insert the key value pair and allow duplicates. /// This insert method allows duplicate keys. Keys are grouped together but /// are not sorted. template typename HashTable::iterator HashTable::insertEqual(const Key& key, const Value& x) { if (mSize >= (U32)mTableSize) _resize(mSize + 1); // The new key is inserted at the head of any group of matching keys. Node** prev = &mTable[_index(key)]; for (Node* itr = *prev; itr; prev = &itr->mNext, itr = itr->mNext) if ( tKeyCompare::equals( itr->mPair.key, key ) ) break; mSize++; *prev = new Node(Pair(key,x),*prev); return iterator(this,*prev); } template void HashTable::erase(const Key& key) { if (!mSize) return; Node** prev = &mTable[_index(key)]; for (Node* itr = *prev; itr; prev = &itr->mNext, itr = itr->mNext) if ( tKeyCompare::equals( itr->mPair.key, key ) ) { // Delete matching keys, which should be grouped together. do { Node* tmp = itr; itr = itr->mNext; delete tmp; mSize--; } while (itr && tKeyCompare::equals( itr->mPair.key, key ) ); *prev = itr; return; } } template void HashTable::erase(iterator node) { Node** prev = &mTable[_index(node->key)]; for (Node* itr = *prev; itr; prev = &itr->mNext, itr = itr->mNext) if (itr == node.mLink) { *prev = itr->mNext; delete itr; mSize--; return; } } //----------------------------------------------------------------------------- /// Find the key, or insert a one if it doesn't exist. /// Returns the first key in the table that matches, or inserts one if there /// are none. template typename HashTable::iterator HashTable::findOrInsert(const Key& key) { if (mSize >= (U32)mTableSize) _resize(mSize + 1); Node** table = &mTable[_index(key)]; for (Node* itr = *table; itr; itr = itr->mNext) if ( tKeyCompare::equals( itr->mPair.key, key ) ) return iterator(this,itr); mSize++; *table = new Node(Pair(key,Value()),*table); return iterator(this,*table); } template typename HashTable::iterator HashTable::find(const Key& key) { if (mTableSize) for (Node* itr = mTable[_index(key)]; itr; itr = itr->mNext) if ( tKeyCompare::equals( itr->mPair.key, key ) ) return iterator(this,itr); return iterator(this,0); } template S32 HashTable::count(const Key& key) { S32 count = 0; if (mTableSize) for (Node* itr = mTable[_index(key)]; itr; itr = itr->mNext) if ( tKeyCompare::equals( itr->mPair.key, key ) ) { // Matching keys should be grouped together. do { count++; itr = itr->mNext; } while (itr && tKeyCompare::equals( itr->mPair.key, key ) ); break; } return count; } //----------------------------------------------------------------------------- // iterator access template inline typename HashTable::iterator HashTable::begin() { return iterator(this,_next(0)); } template inline typename HashTable::const_iterator HashTable::begin() const { return const_iterator(this,_next(0)); } template inline typename HashTable::iterator HashTable::end() { return iterator(this,0); } template inline typename HashTable::const_iterator HashTable::end() const { return const_iterator(this,0); } //----------------------------------------------------------------------------- // operators template void HashTable::operator=(const HashTable& p) { _destroy(); mTableSize = p.mTableSize; mTable = new Node*[mTableSize]; mSize = p.mSize; for (S32 i = 0; i < mTableSize; i++) if (Node* itr = p.mTable[i]) { Node** head = &mTable[i]; do { *head = new Node(itr->mPair,0); head = &(*head)->mNext; itr = itr->mNext; } while (itr); } else mTable[i] = 0; } //----------------------------------------------------------------------------- // iterator class /// A HashMap template class. /// The map class maps between a key and an associated value. Keys /// are unique. /// The hash table class is used as the default implementation so the /// the key must be hashable, see util/hash.h for details. /// @ingroup UtilContainers template > class HashMap: private Sequence { typedef HashTable Parent; private: Sequence mHashMap; public: // types typedef typename Parent::Pair Pair; typedef Pair ValueType; typedef Pair& Reference; typedef const Pair& ConstReference; typedef typename Parent::iterator iterator; typedef typename Parent::const_iterator const_iterator; typedef S32 DifferenceType; typedef U32 SizeType; // initialization HashMap() {} ~HashMap() {} HashMap(const HashMap& p); // management U32 size() const; ///< Return the number of elements void clear(); ///< Empty the HashMap bool isEmpty() const; ///< Returns true if the map is empty // insert & erase elements iterator insert(const Key& key, const Value&); // Documented below... void erase(iterator); ///< Erase the given entry void erase(const Key& key); ///< Erase the key from the map // HashMap lookup iterator find(const Key&); ///< Find entry for the given key const_iterator find(const Key&) const; ///< Find entry for the given key bool contains(const Key&a) { return mHashMap.count(a) > 0; } // forward iterator access iterator begin(); ///< iterator to first element const_iterator begin() const; ///< iterator to first element iterator end(); ///< IIterator to last element + 1 const_iterator end() const; ///< iterator to last element + 1 // operators Value& operator[](const Key&); ///< Index using the given key. If the key is not currently in the map it is added. }; template HashMap::HashMap(const HashMap& p) { *this = p; } //----------------------------------------------------------------------------- // management template inline U32 HashMap::size() const { return mHashMap.size(); } template inline void HashMap::clear() { mHashMap.clear(); } template inline bool HashMap::isEmpty() const { return mHashMap.isEmpty(); } //----------------------------------------------------------------------------- // add & remove elements /// Insert the key value pair but don't allow duplicates. /// The map class does not allow duplicates keys. If the key already exists in /// the map the function will fail and return end(). template typename HashMap::iterator HashMap::insert(const Key& key, const Value& x) { return mHashMap.insertUnique(key,x); } template void HashMap::erase(const Key& key) { mHashMap.erase(key); } template void HashMap::erase(iterator node) { mHashMap.erase(node); } //----------------------------------------------------------------------------- // Searching template typename HashMap::iterator HashMap::find(const Key& key) { return mHashMap.find(key); } //----------------------------------------------------------------------------- // iterator access template inline typename HashMap::iterator HashMap::begin() { return mHashMap.begin(); } template inline typename HashMap::const_iterator HashMap::begin() const { return mHashMap.begin(); } template inline typename HashMap::iterator HashMap::end() { return mHashMap.end(); } template inline typename HashMap::const_iterator HashMap::end() const { return mHashMap.end(); } //----------------------------------------------------------------------------- // operators template inline Value& HashMap::operator[](const Key& key) { return mHashMap.findOrInsert(key)->value; } #endif// _HASHTABLE_H_