//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: Special case hash table for console commands // // $NoKeywords: $ // //===========================================================================// #if !defined( CONCOMMANDHASH_H ) #define CONCOMMANDHASH_H #ifdef _WIN32 #pragma once #endif #include "utllinkedlist.h" #include "generichash.h" // This is a hash table class very similar to the CUtlHashFast, but // modified specifically so that we can look up ConCommandBases // by string names without having to actually store those strings in // the dictionary, and also iterate over all of them. // It uses separate chaining: each key hashes to a bucket, each // bucket is a linked list of hashed commands. We store the hash of // the command's string name as well as its pointer, so we can do // the linked list march part of the Find() operation more quickly. class CConCommandHash { public: typedef intp CCommandHashHandle_t; typedef unsigned int HashKey_t; // Constructor/Deconstructor. CConCommandHash(); ~CConCommandHash(); // Memory. void Purge( bool bReinitialize ); // Invalid handle. static CCommandHashHandle_t InvalidHandle( void ) { return ( CCommandHashHandle_t )~0; } inline bool IsValidHandle( CCommandHashHandle_t hHash ) const; /// Initialize. void Init( void ); // bucket count is hardcoded in enum below. /// Get hash value for a concommand static inline HashKey_t Hash( const ConCommandBase *cmd ); // Size not available; count is meaningless for multilists. // int Count( void ) const; // Insertion. CCommandHashHandle_t Insert( ConCommandBase *cmd ); CCommandHashHandle_t FastInsert( ConCommandBase *cmd ); // Removal. void Remove( CCommandHashHandle_t hHash ) RESTRICT; void RemoveAll( void ); // Retrieval. inline CCommandHashHandle_t Find( const char *name ) const; CCommandHashHandle_t Find( const ConCommandBase *cmd ) const RESTRICT; // A convenience version of Find that skips the handle part // and returns a pointer to a concommand, or NULL if none was found. inline ConCommandBase * FindPtr( const char *name ) const; inline ConCommandBase * &operator[]( CCommandHashHandle_t hHash ); inline ConCommandBase *const &operator[]( CCommandHashHandle_t hHash ) const; #ifdef _DEBUG // Dump a report to MSG void Report( void ); #endif // Iteration struct CCommandHashIterator_t { int bucket; CCommandHashHandle_t handle; CCommandHashIterator_t(int _bucket, const CCommandHashHandle_t &_handle) : bucket(_bucket), handle(_handle) {}; // inline operator UtlHashFastHandle_t() const { return handle; }; }; inline CCommandHashIterator_t First() const; inline CCommandHashIterator_t Next( const CCommandHashIterator_t &hHash ) const; inline bool IsValidIterator( const CCommandHashIterator_t &iter ) const; inline ConCommandBase * &operator[]( const CCommandHashIterator_t &iter ) { return (*this)[iter.handle]; } inline ConCommandBase * const &operator[]( const CCommandHashIterator_t &iter ) const { return (*this)[iter.handle]; } private: // a find func where we've already computed the hash for the string. // (hidden private in case we decide to invent a custom string hash func // for this class) CCommandHashHandle_t Find( const char *name, HashKey_t hash) const RESTRICT; protected: enum { kNUM_BUCKETS = 256, kBUCKETMASK = kNUM_BUCKETS - 1, }; struct HashEntry_t { HashKey_t m_uiKey; ConCommandBase *m_Data; HashEntry_t(unsigned int _hash, ConCommandBase * _cmd) : m_uiKey(_hash), m_Data(_cmd) {}; HashEntry_t(){}; }; typedef CUtlFixedLinkedList datapool_t; CUtlVector m_aBuckets; datapool_t m_aDataPool; }; inline bool CConCommandHash::IsValidHandle( CCommandHashHandle_t hHash ) const { return m_aDataPool.IsValidIndex(hHash); } inline CConCommandHash::CCommandHashHandle_t CConCommandHash::Find( const char *name ) const { return Find( name, HashStringCaseless(name) ); } inline ConCommandBase * &CConCommandHash::operator[]( CCommandHashHandle_t hHash ) { return ( m_aDataPool[hHash].m_Data ); } inline ConCommandBase *const &CConCommandHash::operator[]( CCommandHashHandle_t hHash ) const { return ( m_aDataPool[hHash].m_Data ); } //----------------------------------------------------------------------------- // Purpose: For iterating over the whole hash, return the index of the first element //----------------------------------------------------------------------------- CConCommandHash::CCommandHashIterator_t CConCommandHash::First() const { // walk through the buckets to find the first one that has some data int bucketCount = m_aBuckets.Count(); const CCommandHashHandle_t invalidIndex = m_aDataPool.InvalidIndex(); for ( int bucket = 0 ; bucket < bucketCount ; ++bucket ) { CCommandHashHandle_t iElement = m_aBuckets[bucket]; // get the head of the bucket if ( iElement != invalidIndex ) return CCommandHashIterator_t( bucket, iElement ); } // if we are down here, the list is empty return CCommandHashIterator_t( -1, invalidIndex ); } //----------------------------------------------------------------------------- // Purpose: For iterating over the whole hash, return the next element after // the param one. Or an invalid iterator. //----------------------------------------------------------------------------- CConCommandHash::CCommandHashIterator_t CConCommandHash::Next( const CConCommandHash::CCommandHashIterator_t &iter ) const { // look for the next entry in the current bucket CCommandHashHandle_t next = m_aDataPool.Next(iter.handle); const CCommandHashHandle_t invalidIndex = m_aDataPool.InvalidIndex(); if ( next != invalidIndex ) { // this bucket still has more elements in it return CCommandHashIterator_t(iter.bucket, next); } // otherwise look for the next bucket with data int bucketCount = m_aBuckets.Count(); for ( int bucket = iter.bucket+1 ; bucket < bucketCount ; ++bucket ) { CCommandHashHandle_t next = m_aBuckets[bucket]; // get the head of the bucket if (next != invalidIndex) return CCommandHashIterator_t( bucket, next ); } // if we're here, there's no more data to be had return CCommandHashIterator_t(-1, invalidIndex); } bool CConCommandHash::IsValidIterator( const CCommandHashIterator_t &iter ) const { return ( (iter.bucket >= 0) && (m_aDataPool.IsValidIndex(iter.handle)) ); } inline CConCommandHash::HashKey_t CConCommandHash::Hash( const ConCommandBase *cmd ) { return HashStringCaseless( cmd->GetName() ); } inline ConCommandBase * CConCommandHash::FindPtr( const char *name ) const { CCommandHashHandle_t handle = Find(name); if (handle == InvalidHandle()) { return NULL; } else { return (*this)[handle]; } } #endif