Counter Strike : Global Offensive Source Code
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

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. // Thread-safe hash class
  8. //===========================================================================//
  9. #ifndef UTLTSHASH_H
  10. #define UTLTSHASH_H
  11. #ifdef _WIN32
  12. #pragma once
  13. #endif
  14. #include <limits.h>
  15. #include "tier0/threadtools.h"
  16. #include "tier1/mempool.h"
  17. #include "generichash.h"
  18. //=============================================================================
  19. //
  20. // Threadsafe Hash
  21. //
  22. // Number of buckets must be a power of 2.
  23. // Key must be intp sized (32-bits on x32, 64-bits on x64)
  24. // Designed for a usage pattern where the data is semi-static, and there
  25. // is a well-defined point where we are guaranteed no queries are occurring.
  26. //
  27. // Insertions are added into a thread-safe list, and when Commit() is called,
  28. // the insertions are moved into a lock-free list
  29. //
  30. // Elements are never individually removed; clears must occur at a time
  31. // where we and guaranteed no queries are occurring
  32. //
  33. typedef intp UtlTSHashHandle_t;
  34. template < class T >
  35. abstract_class ITSHashConstructor
  36. {
  37. public:
  38. virtual void Construct( T* pElement ) = 0;
  39. };
  40. template < class T >
  41. class CDefaultTSHashConstructor : public ITSHashConstructor< T >
  42. {
  43. public:
  44. virtual void Construct( T* pElement )
  45. {
  46. ::Construct( pElement );
  47. }
  48. };
  49. template < int BUCKET_COUNT, class KEYTYPE = intp >
  50. class CUtlTSHashGenericHash
  51. {
  52. public:
  53. static int Hash( const KEYTYPE &key, int nBucketMask )
  54. {
  55. int nHash = HashIntConventional( (intp)key );
  56. if ( BUCKET_COUNT <= USHRT_MAX )
  57. {
  58. nHash ^= ( nHash >> 16 );
  59. }
  60. if ( BUCKET_COUNT <= UCHAR_MAX )
  61. {
  62. nHash ^= ( nHash >> 8 );
  63. }
  64. return ( nHash & nBucketMask );
  65. }
  66. static bool Compare( const KEYTYPE &lhs, const KEYTYPE &rhs )
  67. {
  68. return lhs == rhs;
  69. }
  70. };
  71. template < int BUCKET_COUNT, class KEYTYPE >
  72. class CUtlTSHashUseKeyHashMethod
  73. {
  74. public:
  75. static int Hash( const KEYTYPE &key, int nBucketMask )
  76. {
  77. uint32 nHash = key.HashValue();
  78. return ( nHash & nBucketMask );
  79. }
  80. static bool Compare( const KEYTYPE &lhs, const KEYTYPE &rhs )
  81. {
  82. return lhs == rhs;
  83. }
  84. };
  85. template< class T, int BUCKET_COUNT, class KEYTYPE = intp, class HashFuncs = CUtlTSHashGenericHash< BUCKET_COUNT, KEYTYPE >, int nAlignment = 0 >
  86. class CUtlTSHash
  87. {
  88. public:
  89. // Constructor/Deconstructor.
  90. CUtlTSHash( int nAllocationCount );
  91. ~CUtlTSHash();
  92. // Invalid handle.
  93. static UtlTSHashHandle_t InvalidHandle( void ) { return ( UtlTSHashHandle_t )0; }
  94. // Retrieval. Super fast, is thread-safe
  95. UtlTSHashHandle_t Find( KEYTYPE uiKey );
  96. // Insertion ( find or add ).
  97. UtlTSHashHandle_t Insert( KEYTYPE uiKey, const T &data, bool *pDidInsert = NULL );
  98. UtlTSHashHandle_t Insert( KEYTYPE uiKey, ITSHashConstructor<T> *pConstructor, bool *pDidInsert = NULL );
  99. // This insertion method assumes the element is not in the hash table, skips
  100. UtlTSHashHandle_t FastInsert( KEYTYPE uiKey, const T &data );
  101. UtlTSHashHandle_t FastInsert( KEYTYPE uiKey, ITSHashConstructor<T> *pConstructor );
  102. // Commit recent insertions, making finding them faster.
  103. // Only call when you're certain no threads are accessing the hash table
  104. void Commit( );
  105. // Removal. Only call when you're certain no threads are accessing the hash table
  106. void FindAndRemove( KEYTYPE uiKey );
  107. void Remove( UtlTSHashHandle_t hHash ) { FindAndRemove( GetID( hHash ) ); }
  108. void RemoveAll( void );
  109. void Purge( void );
  110. // Returns the number of elements in the hash table
  111. int Count() const;
  112. // Returns elements in the table
  113. int GetElements( int nFirstElement, int nCount, UtlTSHashHandle_t *pHandles ) const;
  114. // Element access
  115. T &Element( UtlTSHashHandle_t hHash );
  116. T const &Element( UtlTSHashHandle_t hHash ) const;
  117. T &operator[]( UtlTSHashHandle_t hHash );
  118. T const &operator[]( UtlTSHashHandle_t hHash ) const;
  119. KEYTYPE GetID( UtlTSHashHandle_t hHash ) const;
  120. // Convert element * to hashHandle
  121. UtlTSHashHandle_t ElementPtrToHandle( T* pElement ) const;
  122. private:
  123. // Templatized for memory tracking purposes
  124. template < typename Data_t >
  125. struct HashFixedDataInternal_t
  126. {
  127. KEYTYPE m_uiKey;
  128. HashFixedDataInternal_t< Data_t >* m_pNext;
  129. Data_t m_Data;
  130. };
  131. typedef HashFixedDataInternal_t<T> HashFixedData_t;
  132. enum
  133. {
  134. BUCKET_MASK = BUCKET_COUNT - 1
  135. };
  136. struct HashBucket_t
  137. {
  138. HashFixedData_t *m_pFirst;
  139. HashFixedData_t *m_pFirstUncommitted;
  140. CThreadSpinRWLock m_AddLock;
  141. };
  142. UtlTSHashHandle_t Find( KEYTYPE uiKey, HashFixedData_t *pFirstElement, HashFixedData_t *pLastElement );
  143. UtlTSHashHandle_t InsertUncommitted( KEYTYPE uiKey, HashBucket_t &bucket );
  144. CMemoryPoolMT m_EntryMemory;
  145. HashBucket_t m_aBuckets[BUCKET_COUNT];
  146. bool m_bNeedsCommit;
  147. #ifdef _DEBUG
  148. CInterlockedInt m_ContentionCheck;
  149. #endif
  150. };
  151. //-----------------------------------------------------------------------------
  152. // Purpose: Constructor
  153. //-----------------------------------------------------------------------------
  154. template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment>
  155. CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::CUtlTSHash( int nAllocationCount ) :
  156. m_EntryMemory( sizeof( HashFixedData_t ), nAllocationCount, CUtlMemoryPool::GROW_SLOW, MEM_ALLOC_CLASSNAME( HashFixedData_t ), nAlignment )
  157. {
  158. #ifdef _DEBUG
  159. m_ContentionCheck = 0;
  160. #endif
  161. m_bNeedsCommit = false;
  162. for ( int i = 0; i < BUCKET_COUNT; i++ )
  163. {
  164. HashBucket_t &bucket = m_aBuckets[ i ];
  165. bucket.m_pFirst = NULL;
  166. bucket.m_pFirstUncommitted = NULL;
  167. }
  168. }
  169. //-----------------------------------------------------------------------------
  170. // Purpose: Deconstructor
  171. //-----------------------------------------------------------------------------
  172. template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment>
  173. CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::~CUtlTSHash()
  174. {
  175. #ifdef _DEBUG
  176. if ( m_ContentionCheck != 0 )
  177. {
  178. DebuggerBreak();
  179. }
  180. #endif
  181. Purge();
  182. }
  183. //-----------------------------------------------------------------------------
  184. // Purpose: Destroy dynamically allocated hash data.
  185. //-----------------------------------------------------------------------------
  186. template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment>
  187. inline void CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::Purge( void )
  188. {
  189. RemoveAll();
  190. }
  191. //-----------------------------------------------------------------------------
  192. // Returns the number of elements in the hash table
  193. //-----------------------------------------------------------------------------
  194. template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment>
  195. inline int CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::Count() const
  196. {
  197. return m_EntryMemory.Count();
  198. }
  199. //-----------------------------------------------------------------------------
  200. // Returns elements in the table
  201. //-----------------------------------------------------------------------------
  202. template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment>
  203. int CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::GetElements( int nFirstElement, int nCount, UtlTSHashHandle_t *pHandles ) const
  204. {
  205. int nIndex = 0;
  206. for ( int i = 0; i < BUCKET_COUNT; i++ )
  207. {
  208. const HashBucket_t &bucket = m_aBuckets[ i ];
  209. bucket.m_AddLock.LockForRead( );
  210. for ( HashFixedData_t *pElement = bucket.m_pFirstUncommitted; pElement; pElement = pElement->m_pNext )
  211. {
  212. if ( --nFirstElement >= 0 )
  213. continue;
  214. pHandles[ nIndex++ ] = (UtlTSHashHandle_t)pElement;
  215. if ( nIndex >= nCount )
  216. {
  217. bucket.m_AddLock.UnlockRead( );
  218. return nIndex;
  219. }
  220. }
  221. bucket.m_AddLock.UnlockRead( );
  222. }
  223. return nIndex;
  224. }
  225. //-----------------------------------------------------------------------------
  226. // Purpose: Insert data into the hash table given its key (KEYTYPE),
  227. // without a check to see if the element already exists within the tree.
  228. //-----------------------------------------------------------------------------
  229. template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment>
  230. inline UtlTSHashHandle_t CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::InsertUncommitted( KEYTYPE uiKey, HashBucket_t &bucket )
  231. {
  232. m_bNeedsCommit = true;
  233. HashFixedData_t *pNewElement = static_cast< HashFixedData_t * >( m_EntryMemory.Alloc() );
  234. pNewElement->m_pNext = bucket.m_pFirstUncommitted;
  235. bucket.m_pFirstUncommitted = pNewElement;
  236. pNewElement->m_uiKey = uiKey;
  237. return (UtlTSHashHandle_t)pNewElement;
  238. }
  239. //-----------------------------------------------------------------------------
  240. // Purpose: Insert data into the hash table given its key, with
  241. // a check to see if the element already exists within the tree.
  242. //-----------------------------------------------------------------------------
  243. template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment>
  244. inline UtlTSHashHandle_t CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::Insert( KEYTYPE uiKey, const T &data, bool *pDidInsert )
  245. {
  246. #ifdef _DEBUG
  247. if ( m_ContentionCheck != 0 )
  248. {
  249. DebuggerBreak();
  250. }
  251. #endif
  252. if ( pDidInsert )
  253. {
  254. *pDidInsert = false;
  255. }
  256. int iBucket = HashFuncs::Hash( uiKey, BUCKET_MASK );
  257. HashBucket_t &bucket = m_aBuckets[ iBucket ];
  258. // First try lock-free
  259. UtlTSHashHandle_t h = Find( uiKey );
  260. if ( h != InvalidHandle() )
  261. return h;
  262. // Now, try again, but only look in uncommitted elements
  263. bucket.m_AddLock.LockForWrite( );
  264. h = Find( uiKey, bucket.m_pFirstUncommitted, bucket.m_pFirst );
  265. if ( h == InvalidHandle() )
  266. {
  267. h = InsertUncommitted( uiKey, bucket );
  268. CopyConstruct( &Element(h), data );
  269. if ( pDidInsert )
  270. {
  271. *pDidInsert = true;
  272. }
  273. }
  274. bucket.m_AddLock.UnlockWrite( );
  275. return h;
  276. }
  277. template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment>
  278. inline UtlTSHashHandle_t CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::Insert( KEYTYPE uiKey, ITSHashConstructor<T> *pConstructor, bool *pDidInsert )
  279. {
  280. #ifdef _DEBUG
  281. if ( m_ContentionCheck != 0 )
  282. {
  283. DebuggerBreak();
  284. }
  285. #endif
  286. if ( pDidInsert )
  287. {
  288. *pDidInsert = false;
  289. }
  290. // First try lock-free
  291. UtlTSHashHandle_t h = Find( uiKey );
  292. if ( h != InvalidHandle() )
  293. return h;
  294. // Now, try again, but only look in uncommitted elements
  295. int iBucket = HashFuncs::Hash( uiKey, BUCKET_MASK );
  296. HashBucket_t &bucket = m_aBuckets[ iBucket ];
  297. bucket.m_AddLock.LockForWrite( );
  298. h = Find( uiKey, bucket.m_pFirstUncommitted, bucket.m_pFirst );
  299. if ( h == InvalidHandle() )
  300. {
  301. // Useful if non-trivial work needs to happen to make data; don't want to
  302. // do it and then have to undo it if it turns out we don't need to add it
  303. h = InsertUncommitted( uiKey, bucket );
  304. pConstructor->Construct( &Element(h) );
  305. if ( pDidInsert )
  306. {
  307. *pDidInsert = true;
  308. }
  309. }
  310. bucket.m_AddLock.UnlockWrite( );
  311. return h;
  312. }
  313. //-----------------------------------------------------------------------------
  314. // Purpose: Insert data into the hash table given its key
  315. // without a check to see if the element already exists within the tree.
  316. //-----------------------------------------------------------------------------
  317. template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment>
  318. inline UtlTSHashHandle_t CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::FastInsert( KEYTYPE uiKey, const T &data )
  319. {
  320. #ifdef _DEBUG
  321. if ( m_ContentionCheck != 0 )
  322. {
  323. DebuggerBreak();
  324. }
  325. #endif
  326. int iBucket = HashFuncs::Hash( uiKey, BUCKET_MASK );
  327. HashBucket_t &bucket = m_aBuckets[ iBucket ];
  328. bucket.m_AddLock.LockForWrite( );
  329. UtlTSHashHandle_t h = InsertUncommitted( uiKey, bucket );
  330. CopyConstruct( &Element(h), data );
  331. bucket.m_AddLock.UnlockWrite( );
  332. return h;
  333. }
  334. template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment>
  335. inline UtlTSHashHandle_t CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::FastInsert( KEYTYPE uiKey, ITSHashConstructor<T> *pConstructor )
  336. {
  337. #ifdef _DEBUG
  338. if ( m_ContentionCheck != 0 )
  339. {
  340. DebuggerBreak();
  341. }
  342. #endif
  343. int iBucket = HashFuncs::Hash( uiKey, BUCKET_MASK );
  344. HashBucket_t &bucket = m_aBuckets[ iBucket ];
  345. bucket.m_AddLock.LockForWrite( );
  346. UtlTSHashHandle_t h = InsertUncommitted( uiKey, bucket );
  347. pConstructor->Construct( &Element(h) );
  348. bucket.m_AddLock.UnlockWrite( );
  349. return h;
  350. }
  351. //-----------------------------------------------------------------------------
  352. // Purpose: Commits all uncommitted insertions
  353. //-----------------------------------------------------------------------------
  354. template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment>
  355. inline void CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::Commit( )
  356. {
  357. // FIXME: Is this legal? Want this to be lock-free
  358. if ( !m_bNeedsCommit )
  359. return;
  360. // This must occur when no queries are occurring
  361. #ifdef _DEBUG
  362. m_ContentionCheck++;
  363. #endif
  364. for ( int i = 0; i < BUCKET_COUNT; i++ )
  365. {
  366. HashBucket_t &bucket = m_aBuckets[ i ];
  367. bucket.m_AddLock.LockForRead( );
  368. bucket.m_pFirst = bucket.m_pFirstUncommitted;
  369. bucket.m_AddLock.UnlockRead( );
  370. }
  371. m_bNeedsCommit = false;
  372. #ifdef _DEBUG
  373. m_ContentionCheck--;
  374. #endif
  375. }
  376. //-----------------------------------------------------------------------------
  377. // Purpose: Remove a single element from the hash
  378. //-----------------------------------------------------------------------------
  379. template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment>
  380. inline void CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::FindAndRemove( KEYTYPE uiKey )
  381. {
  382. if ( m_EntryMemory.Count() == 0 )
  383. return;
  384. // This must occur when no queries are occurring
  385. #ifdef _DEBUG
  386. m_ContentionCheck++;
  387. #endif
  388. int iBucket = HashFuncs::Hash( uiKey, BUCKET_MASK );
  389. HashBucket_t &bucket = m_aBuckets[ iBucket ];
  390. bucket.m_AddLock.LockForWrite( );
  391. HashFixedData_t *pPrev = NULL;
  392. for ( HashFixedData_t *pElement = bucket.m_pFirstUncommitted; pElement; pPrev = pElement, pElement = pElement->m_pNext )
  393. {
  394. if ( !HashFuncs::Compare( pElement->m_uiKey, uiKey ) )
  395. continue;
  396. if ( pPrev )
  397. {
  398. pPrev->m_pNext = pElement->m_pNext;
  399. }
  400. else
  401. {
  402. bucket.m_pFirstUncommitted = pElement->m_pNext;
  403. }
  404. if ( bucket.m_pFirst == pElement )
  405. {
  406. bucket.m_pFirst = bucket.m_pFirst->m_pNext;
  407. }
  408. Destruct( &pElement->m_Data );
  409. #ifdef _DEBUG
  410. memset( pElement, 0xDD, sizeof(HashFixedData_t) );
  411. #endif
  412. m_EntryMemory.Free( pElement );
  413. break;
  414. }
  415. bucket.m_AddLock.UnlockWrite( );
  416. #ifdef _DEBUG
  417. m_ContentionCheck--;
  418. #endif
  419. }
  420. //-----------------------------------------------------------------------------
  421. // Purpose: Remove all elements from the hash
  422. //-----------------------------------------------------------------------------
  423. template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment>
  424. inline void CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::RemoveAll( void )
  425. {
  426. m_bNeedsCommit = false;
  427. if ( m_EntryMemory.Count() == 0 )
  428. return;
  429. // This must occur when no queries are occurring
  430. #ifdef _DEBUG
  431. m_ContentionCheck++;
  432. #endif
  433. for ( int i = 0; i < BUCKET_COUNT; i++ )
  434. {
  435. HashBucket_t &bucket = m_aBuckets[ i ];
  436. bucket.m_AddLock.LockForWrite( );
  437. for ( HashFixedData_t *pElement = bucket.m_pFirstUncommitted; pElement; pElement = pElement->m_pNext )
  438. {
  439. Destruct( &pElement->m_Data );
  440. }
  441. bucket.m_pFirst = NULL;
  442. bucket.m_pFirstUncommitted = NULL;
  443. bucket.m_AddLock.UnlockWrite( );
  444. }
  445. m_EntryMemory.Clear();
  446. #ifdef _DEBUG
  447. m_ContentionCheck--;
  448. #endif
  449. }
  450. //-----------------------------------------------------------------------------
  451. // Finds an element, but only in the committed elements
  452. //-----------------------------------------------------------------------------
  453. template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment>
  454. inline UtlTSHashHandle_t CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::Find( KEYTYPE uiKey, HashFixedData_t *pFirstElement, HashFixedData_t *pLastElement )
  455. {
  456. #ifdef _DEBUG
  457. if ( m_ContentionCheck != 0 )
  458. {
  459. DebuggerBreak();
  460. }
  461. #endif
  462. for ( HashFixedData_t *pElement = pFirstElement; pElement != pLastElement; pElement = pElement->m_pNext )
  463. {
  464. if ( HashFuncs::Compare( pElement->m_uiKey, uiKey ) )
  465. return (UtlTSHashHandle_t)pElement;
  466. }
  467. return InvalidHandle();
  468. }
  469. //-----------------------------------------------------------------------------
  470. // Finds an element, but only in the committed elements
  471. //-----------------------------------------------------------------------------
  472. template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment>
  473. inline UtlTSHashHandle_t CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::Find( KEYTYPE uiKey )
  474. {
  475. int iBucket = HashFuncs::Hash( uiKey, BUCKET_MASK );
  476. const HashBucket_t &bucket = m_aBuckets[iBucket];
  477. UtlTSHashHandle_t h = Find( uiKey, bucket.m_pFirst, NULL );
  478. if ( h != InvalidHandle() )
  479. return h;
  480. // Didn't find it in the fast ( committed ) list. Let's try the slow ( uncommitted ) one
  481. bucket.m_AddLock.LockForRead( );
  482. h = Find( uiKey, bucket.m_pFirstUncommitted, bucket.m_pFirst );
  483. bucket.m_AddLock.UnlockRead( );
  484. return h;
  485. }
  486. //-----------------------------------------------------------------------------
  487. // Purpose: Return data given a hash handle.
  488. //-----------------------------------------------------------------------------
  489. template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment>
  490. inline T &CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::Element( UtlTSHashHandle_t hHash )
  491. {
  492. return ((HashFixedData_t *)hHash)->m_Data;
  493. }
  494. template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment>
  495. inline T const &CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::Element( UtlTSHashHandle_t hHash ) const
  496. {
  497. return ((HashFixedData_t *)hHash)->m_Data;
  498. }
  499. template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment>
  500. inline T &CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::operator[]( UtlTSHashHandle_t hHash )
  501. {
  502. return ((HashFixedData_t *)hHash)->m_Data;
  503. }
  504. template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment>
  505. inline T const &CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::operator[]( UtlTSHashHandle_t hHash ) const
  506. {
  507. return ((HashFixedData_t *)hHash)->m_Data;
  508. }
  509. template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment>
  510. inline KEYTYPE CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::GetID( UtlTSHashHandle_t hHash ) const
  511. {
  512. return ((HashFixedData_t *)hHash)->m_uiKey;
  513. }
  514. // Convert element * to hashHandle
  515. template<class T, int BUCKET_COUNT, class KEYTYPE, class HashFuncs, int nAlignment>
  516. inline UtlTSHashHandle_t CUtlTSHash<T,BUCKET_COUNT,KEYTYPE,HashFuncs,nAlignment>::ElementPtrToHandle( T* pElement ) const
  517. {
  518. Assert( pElement );
  519. HashFixedData_t *pFixedData = (HashFixedData_t*)( (uint8*)pElement - offsetof( HashFixedData_t, m_Data ) );
  520. Assert( m_EntryMemory.IsAllocationWithinPool( pFixedData ) );
  521. return (UtlTSHashHandle_t)pFixedData;
  522. }
  523. #endif // UTLTSHASH_H