You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
625 lines
19 KiB
625 lines
19 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
// |
|
// Thread-safe hash class |
|
//===========================================================================// |
|
|
|
#ifndef UTLTSHASH_H |
|
#define UTLTSHASH_H |
|
|
|
#ifdef _WIN32 |
|
#pragma once |
|
#endif |
|
|
|
#include <limits.h> |
|
#include "tier0/threadtools.h" |
|
#include "tier1/mempool.h" |
|
#include "generichash.h" |
|
|
|
|
|
//============================================================================= |
|
// |
|
// Threadsafe Hash |
|
// |
|
// Number of buckets must be a power of 2. |
|
// Key must be intp sized (32-bits on x32, 64-bits on x64) |
|
// Designed for a usage pattern where the data is semi-static, and there |
|
// is a well-defined point where we are guaranteed no queries are occurring. |
|
// |
|
// Insertions are added into a thread-safe list, and when Commit() is called, |
|
// the insertions are moved into a lock-free list |
|
// |
|
// Elements are never individually removed; clears must occur at a time |
|
// where we and guaranteed no queries are occurring |
|
// |
|
typedef intp UtlTSHashHandle_t; |
|
|
|
template < class T > |
|
abstract_class ITSHashConstructor |
|
{ |
|
public: |
|
virtual void Construct( T* pElement ) = 0; |
|
}; |
|
|
|
template < class T > |
|
class CDefaultTSHashConstructor : public ITSHashConstructor< T > |
|
{ |
|
public: |
|
virtual void Construct( T* pElement ) |
|
{ |
|
::Construct( pElement ); |
|
} |
|
}; |
|
|
|
template < int BUCKET_COUNT, class KEYTYPE = intp > |
|
class CUtlTSHashGenericHash |
|
{ |
|
public: |
|
static int Hash( const KEYTYPE &key, int nBucketMask ) |
|
{ |
|
int nHash = HashIntConventional( (intp)key ); |
|
if ( BUCKET_COUNT <= USHRT_MAX ) |
|
{ |
|
nHash ^= ( nHash >> 16 ); |
|
} |
|
if ( BUCKET_COUNT <= UCHAR_MAX ) |
|
{ |
|
nHash ^= ( nHash >> 8 ); |
|
} |
|
return ( nHash & nBucketMask ); |
|
} |
|
|
|
static bool Compare( const KEYTYPE &lhs, const KEYTYPE &rhs ) |
|
{ |
|
return lhs == rhs; |
|
} |
|
}; |
|
|
|
template < int BUCKET_COUNT, class KEYTYPE > |
|
class CUtlTSHashUseKeyHashMethod |
|
{ |
|
public: |
|
static int Hash( const KEYTYPE &key, int nBucketMask ) |
|
{ |
|
uint32 nHash = key.HashValue(); |
|
return ( nHash & nBucketMask ); |
|
} |
|
|
|
static bool Compare( const KEYTYPE &lhs, const KEYTYPE &rhs ) |
|
{ |
|
return lhs == rhs; |
|
} |
|
}; |
|
|
|
template< class T, int BUCKET_COUNT, class KEYTYPE = intp, class HashFuncs = CUtlTSHashGenericHash< BUCKET_COUNT, KEYTYPE >, int nAlignment = 0 > |
|
class CUtlTSHash |
|
{ |
|
public: |
|
// Constructor/Deconstructor. |
|
CUtlTSHash( int nAllocationCount ); |
|
~CUtlTSHash(); |
|
|
|
// Invalid handle. |
|
static UtlTSHashHandle_t InvalidHandle( void ) { return ( UtlTSHashHandle_t )0; } |
|
|
|
// Retrieval. Super fast, is thread-safe |
|
UtlTSHashHandle_t Find( KEYTYPE uiKey ); |
|
|
|
// Insertion ( find or add ). |
|
UtlTSHashHandle_t Insert( KEYTYPE uiKey, const T &data, bool *pDidInsert = NULL ); |
|
UtlTSHashHandle_t Insert( KEYTYPE uiKey, ITSHashConstructor<T> *pConstructor, bool *pDidInsert = NULL ); |
|
|
|
// This insertion method assumes the element is not in the hash table, skips |
|
UtlTSHashHandle_t FastInsert( KEYTYPE uiKey, const T &data ); |
|
UtlTSHashHandle_t FastInsert( KEYTYPE uiKey, ITSHashConstructor<T> *pConstructor ); |
|
|
|
// Commit recent insertions, making finding them faster. |
|
// Only call when you're certain no threads are accessing the hash table |
|
void Commit( ); |
|
|
|
// Removal. Only call when you're certain no threads are accessing the hash table |
|
void FindAndRemove( KEYTYPE uiKey ); |
|
void Remove( UtlTSHashHandle_t hHash ) { FindAndRemove( GetID( hHash ) ); } |
|
void RemoveAll( void ); |
|
void Purge( void ); |
|
|
|
// Returns the number of elements in the hash table |
|
int Count() const; |
|
|
|
// Returns elements in the table |
|
int GetElements( int nFirstElement, int nCount, UtlTSHashHandle_t *pHandles ) const; |
|
|
|
// Element access |
|
T &Element( UtlTSHashHandle_t hHash ); |
|
T const &Element( UtlTSHashHandle_t hHash ) const; |
|
T &operator[]( UtlTSHashHandle_t hHash ); |
|
T const &operator[]( UtlTSHashHandle_t hHash ) const; |
|
KEYTYPE GetID( UtlTSHashHandle_t hHash ) const; |
|
|
|
// Convert element * to hashHandle |
|
UtlTSHashHandle_t ElementPtrToHandle( T* pElement ) const; |
|
|
|
private: |
|
// Templatized for memory tracking purposes |
|
template < typename Data_t > |
|
struct HashFixedDataInternal_t |
|
{ |
|
KEYTYPE m_uiKey; |
|
HashFixedDataInternal_t< Data_t >* m_pNext; |
|
Data_t m_Data; |
|
}; |
|
|
|
typedef HashFixedDataInternal_t<T> HashFixedData_t; |
|
|
|
enum |
|
{ |
|
BUCKET_MASK = BUCKET_COUNT - 1 |
|
}; |
|
|
|
struct HashBucket_t |
|
{ |
|
HashFixedData_t *m_pFirst; |
|
HashFixedData_t *m_pFirstUncommitted; |
|
CThreadSpinRWLock m_AddLock; |
|
}; |
|
|
|
UtlTSHashHandle_t Find( KEYTYPE uiKey, HashFixedData_t *pFirstElement, HashFixedData_t *pLastElement ); |
|
UtlTSHashHandle_t InsertUncommitted( KEYTYPE uiKey, HashBucket_t &bucket ); |
|
CMemoryPoolMT m_EntryMemory; |
|
HashBucket_t m_aBuckets[BUCKET_COUNT]; |
|
bool m_bNeedsCommit; |
|
|
|
#ifdef _DEBUG |
|
CInterlockedInt m_ContentionCheck; |
|
#endif |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor |
|
//----------------------------------------------------------------------------- |
|
template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment> |
|
CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::CUtlTSHash( int nAllocationCount ) : |
|
m_EntryMemory( sizeof( HashFixedData_t ), nAllocationCount, CUtlMemoryPool::GROW_SLOW, MEM_ALLOC_CLASSNAME( HashFixedData_t ), nAlignment ) |
|
{ |
|
#ifdef _DEBUG |
|
m_ContentionCheck = 0; |
|
#endif |
|
m_bNeedsCommit = false; |
|
for ( int i = 0; i < BUCKET_COUNT; i++ ) |
|
{ |
|
HashBucket_t &bucket = m_aBuckets[ i ]; |
|
bucket.m_pFirst = NULL; |
|
bucket.m_pFirstUncommitted = NULL; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Deconstructor |
|
//----------------------------------------------------------------------------- |
|
template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment> |
|
CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::~CUtlTSHash() |
|
{ |
|
#ifdef _DEBUG |
|
if ( m_ContentionCheck != 0 ) |
|
{ |
|
DebuggerBreak(); |
|
} |
|
#endif |
|
Purge(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Destroy dynamically allocated hash data. |
|
//----------------------------------------------------------------------------- |
|
template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment> |
|
inline void CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::Purge( void ) |
|
{ |
|
RemoveAll(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns the number of elements in the hash table |
|
//----------------------------------------------------------------------------- |
|
template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment> |
|
inline int CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::Count() const |
|
{ |
|
return m_EntryMemory.Count(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns elements in the table |
|
//----------------------------------------------------------------------------- |
|
template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment> |
|
int CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::GetElements( int nFirstElement, int nCount, UtlTSHashHandle_t *pHandles ) const |
|
{ |
|
int nIndex = 0; |
|
for ( int i = 0; i < BUCKET_COUNT; i++ ) |
|
{ |
|
const HashBucket_t &bucket = m_aBuckets[ i ]; |
|
bucket.m_AddLock.LockForRead( ); |
|
for ( HashFixedData_t *pElement = bucket.m_pFirstUncommitted; pElement; pElement = pElement->m_pNext ) |
|
{ |
|
if ( --nFirstElement >= 0 ) |
|
continue; |
|
|
|
pHandles[ nIndex++ ] = (UtlTSHashHandle_t)pElement; |
|
if ( nIndex >= nCount ) |
|
{ |
|
bucket.m_AddLock.UnlockRead( ); |
|
return nIndex; |
|
} |
|
} |
|
bucket.m_AddLock.UnlockRead( ); |
|
} |
|
return nIndex; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Insert data into the hash table given its key (KEYTYPE), |
|
// without a check to see if the element already exists within the tree. |
|
//----------------------------------------------------------------------------- |
|
template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment> |
|
inline UtlTSHashHandle_t CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::InsertUncommitted( KEYTYPE uiKey, HashBucket_t &bucket ) |
|
{ |
|
m_bNeedsCommit = true; |
|
HashFixedData_t *pNewElement = static_cast< HashFixedData_t * >( m_EntryMemory.Alloc() ); |
|
pNewElement->m_pNext = bucket.m_pFirstUncommitted; |
|
bucket.m_pFirstUncommitted = pNewElement; |
|
pNewElement->m_uiKey = uiKey; |
|
return (UtlTSHashHandle_t)pNewElement; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Insert data into the hash table given its key, with |
|
// a check to see if the element already exists within the tree. |
|
//----------------------------------------------------------------------------- |
|
template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment> |
|
inline UtlTSHashHandle_t CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::Insert( KEYTYPE uiKey, const T &data, bool *pDidInsert ) |
|
{ |
|
#ifdef _DEBUG |
|
if ( m_ContentionCheck != 0 ) |
|
{ |
|
DebuggerBreak(); |
|
} |
|
#endif |
|
|
|
if ( pDidInsert ) |
|
{ |
|
*pDidInsert = false; |
|
} |
|
|
|
int iBucket = HashFuncs::Hash( uiKey, BUCKET_MASK ); |
|
HashBucket_t &bucket = m_aBuckets[ iBucket ]; |
|
|
|
// First try lock-free |
|
UtlTSHashHandle_t h = Find( uiKey ); |
|
if ( h != InvalidHandle() ) |
|
return h; |
|
|
|
// Now, try again, but only look in uncommitted elements |
|
bucket.m_AddLock.LockForWrite( ); |
|
|
|
h = Find( uiKey, bucket.m_pFirstUncommitted, bucket.m_pFirst ); |
|
if ( h == InvalidHandle() ) |
|
{ |
|
h = InsertUncommitted( uiKey, bucket ); |
|
CopyConstruct( &Element(h), data ); |
|
if ( pDidInsert ) |
|
{ |
|
*pDidInsert = true; |
|
} |
|
} |
|
|
|
bucket.m_AddLock.UnlockWrite( ); |
|
return h; |
|
} |
|
|
|
template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment> |
|
inline UtlTSHashHandle_t CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::Insert( KEYTYPE uiKey, ITSHashConstructor<T> *pConstructor, bool *pDidInsert ) |
|
{ |
|
#ifdef _DEBUG |
|
if ( m_ContentionCheck != 0 ) |
|
{ |
|
DebuggerBreak(); |
|
} |
|
#endif |
|
|
|
if ( pDidInsert ) |
|
{ |
|
*pDidInsert = false; |
|
} |
|
|
|
// First try lock-free |
|
UtlTSHashHandle_t h = Find( uiKey ); |
|
if ( h != InvalidHandle() ) |
|
return h; |
|
|
|
// Now, try again, but only look in uncommitted elements |
|
int iBucket = HashFuncs::Hash( uiKey, BUCKET_MASK ); |
|
HashBucket_t &bucket = m_aBuckets[ iBucket ]; |
|
bucket.m_AddLock.LockForWrite( ); |
|
|
|
h = Find( uiKey, bucket.m_pFirstUncommitted, bucket.m_pFirst ); |
|
if ( h == InvalidHandle() ) |
|
{ |
|
// Useful if non-trivial work needs to happen to make data; don't want to |
|
// do it and then have to undo it if it turns out we don't need to add it |
|
h = InsertUncommitted( uiKey, bucket ); |
|
pConstructor->Construct( &Element(h) ); |
|
if ( pDidInsert ) |
|
{ |
|
*pDidInsert = true; |
|
} |
|
} |
|
|
|
bucket.m_AddLock.UnlockWrite( ); |
|
return h; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Insert data into the hash table given its key |
|
// without a check to see if the element already exists within the tree. |
|
//----------------------------------------------------------------------------- |
|
template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment> |
|
inline UtlTSHashHandle_t CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::FastInsert( KEYTYPE uiKey, const T &data ) |
|
{ |
|
#ifdef _DEBUG |
|
if ( m_ContentionCheck != 0 ) |
|
{ |
|
DebuggerBreak(); |
|
} |
|
#endif |
|
int iBucket = HashFuncs::Hash( uiKey, BUCKET_MASK ); |
|
HashBucket_t &bucket = m_aBuckets[ iBucket ]; |
|
bucket.m_AddLock.LockForWrite( ); |
|
UtlTSHashHandle_t h = InsertUncommitted( uiKey, bucket ); |
|
CopyConstruct( &Element(h), data ); |
|
bucket.m_AddLock.UnlockWrite( ); |
|
return h; |
|
} |
|
|
|
template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment> |
|
inline UtlTSHashHandle_t CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::FastInsert( KEYTYPE uiKey, ITSHashConstructor<T> *pConstructor ) |
|
{ |
|
#ifdef _DEBUG |
|
if ( m_ContentionCheck != 0 ) |
|
{ |
|
DebuggerBreak(); |
|
} |
|
#endif |
|
int iBucket = HashFuncs::Hash( uiKey, BUCKET_MASK ); |
|
HashBucket_t &bucket = m_aBuckets[ iBucket ]; |
|
bucket.m_AddLock.LockForWrite( ); |
|
UtlTSHashHandle_t h = InsertUncommitted( uiKey, bucket ); |
|
pConstructor->Construct( &Element(h) ); |
|
bucket.m_AddLock.UnlockWrite( ); |
|
return h; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Commits all uncommitted insertions |
|
//----------------------------------------------------------------------------- |
|
template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment> |
|
inline void CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::Commit( ) |
|
{ |
|
// FIXME: Is this legal? Want this to be lock-free |
|
if ( !m_bNeedsCommit ) |
|
return; |
|
|
|
// This must occur when no queries are occurring |
|
#ifdef _DEBUG |
|
m_ContentionCheck++; |
|
#endif |
|
|
|
for ( int i = 0; i < BUCKET_COUNT; i++ ) |
|
{ |
|
HashBucket_t &bucket = m_aBuckets[ i ]; |
|
bucket.m_AddLock.LockForRead( ); |
|
bucket.m_pFirst = bucket.m_pFirstUncommitted; |
|
bucket.m_AddLock.UnlockRead( ); |
|
} |
|
|
|
m_bNeedsCommit = false; |
|
|
|
#ifdef _DEBUG |
|
m_ContentionCheck--; |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Remove a single element from the hash |
|
//----------------------------------------------------------------------------- |
|
template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment> |
|
inline void CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::FindAndRemove( KEYTYPE uiKey ) |
|
{ |
|
if ( m_EntryMemory.Count() == 0 ) |
|
return; |
|
|
|
// This must occur when no queries are occurring |
|
#ifdef _DEBUG |
|
m_ContentionCheck++; |
|
#endif |
|
|
|
int iBucket = HashFuncs::Hash( uiKey, BUCKET_MASK ); |
|
HashBucket_t &bucket = m_aBuckets[ iBucket ]; |
|
bucket.m_AddLock.LockForWrite( ); |
|
|
|
HashFixedData_t *pPrev = NULL; |
|
for ( HashFixedData_t *pElement = bucket.m_pFirstUncommitted; pElement; pPrev = pElement, pElement = pElement->m_pNext ) |
|
{ |
|
if ( !HashFuncs::Compare( pElement->m_uiKey, uiKey ) ) |
|
continue; |
|
|
|
if ( pPrev ) |
|
{ |
|
pPrev->m_pNext = pElement->m_pNext; |
|
} |
|
else |
|
{ |
|
bucket.m_pFirstUncommitted = pElement->m_pNext; |
|
} |
|
|
|
if ( bucket.m_pFirst == pElement ) |
|
{ |
|
bucket.m_pFirst = bucket.m_pFirst->m_pNext; |
|
} |
|
|
|
Destruct( &pElement->m_Data ); |
|
|
|
#ifdef _DEBUG |
|
memset( pElement, 0xDD, sizeof(HashFixedData_t) ); |
|
#endif |
|
|
|
m_EntryMemory.Free( pElement ); |
|
|
|
break; |
|
} |
|
|
|
bucket.m_AddLock.UnlockWrite( ); |
|
|
|
#ifdef _DEBUG |
|
m_ContentionCheck--; |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Remove all elements from the hash |
|
//----------------------------------------------------------------------------- |
|
template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment> |
|
inline void CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::RemoveAll( void ) |
|
{ |
|
m_bNeedsCommit = false; |
|
if ( m_EntryMemory.Count() == 0 ) |
|
return; |
|
|
|
// This must occur when no queries are occurring |
|
#ifdef _DEBUG |
|
m_ContentionCheck++; |
|
#endif |
|
|
|
for ( int i = 0; i < BUCKET_COUNT; i++ ) |
|
{ |
|
HashBucket_t &bucket = m_aBuckets[ i ]; |
|
|
|
bucket.m_AddLock.LockForWrite( ); |
|
|
|
for ( HashFixedData_t *pElement = bucket.m_pFirstUncommitted; pElement; pElement = pElement->m_pNext ) |
|
{ |
|
Destruct( &pElement->m_Data ); |
|
} |
|
|
|
bucket.m_pFirst = NULL; |
|
bucket.m_pFirstUncommitted = NULL; |
|
bucket.m_AddLock.UnlockWrite( ); |
|
} |
|
|
|
m_EntryMemory.Clear(); |
|
|
|
#ifdef _DEBUG |
|
m_ContentionCheck--; |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Finds an element, but only in the committed elements |
|
//----------------------------------------------------------------------------- |
|
template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment> |
|
inline UtlTSHashHandle_t CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::Find( KEYTYPE uiKey, HashFixedData_t *pFirstElement, HashFixedData_t *pLastElement ) |
|
{ |
|
#ifdef _DEBUG |
|
if ( m_ContentionCheck != 0 ) |
|
{ |
|
DebuggerBreak(); |
|
} |
|
#endif |
|
|
|
for ( HashFixedData_t *pElement = pFirstElement; pElement != pLastElement; pElement = pElement->m_pNext ) |
|
{ |
|
if ( HashFuncs::Compare( pElement->m_uiKey, uiKey ) ) |
|
return (UtlTSHashHandle_t)pElement; |
|
} |
|
return InvalidHandle(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Finds an element, but only in the committed elements |
|
//----------------------------------------------------------------------------- |
|
template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment> |
|
inline UtlTSHashHandle_t CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::Find( KEYTYPE uiKey ) |
|
{ |
|
int iBucket = HashFuncs::Hash( uiKey, BUCKET_MASK ); |
|
const HashBucket_t &bucket = m_aBuckets[iBucket]; |
|
UtlTSHashHandle_t h = Find( uiKey, bucket.m_pFirst, NULL ); |
|
if ( h != InvalidHandle() ) |
|
return h; |
|
|
|
// Didn't find it in the fast ( committed ) list. Let's try the slow ( uncommitted ) one |
|
bucket.m_AddLock.LockForRead( ); |
|
h = Find( uiKey, bucket.m_pFirstUncommitted, bucket.m_pFirst ); |
|
bucket.m_AddLock.UnlockRead( ); |
|
|
|
return h; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Return data given a hash handle. |
|
//----------------------------------------------------------------------------- |
|
template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment> |
|
inline T &CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::Element( UtlTSHashHandle_t hHash ) |
|
{ |
|
return ((HashFixedData_t *)hHash)->m_Data; |
|
} |
|
|
|
template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment> |
|
inline T const &CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::Element( UtlTSHashHandle_t hHash ) const |
|
{ |
|
return ((HashFixedData_t *)hHash)->m_Data; |
|
} |
|
|
|
template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment> |
|
inline T &CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::operator[]( UtlTSHashHandle_t hHash ) |
|
{ |
|
return ((HashFixedData_t *)hHash)->m_Data; |
|
} |
|
|
|
template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment> |
|
inline T const &CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::operator[]( UtlTSHashHandle_t hHash ) const |
|
{ |
|
return ((HashFixedData_t *)hHash)->m_Data; |
|
} |
|
|
|
|
|
template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment> |
|
inline KEYTYPE CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::GetID( UtlTSHashHandle_t hHash ) const |
|
{ |
|
return ((HashFixedData_t *)hHash)->m_uiKey; |
|
} |
|
|
|
|
|
// Convert element * to hashHandle |
|
template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment> |
|
inline UtlTSHashHandle_t CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::ElementPtrToHandle( T* pElement ) const |
|
{ |
|
Assert( pElement ); |
|
HashFixedData_t *pFixedData = (HashFixedData_t*)( (uint8*)pElement - offsetof( HashFixedData_t, m_Data ) ); |
|
Assert( m_EntryMemory.IsAllocationWithinPool( pFixedData ) ); |
|
return (UtlTSHashHandle_t)pFixedData; |
|
} |
|
|
|
|
|
#endif // UTLTSHASH_H
|
|
|