csgo-2018-source/vstdlib/concommandhash.h
2021-07-24 21:11:47 -07:00

212 lines
6.6 KiB
C++

//===== 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<HashEntry_t> datapool_t;
CUtlVector<CCommandHashHandle_t> 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