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.

507 lines
14 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. bshash.h
  5. Abstract:
  6. Template for a hash table class.
  7. Author:
  8. Stefan R. Steiner [SSteiner] 1-Mar-1998
  9. Revision History:
  10. 3/9/2000 SSteiner Converted it for use with fsdump
  11. 10/27/1999 aoltean Took it from bscommon and remove the critical section.
  12. --*/
  13. #ifndef _H_BS_HASH_
  14. #define _H_BS_HASH_
  15. #define BSHASHMAP_NO_ERROR 0
  16. #define BSHASHMAP_ALREADY_EXISTS 1
  17. #define BSHASHMAP_OUT_OF_MEMORY 2
  18. //
  19. // Forward defines
  20. //
  21. template< class KeyType, class ValueType > class TBsHashMapBucket;
  22. template< class KeyType, class ValueType > class TBsHashMapBucketElem;
  23. //
  24. // The equality test
  25. //
  26. inline BOOL AreKeysEqual( const PSID& lhK, const PSID& rhK )
  27. {
  28. return ( ::EqualSid( lhK, rhK ) );
  29. }
  30. inline BOOL AreKeysEqual( const LPCWSTR& lhK, const LPCWSTR& rhK )
  31. {
  32. return (::wcscmp(lhK, rhK) == 0);
  33. }
  34. template < class KeyType >
  35. inline BOOL AreKeysEqual( const KeyType& lhK, const KeyType& rhK )
  36. {
  37. return ( ::memcmp( &lhK, &rhK, sizeof KeyType ) == 0 );
  38. // return lhK == rhK;
  39. }
  40. //
  41. // Some possible hash table sizes
  42. //
  43. #define BSHASHMAP_HUGE 65521
  44. #define BSHASHMAP_LARGE 4091
  45. #define BSHASHMAP_MEDIUM 211
  46. #define BSHASHMAP_SMALL 23
  47. //
  48. // template< class KeyType, class ValueType > class bshashmap
  49. //
  50. // TBsHashMap template. Uses a hash table to maintain a mapping of KeyType
  51. // keys to ValueType values.
  52. //
  53. //template < class KeyType, class ValueType > typedef TBsHashMapBucketElem< KeyType, ValueType > ElemType;
  54. /*
  55. Hash table class. methods hash the key value to the correct bucket, the bucket
  56. class methods then operate on the element list associated with the bucket.
  57. */
  58. template < class KeyType, class ValueType >
  59. class TBsHashMap
  60. {
  61. public:
  62. typedef LONG ( *PFN_HASH_FUNC )( const KeyType& Key, LONG NumBuckets );
  63. typedef TBsHashMapBucket< KeyType, ValueType > BucketType;
  64. typedef TBsHashMapBucketElem< KeyType, ValueType > ElemType;
  65. TBsHashMap( LONG NumBuckets = BSHASHMAP_SMALL, PFN_HASH_FUNC pfHashFunc = DefaultHashFunc )
  66. : m_pfHashFunc( pfHashFunc ),
  67. m_cNumBuckets( NumBuckets ),
  68. m_cNumElems( 0 )
  69. {
  70. m_pHashTab = new BucketType [ m_cNumBuckets ];
  71. if ( m_pHashTab == NULL ) {
  72. m_cNumBuckets = 0;
  73. throw E_OUTOFMEMORY; // fix future prefix bug
  74. }
  75. m_pElemEnum = NULL;
  76. m_bInEnum = FALSE;
  77. }
  78. virtual ~TBsHashMap()
  79. {
  80. Unlock(); // unlock the CS from either StartEnum() or TryEnterCriticalSection()
  81. //
  82. // First go through the double-linked list and delete all of the elements
  83. //
  84. for ( ElemType *pElem = m_ElemChainHead.m_pForward, *pNextElem = pElem->m_pForward;
  85. pElem != &m_ElemChainHead;
  86. pElem = pNextElem, pNextElem = pNextElem->m_pForward )
  87. delete pElem;
  88. delete [] m_pHashTab;
  89. }
  90. // Clear all entries
  91. void Clear()
  92. {
  93. if ( m_cNumElems == 0 )
  94. return; // no work to do
  95. Lock();
  96. for ( ElemType *pElem = m_ElemChainHead.m_pForward, *pNextElem = pElem->m_pForward;
  97. pElem != &m_ElemChainHead;
  98. pElem = pNextElem, pNextElem = pNextElem->m_pForward )
  99. delete pElem;
  100. delete [] m_pHashTab;
  101. m_pHashTab = new BucketType [ m_cNumBuckets ];
  102. if ( m_pHashTab == NULL ) {
  103. m_cNumBuckets = 0;
  104. throw E_OUTOFMEMORY; // fix future prefix bug
  105. }
  106. m_pElemEnum = NULL;
  107. m_cNumElems = 0;
  108. Unlock();
  109. }
  110. // returns:
  111. // BSHASHMAP_NO_ERROR - successful completion
  112. // BSHASHMAP_OUT_OF_MEMORY - out of memory
  113. // BSHASHMAP_ALREADY_EXISTS - Key already exists in map. Old value is
  114. // replaced by passed in Value.
  115. LONG Insert(
  116. IN const KeyType& Key,
  117. IN const ValueType& Value,
  118. OUT void **ppCookie = NULL
  119. )
  120. {
  121. Lock();
  122. LONG status;
  123. LONG hashVal = (*m_pfHashFunc)( Key, m_cNumBuckets );
  124. assert( hashVal % m_cNumBuckets == hashVal );
  125. status = m_pHashTab[ hashVal ].Insert( Key, Value, &m_ElemChainHead );
  126. if ( status == BSHASHMAP_NO_ERROR ) {
  127. ++m_cNumElems;
  128. if ( ppCookie != NULL )
  129. *ppCookie = ( void * )m_ElemChainHead.m_pBackward;
  130. }
  131. Unlock();
  132. return status;
  133. }
  134. // Erase an entry. Returns TRUE if it succeeds.
  135. BOOL Erase( const KeyType& Key )
  136. {
  137. Lock();
  138. BOOL erased = FALSE;
  139. LONG hashVal = (*m_pfHashFunc)( Key, m_cNumBuckets );
  140. assert( hashVal % m_cNumBuckets == hashVal );
  141. erased = m_pHashTab[ hashVal ].Erase( Key, &m_ElemChainHead );
  142. if ( erased ) {
  143. --m_cNumElems;
  144. }
  145. Unlock();
  146. return erased;
  147. }
  148. // Erase by cookie
  149. BOOL EraseByCookie( void *pCookie )
  150. {
  151. Lock();
  152. BucketType::EraseElement( ( ElemType *)pCookie );
  153. --m_cNumElems;
  154. Unlock();
  155. return TRUE;
  156. }
  157. // Find an entry. Returns TRUE if it succeeds. pValue may be NULL, in
  158. // which case this method is just a test of existence
  159. BOOL Find( const KeyType& Key, ValueType *pValue = NULL )
  160. {
  161. Lock();
  162. ElemType *pElem;
  163. LONG hashVal = (*m_pfHashFunc)( Key, m_cNumBuckets );
  164. BOOL found = FALSE;
  165. assert( hashVal % m_cNumBuckets == hashVal );
  166. found = m_pHashTab[ hashVal ].Find( Key, &pElem );
  167. if ( found && pValue != NULL ) {
  168. *pValue = pElem->m_Value;
  169. }
  170. Unlock();
  171. return found;
  172. }
  173. // Find an entry and return a pointer to the value to allow inplace update. The
  174. // caller must call Unlock() when finished with the Value item. If the item is
  175. // not found, this method returns FALSE and hash table is not locked.
  176. BOOL FindForUpdate( const KeyType& Key, ValueType **ppValue )
  177. {
  178. Lock();
  179. ElemType *pElem;
  180. LONG hashVal = (*m_pfHashFunc)( Key, m_cNumBuckets );
  181. BOOL found = FALSE;
  182. assert( hashVal % m_cNumBuckets == hashVal );
  183. found = m_pHashTab[ hashVal ].Find( Key, &pElem );
  184. if ( found ) {
  185. *ppValue = &(pElem->m_Value);
  186. } else
  187. Unlock(); // Item not found so unlock the table
  188. return found;
  189. }
  190. // Default hash function
  191. static LONG DefaultHashFunc( const KeyType &Key, LONG NumBuckets )
  192. {
  193. const BYTE *pByteKey = (BYTE *)&Key;
  194. LONG dwHashVal = 0;
  195. for ( LONG i = 0; i < sizeof KeyType; ++i ) {
  196. dwHashVal += pByteKey[i];
  197. }
  198. // wprintf( L"Key: dwSerialNum: %u, hashed to: %u\n", Key.m_dwVolSerialNumber, dwHashVal % NumBuckets );
  199. // cout << "Key: " << Key << " hashed to: " << dwHashVal % NumBuckets << endl;
  200. return dwHashVal % NumBuckets;
  201. }
  202. // Start enumerating all entries in the hash table. Always returns TRUE.
  203. // Sets the index to the first element in the list. Lock() calls
  204. // EnteringCriticalSection.
  205. BOOL StartEnum()
  206. {
  207. assert( m_bInEnum == FALSE );
  208. Lock(); // Enumerating the table locks out all other threads
  209. m_pElemEnum = m_ElemChainHead.m_pForward; // Start at the head of the double-linked list
  210. m_bInEnum = TRUE;
  211. return TRUE;
  212. }
  213. // Returns the value of the current entry, and then moves the index
  214. // to the next item in the list. Must call StartEnum() first.
  215. BOOL GetNextEnum( KeyType *pKey, ValueType *pValue )
  216. {
  217. assert( m_bInEnum == TRUE );
  218. if ( m_pElemEnum == &m_ElemChainHead )
  219. return FALSE; // Finished enumerating
  220. *pKey = m_pElemEnum->m_Key;
  221. *pValue = m_pElemEnum->m_Value;
  222. m_pElemEnum = m_pElemEnum->m_pForward;
  223. return TRUE;
  224. }
  225. // End enumerating the table. This function must be called when finished,
  226. // otherwise other threads will not be able to get past the critical section,
  227. // because the Unlock() call below, calls LeavingCriticalSection().
  228. BOOL EndEnum()
  229. {
  230. assert( m_bInEnum == TRUE );
  231. m_pElemEnum = NULL;
  232. m_bInEnum = FALSE;
  233. Unlock();
  234. return TRUE;
  235. }
  236. LONG Size()
  237. {
  238. return m_cNumElems;
  239. }
  240. LONG NumBuckets()
  241. {
  242. return m_cNumBuckets;
  243. }
  244. inline void Lock()
  245. {
  246. }
  247. inline void Unlock()
  248. {
  249. }
  250. private:
  251. BucketType *m_pHashTab;
  252. LONG m_cNumBuckets;
  253. LONG m_cNumElems;
  254. ElemType m_ElemChainHead; // head of double-linked list of all elements
  255. ElemType *m_pElemEnum; // Current position of the enumeration
  256. BOOL m_bInEnum; // true StartEnum() was called and EndEnum() hasn't
  257. PFN_HASH_FUNC m_pfHashFunc;
  258. };
  259. /*
  260. Hash bucket class. Methods operate on the element list associated with the hash bucket
  261. */
  262. template < class KeyType, class ValueType >
  263. class TBsHashMapBucket {
  264. friend class TBsHashMap< KeyType, ValueType >;
  265. private:
  266. typedef TBsHashMapBucketElem< KeyType, ValueType > ElemType;
  267. TBsHashMapBucket( )
  268. {
  269. m_pHead = NULL; // done here to allow for easier debugging
  270. }
  271. virtual ~TBsHashMapBucket( )
  272. {
  273. ;
  274. } // -- not really needed; however, if commented out, memory exception occurs during destruction
  275. /*
  276. Adds an element to the hash table. If the Key for the new element already
  277. exists, in the table, set the key's vvalue to this new value, in the table.
  278. */
  279. LONG Insert( const KeyType &Key, const ValueType &Val, ElemType *pElemChainHead )
  280. {
  281. ElemType *pElem;
  282. //
  283. // if the element exists in this hash bucket's element list, set the new value
  284. //
  285. if ( Find( Key, &pElem ) == TRUE ) {
  286. pElem->m_Value = Val;
  287. return BSHASHMAP_ALREADY_EXISTS;
  288. }
  289. //
  290. // if the element doesn't exist, create a new element
  291. //
  292. ElemType *pVal = new ElemType( Key, Val );
  293. if ( pVal == NULL ) {
  294. return BSHASHMAP_OUT_OF_MEMORY;
  295. }
  296. //
  297. // Add the element into the hash bucket list
  298. //
  299. if ( m_pHead != NULL )
  300. m_pHead->m_ppPrevious = &(pVal->m_pNext);
  301. pVal->m_pNext = m_pHead;
  302. m_pHead = pVal;
  303. pVal->m_ppPrevious = &m_pHead;
  304. //
  305. // Set the back pointer - double-linked list of elements
  306. //
  307. pVal->m_pBackward = pElemChainHead->m_pBackward;
  308. pVal->m_pForward = pElemChainHead;
  309. pVal->m_pBackward->m_pForward = pVal;
  310. pElemChainHead->m_pBackward = pVal;
  311. return BSHASHMAP_NO_ERROR;
  312. }
  313. /*
  314. Deletes an element from this hash bucket's list, within the hash table.
  315. */
  316. BOOL Erase( const KeyType &Key, ElemType *pElemChainHead )
  317. {
  318. //
  319. // Walk the list of elements for this hash bucket
  320. //
  321. for ( ElemType *pElem = m_pHead; pElem != NULL; pElem = pElem->m_pNext ) {
  322. //
  323. // if the key is found, delete it from the hash bucket's list.
  324. //
  325. if ( AreKeysEqual( pElem->m_Key, Key ) ) {
  326. EraseElement( pElem );
  327. return TRUE;
  328. }
  329. }
  330. return FALSE;
  331. }
  332. /*
  333. Erases one element from the two chains
  334. */
  335. inline static void EraseElement( ElemType *pElem )
  336. {
  337. assert( pElem->IsValid() );
  338. // remove it from the hash chain
  339. if ( pElem->m_pNext != NULL )
  340. pElem->m_pNext->m_ppPrevious = pElem->m_ppPrevious;
  341. *( pElem->m_ppPrevious ) = pElem->m_pNext;
  342. // remove it from the double-linked list of elements
  343. pElem->m_pBackward->m_pForward = pElem->m_pForward;
  344. pElem->m_pForward->m_pBackward = pElem->m_pBackward;
  345. delete pElem;
  346. }
  347. /*
  348. Looks for an element in the list associated with this hash bucket.
  349. */
  350. BOOL Find( const KeyType &Key, ElemType **ppElemFound )
  351. {
  352. //
  353. // Walk the list for this bucket, looking for the key.
  354. //
  355. for ( ElemType *pElem = m_pHead; pElem != NULL; pElem = pElem->m_pNext ) {
  356. if ( AreKeysEqual( pElem->m_Key, Key ) ) {
  357. *ppElemFound = pElem;
  358. return TRUE;
  359. }
  360. }
  361. *ppElemFound = NULL;
  362. return FALSE;
  363. }
  364. private:
  365. ElemType *m_pHead;
  366. };
  367. //
  368. // template< class KeyType, class ValueType > class TBsHashMapBucketElem
  369. //
  370. // Template for individual elements in a bucket of a CMap
  371. //
  372. #define BS_HASH_ELEM_SIGNATURE "ELEMTYPE"
  373. #define BS_HASH_ELEM_SIGNATURE_LEN 8
  374. template< class KeyType, class ValueType >
  375. class TBsHashMapBucketElem {
  376. friend class TBsHashMapBucket< KeyType, ValueType >;
  377. friend class TBsHashMap< KeyType, ValueType >;
  378. private:
  379. typedef TBsHashMapBucketElem< KeyType, ValueType > ElemType;
  380. TBsHashMapBucketElem() : m_ppPrevious( NULL ),
  381. m_pNext( NULL )
  382. {
  383. m_pForward = this;
  384. m_pBackward = this;
  385. }
  386. TBsHashMapBucketElem( const KeyType K, const ValueType V ) : m_Key( K ), m_Value( V )
  387. {
  388. #ifdef _DEBUG
  389. memcpy( m_sSignature, BS_HASH_ELEM_SIGNATURE, sizeof( m_sSignature ) / sizeof( char ) );
  390. #endif
  391. }
  392. BOOL
  393. IsValid()
  394. {
  395. assert( this != NULL );
  396. #ifdef _DEBUG
  397. return( memcmp( m_sSignature, BS_HASH_ELEM_SIGNATURE, sizeof( m_sSignature ) / sizeof( char ) ) == 0 );
  398. #else
  399. return TRUE;
  400. #endif
  401. }
  402. virtual ~TBsHashMapBucketElem()
  403. {
  404. #ifdef _DEBUG // make sure reuse of list will cause errors
  405. m_pNext = NULL;
  406. m_pForward = NULL;
  407. m_pBackward = NULL;
  408. memset( m_sSignature, 0xAA, sizeof( m_sSignature ) / sizeof( char ) );
  409. #endif
  410. }
  411. #ifdef _DEBUG
  412. char m_sSignature[BS_HASH_ELEM_SIGNATURE_LEN];
  413. #endif
  414. ElemType **m_ppPrevious; // pointer to previous reference
  415. ElemType *m_pNext; // pointer to next element in bucket
  416. ElemType *m_pForward; // forward pointer to next element in double-link list of all elements
  417. ElemType *m_pBackward; // backward pointer to next element in double-link list of all elements
  418. KeyType m_Key;
  419. ValueType m_Value;
  420. };
  421. #endif