|
|
|
|
//========= Copyright <EFBFBD> 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
|
|
|
//
|
|
|
|
|
// Purpose:
|
|
|
|
|
//
|
|
|
|
|
// $NoKeywords: $
|
|
|
|
|
//
|
|
|
|
|
// Serialization/unserialization buffer
|
|
|
|
|
//=============================================================================//
|
|
|
|
|
|
|
|
|
|
#ifndef UTLHASH_H
|
|
|
|
|
#define UTLHASH_H
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <limits.h>
|
|
|
|
|
#include "utlmemory.h"
|
|
|
|
|
#include "utlvector.h"
|
|
|
|
|
#include "utllinkedlist.h"
|
|
|
|
|
#include "utllinkedlist.h"
|
|
|
|
|
#include "commonmacros.h"
|
|
|
|
|
#include "generichash.h"
|
|
|
|
|
|
|
|
|
|
typedef unsigned int UtlHashHandle_t;
|
|
|
|
|
|
|
|
|
|
template<class Data, typename C = bool (*)( Data const&, Data const& ), typename K = unsigned int (*)( Data const& ) >
|
|
|
|
|
class CUtlHash
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
// compare and key functions - implemented by the
|
|
|
|
|
typedef C CompareFunc_t;
|
|
|
|
|
typedef K KeyFunc_t;
|
|
|
|
|
|
|
|
|
|
// constructor/deconstructor
|
|
|
|
|
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<Data> HashBucketList_t;
|
|
|
|
|
CUtlVector<HashBucketList_t> 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<class Data, typename C, typename K>
|
|
|
|
|
CUtlHash<Data, C, K>::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<class Data, typename C, typename K>
|
|
|
|
|
CUtlHash<Data, C, K>::~CUtlHash()
|
|
|
|
|
{
|
|
|
|
|
Purge();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, typename C, typename K>
|
|
|
|
|
inline bool CUtlHash<Data, C, K>::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<class Data, typename C, typename K>
|
|
|
|
|
inline int CUtlHash<Data, C, K>::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<class Data, typename C, typename K>
|
|
|
|
|
inline int CUtlHash<Data, C, K>::GetBucketIndex( UtlHashHandle_t handle ) const
|
|
|
|
|
{
|
|
|
|
|
return ( ( ( handle >> 16 ) & 0x0000ffff ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, typename C, typename K>
|
|
|
|
|
inline int CUtlHash<Data, C, K>::GetKeyDataIndex( UtlHashHandle_t handle ) const
|
|
|
|
|
{
|
|
|
|
|
return ( handle & 0x0000ffff );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, typename C, typename K>
|
|
|
|
|
inline UtlHashHandle_t CUtlHash<Data, C, K>::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<class Data, typename C, typename K>
|
|
|
|
|
inline void CUtlHash<Data, C, K>::Purge( void )
|
|
|
|
|
{
|
|
|
|
|
int bucketCount = m_Buckets.Count();
|
|
|
|
|
for( int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++ )
|
|
|
|
|
{
|
|
|
|
|
m_Buckets[ndxBucket].Purge();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, typename C, typename K>
|
|
|
|
|
inline bool CUtlHash<Data, C, K>::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<Data> &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<class Data, typename C, typename K>
|
|
|
|
|
inline UtlHashHandle_t CUtlHash<Data, C, K>::Find( Data const &src ) const
|
|
|
|
|
{
|
|
|
|
|
unsigned int ndxBucket;
|
|
|
|
|
int ndxKeyData = 0;
|
|
|
|
|
|
|
|
|
|
if ( DoFind( src, &ndxBucket, &ndxKeyData ) )
|
|
|
|
|
{
|
|
|
|
|
return ( BuildHandle( ndxBucket, ndxKeyData ) );
|
|
|
|
|
}
|
|
|
|
|
return ( InvalidHandle() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, typename C, typename K>
|
|
|
|
|
inline UtlHashHandle_t CUtlHash<Data, C, K>::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<class Data, typename C, typename K>
|
|
|
|
|
inline UtlHashHandle_t CUtlHash<Data, C, K>::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<class Data, typename C, typename K>
|
|
|
|
|
inline UtlHashHandle_t CUtlHash<Data, C, K>::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<class Data, typename C, typename K>
|
|
|
|
|
inline void CUtlHash<Data, C, K>::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<class Data, typename C, typename K>
|
|
|
|
|
inline void CUtlHash<Data, C, K>::RemoveAll()
|
|
|
|
|
{
|
|
|
|
|
int bucketCount = m_Buckets.Count();
|
|
|
|
|
for( int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++ )
|
|
|
|
|
{
|
|
|
|
|
m_Buckets[ndxBucket].RemoveAll();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, typename C, typename K>
|
|
|
|
|
inline Data &CUtlHash<Data, C, K>::Element( UtlHashHandle_t handle )
|
|
|
|
|
{
|
|
|
|
|
int ndxBucket = GetBucketIndex( handle );
|
|
|
|
|
int ndxKeyData = GetKeyDataIndex( handle );
|
|
|
|
|
|
|
|
|
|
return ( m_Buckets[ndxBucket].Element( ndxKeyData ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, typename C, typename K>
|
|
|
|
|
inline Data const &CUtlHash<Data, C, K>::Element( UtlHashHandle_t handle ) const
|
|
|
|
|
{
|
|
|
|
|
int ndxBucket = GetBucketIndex( handle );
|
|
|
|
|
int ndxKeyData = GetKeyDataIndex( handle );
|
|
|
|
|
|
|
|
|
|
return ( m_Buckets[ndxBucket].Element( ndxKeyData ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, typename C, typename K>
|
|
|
|
|
inline Data &CUtlHash<Data, C, K>::operator[]( UtlHashHandle_t handle )
|
|
|
|
|
{
|
|
|
|
|
int ndxBucket = GetBucketIndex( handle );
|
|
|
|
|
int ndxKeyData = GetKeyDataIndex( handle );
|
|
|
|
|
|
|
|
|
|
return ( m_Buckets[ndxBucket].Element( ndxKeyData ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, typename C, typename K>
|
|
|
|
|
inline Data const &CUtlHash<Data, C, K>::operator[]( UtlHashHandle_t handle ) const
|
|
|
|
|
{
|
|
|
|
|
int ndxBucket = GetBucketIndex( handle );
|
|
|
|
|
int ndxKeyData = GetKeyDataIndex( handle );
|
|
|
|
|
|
|
|
|
|
return ( m_Buckets[ndxBucket].Element( ndxKeyData ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, typename C, typename K>
|
|
|
|
|
inline UtlHashHandle_t CUtlHash<Data, C, K>::GetFirstHandle() const
|
|
|
|
|
{
|
|
|
|
|
return GetNextHandle( ( UtlHashHandle_t )-1 );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<class Data, typename C, typename K>
|
|
|
|
|
inline UtlHashHandle_t CUtlHash<Data, C, K>::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<class Data, typename C, typename K>
|
|
|
|
|
inline void CUtlHash<Data, C, K>::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<class Data, typename C, typename K>
|
|
|
|
|
inline void CUtlHash<Data, C, K>::Dump( )
|
|
|
|
|
{
|
|
|
|
|
int maxBucketSize = 0;
|
|
|
|
|
int numBucketsEmpty = 0;
|
|
|
|
|
|
|
|
|
|
int bucketCount = m_Buckets.Count();
|
|
|
|
|
Msg( "\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( "Bucket %d: %d\n", ndxBucket, count );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Msg( "\nBucketHeads Used: %d\n", bucketCount - numBucketsEmpty );
|
|
|
|
|
Msg( "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 int 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 Data, class HashFuncs = CUtlHashFastNoHash >
|
|
|
|
|
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( unsigned int uiKey, const Data &data );
|
|
|
|
|
UtlHashFastHandle_t FastInsert( unsigned int uiKey, const Data &data );
|
|
|
|
|
|
|
|
|
|
// Removal.
|
|
|
|
|
void Remove( UtlHashFastHandle_t hHash );
|
|
|
|
|
void RemoveAll( void );
|
|
|
|
|
|
|
|
|
|
// Retrieval.
|
|
|
|
|
UtlHashFastHandle_t Find( unsigned int 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 <typename HashData>
|
|
|
|
|
struct HashFastData_t_
|
|
|
|
|
{
|
|
|
|
|
unsigned int m_uiKey;
|
|
|
|
|
HashData m_Data;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef HashFastData_t_<Data> HashFastData_t;
|
|
|
|
|
|
|
|
|
|
unsigned int m_uiBucketMask;
|
|
|
|
|
CUtlVector<UtlHashFastHandle_t> m_aBuckets;
|
|
|
|
|
CUtlFixedLinkedList<HashFastData_t> m_aDataPool;
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: Constructor
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, class HashFuncs> CUtlHashFast<Data,HashFuncs>::CUtlHashFast()
|
|
|
|
|
{
|
|
|
|
|
Purge();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: Deconstructor
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, class HashFuncs> CUtlHashFast<Data,HashFuncs>::~CUtlHashFast()
|
|
|
|
|
{
|
|
|
|
|
Purge();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: Destroy dynamically allocated hash data.
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, class HashFuncs> inline void CUtlHashFast<Data,HashFuncs>::Purge( void )
|
|
|
|
|
{
|
|
|
|
|
m_aBuckets.Purge();
|
|
|
|
|
m_aDataPool.Purge();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: Initialize the hash - set bucket count and hash grow amount.
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, class HashFuncs> bool CUtlHashFast<Data,HashFuncs>::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<class Data, class HashFuncs> inline int CUtlHashFast<Data,HashFuncs>::Count( void ) const
|
|
|
|
|
{
|
|
|
|
|
return m_aDataPool.Count();
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// 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<class Data, class HashFuncs> inline UtlHashFastHandle_t CUtlHashFast<Data,HashFuncs>::Insert( unsigned int 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 (unsigned int),
|
|
|
|
|
// without a check to see if the element already exists within the tree.
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, class HashFuncs> inline UtlHashFastHandle_t CUtlHashFast<Data,HashFuncs>::FastInsert( unsigned int uiKey, const Data &data )
|
|
|
|
|
{
|
|
|
|
|
// Get a new element from the pool.
|
|
|
|
|
int 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<class Data, class HashFuncs> inline void CUtlHashFast<Data,HashFuncs>::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<class Data, class HashFuncs> inline void CUtlHashFast<Data,HashFuncs>::RemoveAll( void )
|
|
|
|
|
{
|
|
|
|
|
m_aBuckets.RemoveAll();
|
|
|
|
|
m_aDataPool.RemoveAll();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, class HashFuncs> inline UtlHashFastHandle_t CUtlHashFast<Data,HashFuncs>::Find( unsigned int uiKey ) const
|
|
|
|
|
{
|
|
|
|
|
// hash the "key" - get the correct hash table "bucket"
|
|
|
|
|
int iBucket = HashFuncs::Hash( uiKey, m_uiBucketMask );
|
|
|
|
|
|
|
|
|
|
for ( int 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<class Data, class HashFuncs> inline Data &CUtlHashFast<Data,HashFuncs>::Element( UtlHashFastHandle_t hHash )
|
|
|
|
|
{
|
|
|
|
|
return ( m_aDataPool[hHash].m_Data );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: Return data given a hash handle.
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, class HashFuncs> inline Data const &CUtlHashFast<Data,HashFuncs>::Element( UtlHashFastHandle_t hHash ) const
|
|
|
|
|
{
|
|
|
|
|
return ( m_aDataPool[hHash].m_Data );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: Return data given a hash handle.
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, class HashFuncs> inline Data &CUtlHashFast<Data,HashFuncs>::operator[]( UtlHashFastHandle_t hHash )
|
|
|
|
|
{
|
|
|
|
|
return ( m_aDataPool[hHash].m_Data );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: Return data given a hash handle.
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, class HashFuncs> inline Data const &CUtlHashFast<Data,HashFuncs>::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<class Data, class HashFuncs>
|
|
|
|
|
typename CUtlHashFast<Data,HashFuncs>::UtlHashFastIterator_t
|
|
|
|
|
CUtlHashFast<Data,HashFuncs>::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<class Data, class HashFuncs>
|
|
|
|
|
typename CUtlHashFast<Data,HashFuncs>::UtlHashFastIterator_t
|
|
|
|
|
CUtlHashFast<Data,HashFuncs>::Next( const typename CUtlHashFast<Data,HashFuncs>::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<class Data, class HashFuncs>
|
|
|
|
|
bool CUtlHashFast<Data,HashFuncs>::IsValidIterator( const typename CUtlHashFast::UtlHashFastIterator_t &iter ) const
|
|
|
|
|
{
|
|
|
|
|
return ( (iter.bucket >= 0) && (m_aDataPool.IsValidIndex(iter.handle)) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<class Data, class HashFuncs> inline bool CUtlHashFast<Data,HashFuncs>::IsValidHandle( UtlHashFastHandle_t hHash ) const
|
|
|
|
|
{
|
|
|
|
|
return m_aDataPool.IsValidIndex(hHash);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
|
//
|
|
|
|
|
// Fixed Hash
|
|
|
|
|
//
|
|
|
|
|
// Number of buckets must be a power of 2.
|
|
|
|
|
// Key must be 32-bits (unsigned int).
|
|
|
|
|
//
|
|
|
|
|
typedef int UtlHashFixedHandle_t;
|
|
|
|
|
|
|
|
|
|
template <int NUM_BUCKETS>
|
|
|
|
|
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 Data, int NUM_BUCKETS, class CHashFuncs = CUtlHashFastNoHash >
|
|
|
|
|
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 <typename Data_t>
|
|
|
|
|
struct HashFixedData_t_
|
|
|
|
|
{
|
|
|
|
|
unsigned int m_uiKey;
|
|
|
|
|
Data_t m_Data;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef HashFixedData_t_<Data> HashFixedData_t;
|
|
|
|
|
|
|
|
|
|
enum
|
|
|
|
|
{
|
|
|
|
|
BUCKET_MASK = NUM_BUCKETS - 1
|
|
|
|
|
};
|
|
|
|
|
CUtlPtrLinkedList<HashFixedData_t> m_aBuckets[NUM_BUCKETS];
|
|
|
|
|
int m_nElements;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: Constructor
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, int NUM_BUCKETS, class HashFuncs> CUtlHashFixed<Data,NUM_BUCKETS,HashFuncs>::CUtlHashFixed()
|
|
|
|
|
{
|
|
|
|
|
Purge();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: Deconstructor
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, int NUM_BUCKETS, class HashFuncs> CUtlHashFixed<Data,NUM_BUCKETS,HashFuncs>::~CUtlHashFixed()
|
|
|
|
|
{
|
|
|
|
|
Purge();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: Destroy dynamically allocated hash data.
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, int NUM_BUCKETS, class HashFuncs> inline void CUtlHashFixed<Data,NUM_BUCKETS,HashFuncs>::Purge( void )
|
|
|
|
|
{
|
|
|
|
|
RemoveAll();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: Return the number of elements in the hash.
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, int NUM_BUCKETS, class HashFuncs> inline int CUtlHashFixed<Data,NUM_BUCKETS,HashFuncs>::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<class Data, int NUM_BUCKETS, class HashFuncs> inline UtlHashFixedHandle_t CUtlHashFixed<Data,NUM_BUCKETS,HashFuncs>::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<class Data, int NUM_BUCKETS, class HashFuncs> inline UtlHashFixedHandle_t CUtlHashFixed<Data,NUM_BUCKETS,HashFuncs>::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<class Data, int NUM_BUCKETS, class HashFuncs> inline void CUtlHashFixed<Data,NUM_BUCKETS,HashFuncs>::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<class Data, int NUM_BUCKETS, class HashFuncs> inline void CUtlHashFixed<Data,NUM_BUCKETS,HashFuncs>::RemoveAll( void )
|
|
|
|
|
{
|
|
|
|
|
for ( int i = 0; i < NUM_BUCKETS; i++ )
|
|
|
|
|
{
|
|
|
|
|
m_aBuckets[i].RemoveAll();
|
|
|
|
|
}
|
|
|
|
|
m_nElements = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, int NUM_BUCKETS, class HashFuncs> inline UtlHashFixedHandle_t CUtlHashFixed<Data,NUM_BUCKETS,HashFuncs>::Find( unsigned int uiKey )
|
|
|
|
|
{
|
|
|
|
|
int iBucket = HashFuncs::Hash( uiKey, NUM_BUCKETS - 1 );
|
|
|
|
|
CUtlPtrLinkedList<HashFixedData_t> &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<class Data, int NUM_BUCKETS, class HashFuncs> inline Data &CUtlHashFixed<Data,NUM_BUCKETS,HashFuncs>::Element( UtlHashFixedHandle_t hHash )
|
|
|
|
|
{
|
|
|
|
|
return ((HashFixedData_t *)hHash)->m_Data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: Return data given a hash handle.
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, int NUM_BUCKETS, class HashFuncs> inline Data const &CUtlHashFixed<Data,NUM_BUCKETS,HashFuncs>::Element( UtlHashFixedHandle_t hHash ) const
|
|
|
|
|
{
|
|
|
|
|
return ((HashFixedData_t *)hHash)->m_Data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: Return data given a hash handle.
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, int NUM_BUCKETS, class HashFuncs> inline Data &CUtlHashFixed<Data,NUM_BUCKETS,HashFuncs>::operator[]( UtlHashFixedHandle_t hHash )
|
|
|
|
|
{
|
|
|
|
|
return ((HashFixedData_t *)hHash)->m_Data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: Return data given a hash handle.
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template<class Data, int NUM_BUCKETS, class HashFuncs> inline Data const &CUtlHashFixed<Data,NUM_BUCKETS,HashFuncs>::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 Data, class CHashFunction = CDefaultHash32>
|
|
|
|
|
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<class Data, class CHashFunction> CUtlScalarHash<Data, CHashFunction>::CUtlScalarHash()
|
|
|
|
|
{
|
|
|
|
|
m_pData = NULL;
|
|
|
|
|
m_uiBucketMask = 0;
|
|
|
|
|
m_maxData = 0;
|
|
|
|
|
m_dataCount = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<class Data, class CHashFunction> CUtlScalarHash<Data, CHashFunction>::~CUtlScalarHash()
|
|
|
|
|
{
|
|
|
|
|
delete[] m_pData;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<class Data, class CHashFunction> bool CUtlScalarHash<Data, CHashFunction>::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<class Data, class CHashFunction> void CUtlScalarHash<Data, CHashFunction>::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<class Data, class CHashFunction> UtlHashFastHandle_t CUtlScalarHash<Data, CHashFunction>::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<class Data, class CHashFunction> void CUtlScalarHash<Data, CHashFunction>::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<class Data, class CHashFunction> void CUtlScalarHash<Data, CHashFunction>::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<class Data, class CHashFunction> void CUtlScalarHash<Data, CHashFunction>::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<class Data, class CHashFunction> UtlHashFastHandle_t CUtlScalarHash<Data, CHashFunction>::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<class Data, class CHashFunction> UtlHashFastHandle_t CUtlScalarHash<Data, CHashFunction>::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
|