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.

698 lines
22 KiB

  1. //========= Copyright 1996-2004, Valve LLC, All rights reserved. ============
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================
  7. #ifndef THASH_H
  8. #define THASH_H
  9. #ifdef _WIN32
  10. #pragma once
  11. #endif
  12. #include <typeinfo>
  13. //#define DBGFLAG_THASH // Perform extra sanity checks on the THash
  14. // THash
  15. // This is a heavyweight, templatized version of DHash.
  16. // It differs from DHash in the following ways:
  17. // - It's templetized, and automatically constructs and destructs its records as appropriate
  18. // - It provides a scheduling service, which can be used to touch every record in the table
  19. // at a specified interval. The scheduler is low-overhead, and provides a very smooth
  20. // distribution of touches across frames.
  21. // Template arguments:
  22. // Data: the class to be stored in the hash table
  23. // I: the type of the primary key (uint32 by default)
  24. template<class Data, typename I=uint32>
  25. class CTHash
  26. {
  27. private:
  28. // RecHdr
  29. // We insert one of these at the beginning of every record. It's used for
  30. // keeping the records in a linked list.
  31. typedef struct RecHdr_t
  32. {
  33. RecHdr_t *m_pRecHdrNext; // Next item in our linked list
  34. RecHdr_t *m_pRecHdrPrev; // Previous item in our linked list
  35. I m_unKey; // Key of this item
  36. int m_iBucket; // The bucket we're in
  37. int m_nRunRatio; // We want to run 1 cycle out of every m_nRunRatio
  38. // cycles (not at all if 0).
  39. #ifdef DBGFLAG_THASH
  40. uint m_iCycleLast; // Last cycle we were visited (whether or not we ran)
  41. #endif
  42. } RecHdr_t;
  43. // Bucket
  44. // Each hash bucket is represented by a Bucket structure, which points to the
  45. // first record with the bucket's hash.
  46. typedef struct Bucket_t
  47. {
  48. RecHdr_t *m_pRecHdrFirst; // First record in our list
  49. } Bucket_t;
  50. public:
  51. // Constructors & destructors
  52. CTHash( int cFramesPerCycle );
  53. ~CTHash();
  54. // Initializer
  55. void Init( int cRecordInit, int cBuckets );
  56. // Insert a record into the table
  57. Data *PvRecordInsert( I unKey );
  58. // Insert a record into the table and set the allocated object's pointer as the hash key
  59. Data *PvRecordInsertAutoKey();
  60. // Changes the key for a previously inserted item
  61. void ChangeKey( Data * pvRecord, I unOldKey, I unNewKey );
  62. // Remove a record
  63. void Remove( I unKey );
  64. // Remove a record
  65. void Remove( Data * pvRecord );
  66. // Remove all records
  67. void RemoveAll();
  68. // Find a record
  69. Data *PvRecordFind( I unKey ) const;
  70. // How many records do we have
  71. int Count() const { return m_cRecordInUse; }
  72. // Iterate through our members
  73. Data *PvRecordFirst() const;
  74. Data *PvRecordNext( Data *pvRecordCur ) const;
  75. // We provide a scheduling service. Call StartFrameSchedule when you want to start running
  76. // records in a given frame, and repeatedly call PvRecordRun until it returns NULL (or you
  77. // run our of time).
  78. void SetRunRatio( Data *pvRecord, int nRunRatio );
  79. void SetMicroSecPerCycle( int cMicroSecPerCycle, int cMicroSecPerFrame ) { m_cFramesPerCycle = cMicroSecPerCycle / cMicroSecPerFrame; }
  80. void StartFrameSchedule( bool bNewFrame );
  81. Data *PvRecordRun();
  82. bool BCompletedPass();
  83. #ifdef DBGFLAG_VALIDATE
  84. virtual void Validate( CValidator &validator, const char *pchName );
  85. #endif // DBGFLAG_VALIDATE
  86. private:
  87. // Insert a record into the table
  88. Data *PvRecordInsertInternal( RecHdr_t *pRecHdr, I unKey );
  89. // Get the record associated with a THashHdr
  90. Data *PvRecordFromPRecHdr( RecHdr_t *pRecHdr ) const { return ( Data * ) ( ( ( uint8 * ) pRecHdr + sizeof( RecHdr_t ) ) ); }
  91. // Get the RecHdr preceding a PvRecord
  92. RecHdr_t *PRecHdrFromPvRecord( Data *pvRecord ) const { return ( ( ( RecHdr_t * ) pvRecord ) - 1 ); }
  93. // Get the hash bucket corresponding to a key
  94. int IBucket( I unKey ) const;
  95. void InsertIntoHash( RecHdr_t *pRecHdr, I unKey );
  96. void RemoveFromHash( Data * pvRecord );
  97. int m_cBucket; // # of hash buckets we have
  98. Bucket_t *m_pBucket; // Big array of hash buckets
  99. CUtlMemoryPool *m_pMemoryPoolRecord; // All our data records
  100. int m_cRecordInUse; // # of records in use
  101. RecHdr_t m_RecHdrHead; // Head of our linked list
  102. RecHdr_t m_RecHdrTail; // Tail of our linked list
  103. int m_cFramesPerCycle; // Run each of our records once every m_cFramesPerCycle frames
  104. RecHdr_t *m_pRecHdrRunNext; // Next record to run (be careful-- this is more complicated than it sounds)
  105. int m_iBucketRunMax; // Stop running when we get to this bucket
  106. uint m_iCycleCur; // Current cycle (ie, how many times we've made a complete scheduler pass)
  107. uint m_iCycleLast; // Our previous cycle
  108. uint m_iFrameCur; // Our current frame (incremented once each StartFrameSchedule)
  109. uint m_iCycleLastReported; // Last cycle checked by BCompletedPass()
  110. };
  111. //-----------------------------------------------------------------------------
  112. // Purpose: Constructor
  113. // Input: cMicroSecRunInterval - How often we want the scheduler to run each of our records
  114. //-----------------------------------------------------------------------------
  115. template<class Data, class I>
  116. CTHash<Data,I>::CTHash( int cFramesPerCycle )
  117. {
  118. m_cBucket = 0;
  119. m_pBucket = NULL;
  120. m_pMemoryPoolRecord = NULL;
  121. m_cRecordInUse = 0;
  122. m_cFramesPerCycle = cFramesPerCycle;
  123. m_pRecHdrRunNext = &m_RecHdrTail; // This will make us start at the beginning on our first frame
  124. m_iBucketRunMax = 0;
  125. m_iCycleCur = 0;
  126. m_iCycleLast = 0;
  127. m_iFrameCur = 0;
  128. m_iCycleLastReported = 0;
  129. m_RecHdrHead.m_pRecHdrPrev = NULL;
  130. m_RecHdrHead.m_pRecHdrNext = &m_RecHdrTail;
  131. m_RecHdrHead.m_iBucket = -1;
  132. m_RecHdrTail.m_pRecHdrPrev = &m_RecHdrHead;
  133. m_RecHdrTail.m_pRecHdrNext = NULL;
  134. }
  135. //-----------------------------------------------------------------------------
  136. // Purpose: Destructor
  137. //-----------------------------------------------------------------------------
  138. template<class Data, class I>
  139. CTHash<Data,I>::~CTHash()
  140. {
  141. RemoveAll();
  142. if ( NULL != m_pBucket )
  143. FreePv( m_pBucket );
  144. m_pBucket = NULL;
  145. if ( NULL != m_pMemoryPoolRecord )
  146. delete( m_pMemoryPoolRecord );
  147. m_pMemoryPoolRecord = NULL;
  148. }
  149. //-----------------------------------------------------------------------------
  150. // Purpose: Initializer. Allocate our various arrays, and set up the free
  151. // list.
  152. // Input: cRecordInit - Initial # of data records we can contain
  153. // cBucket - # of hash buckets we should use
  154. //-----------------------------------------------------------------------------
  155. template<class Data, class I>
  156. void CTHash<Data,I>::Init( int cRecordInit, int cBucket )
  157. {
  158. Assert( cRecordInit > 0 ); // need to init with non-zero value or memory pool will never grow
  159. // Copy our parameters
  160. m_cBucket = cBucket;
  161. // Alloc our arrays
  162. m_pBucket = ( Bucket_t * ) PvAlloc( sizeof( Bucket_t ) * m_cBucket );
  163. m_pMemoryPoolRecord = new CUtlMemoryPool( sizeof( Data ) + sizeof( RecHdr_t ), cRecordInit,
  164. CUtlMemoryPool::GROW_SLOW );
  165. // Init the hash buckets
  166. for ( int iBucket = 0; iBucket < m_cBucket; iBucket++ )
  167. m_pBucket[iBucket].m_pRecHdrFirst = NULL;
  168. // Make the tail have an illegally large bucket
  169. m_RecHdrTail.m_iBucket = m_cBucket + 1;
  170. }
  171. //-----------------------------------------------------------------------------
  172. // Purpose: Inserts a new record into the table
  173. // Input: unKey - Primary key of the new record
  174. // Output: Pointer to the new record
  175. //-----------------------------------------------------------------------------
  176. template<class Data, class I>
  177. Data *CTHash<Data,I>::PvRecordInsert( I unKey )
  178. {
  179. Assert( PvRecordFind( unKey ) == NULL ); // keys are unique; no record with this key may exist
  180. // Find a free record
  181. RecHdr_t *pRecHdr = ( RecHdr_t * ) m_pMemoryPoolRecord->Alloc();
  182. return PvRecordInsertInternal( pRecHdr, unKey );
  183. }
  184. //-----------------------------------------------------------------------------
  185. // Purpose: Inserts a new record into the table and sets its key to the pointer
  186. // value of the record
  187. // Output: Pointer to the new record
  188. //-----------------------------------------------------------------------------
  189. template<class Data, class I>
  190. Data *CTHash<Data,I>::PvRecordInsertAutoKey()
  191. {
  192. // Find a free record
  193. RecHdr_t *pRecHdr = ( RecHdr_t * ) m_pMemoryPoolRecord->Alloc();
  194. return PvRecordInsertInternal( pRecHdr, (I) PvRecordFromPRecHdr( pRecHdr ) );
  195. }
  196. //-----------------------------------------------------------------------------
  197. // Purpose: Inserts an allocated record into the hash table with specified key
  198. // and calls the constructor of the allocated object
  199. // Input: pRecHdr - record to insert
  200. // unKey - hash key to use for record
  201. // Output: Pointer to the new record
  202. //-----------------------------------------------------------------------------
  203. template<class Data, class I>
  204. Data *CTHash<Data,I>::PvRecordInsertInternal( RecHdr_t *pRecHdr, I unKey )
  205. {
  206. InsertIntoHash( pRecHdr, unKey );
  207. // assert that we don't have too many items per bucket
  208. static bool s_bPerfWarning = false;
  209. if ( !s_bPerfWarning && Count() >= ( 5 * m_cBucket ) )
  210. {
  211. s_bPerfWarning = true;
  212. AssertMsg( false, "Performance warning: too many items, not enough buckets" );
  213. Msg( "not enough buckets in thash class %s (%d records, %d buckets)\n",
  214. #ifdef _WIN32
  215. typeid(*this).raw_name(),
  216. #else
  217. typeid(*this).name(),
  218. #endif
  219. Count(), m_cBucket );
  220. }
  221. // Construct ourselves
  222. Data *pData = PvRecordFromPRecHdr( pRecHdr );
  223. Construct<Data>( pData );
  224. return pData;
  225. }
  226. //-----------------------------------------------------------------------------
  227. // Purpose: Changes key on previously inserted item
  228. // Input: pvRecord - record to change key for
  229. // unOldKey - old key (not strictly needed, but helpful consistency check)
  230. // unNewKey - new key to use
  231. //-----------------------------------------------------------------------------
  232. template<class Data, class I>
  233. void CTHash<Data,I>::ChangeKey( Data * pvRecord, I unOldKey, I unNewKey )
  234. {
  235. Data * pvRecordFound = PvRecordFind( unOldKey );
  236. Assert( pvRecordFound == pvRecord );
  237. if ( pvRecordFound == pvRecord )
  238. {
  239. RemoveFromHash( pvRecord );
  240. InsertIntoHash( PRecHdrFromPvRecord( pvRecord), unNewKey );
  241. }
  242. }
  243. //-----------------------------------------------------------------------------
  244. // Purpose: Removes the entry with a specified key from the table
  245. // Input: unKey - Key of the entry to remove
  246. //-----------------------------------------------------------------------------
  247. template<class Data, class I>
  248. void CTHash<Data,I>::Remove( I unKey )
  249. {
  250. Data *pvRemove = ( Data * ) PvRecordFind( unKey );
  251. Assert( pvRemove );
  252. if ( !pvRemove )
  253. return;
  254. Remove( pvRemove );
  255. }
  256. //-----------------------------------------------------------------------------
  257. // Purpose: Removes the specified entry from the table
  258. // Input: pvRemove - Pointer to the entry to remove
  259. //-----------------------------------------------------------------------------
  260. template<class Data, class I>
  261. void CTHash<Data,I>::Remove( Data * pvRemove )
  262. {
  263. // Destruct the record we're removing
  264. Destruct<Data>( pvRemove );
  265. RemoveFromHash( pvRemove );
  266. m_pMemoryPoolRecord->Free( PRecHdrFromPvRecord( pvRemove ) );
  267. }
  268. //-----------------------------------------------------------------------------
  269. // Purpose: Removes all entries from the table
  270. //-----------------------------------------------------------------------------
  271. template<class Data, class I>
  272. void CTHash<Data,I>::RemoveAll()
  273. {
  274. Data * pvRecord = PvRecordFirst();
  275. while ( pvRecord )
  276. {
  277. Data *pvRecordNext = PvRecordNext( pvRecord );
  278. Remove( pvRecord );
  279. pvRecord = pvRecordNext;
  280. }
  281. }
  282. //-----------------------------------------------------------------------------
  283. // Purpose: Finds the entry with a specified key
  284. // Input: unKey - Key to find
  285. //-----------------------------------------------------------------------------
  286. template<class Data, class I>
  287. Data *CTHash<Data,I>::PvRecordFind( I unKey ) const
  288. {
  289. // Find our hash bucket
  290. int iBucket = IBucket( unKey );
  291. // Walk the bucket's list looking for an exact match
  292. for ( RecHdr_t *pRecHdr = m_pBucket[iBucket].m_pRecHdrFirst;
  293. NULL != pRecHdr && pRecHdr->m_iBucket == iBucket;
  294. pRecHdr = pRecHdr->m_pRecHdrNext )
  295. {
  296. if ( unKey == pRecHdr->m_unKey )
  297. return PvRecordFromPRecHdr( pRecHdr );
  298. }
  299. // Didn't find a match
  300. return NULL;
  301. }
  302. //-----------------------------------------------------------------------------
  303. // Purpose: Finds our first record
  304. // Output: Pointer to our first record
  305. //-----------------------------------------------------------------------------
  306. template<class Data, class I>
  307. Data *CTHash<Data,I>::PvRecordFirst() const
  308. {
  309. if ( &m_RecHdrTail != m_RecHdrHead.m_pRecHdrNext )
  310. return PvRecordFromPRecHdr( m_RecHdrHead.m_pRecHdrNext );
  311. else
  312. return NULL;
  313. }
  314. //-----------------------------------------------------------------------------
  315. // Purpose: Iterates to the record after a given record
  316. // Input: Pointer to a current record
  317. // Output: Pointer to the next record
  318. //-----------------------------------------------------------------------------
  319. template<class Data, class I>
  320. Data *CTHash<Data,I>::PvRecordNext( Data *pvRecordCur ) const
  321. {
  322. RecHdr_t *pRecHdr = PRecHdrFromPvRecord( pvRecordCur );
  323. if ( &m_RecHdrTail == pRecHdr->m_pRecHdrNext )
  324. return NULL;
  325. return PvRecordFromPRecHdr( pRecHdr->m_pRecHdrNext );
  326. }
  327. //-----------------------------------------------------------------------------
  328. // Purpose: Sets the run ratio of a particular record in the hash table.
  329. // The record will be run 1 cycle out of every nRunRatio cycles.
  330. // Input: pvRecord - The record we're setting
  331. // nRunRatio - The run ratio for this record
  332. //-----------------------------------------------------------------------------
  333. template<class Data, class I>
  334. void CTHash<Data,I>::SetRunRatio( Data *pvRecord, int nRunRatio )
  335. {
  336. PRecHdrFromPvRecord( pvRecord )->m_nRunRatio = nRunRatio;
  337. }
  338. //-----------------------------------------------------------------------------
  339. // Purpose: Prepares us to run all records that are due to be run this frame.
  340. // Records are run at a particular time dependent on their hash bucket,
  341. // regardless of when they were last run.
  342. // Input: bNewFrame - True if this is a new frame. If false, we've run
  343. // off the end of the list and are checking whether
  344. // we need to keep going at the beginning.
  345. //-----------------------------------------------------------------------------
  346. template<class Data, class I>
  347. void CTHash<Data,I>::StartFrameSchedule( bool bNewFrame )
  348. {
  349. // Calculate our current frame and cycle cycle
  350. if ( bNewFrame )
  351. {
  352. m_iCycleLast = m_iCycleCur;
  353. m_iFrameCur++;
  354. m_iCycleCur = m_iFrameCur / m_cFramesPerCycle;
  355. }
  356. // Calculate the last bucket to run
  357. int iFrameInCycle = m_iFrameCur % m_cFramesPerCycle;
  358. m_iBucketRunMax = ( int ) ( ( ( int64 ) ( iFrameInCycle + 1 ) * ( int64 ) m_cBucket )
  359. / ( int64 ) m_cFramesPerCycle );
  360. AssertFatal( m_iBucketRunMax >= 0 && m_iBucketRunMax <= m_cBucket );
  361. // Are we starting a new cycle?
  362. if ( m_iCycleCur > m_iCycleLast )
  363. {
  364. #ifdef DBGFLAG_THASH
  365. Assert( m_iCycleCur == m_iCycleLast + 1 );
  366. #endif
  367. // Did we finish the last cycle?
  368. if ( &m_RecHdrTail == m_pRecHdrRunNext )
  369. {
  370. m_pRecHdrRunNext = m_RecHdrHead.m_pRecHdrNext;
  371. }
  372. // No-- finish it up before moving on
  373. else
  374. {
  375. m_iBucketRunMax = m_cBucket + 1;
  376. }
  377. }
  378. }
  379. //-----------------------------------------------------------------------------
  380. // Purpose: Returns the next record to run, if any
  381. // Output: Pointer to the next record that needs to run (NULL if we're done)
  382. //-----------------------------------------------------------------------------
  383. template<class Data, class I>
  384. Data *CTHash<Data,I>::PvRecordRun()
  385. {
  386. // Loop until we find a record to run, or until we pass m_iBucketRunMax
  387. for ( ; ; )
  388. {
  389. // Are we past our stopping point?
  390. if ( m_pRecHdrRunNext->m_iBucket >= m_iBucketRunMax )
  391. {
  392. // If this cycle ran to the very end, see if we need to start over
  393. if ( m_iBucketRunMax > m_cBucket )
  394. {
  395. StartFrameSchedule( false );
  396. continue;
  397. }
  398. return NULL;
  399. }
  400. #ifdef DBGFLAG_THASH
  401. Assert( m_pRecHdrRunNext->m_iBucket >= m_iBucketRunFirst );
  402. if ( 0 != m_pRecHdrRunNext->m_iCycleLast )
  403. {
  404. if ( m_pRecHdrRunNext->m_iCycleLast == m_iCycleCur )
  405. {
  406. DMsg( SPEW_CONSOLE, 1, "Double cycle: hdr = 0x%x, last frame = %d, curFrame = %d, first = %d, last = %d, bucket = %d\n",
  407. m_pRecHdrRunNext, m_pRecHdrRunNext->m_iFrameLast, m_iFrame,
  408. m_iBucketRunFirst, m_iBucketRunMax, m_pRecHdrRunNext->m_iBucket );
  409. }
  410. else if ( m_pRecHdrRunNext->m_iCycleLast != m_iCycleCur - 1 )
  411. {
  412. DMsg( SPEW_CONSOLE, 1, "Skipped cycle: hdr = 0x%x, cycleLast = %u, cycleCur = %u (missed %u cycles)\n",
  413. m_pRecHdrRunNext, m_pRecHdrRunNext->m_iCycleLast, m_iCycleCur,
  414. m_iCycleCur - m_pRecHdrRunNext->m_iCycleLast );
  415. Assert( false );
  416. }
  417. }
  418. m_pRecHdrRunNext->m_iCycleLast = m_iCycleCur;
  419. m_pRecHdrRunNext->m_iFrameLast = m_iFrame;
  420. #endif
  421. // Set up the record to run next time
  422. RecHdr_t *pRecHdrCur = m_pRecHdrRunNext;
  423. m_pRecHdrRunNext = m_pRecHdrRunNext->m_pRecHdrNext;
  424. // Does this record need to run?
  425. if ( 0 == pRecHdrCur->m_nRunRatio )
  426. continue;
  427. if ( 0 == m_iCycleCur % pRecHdrCur->m_nRunRatio )
  428. return PvRecordFromPRecHdr( pRecHdrCur );
  429. }
  430. }
  431. //-----------------------------------------------------------------------------
  432. // Purpose: Return true if we've completed a scheduler pass since last called
  433. //-----------------------------------------------------------------------------
  434. template<class Data, class I>
  435. bool CTHash<Data,I>::BCompletedPass()
  436. {
  437. if ( m_iCycleCur != m_iCycleLastReported )
  438. {
  439. m_iCycleLastReported = m_iCycleCur;
  440. return true;
  441. }
  442. return false;
  443. }
  444. extern const unsigned char g_CTHashRandomValues[256]; // definition lives in globals.cpp
  445. //-----------------------------------------------------------------------------
  446. // Purpose: Returns the index of the hash bucket corresponding to a particular key
  447. // Input: unKey - Key to find
  448. // Output: Index of the hash bucket corresponding to unKey
  449. //-----------------------------------------------------------------------------
  450. template<class Data, class I>
  451. int CTHash<Data,I>::IBucket( I unKey ) const
  452. {
  453. AssertFatal( m_cBucket > 0 );
  454. // This is a pearsons hash variant that returns a maximum of 32 bits
  455. size_t size = sizeof(I);
  456. const uint8 * k = (const uint8 *) &unKey;
  457. uint32 byte_one = 0, byte_two = 0, byte_three = 0, byte_four = 0, n;
  458. while (size)
  459. {
  460. --size;
  461. n = *k++;
  462. byte_one = g_CTHashRandomValues[byte_one ^ n];
  463. if (size)
  464. {
  465. --size;
  466. n = *k++;
  467. byte_two = g_CTHashRandomValues[byte_two ^ n];
  468. }
  469. else
  470. break;
  471. if (size)
  472. {
  473. --size;
  474. n = *k++;
  475. byte_three = g_CTHashRandomValues[byte_three ^ n];
  476. }
  477. else
  478. break;
  479. if (size)
  480. {
  481. --size;
  482. n = *k++;
  483. byte_four = g_CTHashRandomValues[byte_four ^ n];
  484. }
  485. else
  486. break;
  487. }
  488. uint32 idx = ( byte_four << 24 ) | ( byte_three << 16 ) | ( byte_two << 8 ) | byte_one;
  489. idx = idx % m_cBucket;
  490. return ( (int) idx );
  491. }
  492. #ifdef DBGFLAG_VALIDATE
  493. //-----------------------------------------------------------------------------
  494. // Purpose: Run a global validation pass on all of our data structures and memory
  495. // allocations.
  496. // Input: validator - Our global validator object
  497. // pchName - Our name (typically a member var in our container)
  498. //-----------------------------------------------------------------------------
  499. template<class Data, class I>
  500. void CTHash<Data,I>::Validate( CValidator &validator, const char *pchName )
  501. {
  502. VALIDATE_SCOPE();
  503. validator.ClaimMemory( m_pBucket );
  504. ValidatePtr( m_pMemoryPoolRecord );
  505. #if defined( _DEBUG )
  506. // first verify m_cRecordInUse
  507. Data * pvRecord = PvRecordFirst();
  508. int cItems = 0;
  509. while ( pvRecord )
  510. {
  511. Data *pvRecordNext = PvRecordNext( pvRecord );
  512. cItems++;
  513. pvRecord = pvRecordNext;
  514. }
  515. Assert( m_cRecordInUse == cItems );
  516. // then ask the mempool to verify this
  517. if ( m_pMemoryPoolRecord )
  518. m_pMemoryPoolRecord->LeakCheck( cItems );
  519. #endif // _DEBUG
  520. }
  521. #endif // DBGFLAG_VALIDATE
  522. //-----------------------------------------------------------------------------
  523. // Purpose: Inserts a new record into the table
  524. // Input: unKey - Primary key of the new record
  525. // Output: Pointer to the new record
  526. //-----------------------------------------------------------------------------
  527. template<class Data, class I>
  528. void CTHash<Data,I>::InsertIntoHash( RecHdr_t *pRecHdr, I unKey )
  529. {
  530. m_cRecordInUse++;
  531. // Init the RecHdr
  532. pRecHdr->m_unKey = unKey;
  533. pRecHdr->m_nRunRatio = 1;
  534. // Find our hash bucket
  535. int iBucket = IBucket( unKey );
  536. pRecHdr->m_iBucket = iBucket;
  537. #ifdef DBGFLAG_THASH
  538. pRecHdr->m_iCycleLast = 0;
  539. #endif
  540. // Find where to insert ourselves in the linked list
  541. RecHdr_t *pRecHdrInsertBefore = &m_RecHdrTail;
  542. // Find the first bucket with anything in it that's at or after our bucket
  543. for ( int iBucketT = iBucket; iBucketT < m_cBucket; iBucketT++ )
  544. {
  545. if ( NULL != m_pBucket[iBucketT].m_pRecHdrFirst )
  546. {
  547. pRecHdrInsertBefore = m_pBucket[iBucketT].m_pRecHdrFirst;
  548. break;
  549. }
  550. }
  551. // Insert ourselves
  552. pRecHdr->m_pRecHdrNext = pRecHdrInsertBefore;
  553. pRecHdr->m_pRecHdrPrev = pRecHdrInsertBefore->m_pRecHdrPrev;
  554. pRecHdrInsertBefore->m_pRecHdrPrev = pRecHdr;
  555. pRecHdr->m_pRecHdrPrev->m_pRecHdrNext = pRecHdr;
  556. // Our bucket should point to us
  557. m_pBucket[iBucket].m_pRecHdrFirst = pRecHdr;
  558. }
  559. //-----------------------------------------------------------------------------
  560. // Purpose: Removes the specified entry from the table
  561. // Input: pvRemove - Pointer to the entry to remove
  562. //-----------------------------------------------------------------------------
  563. template<class Data, class I>
  564. void CTHash<Data,I>::RemoveFromHash( Data * pvRemove )
  565. {
  566. // Find our RecHdr
  567. RecHdr_t *pRecHdr = PRecHdrFromPvRecord( pvRemove );
  568. // If our bucket points to us, point it to the next record (or else NULL)
  569. int iBucket = IBucket( pRecHdr->m_unKey );
  570. if ( pRecHdr == m_pBucket[iBucket].m_pRecHdrFirst )
  571. {
  572. if ( pRecHdr->m_pRecHdrNext->m_iBucket == iBucket )
  573. m_pBucket[iBucket].m_pRecHdrFirst = pRecHdr->m_pRecHdrNext;
  574. else
  575. m_pBucket[iBucket].m_pRecHdrFirst = NULL;
  576. }
  577. // Remove us from the linked list
  578. pRecHdr->m_pRecHdrPrev->m_pRecHdrNext = pRecHdr->m_pRecHdrNext;
  579. pRecHdr->m_pRecHdrNext->m_pRecHdrPrev = pRecHdr->m_pRecHdrPrev;
  580. // Are we the next record to run?
  581. if ( pRecHdr == m_pRecHdrRunNext )
  582. m_pRecHdrRunNext = pRecHdr->m_pRecHdrNext;
  583. m_cRecordInUse--;
  584. }
  585. #endif // THASH_H