// // The developer of the original code and/or files is Tripwire, Inc. // Portions created by Tripwire, Inc. are copyright (C) 2000-2017 Tripwire, // Inc. Tripwire is a registered trademark of Tripwire, Inc. All rights // reserved. // // This program is free software. The contents of this file are subject // to the terms of the GNU General Public License as published by the // Free Software Foundation; either version 2 of the License, or (at your // option) any later version. You may redistribute it and/or modify it // only in compliance with the GNU General Public License. // // This program is distributed in the hope that it will be useful. // However, this program is distributed AS-IS WITHOUT ANY // WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS // FOR A PARTICULAR PURPOSE. Please see the GNU General Public License // for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, // USA. // // Nothing in the GNU General Public License or any other license to use // the code or files shall permit you to use Tripwire's trademarks, // service marks, or other intellectual property without Tripwire's // prior written consent. // // If you have any questions, please contact Tripwire, Inc. at either // info@tripwire.org or www.tripwire.org. // // hashtable.h : a template class for mapping tuples using TCHAR*'s // // implements cHashTable, which maps a key of arbitrary type to a value // of arbitrary type. The key data type MUST have the const byte*() // operator overloaded in order for this to work. TSTRINGS will always // work as the key value because of the overloaded-template-function // // Note: Any overloaded const byte*() operator must return an // length of key as well. see cDefaultConvert // // IMPORTANT -- cDefaultConvert only works for pointers to objects // -- cDefaultCompare only (mostly) works for objects #ifndef __HASHTABLE_H #define __HASHTABLE_H #ifndef __TYPES_H #include "types.h" #endif #ifndef __TCHAR_H #include "core/tchar.h" #endif #ifndef __DEBUG_H #include "debug.h" #endif #ifndef __ERROR_H #include "error.h" #endif /////////////////////////////////////////////////////////////////////////////// // Comparison function objects ... these are used by the hash table to determine // equality. The one defined should work for objects that use op== to define // equality. There is also a specialization for TSTRINGS. If neither of these // fit your needs, you must pass the hash table your own fn pointer or class /////////////////////////////////////////////////////////////////////////////// template class cDefaultCompare { public: bool operator()(const T& lhs, const T& rhs) { return lhs == rhs; } }; ///////////////////////////////////////////////////////// // specialization for TSTRINGS ///////////////////////////////////////////////////////// template<> inline bool cDefaultCompare::operator()(const TSTRING& lhs, const TSTRING& rhs) { return (lhs.compare(rhs) == 0); } /////////////////////////////////////////////////////////////////////////////// // Conversion function objects ... used by the hash table to locate the key in KEY_TYPE // into a byte* and a key length (for hashing purposes). The default implementation // just does a cast. A specialization is also provided for TSTRINGs. /////////////////////////////////////////////////////////////////////////////// template class cDefaultConvert { public: const byte* operator()(const T& obj, int* const pcbKeyLen) { // HACK! TODO: in the interest of time, I've left this as it is..... *pcbKeyLen = sizeof(TCHAR) * _tcslen(obj); return (byte*)obj; } }; ///////////////////////////////////////////////////////// // specialization for TSTRINGS ///////////////////////////////////////////////////////// template<> inline const byte* cDefaultConvert::operator()(const TSTRING& obj, int* const pcbKeyLen ) { *pcbKeyLen = sizeof(TCHAR) * obj.length(); return (byte*)obj.c_str(); } /////////////////////////////////////////////////////////////////////////////// // cHashTable // KEY -- the key you are hashing on // VAL -- the value you want associated with that key // CMP -- a function object that takes (KEY, KEY) and returns true if they // are equal. // CONVERTER -- function object that takes (KEY, int* pcbKeyLen) and returns a const byte* // ( points to start of key ) and a byte length (in pcbKeyLen) that tells the hashtable // how long the key is /////////////////////////////////////////////////////////////////////////////// // these were moved outside of the class because it sucks to have to name the class with template parameters // ie -- mTable(cHashTable::MEDIUM enum cHashTable_TableSize { HASH_VERY_SMALL = 17, HASH_SMALL = 2007, HASH_MEDIUM = 6007, HASH_LARGE = 13007, HASH_VERY_LARGE = 49999 }; // forward declaration template , class CONVERTER = cDefaultConvert > class cHashTableIter; //General version of cHashTable template: template , class CONVERTER = cDefaultConvert > class cHashTable { friend class cHashTableIter; public: //structure for hash table nodes. struct node { KEY_TYPE nKey; VAL_TYPE nData; node* next; }; cHashTable(int tblSize = HASH_MEDIUM); ~cHashTable(); bool Insert(KEY_TYPE key, VAL_TYPE data_in); // The pointer, data_in, is stored in a node based on string_in's hashing. // // if (key) already exists in the table, then it's value is replaced by (data_in) // returns true if (key) already existed in table. otherwise, returns false bool Lookup(KEY_TYPE key, VAL_TYPE& data_out) const; //bool Lookup(TSTRING key, VAL_TYPE& data_out) const; //Lookup returns true if a match is found for string_check. A reference //to the node in the table that matches string_check is passed back (by ref). bool Remove(KEY_TYPE key); //The node that matches string_out is de-allocated. bool Clear(void); //Clears the entire table and sets all node pointers to NULL bool IsEmpty(void) const; uint32 Hash( const KEY_TYPE& key ) const; //The hashing function, taken from old Tripwire int32 GetNumValues() const { return mValuesInTable; }; // returns number of table entries filled #ifdef DEBUG void TraceDiagnostics() const; // traces hash table statistics #endif private: cHashTable(const cHashTable& rhs); // not impl void operator=(const cHashTable& rhs); // not impl node** mTable; int mTableSize; int32 mValuesInTable; }; /////////////////////////////////////////////////////////////////////////////// // cHashTableIter /////////////////////////////////////////////////////////////////////////////// template class cHashTableIter { public: cHashTableIter(const cHashTable& hashTbl); void SeekBegin() const; bool Done() const; void Next() const; const KEY_TYPE& Key() const; const VAL_TYPE& Val() const; VAL_TYPE& Val(); private: mutable int mCurIndex; mutable typename cHashTable::node* mpCurNode; const cHashTable& mHashTable; // helper function void SeekNextValid() const; }; //############################################################################# // implementation /////////////////////////////////////////////////////////////////////////////// // iterator /////////////////////////////////////////////////////////////////////////////// template inline cHashTableIter::cHashTableIter( const cHashTable& hashTbl) : mHashTable(hashTbl) { SeekBegin(); } template inline void cHashTableIter::SeekBegin() const { mCurIndex = 0; mpCurNode = mHashTable.mTable[0]; if(! mpCurNode) SeekNextValid(); } template inline bool cHashTableIter::Done() const { return ((mCurIndex < 0) || (mCurIndex >= mHashTable.mTableSize)); } template inline void cHashTableIter::Next() const { SeekNextValid(); } template inline void cHashTableIter::SeekNextValid() const { if(mpCurNode) mpCurNode = mpCurNode->next; //mCurIndex++; while((! mpCurNode) && (mCurIndex < mHashTable.mTableSize)) { mpCurNode = mHashTable.mTable[++mCurIndex]; } } template inline const KEY_TYPE& cHashTableIter::Key() const { ASSERT(! Done()); return mpCurNode->nKey; } template inline const VAL_TYPE& cHashTableIter::Val() const { ASSERT(! Done()); return mpCurNode->nData; } template inline VAL_TYPE& cHashTableIter::Val() { ASSERT(! Done()); return mpCurNode->nData; } /////////////////////////////////////////////////////////////////////////////// // Constructors & Destructor /////////////////////////////////////////////////////////////////////////////// //Default value for tblSize == 6007 template cHashTable::cHashTable(int tblSize) { mValuesInTable = 0; mTableSize = tblSize; mTable = new node*[mTableSize]; for (int i=0; i < mTableSize; ++i) mTable[i] = NULL; } //Destructor steps through table and deallocates all dynamic memory template cHashTable::~cHashTable() { for (int i=0; inext; delete del; } } } delete [] mTable; } //////////////////////////////////////////////////////////////////////////////// // Insert -- Hashes a const TCHAR* to a new index. Collisions are resolved // using seperate chaining (link lists). //////////////////////////////////////////////////////////////////////////////// // General Version: template bool cHashTable::Insert(KEY_TYPE key, VAL_TYPE d_in) { COMPARE_OP compare; int hindex = Hash( key ); if (mTable[hindex] == NULL) { //open index, perform insert mTable[hindex] = new node; (mTable[hindex])->nKey = key; (mTable[hindex])->next = NULL; (mTable[hindex])->nData = d_in; mValuesInTable++; return false; } else //collision, do linked list insert { // case 1: key already exists in list -- replace existing one // case 2: key does not exist -- add to end of list node* nodeptr = mTable[hindex]; bool found = false; while (true) { if ( compare(nodeptr->nKey, key)) { // we found a duplicate! found = true; break; } // break if this is the last node in the list if(! nodeptr->next) break; // otherwise, keep traversing nodeptr = nodeptr->next; } // add a node if the key was not found if (! found) { node *prev = nodeptr; nodeptr = new node; nodeptr->nKey = key; nodeptr->next = NULL; prev->next = nodeptr; mValuesInTable++; } // whether it is a new node or not, set the data to this new value nodeptr->nData = d_in; return found; } } //////////////////////////////////////////////////////////////////////////////// // Lookup -- Attempts to find 'string' in the hash table. //////////////////////////////////////////////////////////////////////////////// // General Version: template bool cHashTable::Lookup(KEY_TYPE key, VAL_TYPE& d_out) const { COMPARE_OP compare; int hindex = Hash( key ); if (mTable[hindex] == NULL) return false; else { node* nodeptr = mTable[hindex]; while (nodeptr != NULL) { if( compare(nodeptr->nKey, key)) { d_out = nodeptr->nData; return true; } nodeptr = nodeptr->next; } } return false; //mTable entries exhausted without a match } //////////////////////////////////////////////////////////////////////////////// // Remove -- Removes a single entry from the hash table. Returns false if // the nKey is not found in the table. //////////////////////////////////////////////////////////////////////////////// // General Version - template bool cHashTable::Remove(KEY_TYPE key) { COMPARE_OP compare; int hindex = Hash( key ); if (mTable[hindex] == NULL) { delete (mTable[hindex]); mTable[hindex] = NULL; return true; } else { node* nodeptr = mTable[hindex]; node* prev; while(nodeptr != NULL) { prev = nodeptr; if(compare(mTable[hindex]->nKey, key)) { prev->next = nodeptr->next; delete nodeptr; if (nodeptr == mTable[hindex]) mTable[hindex] = NULL; nodeptr = NULL; return true; }//end if nodeptr = nodeptr->next; }//end while }//end else return false; //match was not found, no node deleted } //////////////////////////////////////////////////////////////////////////////// // Clear -- Clears entire hash table so that all indices are NULL //////////////////////////////////////////////////////////////////////////////// template bool cHashTable::Clear(void) { for (int i=0; inext; delete del; if (del == mTable[i]) mTable[i] = NULL; del = NULL; }//end delete chain loop }//end if mTable[i]!= NULL }//end for return (IsEmpty()); } //////////////////////////////////////////////////////////////////////////////// // IsEmpty -- //////////////////////////////////////////////////////////////////////////////// template bool cHashTable::IsEmpty(void) const { bool ret = true; for(int i=0; i< mTableSize; ++i) ret &= (mTable[i] == NULL); return ret; } //////////////////////////////////////////////////////////////////////////////// // Hash -- performs hashing on key, returns an integer index val. //////////////////////////////////////////////////////////////////////////////// template uint32 cHashTable::Hash( const KEY_TYPE& key ) const { CONVERTER converter; int len; const byte* pb = converter( key, &len ); //locates key uint32 hindex; hindex = *pb; while (len-- > 0) hindex = ((hindex << 9) ^ *pb++) % mTableSize; return hindex; } #ifdef DEBUG template void cHashTable::TraceDiagnostics() const { cDebug d("cHashTable::Diagnostics"); int slotsFilled = 0, numItems = 0, numMultiSlot = 0; node* pNode; for(int i=0; i < mTableSize; i++) { if(mTable[i] != NULL) { slotsFilled++; numItems++; pNode = (mTable[i])->next; if(pNode != NULL) numMultiSlot++; while(pNode) { numItems++; pNode = pNode->next; } } } d.TraceDebug("---------------Hash Table Statisics---------------\n"); d.TraceDebug("-- Number of slots: %d\n", mTableSize); d.TraceDebug("-- Number of items: %d\n", numItems); d.TraceDebug("-- Slots filled: %d (%lf %%)\n",slotsFilled, ((double)slotsFilled / (double)mTableSize) * 100.0); d.TraceDebug("-- Slots with >1 item: %d (%lf %%)\n",numMultiSlot, ((double)numMultiSlot / (double)slotsFilled) * 100.0); d.TraceDebug("--------------------------------------------------\n"); } #endif // DEBUG #endif //__HASHTABLE_H