diff --git a/src/public/tier0/dbg.h b/src/public/tier0/dbg.h index fcc69833..14e8fe50 100644 --- a/src/public/tier0/dbg.h +++ b/src/public/tier0/dbg.h @@ -161,6 +161,38 @@ template inline void AssertValidReadWritePtr(T* /*ptr*/, int count = 1) #define AssertValidThis() #endif +//----------------------------------------------------------------------------- +// Macro to protect functions that are not reentrant + +#ifdef _DEBUG +class CReentryGuard +{ +public: + CReentryGuard(int* pSemaphore) + : m_pSemaphore(pSemaphore) + { + ++(*m_pSemaphore); + } + + ~CReentryGuard() + { + --(*m_pSemaphore); + } + +private: + int* m_pSemaphore; +}; + +#define ASSERT_NO_REENTRY() \ + static int fSemaphore##__LINE__; \ + Assert( !fSemaphore##__LINE__ ); \ + CReentryGuard ReentryGuard##__LINE__( &fSemaphore##__LINE__ ) +#else +#define ASSERT_NO_REENTRY() +#endif + +#define AssertMsg(condition, ...) assert(condition) + typedef void (*CoreMsgVCallbackSink_t)(LogType_t logType, LogLevel_t logLevel, eDLL_T context, const char* pszLogger, const char* pszFormat, va_list args, const UINT exitCode, const char* pszUptimeOverride); diff --git a/src/public/tier1/utlhash.h b/src/public/tier1/utlhash.h new file mode 100644 index 00000000..014504e5 --- /dev/null +++ b/src/public/tier1/utlhash.h @@ -0,0 +1,1299 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======// +// +// Purpose: +// +// $NoKeywords: $ +// +// Serialization/unserialization buffer +//=============================================================================// + +#ifndef UTLHASH_H +#define UTLHASH_H +#pragma once + +#include +#include "tier0/commonmacros.h" +#include "utlmemory.h" +#include "utlvector.h" +#include "utllinkedlist.h" +#include "utllinkedlist.h" +#include "generichash.h" + +typedef unsigned int UtlHashHandle_t; + +template +class CUtlHash +{ +public: + // compare and key functions - implemented by the + typedef C CompareFunc_t; + typedef K KeyFunc_t; + + // constructor/deconstructor + explicit CUtlHash( int bucketCount = 0, int growCount = 0, int initCount = 0, + CompareFunc_t compareFunc = 0, KeyFunc_t keyFunc = 0 ); + ~CUtlHash(); + + // invalid handle + static UtlHashHandle_t InvalidHandle( void ) { return ( UtlHashHandle_t )~0; } + bool IsValidHandle( UtlHashHandle_t handle ) const; + + // size + int Count( void ) const; + + // memory + void Purge( void ); + + // insertion methods + UtlHashHandle_t Insert( Data const &src ); + UtlHashHandle_t Insert( Data const &src, bool *pDidInsert ); + UtlHashHandle_t AllocEntryFromKey( Data const &src ); + + // removal methods + void Remove( UtlHashHandle_t handle ); + void RemoveAll(); + + // retrieval methods + UtlHashHandle_t Find( Data const &src ) const; + + Data &Element( UtlHashHandle_t handle ); + Data const &Element( UtlHashHandle_t handle ) const; + Data &operator[]( UtlHashHandle_t handle ); + Data const &operator[]( UtlHashHandle_t handle ) const; + + UtlHashHandle_t GetFirstHandle() const; + UtlHashHandle_t GetNextHandle( UtlHashHandle_t h ) const; + + // debugging!! + void Log( const char *filename ); + void Dump(); + +protected: + + int GetBucketIndex( UtlHashHandle_t handle ) const; + int GetKeyDataIndex( UtlHashHandle_t handle ) const; + UtlHashHandle_t BuildHandle( int ndxBucket, int ndxKeyData ) const; + + bool DoFind( Data const &src, unsigned int *pBucket, int *pIndex ) const; + +protected: + + // handle upper 16 bits = bucket index (bucket heads) + // handle lower 16 bits = key index (bucket list) + typedef CUtlVector HashBucketList_t; + CUtlVector m_Buckets; + + CompareFunc_t m_CompareFunc; // function used to handle unique compares on data + KeyFunc_t m_KeyFunc; // function used to generate the key value + + bool m_bPowerOfTwo; // if the bucket value is a power of two, + unsigned int m_ModMask; // use the mod mask to "mod" +}; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +CUtlHash::CUtlHash( int bucketCount, int growCount, int initCount, + CompareFunc_t compareFunc, KeyFunc_t keyFunc ) : + m_CompareFunc( compareFunc ), + m_KeyFunc( keyFunc ) +{ + bucketCount = Min(bucketCount, 65536); + m_Buckets.SetSize( bucketCount ); + for( int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++ ) + { + m_Buckets[ndxBucket].SetSize( initCount ); + m_Buckets[ndxBucket].SetGrowSize( growCount ); + } + + // check to see if the bucket count is a power of 2 and set up + // optimizations appropriately + m_bPowerOfTwo = IsPowerOfTwo( bucketCount ); + m_ModMask = m_bPowerOfTwo ? (bucketCount-1) : 0; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +CUtlHash::~CUtlHash() +{ + Purge(); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline bool CUtlHash::IsValidHandle( UtlHashHandle_t handle ) const +{ + int ndxBucket = GetBucketIndex( handle ); + int ndxKeyData = GetKeyDataIndex( handle ); + + // ndxBucket and ndxKeyData can't possibly be less than zero -- take a + // look at the definition of the Get..Index functions for why. However, + // if you override those functions, you will need to override this one + // as well. + if( /*( ndxBucket >= 0 ) && */ ( ndxBucket < m_Buckets.Count() ) ) + { + if( /*( ndxKeyData >= 0 ) && */ ( ndxKeyData < m_Buckets[ndxBucket].Count() ) ) + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline int CUtlHash::Count( void ) const +{ + int count = 0; + + int bucketCount = m_Buckets.Count(); + for( int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++ ) + { + count += m_Buckets[ndxBucket].Count(); + } + + return count; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline int CUtlHash::GetBucketIndex( UtlHashHandle_t handle ) const +{ + return ( ( ( handle >> 16 ) & 0x0000ffff ) ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline int CUtlHash::GetKeyDataIndex( UtlHashHandle_t handle ) const +{ + return ( handle & 0x0000ffff ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline UtlHashHandle_t CUtlHash::BuildHandle( int ndxBucket, int ndxKeyData ) const +{ + Assert( ( ndxBucket >= 0 ) && ( ndxBucket < 65536 ) ); + Assert( ( ndxKeyData >= 0 ) && ( ndxKeyData < 65536 ) ); + + UtlHashHandle_t handle = ndxKeyData; + handle |= ( ndxBucket << 16 ); + + return handle; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline void CUtlHash::Purge( void ) +{ + int bucketCount = m_Buckets.Count(); + for( int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++ ) + { + m_Buckets[ndxBucket].Purge(); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline bool CUtlHash::DoFind( Data const &src, unsigned int *pBucket, int *pIndex ) const +{ + // generate the data "key" + unsigned int key = m_KeyFunc( src ); + + // hash the "key" - get the correct hash table "bucket" + unsigned int ndxBucket; + if( m_bPowerOfTwo ) + { + *pBucket = ndxBucket = ( key & m_ModMask ); + } + else + { + int bucketCount = m_Buckets.Count(); + *pBucket = ndxBucket = key % bucketCount; + } + + int ndxKeyData = 0; + const CUtlVector &bucket = m_Buckets[ndxBucket]; + int keyDataCount = bucket.Count(); + for( ndxKeyData = 0; ndxKeyData < keyDataCount; ndxKeyData++ ) + { + if( m_CompareFunc( bucket.Element( ndxKeyData ), src ) ) + break; + } + + if( ndxKeyData == keyDataCount ) + return false; + + *pIndex = ndxKeyData; + return true; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline UtlHashHandle_t CUtlHash::Find( Data const &src ) const +{ + unsigned int ndxBucket; + int ndxKeyData = 0; + + if ( DoFind( src, &ndxBucket, &ndxKeyData ) ) + { + return ( BuildHandle( ndxBucket, ndxKeyData ) ); + } + return ( InvalidHandle() ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline UtlHashHandle_t CUtlHash::Insert( Data const &src ) +{ + unsigned int ndxBucket; + int ndxKeyData = 0; + + if ( DoFind( src, &ndxBucket, &ndxKeyData ) ) + { + return ( BuildHandle( ndxBucket, ndxKeyData ) ); + } + + ndxKeyData = m_Buckets[ndxBucket].AddToTail( src ); + + return ( BuildHandle( ndxBucket, ndxKeyData ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline UtlHashHandle_t CUtlHash::Insert( Data const &src, bool *pDidInsert ) +{ + unsigned int ndxBucket; + int ndxKeyData = 0; + + if ( DoFind( src, &ndxBucket, &ndxKeyData ) ) + { + *pDidInsert = false; + return ( BuildHandle( ndxBucket, ndxKeyData ) ); + } + + *pDidInsert = true; + ndxKeyData = m_Buckets[ndxBucket].AddToTail( src ); + + return ( BuildHandle( ndxBucket, ndxKeyData ) ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline UtlHashHandle_t CUtlHash::AllocEntryFromKey( Data const &src ) +{ + unsigned int ndxBucket; + int ndxKeyData = 0; + + if ( DoFind( src, &ndxBucket, &ndxKeyData ) ) + { + return ( BuildHandle( ndxBucket, ndxKeyData ) ); + } + + ndxKeyData = m_Buckets[ndxBucket].AddToTail(); + + return ( BuildHandle( ndxBucket, ndxKeyData ) ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline void CUtlHash::Remove( UtlHashHandle_t handle ) +{ + Assert( IsValidHandle( handle ) ); + + // check to see if the bucket exists + int ndxBucket = GetBucketIndex( handle ); + int ndxKeyData = GetKeyDataIndex( handle ); + + if( m_Buckets[ndxBucket].IsValidIndex( ndxKeyData ) ) + { + m_Buckets[ndxBucket].FastRemove( ndxKeyData ); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline void CUtlHash::RemoveAll() +{ + int bucketCount = m_Buckets.Count(); + for( int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++ ) + { + m_Buckets[ndxBucket].RemoveAll(); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline Data &CUtlHash::Element( UtlHashHandle_t handle ) +{ + int ndxBucket = GetBucketIndex( handle ); + int ndxKeyData = GetKeyDataIndex( handle ); + + return ( m_Buckets[ndxBucket].Element( ndxKeyData ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline Data const &CUtlHash::Element( UtlHashHandle_t handle ) const +{ + int ndxBucket = GetBucketIndex( handle ); + int ndxKeyData = GetKeyDataIndex( handle ); + + return ( m_Buckets[ndxBucket].Element( ndxKeyData ) ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline Data &CUtlHash::operator[]( UtlHashHandle_t handle ) +{ + int ndxBucket = GetBucketIndex( handle ); + int ndxKeyData = GetKeyDataIndex( handle ); + + return ( m_Buckets[ndxBucket].Element( ndxKeyData ) ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline Data const &CUtlHash::operator[]( UtlHashHandle_t handle ) const +{ + int ndxBucket = GetBucketIndex( handle ); + int ndxKeyData = GetKeyDataIndex( handle ); + + return ( m_Buckets[ndxBucket].Element( ndxKeyData ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline UtlHashHandle_t CUtlHash::GetFirstHandle() const +{ + return GetNextHandle( ( UtlHashHandle_t )-1 ); +} + +template +inline UtlHashHandle_t CUtlHash::GetNextHandle( UtlHashHandle_t handle ) const +{ + ++handle; // start at the first possible handle after the one given + + int bi = GetBucketIndex( handle ); + int ki = GetKeyDataIndex( handle ); + + int nBuckets = m_Buckets.Count(); + for ( ; bi < nBuckets; ++bi ) + { + if ( ki < m_Buckets[ bi ].Count() ) + return BuildHandle( bi, ki ); + + ki = 0; + } + + return InvalidHandle(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline void CUtlHash::Log( const char *filename ) +{ + FILE *pDebugFp; + pDebugFp = fopen( filename, "w" ); + if( !pDebugFp ) + return; + + int maxBucketSize = 0; + int numBucketsEmpty = 0; + + int bucketCount = m_Buckets.Count(); + fprintf( pDebugFp, "\n%d Buckets\n", bucketCount ); + + for( int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++ ) + { + int count = m_Buckets[ndxBucket].Count(); + + if( count > maxBucketSize ) { maxBucketSize = count; } + if( count == 0 ) + numBucketsEmpty++; + + fprintf( pDebugFp, "Bucket %d: %d\n", ndxBucket, count ); + } + + fprintf( pDebugFp, "\nBucketHeads Used: %d\n", bucketCount - numBucketsEmpty ); + fprintf( pDebugFp, "Max Bucket Size: %d\n", maxBucketSize ); + + fclose( pDebugFp ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline void CUtlHash::Dump( ) +{ + int maxBucketSize = 0; + int numBucketsEmpty = 0; + + int bucketCount = m_Buckets.Count(); + Msg( eDLL_T::COMMON, "\n%d Buckets\n", bucketCount ); + + for( int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++ ) + { + int count = m_Buckets[ndxBucket].Count(); + + if( count > maxBucketSize ) { maxBucketSize = count; } + if( count == 0 ) + numBucketsEmpty++; + + Msg( eDLL_T::COMMON, "Bucket %d: %d\n", ndxBucket, count ); + } + + Msg( eDLL_T::COMMON, "\nBucketHeads Used: %d\n", bucketCount - numBucketsEmpty ); + Msg( eDLL_T::COMMON, "Max Bucket Size: %d\n", maxBucketSize ); +} + +//============================================================================= +// +// Fast Hash +// +// Number of buckets must be a power of 2. +// Key must be 32-bits (unsigned int). +// +typedef intp UtlHashFastHandle_t; + +#define UTLHASH_POOL_SCALAR 2 + +class CUtlHashFastNoHash +{ +public: + static int Hash( int key, int bucketMask ) + { + return ( key & bucketMask ); + } +}; + +class CUtlHashFastGenericHash +{ +public: + static int Hash( int key, int bucketMask ) + { + return ( HashIntConventional( key ) & bucketMask ); + } +}; + +template +class CUtlHashFast +{ +public: + + // Constructor/Deconstructor. + CUtlHashFast(); + ~CUtlHashFast(); + + // Memory. + void Purge( void ); + + // Invalid handle. + static UtlHashFastHandle_t InvalidHandle( void ) { return ( UtlHashFastHandle_t )~0; } + inline bool IsValidHandle( UtlHashFastHandle_t hHash ) const; + + // Initialize. + bool Init( int nBucketCount ); + + // Size not available; count is meaningless for multilists. + // int Count( void ) const; + + // Insertion. + UtlHashFastHandle_t Insert( uintp uiKey, const Data &data ); + UtlHashFastHandle_t FastInsert( uintp uiKey, const Data &data ); + + // Removal. + void Remove( UtlHashFastHandle_t hHash ); + void RemoveAll( void ); + + // Retrieval. + UtlHashFastHandle_t Find( uintp uiKey ) const; + + Data &Element( UtlHashFastHandle_t hHash ); + Data const &Element( UtlHashFastHandle_t hHash ) const; + Data &operator[]( UtlHashFastHandle_t hHash ); + Data const &operator[]( UtlHashFastHandle_t hHash ) const; + + // Iteration + struct UtlHashFastIterator_t + { + int bucket; + UtlHashFastHandle_t handle; + + UtlHashFastIterator_t(int _bucket, const UtlHashFastHandle_t &_handle) + : bucket(_bucket), handle(_handle) {}; + // inline operator UtlHashFastHandle_t() const { return handle; }; + }; + inline UtlHashFastIterator_t First() const; + inline UtlHashFastIterator_t Next( const UtlHashFastIterator_t &hHash ) const; + inline bool IsValidIterator( const UtlHashFastIterator_t &iter ) const; + inline Data &operator[]( const UtlHashFastIterator_t &iter ) { return (*this)[iter.handle]; } + inline Data const &operator[]( const UtlHashFastIterator_t &iter ) const { return (*this)[iter.handle]; } + +//protected: + + // Templatized for memory tracking purposes + template + struct HashFastData_t_ + { + uintp m_uiKey; + HashData m_Data; + }; + + typedef HashFastData_t_ HashFastData_t; + + uintp m_uiBucketMask; + CUtlVector m_aBuckets; + CUtlFixedLinkedList m_aDataPool; + +}; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +template CUtlHashFast::CUtlHashFast() +{ + Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: Deconstructor +//----------------------------------------------------------------------------- +template CUtlHashFast::~CUtlHashFast() +{ + Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: Destroy dynamically allocated hash data. +//----------------------------------------------------------------------------- +template inline void CUtlHashFast::Purge( void ) +{ + m_aBuckets.Purge(); + m_aDataPool.Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: Initialize the hash - set bucket count and hash grow amount. +//----------------------------------------------------------------------------- +template bool CUtlHashFast::Init( int nBucketCount ) +{ + // Verify the bucket count is power of 2. + if ( !IsPowerOfTwo( nBucketCount ) ) + return false; + + // Set the bucket size. + m_aBuckets.SetSize( nBucketCount ); + for ( int iBucket = 0; iBucket < nBucketCount; ++iBucket ) + { + m_aBuckets[iBucket] = m_aDataPool.InvalidIndex(); + } + + // Set the mod mask. + m_uiBucketMask = nBucketCount - 1; + + // Calculate the grow size. + int nGrowSize = UTLHASH_POOL_SCALAR * nBucketCount; + m_aDataPool.SetGrowSize( nGrowSize ); + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Return the number of elements in the hash. +// Not available because count isn't accurately maintained for multilists. +//----------------------------------------------------------------------------- +/* +template inline int CUtlHashFast::Count( void ) const +{ + return m_aDataPool.Count(); +} +*/ + +//----------------------------------------------------------------------------- +// Purpose: Insert data into the hash table given its key (uintp), with +// a check to see if the element already exists within the tree. +//----------------------------------------------------------------------------- +template inline UtlHashFastHandle_t CUtlHashFast::Insert( uintp uiKey, const Data &data ) +{ + // Check to see if that key already exists in the buckets (should be unique). + UtlHashFastHandle_t hHash = Find( uiKey ); + if( hHash != InvalidHandle() ) + return hHash; + + return FastInsert( uiKey, data ); +} + +//----------------------------------------------------------------------------- +// Purpose: Insert data into the hash table given its key (uintp), +// without a check to see if the element already exists within the tree. +//----------------------------------------------------------------------------- +template inline UtlHashFastHandle_t CUtlHashFast::FastInsert( uintp uiKey, const Data &data ) +{ + // Get a new element from the pool. + intp iHashData = m_aDataPool.Alloc( true ); + HashFastData_t *pHashData = &m_aDataPool[iHashData]; + if ( !pHashData ) + return InvalidHandle(); + + // Add data to new element. + pHashData->m_uiKey = uiKey; + pHashData->m_Data = data; + + // Link element. + int iBucket = HashFuncs::Hash( uiKey, m_uiBucketMask ); + m_aDataPool.LinkBefore( m_aBuckets[iBucket], iHashData ); + m_aBuckets[iBucket] = iHashData; + + return iHashData; +} + +//----------------------------------------------------------------------------- +// Purpose: Remove a given element from the hash. +//----------------------------------------------------------------------------- +template inline void CUtlHashFast::Remove( UtlHashFastHandle_t hHash ) +{ + int iBucket = HashFuncs::Hash( m_aDataPool[hHash].m_uiKey, m_uiBucketMask ); + if ( m_aBuckets[iBucket] == hHash ) + { + // It is a bucket head. + m_aBuckets[iBucket] = m_aDataPool.Next( hHash ); + } + else + { + // Not a bucket head. + m_aDataPool.Unlink( hHash ); + } + + // Remove the element. + m_aDataPool.Remove( hHash ); +} + +//----------------------------------------------------------------------------- +// Purpose: Remove all elements from the hash +//----------------------------------------------------------------------------- +template inline void CUtlHashFast::RemoveAll( void ) +{ + m_aBuckets.RemoveAll(); + m_aDataPool.RemoveAll(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template inline UtlHashFastHandle_t CUtlHashFast::Find( uintp uiKey ) const +{ + // hash the "key" - get the correct hash table "bucket" + int iBucket = HashFuncs::Hash( uiKey, m_uiBucketMask ); + + for ( intp iElement = m_aBuckets[iBucket]; iElement != m_aDataPool.InvalidIndex(); iElement = m_aDataPool.Next( iElement ) ) + { + if ( m_aDataPool[iElement].m_uiKey == uiKey ) + return iElement; + } + + return InvalidHandle(); +} + +//----------------------------------------------------------------------------- +// Purpose: Return data given a hash handle. +//----------------------------------------------------------------------------- +template inline Data &CUtlHashFast::Element( UtlHashFastHandle_t hHash ) +{ + return ( m_aDataPool[hHash].m_Data ); +} + +//----------------------------------------------------------------------------- +// Purpose: Return data given a hash handle. +//----------------------------------------------------------------------------- +template inline Data const &CUtlHashFast::Element( UtlHashFastHandle_t hHash ) const +{ + return ( m_aDataPool[hHash].m_Data ); +} + +//----------------------------------------------------------------------------- +// Purpose: Return data given a hash handle. +//----------------------------------------------------------------------------- +template inline Data &CUtlHashFast::operator[]( UtlHashFastHandle_t hHash ) +{ + return ( m_aDataPool[hHash].m_Data ); +} + +//----------------------------------------------------------------------------- +// Purpose: Return data given a hash handle. +//----------------------------------------------------------------------------- +template inline Data const &CUtlHashFast::operator[]( UtlHashFastHandle_t hHash ) const +{ + return ( m_aDataPool[hHash].m_Data ); +} + +//----------------------------------------------------------------------------- +// Purpose: For iterating over the whole hash, return the index of the first element +//----------------------------------------------------------------------------- +template + typename CUtlHashFast::UtlHashFastIterator_t + CUtlHashFast::First() const +{ + // walk through the buckets to find the first one that has some data + int bucketCount = m_aBuckets.Count(); + const UtlHashFastHandle_t invalidIndex = m_aDataPool.InvalidIndex(); + for ( int bucket = 0 ; bucket < bucketCount ; ++bucket ) + { + UtlHashFastHandle_t iElement = m_aBuckets[bucket]; // get the head of the bucket + if (iElement != invalidIndex) + return UtlHashFastIterator_t(bucket,iElement); + } + + // if we are down here, the list is empty + return UtlHashFastIterator_t(-1, invalidIndex); +} + +//----------------------------------------------------------------------------- +// Purpose: For iterating over the whole hash, return the next element after +// the param one. Or an invalid iterator. +//----------------------------------------------------------------------------- +template + typename CUtlHashFast::UtlHashFastIterator_t + CUtlHashFast::Next( const typename CUtlHashFast::UtlHashFastIterator_t &iter ) const +{ + // look for the next entry in the current bucket + UtlHashFastHandle_t next = m_aDataPool.Next(iter.handle); + const UtlHashFastHandle_t invalidIndex = m_aDataPool.InvalidIndex(); + if (next != invalidIndex) + { + // this bucket still has more elements in it + return UtlHashFastIterator_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 ) + { + UtlHashFastHandle_t next = m_aBuckets[bucket]; // get the head of the bucket + if (next != invalidIndex) + return UtlHashFastIterator_t( bucket, next ); + } + + // if we're here, there's no more data to be had + return UtlHashFastIterator_t(-1, invalidIndex); +} + +template + bool CUtlHashFast::IsValidIterator( const typename CUtlHashFast::UtlHashFastIterator_t &iter ) const +{ + return ( (iter.bucket >= 0) && (m_aDataPool.IsValidIndex(iter.handle)) ); +} + + +template inline bool CUtlHashFast::IsValidHandle( UtlHashFastHandle_t hHash ) const +{ + return m_aDataPool.IsValidIndex(hHash); +} + +//============================================================================= +// +// Fixed Hash +// +// Number of buckets must be a power of 2. +// Key must be pointer-size (uintp). +// +typedef intp UtlHashFixedHandle_t; + +template +class CUtlHashFixedGenericHash +{ +public: + static int Hash( int key, int bucketMask ) + { + int hash = HashIntConventional( key ); + if ( NUM_BUCKETS <= USHRT_MAX ) + { + hash ^= ( hash >> 16 ); + } + if ( NUM_BUCKETS <= UCHAR_MAX ) + { + hash ^= ( hash >> 8 ); + } + return ( hash & bucketMask ); + } +}; + +template +class CUtlHashFixed +{ +public: + + // Constructor/Deconstructor. + CUtlHashFixed(); + ~CUtlHashFixed(); + + // Memory. + void Purge( void ); + + // Invalid handle. + static UtlHashFixedHandle_t InvalidHandle( void ) { return ( UtlHashFixedHandle_t )~0; } + + // Size. + int Count( void ); + + // Insertion. + UtlHashFixedHandle_t Insert( unsigned int uiKey, const Data &data ); + UtlHashFixedHandle_t FastInsert( unsigned int uiKey, const Data &data ); + + // Removal. + void Remove( UtlHashFixedHandle_t hHash ); + void RemoveAll( void ); + + // Retrieval. + UtlHashFixedHandle_t Find( unsigned int uiKey ); + + Data &Element( UtlHashFixedHandle_t hHash ); + Data const &Element( UtlHashFixedHandle_t hHash ) const; + Data &operator[]( UtlHashFixedHandle_t hHash ); + Data const &operator[]( UtlHashFixedHandle_t hHash ) const; + + //protected: + + // Templatized for memory tracking purposes + template + struct HashFixedData_t_ + { + unsigned int m_uiKey; + Data_t m_Data; + }; + + typedef HashFixedData_t_ HashFixedData_t; + + enum + { + BUCKET_MASK = NUM_BUCKETS - 1 + }; + CUtlPtrLinkedList m_aBuckets[NUM_BUCKETS]; + int m_nElements; +}; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +template CUtlHashFixed::CUtlHashFixed() +{ + Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: Deconstructor +//----------------------------------------------------------------------------- +template CUtlHashFixed::~CUtlHashFixed() +{ + Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: Destroy dynamically allocated hash data. +//----------------------------------------------------------------------------- +template inline void CUtlHashFixed::Purge( void ) +{ + RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: Return the number of elements in the hash. +//----------------------------------------------------------------------------- +template inline int CUtlHashFixed::Count( void ) +{ + return m_nElements; +} + +//----------------------------------------------------------------------------- +// Purpose: Insert data into the hash table given its key (unsigned int), with +// a check to see if the element already exists within the tree. +//----------------------------------------------------------------------------- +template inline UtlHashFixedHandle_t CUtlHashFixed::Insert( unsigned int uiKey, const Data &data ) +{ + // Check to see if that key already exists in the buckets (should be unique). + UtlHashFixedHandle_t hHash = Find( uiKey ); + if( hHash != InvalidHandle() ) + return hHash; + + return FastInsert( uiKey, data ); +} + +//----------------------------------------------------------------------------- +// Purpose: Insert data into the hash table given its key (unsigned int), +// without a check to see if the element already exists within the tree. +//----------------------------------------------------------------------------- +template inline UtlHashFixedHandle_t CUtlHashFixed::FastInsert( unsigned int uiKey, const Data &data ) +{ + int iBucket = HashFuncs::Hash( uiKey, NUM_BUCKETS - 1 ); + UtlPtrLinkedListIndex_t iElem = m_aBuckets[iBucket].AddToHead(); + + HashFixedData_t *pHashData = &m_aBuckets[iBucket][iElem]; + + Assert( (UtlPtrLinkedListIndex_t)pHashData == iElem ); + + // Add data to new element. + pHashData->m_uiKey = uiKey; + pHashData->m_Data = data; + + m_nElements++; + return (UtlHashFixedHandle_t)pHashData; +} + +//----------------------------------------------------------------------------- +// Purpose: Remove a given element from the hash. +//----------------------------------------------------------------------------- +template inline void CUtlHashFixed::Remove( UtlHashFixedHandle_t hHash ) +{ + HashFixedData_t *pHashData = (HashFixedData_t *)hHash; + Assert( Find(pHashData->m_uiKey) != InvalidHandle() ); + int iBucket = HashFuncs::Hash( pHashData->m_uiKey, NUM_BUCKETS - 1 ); + m_aBuckets[iBucket].Remove( (UtlPtrLinkedListIndex_t)pHashData ); + m_nElements--; +} + +//----------------------------------------------------------------------------- +// Purpose: Remove all elements from the hash +//----------------------------------------------------------------------------- +template inline void CUtlHashFixed::RemoveAll( void ) +{ + for ( int i = 0; i < NUM_BUCKETS; i++ ) + { + m_aBuckets[i].RemoveAll(); + } + m_nElements = 0; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template inline UtlHashFixedHandle_t CUtlHashFixed::Find( unsigned int uiKey ) +{ + int iBucket = HashFuncs::Hash( uiKey, NUM_BUCKETS - 1 ); + CUtlPtrLinkedList &bucket = m_aBuckets[iBucket]; + + for ( UtlPtrLinkedListIndex_t iElement = bucket.Head(); iElement != bucket.InvalidIndex(); iElement = bucket.Next( iElement ) ) + { + if ( bucket[iElement].m_uiKey == uiKey ) + return (UtlHashFixedHandle_t)iElement; + } + + return InvalidHandle(); +} + +//----------------------------------------------------------------------------- +// Purpose: Return data given a hash handle. +//----------------------------------------------------------------------------- +template inline Data &CUtlHashFixed::Element( UtlHashFixedHandle_t hHash ) +{ + return ((HashFixedData_t *)hHash)->m_Data; +} + +//----------------------------------------------------------------------------- +// Purpose: Return data given a hash handle. +//----------------------------------------------------------------------------- +template inline Data const &CUtlHashFixed::Element( UtlHashFixedHandle_t hHash ) const +{ + return ((HashFixedData_t *)hHash)->m_Data; +} + +//----------------------------------------------------------------------------- +// Purpose: Return data given a hash handle. +//----------------------------------------------------------------------------- +template inline Data &CUtlHashFixed::operator[]( UtlHashFixedHandle_t hHash ) +{ + return ((HashFixedData_t *)hHash)->m_Data; +} + +//----------------------------------------------------------------------------- +// Purpose: Return data given a hash handle. +//----------------------------------------------------------------------------- +template inline Data const &CUtlHashFixed::operator[]( UtlHashFixedHandle_t hHash ) const +{ + return ((HashFixedData_t *)hHash)->m_Data; +} + +class CDefaultHash32 +{ +public: + static inline uint32 HashKey32( uint32 nKey ) { return HashIntConventional(nKey); } +}; + +class CPassthroughHash32 +{ +public: + static inline uint32 HashKey32( uint32 nKey ) { return nKey; } +}; + + +// This is a simpler hash for scalar types that stores the entire hash + buckets in a single linear array +// This is much more cache friendly for small (e.g. 32-bit) types stored in the hash +template +class CUtlScalarHash +{ +public: + + // Constructor/Destructor. + CUtlScalarHash(); + ~CUtlScalarHash(); + + // Memory. + // void Purge( void ); + + // Invalid handle. + static const UtlHashFastHandle_t InvalidHandle( void ) { return (unsigned int)~0; } + + // Initialize. + bool Init( int nBucketCount ); + + // Size. + int Count( void ) const { return m_dataCount; } + + // Insertion. + UtlHashFastHandle_t Insert( unsigned int uiKey, const Data &data ); + + // Removal. + void FindAndRemove( unsigned int uiKey, const Data &dataRecord ); + void Remove( UtlHashFastHandle_t hHash ); + void RemoveAll( void ); + void Grow(); + + // Retrieval. Finds by uiKey and then by comparing dataRecord + UtlHashFastHandle_t Find( unsigned int uiKey, const Data &dataRecord ) const; + UtlHashFastHandle_t FindByUniqueKey( unsigned int uiKey ) const; + + Data &Element( UtlHashFastHandle_t hHash ) { Assert(unsigned(hHash)<=m_uiBucketMask); return m_pData[hHash].m_Data; } + Data const &Element( UtlHashFastHandle_t hHash ) const { Assert(unsigned(hHash)<=m_uiBucketMask); return m_pData[hHash].m_Data; } + Data &operator[]( UtlHashFastHandle_t hHash ) { Assert(unsigned(hHash)<=m_uiBucketMask); return m_pData[hHash].m_Data; } + Data const &operator[]( UtlHashFastHandle_t hHash ) const { Assert(unsigned(hHash)<=m_uiBucketMask); return m_pData[hHash].m_Data; } + + unsigned int Key( UtlHashFastHandle_t hHash ) const { Assert(unsigned(hHash)<=m_uiBucketMask); return m_pData[hHash].m_uiKey; } + + UtlHashFastHandle_t FirstInorder() const + { + return NextInorder(-1); + } + UtlHashFastHandle_t NextInorder( UtlHashFastHandle_t nStart ) const + { + int nElementCount = m_maxData * 2; + unsigned int nUnusedListElement = (unsigned int)InvalidHandle(); + for ( int i = nStart+1; i < nElementCount; i++ ) + { + if ( m_pData[i].m_uiKey != nUnusedListElement ) + return i; + } + return nUnusedListElement; + } + + //protected: + + struct HashScalarData_t + { + unsigned int m_uiKey; + Data m_Data; + }; + + unsigned int m_uiBucketMask; + HashScalarData_t *m_pData; + int m_maxData; + int m_dataCount; +}; + +template CUtlScalarHash::CUtlScalarHash() +{ + m_pData = NULL; + m_uiBucketMask = 0; + m_maxData = 0; + m_dataCount = 0; +} + +template CUtlScalarHash::~CUtlScalarHash() +{ + delete[] m_pData; +} + +template bool CUtlScalarHash::Init( int nBucketCount ) +{ + Assert(m_dataCount==0); + m_maxData = SmallestPowerOfTwoGreaterOrEqual(nBucketCount); + int elementCount = m_maxData * 2; + m_pData = new HashScalarData_t[elementCount]; + m_uiBucketMask = elementCount - 1; + RemoveAll(); + return true; +} + +template void CUtlScalarHash::Grow() +{ + ASSERT_NO_REENTRY(); + int oldElementCount = m_maxData * 2; + HashScalarData_t *pOldData = m_pData; + + // Grow to a minimum size of 16 + m_maxData = Max( oldElementCount, 16 ); + int elementCount = m_maxData * 2; + m_pData = new HashScalarData_t[elementCount]; + m_uiBucketMask = elementCount-1; + m_dataCount = 0; + for ( int i = 0; i < elementCount; i++ ) + { + m_pData[i].m_uiKey = InvalidHandle(); + } + for ( int i = 0; i < oldElementCount; i++ ) + { + if ( pOldData[i].m_uiKey != (unsigned)InvalidHandle() ) + { + Insert( pOldData[i].m_uiKey, pOldData[i].m_Data ); + } + } + delete[] pOldData; +} + +template UtlHashFastHandle_t CUtlScalarHash::Insert( unsigned int uiKey, const Data &data ) +{ + if ( m_dataCount >= m_maxData ) + { + Grow(); + } + m_dataCount++; + Assert(uiKey != (uint)InvalidHandle()); // This hash stores less data by assuming uiKey != ~0 + int index = CHashFunction::HashKey32(uiKey) & m_uiBucketMask; + unsigned int endOfList = (unsigned int)InvalidHandle(); + while ( m_pData[index].m_uiKey != endOfList ) + { + index = (index+1) & m_uiBucketMask; + } + m_pData[index].m_uiKey = uiKey; + m_pData[index].m_Data = data; + + return index; +} + +// Removal. +template void CUtlScalarHash::Remove( UtlHashFastHandle_t hHash ) +{ + int mid = (m_uiBucketMask+1) / 2; + int lastRemoveIndex = hHash; + // remove the item + m_pData[lastRemoveIndex].m_uiKey = InvalidHandle(); + m_dataCount--; + + // now search for any items needing to be swapped down + unsigned int endOfList = (unsigned int)InvalidHandle(); + for ( int index = (hHash+1) & m_uiBucketMask; m_pData[index].m_uiKey != endOfList; index = (index+1) & m_uiBucketMask ) + { + int ideal = CHashFunction::HashKey32(m_pData[index].m_uiKey) & m_uiBucketMask; + + // is the ideal index for this element <= (in a wrapped buffer sense) the ideal index of the removed element? + // if so, swap + int diff = ideal - lastRemoveIndex; + if ( diff > mid ) + { + diff -= (m_uiBucketMask+1); + } + if ( diff < -mid ) + { + diff += (m_uiBucketMask+1); + } + + // should I swap this? + if ( diff <= 0 ) + { + m_pData[lastRemoveIndex] = m_pData[index]; + lastRemoveIndex = index; + m_pData[index].m_uiKey = InvalidHandle(); + } + } +} + +template void CUtlScalarHash::FindAndRemove( unsigned int uiKey, const Data &dataRecord ) +{ + int index = CHashFunction::HashKey32(uiKey) & m_uiBucketMask; + unsigned int endOfList = (unsigned int)InvalidHandle(); + while ( m_pData[index].m_uiKey != endOfList ) + { + if ( m_pData[index].m_uiKey == uiKey && m_pData[index].m_Data == dataRecord ) + { + Remove(index); + return; + } + index = (index+1) & m_uiBucketMask; + } +} + +template void CUtlScalarHash::RemoveAll( void ) +{ + int elementCount = m_maxData * 2; + for ( int i = 0; i < elementCount; i++ ) + { + m_pData[i].m_uiKey = (unsigned)InvalidHandle(); + } + m_dataCount = 0; +} + +// Retrieval. +template UtlHashFastHandle_t CUtlScalarHash::Find( unsigned int uiKey, const Data &dataRecord ) const +{ + if ( m_pData == NULL ) + return InvalidHandle(); + int index = CHashFunction::HashKey32(uiKey) & m_uiBucketMask; + unsigned int endOfList = (unsigned int)InvalidHandle(); + while ( m_pData[index].m_uiKey != endOfList ) + { + if ( m_pData[index].m_uiKey == uiKey && m_pData[index].m_Data == dataRecord ) + return index; + index = (index+1) & m_uiBucketMask; + } + return InvalidHandle(); +} + +template UtlHashFastHandle_t CUtlScalarHash::FindByUniqueKey( unsigned int uiKey ) const +{ + if ( m_pData == NULL ) + return InvalidHandle(); + int index = CHashFunction::HashKey32(uiKey) & m_uiBucketMask; + unsigned int endOfList = (unsigned int)InvalidHandle(); + while ( m_pData[index].m_uiKey != endOfList ) + { + if ( m_pData[index].m_uiKey == uiKey ) + return index; + index = (index+1) & m_uiBucketMask; + } + return InvalidHandle(); +} + + +#endif // UTLHASH_H