Leaked source code of windows server 2003
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.

598 lines
14 KiB

  1. //
  2. // FHash.inl
  3. //
  4. // This file contains the template implementation of the class TFHash.
  5. //
  6. //---------------------------------------------
  7. template< class Data, class Key >
  8. TFHash< Data, Key >::TFHash( ) : m_cBuckets( 0 ), m_cActiveBuckets( 0 ),
  9. m_cNumAlloced( 0 ), m_cIncrement( 0 ), m_ppBucket( 0 ), m_pfnHash( 0 ), m_load( 0 ),
  10. m_pFreeStack( 0 ), m_cFreeStack( 0 ) {
  11. //
  12. // Very basic constructor
  13. //
  14. }
  15. //---------------------------------------------
  16. template< class Data, class Key >
  17. BOOL TFHash< Data, Key >::Init(
  18. int cInitial,
  19. int cIncrement,
  20. DWORD (*pfnHash)(const Key&),
  21. int load,
  22. int cMaxBucketCache
  23. ) {
  24. //
  25. // The initialization function will allocate the initial array of Bucket pointers
  26. // and set the member variables. The user can specify the following :
  27. //
  28. // cInitial - the initial size of the hash table (this is rounded to the nearest power of 2.)
  29. // cIncrement - the amount the hash table should grow on each growth
  30. // pfnHash() - The function which computes the hash values for the key.
  31. // load - the number of elements we should have on average in each collision chain.
  32. //
  33. //
  34. // Compute nearest power of 2
  35. //
  36. m_cMaxFreeStack = cMaxBucketCache ;
  37. int power = cInitial ;
  38. while( power & (power-1) )
  39. power = power & (power-1) ;
  40. power<<= 1 ;
  41. cInitial = power;
  42. m_load = load ;
  43. m_pfnHash = pfnHash ;
  44. //
  45. // Number of ActiveBuckets is initially half that of the number of buckets.
  46. //
  47. m_cActiveBuckets = power/2 ;
  48. m_cBuckets = power ;
  49. m_cInserts = m_cActiveBuckets * m_load ;
  50. m_cIncrement = m_cActiveBuckets / 4;
  51. m_cNumAlloced = cInitial + 5 * m_cIncrement ;
  52. //
  53. // Allocate bucket pointers and zero initialize
  54. //
  55. m_ppBucket = new CBucket*[m_cNumAlloced] ;
  56. if( m_ppBucket ) {
  57. ZeroMemory( m_ppBucket, m_cNumAlloced * sizeof( CBucket*) ) ;
  58. Assert( IsValid( FALSE ) ) ;
  59. return TRUE ;
  60. }
  61. return FALSE ;
  62. }
  63. //------------------------------------------------
  64. template< class Data, class Key >
  65. BOOL TFHash< Data, Key >::IsValid( BOOL fCheckHash ) {
  66. //
  67. // This function checks that all member variables are consistent and correct.
  68. // Do not call this function until AFTER calling the Init() function.
  69. //
  70. if( m_cBuckets <= 0 ||
  71. m_cActiveBuckets <= 0 ||
  72. m_cNumAlloced <= 0 ||
  73. m_cIncrement <= 0 ||
  74. m_load <= 0 )
  75. return FALSE ;
  76. if( m_cActiveBuckets < (m_cBuckets / 2) || m_cActiveBuckets > m_cBuckets )
  77. return FALSE ;
  78. if( m_cActiveBuckets > m_cNumAlloced )
  79. return FALSE ;
  80. if( m_cInserts > (m_load * m_cActiveBuckets) )
  81. return FALSE ;
  82. if( m_ppBucket == 0 )
  83. return FALSE ;
  84. if( fCheckHash ) {
  85. //
  86. // Examine every bucket chain to ensure that elements are in correct slots.
  87. //
  88. for( int i=0; i<m_cNumAlloced; i++ ) {
  89. if( i>=m_cActiveBuckets ) {
  90. if( m_ppBucket[i] != 0 ) {
  91. return FALSE ;
  92. }
  93. } else {
  94. for( CBucket *p = m_ppBucket[i]; p != 0; p = p->m_pNext ) {
  95. if( ComputeIndex( m_pfnHash( p->m_data.GetKey() ) ) != unsigned(i) ) {
  96. return FALSE ;
  97. }
  98. }
  99. }
  100. }
  101. }
  102. return TRUE ;
  103. }
  104. //-------------------------------------------------
  105. template< class Data, class Key >
  106. TFHash< Data, Key >::~TFHash() {
  107. //
  108. // The destructor discards any memory we have allocated.
  109. //
  110. Clear();
  111. }
  112. //-------------------------------------------------
  113. template< class Data, class Key >
  114. void TFHash< Data, Key >::Clear() {
  115. //
  116. // Discards any memory we have allocated - after this, you must
  117. // call Init() again!
  118. //
  119. if( m_ppBucket ) {
  120. Assert( IsValid( TRUE ) ) ;
  121. for( int i=0; i<m_cNumAlloced; i++ ) {
  122. CBucket *p, *pNext ;
  123. for( p = m_ppBucket[i], pNext = p ? p->m_pNext : 0;
  124. p!=0; p=pNext, pNext= p ? p->m_pNext : 0 ) {
  125. delete p ;
  126. DWORDLONG* pdwl = (DWORDLONG*)((void*)p) ;
  127. delete[] pdwl ;
  128. }
  129. }
  130. delete[] m_ppBucket;
  131. }
  132. for( CFreeElement* p = m_pFreeStack;
  133. p != 0;
  134. p = m_pFreeStack ) {
  135. m_pFreeStack = p->m_pNext ;
  136. DWORDLONG* pdwl = (DWORDLONG*)((void*)p) ;
  137. delete[] pdwl ;
  138. m_cFreeStack -- ;
  139. }
  140. _ASSERT( m_cFreeStack == 0 && m_pFreeStack == 0 ) ;
  141. m_cBuckets = 0;
  142. m_cActiveBuckets = 0;
  143. m_cNumAlloced = 0;
  144. m_cIncrement = 0;
  145. m_ppBucket = 0;
  146. m_pfnHash = 0;
  147. m_load = 0;
  148. m_pFreeStack = 0;
  149. m_cFreeStack = 0;
  150. }
  151. //-------------------------------------------------
  152. template< class Data, class Key >
  153. DWORD TFHash<Data, Key>::ComputeIndex( DWORD dw ) {
  154. //
  155. // This function tells us where we should store elements. To do this we mod with
  156. // m_cBuckets. Since we only have m_cActiveBuckets in reality, we check the result
  157. // of the mod and subtract m_cBuckets over 2 if necessary.
  158. //
  159. DWORD dwTemp = dw % m_cBuckets ;
  160. return (dwTemp >= (unsigned)m_cActiveBuckets) ? dwTemp - (m_cBuckets/2) : dwTemp ;
  161. }
  162. #if 0
  163. //-------------------------------------------------
  164. template< class Data, class Key >
  165. BOOL TFHash< Data, Key >::Insert( Data& d ) {
  166. //
  167. // This function will Insert an element into the Hash table. We will
  168. // actually make a copy of the element using the Copy COnstructor Data::Data( Data& )
  169. // when we do so.
  170. //
  171. Assert( IsValid( FALSE ) ) ;
  172. //
  173. // Do we have some free memory in our cache !?
  174. //
  175. CFreeElement* pTemp = m_pFreeStack ;
  176. if( m_pFreeStack != 0 ) {
  177. m_pFreeStack = m_pFreeStack->m_pNext ;
  178. m_cFreeStack -- ;
  179. }
  180. CBucket* pNew = new( pTemp ) CBucket( d ) ;
  181. if( pNew == 0 ) {
  182. return FALSE ;
  183. }
  184. //
  185. // First check whether it is time to grow the hash table.
  186. //
  187. if( InterlockedDecrement( &m_cInserts ) == 0 ) {
  188. //
  189. // Check whether we need to reallocate the array of Bucket pointers.
  190. //
  191. if( m_cIncrement + m_cActiveBuckets > m_cNumAlloced ) {
  192. CBucket** pTemp = new CBucket*[m_cNumAlloced + 10 * m_cIncrement ] ;
  193. if( pTemp == 0 ) {
  194. if( pNew ) { delete pNew; pNew = NULL; }
  195. return FALSE ;
  196. } else {
  197. ZeroMemory( pTemp, (m_cNumAlloced + 10 *m_cIncrement)* sizeof( CBucket*) ) ;
  198. CopyMemory( pTemp, m_ppBucket, m_cNumAlloced * sizeof( CBucket* ) ) ;
  199. delete[] m_ppBucket;
  200. m_cNumAlloced += 10 * m_cIncrement ;
  201. m_ppBucket = pTemp ;
  202. }
  203. }
  204. //
  205. // Okay grow the array by m_cIncrement.
  206. //
  207. m_cActiveBuckets += m_cIncrement ;
  208. if( m_cActiveBuckets > m_cBuckets ) m_cBuckets *= 2 ;
  209. m_cInserts = m_cIncrement * m_load ;
  210. //
  211. // Now do some rehashing of elements.
  212. //
  213. for( int i = -m_cIncrement; i < 0; i++ ) {
  214. int iCurrent = (m_cActiveBuckets + i) - (m_cBuckets / 2) ;
  215. CBucket** ppNext = &m_ppBucket[ iCurrent ] ;
  216. CBucket* p = *ppNext ;
  217. while( p ) {
  218. int index = ComputeIndex( m_pfnHash( p->m_data.GetKey() ) ) ;
  219. CBucket* pNext = p->m_pNext ;
  220. if( index != iCurrent) {
  221. *ppNext = pNext ;
  222. p->m_pNext = m_ppBucket[index] ;
  223. m_ppBucket[index] = p ;
  224. } else {
  225. ppNext = &p->m_pNext ;
  226. }
  227. p = pNext ;
  228. }
  229. }
  230. }
  231. //
  232. // Finally, insert into the Hash Table.
  233. //
  234. DWORD index = ComputeIndex( m_pfnHash( d.GetKey() ) ) ;
  235. Assert( index < unsigned(m_cActiveBuckets) ) ;
  236. #if 0
  237. CBucket* pNew = new CBucket( d ) ;
  238. #endif
  239. Assert( pNew );
  240. pNew->m_pNext = m_ppBucket[index] ;
  241. m_ppBucket[index] = pNew ;
  242. Assert( IsValid( FALSE ) ) ;
  243. return TRUE ;
  244. }
  245. #endif
  246. template< class Data, class Key >
  247. BOOL TFHash< Data, Key >::Insert( Data& d ) {
  248. if( InsertData( d ) )
  249. return TRUE ;
  250. return FALSE ;
  251. }
  252. //-------------------------------------------------
  253. template< class Data, class Key >
  254. Data* TFHash< Data, Key >::InsertDataHash(
  255. DWORD dwHash,
  256. Data& d
  257. ) {
  258. //
  259. // This function will Insert an element into the Hash table. We will
  260. // actually make a copy of the element using the Copy COnstructor Data::Data( Data& )
  261. // when we do so.
  262. //
  263. Assert( IsValid( FALSE ) ) ;
  264. //
  265. // Do we have some free memory in our cache !?
  266. //
  267. CFreeElement* pTemp = m_pFreeStack ;
  268. if( m_pFreeStack != 0 ) {
  269. m_pFreeStack = m_pFreeStack->m_pNext ;
  270. m_cFreeStack -- ;
  271. }
  272. CBucket* pNew = new( pTemp ) CBucket( d ) ;
  273. if( pNew == 0 ) {
  274. return FALSE ;
  275. }
  276. //
  277. // First check whether it is time to grow the hash table.
  278. //
  279. if( InterlockedDecrement( &m_cInserts ) == 0 ) {
  280. //
  281. // Check whether we need to reallocate the array of Bucket pointers.
  282. //
  283. if( m_cIncrement + m_cActiveBuckets > m_cNumAlloced ) {
  284. CBucket** pTemp = new CBucket*[m_cNumAlloced + 10 * m_cIncrement ] ;
  285. if( pTemp == 0 ) {
  286. //
  287. // bugbug ... need to handles this error better !?
  288. //
  289. if( pNew ) {
  290. delete pNew;
  291. pNew = NULL;
  292. }
  293. return FALSE ;
  294. } else {
  295. ZeroMemory( pTemp, (m_cNumAlloced + 10 *m_cIncrement)* sizeof( CBucket*) ) ;
  296. CopyMemory( pTemp, m_ppBucket, m_cNumAlloced * sizeof( CBucket* ) ) ;
  297. delete[] m_ppBucket;
  298. m_cNumAlloced += 10 * m_cIncrement ;
  299. m_ppBucket = pTemp ;
  300. }
  301. }
  302. //
  303. // Okay grow the array by m_cIncrement.
  304. //
  305. m_cActiveBuckets += m_cIncrement ;
  306. if( m_cActiveBuckets > m_cBuckets ) m_cBuckets *= 2 ;
  307. m_cInserts = m_cIncrement * m_load ;
  308. //
  309. // Now do some rehashing of elements.
  310. //
  311. for( int i = -m_cIncrement; i < 0; i++ ) {
  312. int iCurrent = (m_cActiveBuckets + i) - (m_cBuckets / 2) ;
  313. CBucket** ppNext = &m_ppBucket[ iCurrent ] ;
  314. CBucket* p = *ppNext ;
  315. while( p ) {
  316. int index = ComputeIndex( m_pfnHash( p->m_data.GetKey() ) ) ;
  317. CBucket* pNext = p->m_pNext ;
  318. if( index != iCurrent) {
  319. *ppNext = pNext ;
  320. p->m_pNext = m_ppBucket[index] ;
  321. m_ppBucket[index] = p ;
  322. } else {
  323. ppNext = &p->m_pNext ;
  324. }
  325. p = pNext ;
  326. }
  327. }
  328. }
  329. //
  330. // Finally, insert into the Hash Table.
  331. //
  332. //DWORD index = ComputeIndex( m_pfnHash( d.GetKey() ) ) ;
  333. Assert( dwHash == m_pfnHash( d.GetKey() ) ) ;
  334. DWORD index = ComputeIndex( dwHash ) ;
  335. Assert( index < unsigned(m_cActiveBuckets) ) ;
  336. Assert( pNew );
  337. pNew->m_pNext = m_ppBucket[index] ;
  338. m_ppBucket[index] = pNew ;
  339. Assert( IsValid( FALSE ) ) ;
  340. return &pNew->m_data;
  341. }
  342. //-------------------------------------------------
  343. template< class Data, class Key >
  344. inline Data*
  345. TFHash< Data, Key >::InsertData( Data& d ) {
  346. //
  347. // This function will Insert an element into the Hash table. We will
  348. // actually make a copy of the element using the Copy COnstructor Data::Data( Data& )
  349. // when we do so.
  350. //
  351. Assert( IsValid( FALSE ) ) ;
  352. return InsertDataHash( m_pfnHash( d.GetKey() ), d ) ;
  353. }
  354. //-----------------------------------------------
  355. template< class Data, class Key >
  356. BOOL
  357. TFHash< Data, Key >::Search( Key& k, Data &dOut ) {
  358. //
  359. // Search for an element in the hash table.
  360. // We will return TRUE if found, FALSE otherwise.
  361. // If we return false the dOut return parameter is untouched.
  362. //
  363. const Data* pData = SearchKey( k ) ;
  364. if( pData ) {
  365. dOut = *pData ;
  366. return TRUE ;
  367. }
  368. return FALSE ;
  369. }
  370. //-----------------------------------------------
  371. template< class Data, class Key >
  372. Data*
  373. TFHash< Data, Key >::SearchKeyHash(
  374. DWORD dwHash,
  375. Key& k
  376. ) {
  377. //
  378. // Search for an element in the hash table.
  379. // We will return TRUE if found, FALSE otherwise.
  380. // If we return false the dOut return parameter is untouched.
  381. //
  382. Assert( IsValid( FALSE ) ) ;
  383. Assert( dwHash == (m_pfnHash)(k) ) ;
  384. DWORD index = ComputeIndex( dwHash ) ;
  385. CBucket* p = m_ppBucket[index] ;
  386. while( p ) {
  387. if( p->m_data.MatchKey( k ) )
  388. break ;
  389. p = p->m_pNext ;
  390. }
  391. if( p ) {
  392. return &p->m_data ;
  393. }
  394. return 0 ;
  395. }
  396. //-----------------------------------------------
  397. template< class Data, class Key >
  398. inline Data*
  399. TFHash< Data, Key >::SearchKey( Key& k ) {
  400. //
  401. // Search for an element in the hash table.
  402. // We will return TRUE if found, FALSE otherwise.
  403. // If we return false the dOut return parameter is untouched.
  404. //
  405. Assert( IsValid( FALSE ) ) ;
  406. return SearchKeyHash( m_pfnHash( k ), k ) ;
  407. }
  408. //-----------------------------------------------
  409. template< class Data, class Key >
  410. BOOL TFHash< Data, Key >::Delete( Key k ) {
  411. //
  412. // Remove an element from the Hash Table. We only need the
  413. // Key to find the element we wish to remove.
  414. //
  415. return DeleteData( k, 0 ) ;
  416. }
  417. //-----------------------------------------------
  418. template< class Data, class Key >
  419. BOOL TFHash< Data, Key >::DeleteData( Key& k,
  420. Data* pd
  421. ) {
  422. //
  423. // Remove an element from the Hash Table. We only need the
  424. // Key to find the element we wish to remove.
  425. //
  426. Assert( IsValid( FALSE ) ) ;
  427. DWORD dwHash = (m_pfnHash)( k ) ;
  428. DWORD index = ComputeIndex( dwHash ) ;
  429. CBucket** ppNext = &m_ppBucket[index] ;
  430. CBucket* p = *ppNext ;
  431. while( p ) {
  432. if( p->m_data.MatchKey( k ) )
  433. break ;
  434. ppNext = &p->m_pNext ;
  435. p = *ppNext ;
  436. }
  437. if( p ) {
  438. //
  439. // If we were given a pointer to a data block, than the client
  440. // wants us to check to make sure that we are deleting the correct
  441. // instance !!
  442. //
  443. if( !pd || pd == &p->m_data ) {
  444. *ppNext = p->m_pNext ;
  445. //
  446. // Call our do-nothing delete operator - we need to do more
  447. // work to manage the free'd memory !
  448. //
  449. delete p ;
  450. //
  451. // Now in debug versions zap that memory to make sure nobody tries
  452. // to use it !!
  453. //
  454. #ifdef DEBUG
  455. FillMemory( p, sizeof( *p ), 0xCC ) ;
  456. #endif
  457. //
  458. // Okay, put that piece of free memory on a little queue we
  459. // maintain if we haven't saved up too much already !
  460. //
  461. if( m_cFreeStack < m_cMaxFreeStack ) {
  462. CFreeElement* pFree = (CFreeElement*)((void*)p) ;
  463. pFree->m_pNext = m_pFreeStack ;
  464. m_pFreeStack = pFree ;
  465. m_cFreeStack ++ ;
  466. } else {
  467. //
  468. // otherwise - release this memory to the system !
  469. //
  470. DWORDLONG* pdwl = (DWORDLONG*)((void*)p) ;
  471. ::delete[] pdwl ;
  472. }
  473. //
  474. // Finally - since we removed something from the hash table
  475. // increment the number of inserts so that we don't keep splitting
  476. // the table unnecessarily !
  477. //
  478. InterlockedIncrement( &m_cInserts ) ;
  479. Assert( IsValid( FALSE ) ) ;
  480. return TRUE ;
  481. }
  482. }
  483. Assert( IsValid( FALSE ) ) ;
  484. return FALSE ;
  485. }