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.

693 lines
21 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. =================//
  2. //
  3. // Purpose: index-based hash map container well suited for large and growing
  4. // datasets. It uses less memory than other hash maps and incrementally
  5. // rehashes to reduce reallocation spikes.
  6. //
  7. //=============================================================================//
  8. #ifndef UTLHASHMAPLARGE_H
  9. #define UTLHASHMAPLARGE_H
  10. #ifdef _WIN32
  11. #pragma once
  12. #endif
  13. #include "tier0/dbg.h"
  14. #include "bitvec.h"
  15. #include "murmurhash3.h"
  16. // fast mod for power of 2 numbers
  17. namespace basetypes
  18. {
  19. template <class T>
  20. inline bool IsPowerOf2(T n)
  21. {
  22. return n > 0 && (n & (n-1)) == 0;
  23. }
  24. template <class T1, class T2>
  25. inline T2 ModPowerOf2(T1 a, T2 b)
  26. {
  27. return T2(a) & (b-1);
  28. }
  29. }
  30. // default comparison operator
  31. template <typename T>
  32. class CDefEquals
  33. {
  34. public:
  35. CDefEquals() {}
  36. CDefEquals( int i ) {}
  37. inline bool operator()( const T &lhs, const T &rhs ) const { return ( lhs == rhs ); }
  38. inline bool operator!() const { return false; }
  39. };
  40. // Specialization to compare pointers
  41. template <typename T>
  42. class CDefEquals<T*>
  43. {
  44. public:
  45. CDefEquals() {}
  46. CDefEquals( int i ) {}
  47. inline bool operator()( const T *lhs, const T *rhs ) const
  48. {
  49. if ( lhs == rhs )
  50. return true;
  51. else if ( NULL == lhs || NULL == rhs )
  52. return false;
  53. else
  54. return ( *lhs == *rhs );
  55. }
  56. inline bool operator!() const { return false; }
  57. };
  58. // Hash specialization for CUtlStrings
  59. template<>
  60. struct MurmurHash3Functor<CUtlString>
  61. {
  62. typedef uint32 TargetType ;
  63. TargetType operator()(const CUtlString &strKey) const
  64. {
  65. return MurmurHash3Functor<const char*>()( strKey.String() );
  66. }
  67. };
  68. //hash 3 function for a general case sensitive string compares
  69. struct MurmurHash3ConstCharPtr
  70. {
  71. typedef uint32 TargetType ;
  72. TargetType operator()( const char* pszKey ) const { return MurmurHash3Functor<const char*>()( pszKey ); }
  73. };
  74. struct CaseSensitiveStrEquals
  75. {
  76. bool operator()( const char* pszLhs, const char* pszRhs ) const { return strcmp( pszLhs, pszRhs ) == 0; }
  77. };
  78. //-----------------------------------------------------------------------------
  79. //
  80. // Purpose: An associative container. Pretty much identical to CUtlMap without the ability to walk in-order
  81. // This container is well suited for large and growing datasets. It uses less
  82. // memory than other hash maps and incrementally rehashes to reduce reallocation spikes.
  83. // However, it is slower (by about 20%) than CUtlHashTable
  84. //
  85. //-----------------------------------------------------------------------------
  86. template <typename K, typename T, typename L = CDefEquals<K>, typename H = MurmurHash3Functor<K> >
  87. class CUtlHashMapLarge : public base_utlmap_t
  88. {
  89. public:
  90. // This enum exists so that FOR_EACH_MAP and FOR_EACH_MAP_FAST cannot accidentally
  91. // be used on a type that is not a CUtlMap. If the code compiles then all is well.
  92. // The check for IsUtlMap being true should be free.
  93. // Using an enum rather than a static const bool ensures that this trick works even
  94. // with optimizations disabled on gcc.
  95. enum CompileTimeCheck
  96. {
  97. IsUtlMap = 1
  98. };
  99. typedef K KeyType_t;
  100. typedef T ElemType_t;
  101. typedef int IndexType_t;
  102. typedef L EqualityFunc_t;
  103. typedef H HashFunc_t;
  104. CUtlHashMapLarge()
  105. {
  106. m_cElements = 0;
  107. m_nMaxElement = 0;
  108. m_nMinRehashedBucket = InvalidIndex();
  109. m_nMaxRehashedBucket = InvalidIndex();
  110. m_iNodeFreeListHead = InvalidIndex();
  111. }
  112. CUtlHashMapLarge( int cElementsExpected )
  113. {
  114. m_cElements = 0;
  115. m_nMaxElement = 0;
  116. m_nMinRehashedBucket = InvalidIndex();
  117. m_nMaxRehashedBucket = InvalidIndex();
  118. m_iNodeFreeListHead = InvalidIndex();
  119. EnsureCapacity( cElementsExpected );
  120. }
  121. ~CUtlHashMapLarge()
  122. {
  123. RemoveAll();
  124. }
  125. // gets particular elements
  126. ElemType_t & Element( IndexType_t i ) { return m_memNodes.Element( i ).m_elem; }
  127. const ElemType_t & Element( IndexType_t i ) const { return m_memNodes.Element( i ).m_elem; }
  128. ElemType_t & operator[]( IndexType_t i ) { return m_memNodes.Element( i ).m_elem; }
  129. const ElemType_t & operator[]( IndexType_t i ) const { return m_memNodes.Element( i ).m_elem; }
  130. KeyType_t & Key( IndexType_t i ) { return m_memNodes.Element( i ).m_key; }
  131. const KeyType_t & Key( IndexType_t i ) const { return m_memNodes.Element( i ).m_key; }
  132. // Num elements
  133. IndexType_t Count() const { return m_cElements; }
  134. // Max "size" of the vector
  135. IndexType_t MaxElement() const { return m_nMaxElement; }
  136. // Checks if a node is valid and in the map
  137. bool IsValidIndex( IndexType_t i ) const { return i >= 0 && i < m_nMaxElement && !IsFreeNodeID( m_memNodes[i].m_iNextNode ); }
  138. // Invalid index
  139. static IndexType_t InvalidIndex() { return -1; }
  140. // Insert method
  141. IndexType_t Insert( const KeyType_t &key, const ElemType_t &insert ) { return InsertInternal( key, insert, eInsert_UpdateExisting ); }
  142. IndexType_t Insert( const KeyType_t &key ) { return InsertInternal( key, ElemType_t(), eInsert_UpdateExisting ); }
  143. IndexType_t InsertWithDupes( const KeyType_t &key, const ElemType_t &insert ) { return InsertInternal( key, insert, eInsert_CreateDupes ); }
  144. IndexType_t FindOrInsert( const KeyType_t &key, const ElemType_t &insert ) { return InsertInternal( key, insert, eInsert_LeaveExisting ); }
  145. IndexType_t InsertOrReplace( const KeyType_t &key, const ElemType_t &insert ) { return InsertInternal( key, insert, eInsert_UpdateExisting ); }
  146. // Finds an element
  147. IndexType_t Find( const KeyType_t &key ) const;
  148. // has an element
  149. bool HasElement( const KeyType_t &key ) const
  150. {
  151. return Find( key ) != InvalidIndex();
  152. }
  153. void EnsureCapacity( int num );
  154. void RemoveAt( IndexType_t i );
  155. bool Remove( const KeyType_t &key )
  156. {
  157. int iMap = Find( key );
  158. if ( iMap != InvalidIndex() )
  159. {
  160. RemoveAt( iMap );
  161. return true;
  162. }
  163. return false;
  164. }
  165. void RemoveAll();
  166. void Purge();
  167. void PurgeAndDeleteElements();
  168. void Swap( CUtlHashMapLarge<K,T,L,H> &rhs )
  169. {
  170. m_vecHashBuckets.Swap( rhs.m_vecHashBuckets );
  171. V_swap( m_bitsMigratedBuckets, rhs.m_bitsMigratedBuckets );
  172. m_memNodes.Swap( rhs.m_memNodes );
  173. V_swap( m_iNodeFreeListHead, rhs.m_iNodeFreeListHead );
  174. V_swap( m_cElements, rhs.m_cElements );
  175. V_swap( m_nMaxElement, rhs.m_nMaxElement );
  176. V_swap( m_nMinRehashedBucket, rhs.m_nMinRehashedBucket );
  177. V_swap( m_nMaxRehashedBucket, rhs.m_nMaxRehashedBucket );
  178. V_swap( m_EqualityFunc, rhs.m_EqualityFunc );
  179. V_swap( m_HashFunc, rhs.m_HashFunc );
  180. }
  181. private:
  182. enum EInsertPolicy { eInsert_UpdateExisting, eInsert_LeaveExisting, eInsert_CreateDupes };
  183. IndexType_t InsertInternal( const KeyType_t &key, const ElemType_t &insert, EInsertPolicy ePolicy );
  184. inline IndexType_t FreeNodeIDToIndex( IndexType_t i ) const { return (0-i)-3; }
  185. inline IndexType_t FreeNodeIndexToID( IndexType_t i ) const { return (-3)-i; }
  186. inline bool IsFreeNodeID( IndexType_t i ) const { return i < InvalidIndex(); }
  187. int FindInBucket( int iBucket, const KeyType_t &key ) const;
  188. int AllocNode();
  189. void RehashNodesInBucket( int iBucket );
  190. void LinkNodeIntoBucket( int iBucket, int iNewNode );
  191. void UnlinkNodeFromBucket( int iBucket, int iNewNode );
  192. bool RemoveNodeFromBucket( int iBucket, int iNodeToRemove );
  193. void IncrementalRehash();
  194. struct HashBucket_t
  195. {
  196. IndexType_t m_iNode;
  197. };
  198. CUtlVector<HashBucket_t> m_vecHashBuckets;
  199. CLargeVarBitVec m_bitsMigratedBuckets;
  200. struct Node_t
  201. {
  202. KeyType_t m_key;
  203. ElemType_t m_elem;
  204. int m_iNextNode;
  205. };
  206. CUtlMemory<Node_t> m_memNodes;
  207. IndexType_t m_iNodeFreeListHead;
  208. IndexType_t m_cElements;
  209. IndexType_t m_nMaxElement;
  210. IndexType_t m_nMinRehashedBucket, m_nMaxRehashedBucket;
  211. EqualityFunc_t m_EqualityFunc;
  212. HashFunc_t m_HashFunc;
  213. };
  214. //-----------------------------------------------------------------------------
  215. // Purpose: inserts an item into the map
  216. //-----------------------------------------------------------------------------
  217. template <typename K, typename T, typename L, typename H>
  218. inline int CUtlHashMapLarge<K,T,L,H>::InsertInternal( const KeyType_t &key, const ElemType_t &insert, EInsertPolicy ePolicy )
  219. {
  220. // make sure we have room in the hash table
  221. if ( m_cElements >= m_vecHashBuckets.Count() )
  222. EnsureCapacity( MAX( 16, m_vecHashBuckets.Count() * 2 ) );
  223. if ( m_cElements >= m_memNodes.Count() )
  224. m_memNodes.Grow( m_memNodes.Count() * 2 );
  225. // rehash incrementally
  226. IncrementalRehash();
  227. // hash the item
  228. uint32 hash = m_HashFunc( key );
  229. // migrate data forward, if necessary
  230. int cBucketsToModAgainst = m_vecHashBuckets.Count() >> 1;
  231. int iBucket = basetypes::ModPowerOf2(hash, cBucketsToModAgainst);
  232. while ( iBucket >= m_nMinRehashedBucket
  233. && !m_bitsMigratedBuckets.Get( iBucket ) )
  234. {
  235. RehashNodesInBucket( iBucket );
  236. cBucketsToModAgainst >>= 1;
  237. iBucket = basetypes::ModPowerOf2(hash, cBucketsToModAgainst);
  238. }
  239. // prevent duplicates if necessary
  240. if ( ( ePolicy != eInsert_CreateDupes ) && m_cElements )
  241. {
  242. // look in the bucket to see if we have a conflict
  243. int iBucket2 = basetypes::ModPowerOf2( hash, m_vecHashBuckets.Count() );
  244. IndexType_t iNode = FindInBucket( iBucket2, key );
  245. if ( iNode != InvalidIndex() )
  246. {
  247. // a duplicate - update in place (matching CUtlMap)
  248. if( ePolicy == eInsert_UpdateExisting )
  249. {
  250. m_memNodes[iNode].m_elem = insert;
  251. }
  252. return iNode;
  253. }
  254. }
  255. // make an item
  256. int iNewNode = AllocNode();
  257. m_memNodes[iNewNode].m_iNextNode = InvalidIndex();
  258. CopyConstruct( &m_memNodes[iNewNode].m_key, key );
  259. CopyConstruct( &m_memNodes[iNewNode].m_elem, insert );
  260. iBucket = basetypes::ModPowerOf2( hash, m_vecHashBuckets.Count() );
  261. // link ourselves in
  262. // ::OutputDebugStr( CFmtStr( "insert %d into bucket %d\n", key, iBucket ).Access() );
  263. LinkNodeIntoBucket( iBucket, iNewNode );
  264. // return the new node
  265. return iNewNode;
  266. }
  267. //-----------------------------------------------------------------------------
  268. // Purpose: grows the map to fit the specified amount
  269. //-----------------------------------------------------------------------------
  270. template <typename K, typename T, typename L, typename H>
  271. inline void CUtlHashMapLarge<K,T,L,H>::EnsureCapacity( int amount )
  272. {
  273. m_memNodes.EnsureCapacity( amount );
  274. // ::OutputDebugStr( CFmtStr( "grown m_memNodes from %d to %d\n", m_cElements, m_memNodes.Count() ).Access() );
  275. if ( amount <= m_vecHashBuckets.Count() )
  276. return;
  277. int cBucketsNeeded = MAX( 16, m_vecHashBuckets.Count() );
  278. while ( cBucketsNeeded < amount )
  279. cBucketsNeeded *= 2;
  280. // ::OutputDebugStr( CFmtStr( "grown m_vecHashBuckets from %d to %d\n", m_vecHashBuckets.Count(), cBucketsNeeded ).Access() );
  281. // grow the hash buckets
  282. int grow = cBucketsNeeded - m_vecHashBuckets.Count();
  283. int iFirst = m_vecHashBuckets.AddMultipleToTail( grow );
  284. // clear all the new data to invalid bits
  285. memset( &m_vecHashBuckets[iFirst], 0xFFFFFFFF, grow*sizeof(m_vecHashBuckets[iFirst]) );
  286. Assert( basetypes::IsPowerOf2( m_vecHashBuckets.Count() ) );
  287. // we'll have to rehash, all the buckets that existed before growth
  288. m_nMinRehashedBucket = 0;
  289. m_nMaxRehashedBucket = iFirst;
  290. if ( m_cElements > 0 )
  291. {
  292. // remove all the current bits
  293. m_bitsMigratedBuckets.Resize( 0 );
  294. // re-add new bits; these will all be reset to 0
  295. m_bitsMigratedBuckets.Resize( m_vecHashBuckets.Count() );
  296. }
  297. else
  298. {
  299. // no elements - no rehashing
  300. m_nMinRehashedBucket = m_vecHashBuckets.Count();
  301. }
  302. }
  303. //-----------------------------------------------------------------------------
  304. // Purpose: gets a new node, from the free list if possible
  305. //-----------------------------------------------------------------------------
  306. template <typename K, typename T, typename L, typename H>
  307. inline int CUtlHashMapLarge<K,T,L,H>::AllocNode()
  308. {
  309. // if we're out of free elements, get the max
  310. if ( m_cElements == m_nMaxElement )
  311. {
  312. m_cElements++;
  313. return m_nMaxElement++;
  314. }
  315. // pull from the free list
  316. Assert( m_iNodeFreeListHead != InvalidIndex() );
  317. int iNewNode = m_iNodeFreeListHead;
  318. m_iNodeFreeListHead = FreeNodeIDToIndex( m_memNodes[iNewNode].m_iNextNode );
  319. m_cElements++;
  320. return iNewNode;
  321. }
  322. //-----------------------------------------------------------------------------
  323. // Purpose: takes a bucket of nodes and re-hashes them into a more optimal bucket
  324. //-----------------------------------------------------------------------------
  325. template <typename K, typename T, typename L, typename H>
  326. inline void CUtlHashMapLarge<K,T,L,H>::RehashNodesInBucket( int iBucketSrc )
  327. {
  328. // mark us as migrated
  329. m_bitsMigratedBuckets.Set( iBucketSrc );
  330. // walk the list of items, re-hashing them
  331. IndexType_t iNode = m_vecHashBuckets[iBucketSrc].m_iNode;
  332. while ( iNode != InvalidIndex() )
  333. {
  334. IndexType_t iNodeNext = m_memNodes[iNode].m_iNextNode;
  335. Assert( iNodeNext != iNode );
  336. // work out where the node should go
  337. const KeyType_t &key = m_memNodes[iNode].m_key;
  338. uint32 hash = m_HashFunc( key );
  339. int iBucketDest = basetypes::ModPowerOf2( hash, m_vecHashBuckets.Count() );
  340. // if the hash bucket has changed, move it
  341. if ( iBucketDest != iBucketSrc )
  342. {
  343. // ::OutputDebugStr( CFmtStr( "moved key %d from bucket %d to %d\n", key, iBucketSrc, iBucketDest ).Access() );
  344. // remove from this bucket list
  345. UnlinkNodeFromBucket( iBucketSrc, iNode );
  346. // link into new bucket list
  347. LinkNodeIntoBucket( iBucketDest, iNode );
  348. }
  349. iNode = iNodeNext;
  350. }
  351. }
  352. //-----------------------------------------------------------------------------
  353. // Purpose: searches for an item by key, returning the index handle
  354. //-----------------------------------------------------------------------------
  355. template <typename K, typename T, typename L, typename H>
  356. inline int CUtlHashMapLarge<K,T,L,H>::Find( const KeyType_t &key ) const
  357. {
  358. if ( m_cElements == 0 )
  359. return InvalidIndex();
  360. // hash the item
  361. uint32 hash = m_HashFunc( key );
  362. // find the bucket
  363. int cBucketsToModAgainst = m_vecHashBuckets.Count();
  364. int iBucket = basetypes::ModPowerOf2( hash, cBucketsToModAgainst );
  365. // look in the bucket for the item
  366. int iNode = FindInBucket( iBucket, key );
  367. if ( iNode != InvalidIndex() )
  368. return iNode;
  369. // not found? we may have to look in older buckets
  370. cBucketsToModAgainst >>= 1;
  371. while ( cBucketsToModAgainst >= m_nMinRehashedBucket )
  372. {
  373. iBucket = basetypes::ModPowerOf2( hash, cBucketsToModAgainst );
  374. if ( !m_bitsMigratedBuckets.Get( iBucket ) )
  375. {
  376. int iNode2 = FindInBucket( iBucket, key );
  377. if ( iNode2 != InvalidIndex() )
  378. return iNode2;
  379. }
  380. cBucketsToModAgainst >>= 1;
  381. }
  382. return InvalidIndex();
  383. }
  384. //-----------------------------------------------------------------------------
  385. // Purpose: searches for an item by key, returning the index handle
  386. //-----------------------------------------------------------------------------
  387. template <typename K, typename T, typename L, typename H>
  388. inline int CUtlHashMapLarge<K,T,L,H>::FindInBucket( int iBucket, const KeyType_t &key ) const
  389. {
  390. if ( m_vecHashBuckets[iBucket].m_iNode != InvalidIndex() )
  391. {
  392. IndexType_t iNode = m_vecHashBuckets[iBucket].m_iNode;
  393. Assert( iNode < m_nMaxElement );
  394. while ( iNode != InvalidIndex() )
  395. {
  396. // equality check
  397. if ( m_EqualityFunc( key, m_memNodes[iNode].m_key ) )
  398. return iNode;
  399. iNode = m_memNodes[iNode].m_iNextNode;
  400. }
  401. }
  402. return InvalidIndex();
  403. }
  404. //-----------------------------------------------------------------------------
  405. // Purpose: links a node into a bucket
  406. //-----------------------------------------------------------------------------
  407. template <typename K, typename T, typename L, typename H>
  408. void CUtlHashMapLarge<K,T,L,H>::LinkNodeIntoBucket( int iBucket, int iNewNode )
  409. {
  410. // add into the start of the bucket's list
  411. m_memNodes[iNewNode].m_iNextNode = m_vecHashBuckets[iBucket].m_iNode;
  412. m_vecHashBuckets[iBucket].m_iNode = iNewNode;
  413. }
  414. //-----------------------------------------------------------------------------
  415. // Purpose: unlinks a node from the bucket
  416. //-----------------------------------------------------------------------------
  417. template <typename K, typename T, typename L, typename H>
  418. void CUtlHashMapLarge<K,T,L,H>::UnlinkNodeFromBucket( int iBucket, int iNodeToUnlink )
  419. {
  420. int iNodeNext = m_memNodes[iNodeToUnlink].m_iNextNode;
  421. // if it's the first node, just update the bucket to point to the new place
  422. int iNode = m_vecHashBuckets[iBucket].m_iNode;
  423. if ( iNode == iNodeToUnlink )
  424. {
  425. m_vecHashBuckets[iBucket].m_iNode = iNodeNext;
  426. return;
  427. }
  428. // walk the list to find where
  429. while ( iNode != InvalidIndex() )
  430. {
  431. if ( m_memNodes[iNode].m_iNextNode == iNodeToUnlink )
  432. {
  433. m_memNodes[iNode].m_iNextNode = iNodeNext;
  434. return;
  435. }
  436. iNode = m_memNodes[iNode].m_iNextNode;
  437. }
  438. // should always be valid to unlink
  439. Assert( false );
  440. }
  441. //-----------------------------------------------------------------------------
  442. // Purpose: removes a single item from the map
  443. //-----------------------------------------------------------------------------
  444. template <typename K, typename T, typename L, typename H>
  445. inline void CUtlHashMapLarge<K,T,L,H>::RemoveAt( IndexType_t i )
  446. {
  447. if ( !IsValidIndex( i ) )
  448. {
  449. Assert( false );
  450. return;
  451. }
  452. // unfortunately, we have to re-hash to find which bucket we're in
  453. uint32 hash = m_HashFunc( m_memNodes[i].m_key );
  454. int cBucketsToModAgainst = m_vecHashBuckets.Count();
  455. int iBucket = basetypes::ModPowerOf2( hash, cBucketsToModAgainst );
  456. if ( RemoveNodeFromBucket( iBucket, i ) )
  457. return;
  458. // wasn't found; look in older buckets
  459. cBucketsToModAgainst >>= 1;
  460. while ( cBucketsToModAgainst >= m_nMinRehashedBucket )
  461. {
  462. iBucket = basetypes::ModPowerOf2( hash, cBucketsToModAgainst );
  463. if ( !m_bitsMigratedBuckets.Get( iBucket ) )
  464. {
  465. if ( RemoveNodeFromBucket( iBucket, i ) )
  466. return;
  467. }
  468. cBucketsToModAgainst >>= 1;
  469. }
  470. // never found, container is busted
  471. Assert( false );
  472. }
  473. //-----------------------------------------------------------------------------
  474. // Purpose: removes a node from the bucket, return true if it was found
  475. //-----------------------------------------------------------------------------
  476. template <typename K, typename T, typename L, typename H>
  477. inline bool CUtlHashMapLarge<K,T,L,H>::RemoveNodeFromBucket( IndexType_t iBucket, int iNodeToRemove )
  478. {
  479. IndexType_t iNode = m_vecHashBuckets[iBucket].m_iNode;
  480. while ( iNode != InvalidIndex() )
  481. {
  482. if ( iNodeToRemove == iNode )
  483. {
  484. // found it, remove
  485. UnlinkNodeFromBucket( iBucket, iNodeToRemove );
  486. Destruct( &m_memNodes[iNode].m_key );
  487. Destruct( &m_memNodes[iNode].m_elem );
  488. // link into free list
  489. m_memNodes[iNode].m_iNextNode = FreeNodeIndexToID( m_iNodeFreeListHead );
  490. m_iNodeFreeListHead = iNode;
  491. m_cElements--;
  492. if ( m_cElements == 0 )
  493. {
  494. m_nMinRehashedBucket = m_vecHashBuckets.Count();
  495. }
  496. return true;
  497. }
  498. iNode = m_memNodes[iNode].m_iNextNode;
  499. }
  500. return false;
  501. }
  502. //-----------------------------------------------------------------------------
  503. // Purpose: removes all items from the hash map
  504. //-----------------------------------------------------------------------------
  505. template <typename K, typename T, typename L, typename H>
  506. inline void CUtlHashMapLarge<K,T,L,H>::RemoveAll()
  507. {
  508. FOR_EACH_MAP_FAST( *this, i )
  509. {
  510. Destruct( &m_memNodes[i].m_key );
  511. Destruct( &m_memNodes[i].m_elem );
  512. }
  513. m_cElements = 0;
  514. m_nMaxElement = 0;
  515. m_iNodeFreeListHead = InvalidIndex();
  516. m_nMinRehashedBucket = m_vecHashBuckets.Count();
  517. m_nMaxRehashedBucket = InvalidIndex();
  518. m_bitsMigratedBuckets.Resize( 0 );
  519. memset( m_vecHashBuckets.Base(), 0xFF, m_vecHashBuckets.Count() * sizeof(HashBucket_t) );
  520. }
  521. //-----------------------------------------------------------------------------
  522. // Purpose: removes all items from the hash map and releases memory
  523. //-----------------------------------------------------------------------------
  524. template <typename K, typename T, typename L, typename H>
  525. inline void CUtlHashMapLarge<K,T,L,H>::Purge()
  526. {
  527. FOR_EACH_MAP_FAST( *this, i )
  528. {
  529. Destruct( &m_memNodes[i].m_key );
  530. Destruct( &m_memNodes[i].m_elem );
  531. }
  532. m_cElements = 0;
  533. m_nMaxElement = 0;
  534. m_iNodeFreeListHead = InvalidIndex();
  535. m_nMinRehashedBucket = InvalidIndex();
  536. m_nMaxRehashedBucket = InvalidIndex();
  537. m_bitsMigratedBuckets.Resize( 0 );
  538. m_memNodes.Purge();
  539. m_vecHashBuckets.Purge();
  540. }
  541. //-----------------------------------------------------------------------------
  542. // Purpose: removes and deletes all items from the hash map and releases memory
  543. //-----------------------------------------------------------------------------
  544. template <typename K, typename T, typename L, typename H>
  545. inline void CUtlHashMapLarge<K,T,L,H>::PurgeAndDeleteElements()
  546. {
  547. FOR_EACH_MAP_FAST( *this, i )
  548. {
  549. delete this->Element( i );
  550. }
  551. Purge();
  552. }
  553. //-----------------------------------------------------------------------------
  554. // Purpose: rehashes buckets
  555. //-----------------------------------------------------------------------------
  556. template <typename K, typename T, typename L, typename H>
  557. inline void CUtlHashMapLarge<K,T,L,H>::IncrementalRehash()
  558. {
  559. if ( m_nMinRehashedBucket < m_nMaxRehashedBucket )
  560. {
  561. while ( m_nMinRehashedBucket < m_nMaxRehashedBucket )
  562. {
  563. // see if the bucket needs rehashing
  564. if ( m_vecHashBuckets[m_nMinRehashedBucket].m_iNode != InvalidIndex()
  565. && !m_bitsMigratedBuckets.Get(m_nMinRehashedBucket) )
  566. {
  567. // rehash this bucket
  568. RehashNodesInBucket( m_nMinRehashedBucket );
  569. // only actively do one - don't want to do it too fast since we may be on a rapid growth path
  570. ++m_nMinRehashedBucket;
  571. break;
  572. }
  573. // nothing to rehash in that bucket - increment and look again
  574. ++m_nMinRehashedBucket;
  575. }
  576. if ( m_nMinRehashedBucket >= m_nMaxRehashedBucket )
  577. {
  578. // we're done; don't need any bits anymore
  579. m_nMinRehashedBucket = m_vecHashBuckets.Count();
  580. m_nMaxRehashedBucket = InvalidIndex();
  581. m_bitsMigratedBuckets.Resize( 0 );
  582. }
  583. }
  584. }
  585. #endif // UTLHASHMAPLARGE_H