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.

668 lines
16 KiB

  1. //
  2. // Copyright (C) 2000, Microsoft Corporation
  3. //
  4. // File: shash.c
  5. //
  6. // Contents: Generic hashtable
  7. // Classes:
  8. //
  9. // History: April. 9 2001, Author: Rohanp
  10. //
  11. //-----------------------------------------------------------------------------
  12. #ifdef KERNEL_MODE
  13. #include <ntos.h>
  14. #include <string.h>
  15. #include <fsrtl.h>
  16. #include <zwapi.h>
  17. #include <windef.h>
  18. #else
  19. #include <nt.h>
  20. #include <ntrtl.h>
  21. #include <nturtl.h>
  22. #include <windows.h>
  23. #endif
  24. #include <shash.h>
  25. extern
  26. ULONG
  27. SHashComputeHashValue(
  28. IN void* lpv
  29. );
  30. extern
  31. int
  32. SHashMatchNameKeysCaseInsensitive( void* pvKey1,
  33. void* pvKey2
  34. );
  35. extern
  36. void*
  37. SHashAllocate( ULONG cbAlloc );
  38. extern
  39. void
  40. SHashFree( void* lpv );
  41. extern
  42. BOOLEAN
  43. SHashReadLockTable(PSHASH_TABLE pTable);
  44. extern
  45. BOOLEAN
  46. SHashWriteLockTable(PSHASH_TABLE pTable);
  47. extern
  48. BOOLEAN
  49. SHashReadUnLockTable(PSHASH_TABLE pTable);
  50. extern
  51. BOOLEAN
  52. SHashWriteUnLockTable(PSHASH_TABLE pTable);
  53. void *
  54. SHashAllocLock(void);
  55. void
  56. SHashFreeLock(void * pMem);
  57. DWORD g_ExpireSeconds = SHASH_DEFAULT_HASHTIMEOUT;
  58. DWORD
  59. ShashInitializeFunctionTable(PSHASH_TABLE pHashTable,
  60. PSHASH_FUNCTABLE pFuncTable)
  61. {
  62. pHashTable->HashFunc = SHashComputeHashValue;
  63. pHashTable->CompareFunc = SHashMatchNameKeysCaseInsensitive;
  64. pHashTable->AllocFunc = SHashAllocate;
  65. pHashTable->FreeFunc = SHashFree;
  66. pHashTable->AllocHashEntryFunc = SHashAllocate;
  67. pHashTable->FreeHashEntryFunc = SHashFree;
  68. pHashTable->AllocLockFunc = SHashAllocLock;
  69. pHashTable->FreeLockFunc = SHashFreeLock;
  70. pHashTable->WriteLockFunc = SHashWriteLockTable;
  71. pHashTable->ReadLockFunc = SHashReadLockTable;
  72. pHashTable->ReleaseWriteLockFunc = SHashWriteUnLockTable;
  73. pHashTable->ReleaseReadLockFunc = SHashReadUnLockTable;
  74. if(pFuncTable->HashFunc)
  75. {
  76. pHashTable->HashFunc = pFuncTable->HashFunc;
  77. }
  78. if(pFuncTable->CompareFunc)
  79. {
  80. pHashTable->CompareFunc = pFuncTable->CompareFunc;
  81. }
  82. if ((pFuncTable->AllocFunc) || (pFuncTable->FreeFunc))
  83. {
  84. if ((pFuncTable->AllocFunc == NULL) ||
  85. (pFuncTable->FreeFunc == NULL))
  86. {
  87. return STATUS_INVALID_PARAMETER;
  88. }
  89. pHashTable->AllocFunc = pFuncTable->AllocFunc;
  90. pHashTable->FreeFunc = pFuncTable->FreeFunc;
  91. //
  92. // If Alloc/Free functions are user-defined, then AllocHashEntry and FreeHashEntry
  93. // will also default to those same AllocFunc and FreeFunc calls. These can of course,
  94. // be overridden with their own user-defined functions (see below).
  95. //
  96. pHashTable->AllocHashEntryFunc = pFuncTable->AllocFunc;
  97. pHashTable->FreeHashEntryFunc = pFuncTable->FreeFunc;
  98. }
  99. // separate alloc/free functions for individual hash data.
  100. if ((pFuncTable->AllocHashEntryFunc) || (pFuncTable->FreeHashEntryFunc))
  101. {
  102. if ((pFuncTable->AllocHashEntryFunc == NULL) ||
  103. (pFuncTable->FreeHashEntryFunc == NULL))
  104. {
  105. return STATUS_INVALID_PARAMETER;
  106. }
  107. pHashTable->AllocHashEntryFunc = pFuncTable->AllocHashEntryFunc;
  108. pHashTable->FreeHashEntryFunc = pFuncTable->FreeHashEntryFunc;
  109. }
  110. if ((pFuncTable->AllocLockFunc) || (pFuncTable->FreeLockFunc))
  111. {
  112. if ((pFuncTable->AllocLockFunc == NULL) ||
  113. (pFuncTable->FreeLockFunc == NULL))
  114. {
  115. return STATUS_INVALID_PARAMETER;
  116. }
  117. pHashTable->AllocLockFunc = pFuncTable->AllocLockFunc;
  118. pHashTable->FreeLockFunc = pFuncTable->FreeLockFunc;
  119. }
  120. if ((pFuncTable->WriteLockFunc) || (pFuncTable->ReadLockFunc) ||
  121. (pFuncTable->ReleaseWriteLockFunc) || (pFuncTable->ReleaseReadLockFunc))
  122. {
  123. pHashTable->WriteLockFunc = pFuncTable->WriteLockFunc;
  124. pHashTable->ReadLockFunc = pFuncTable->ReadLockFunc;
  125. pHashTable->ReleaseWriteLockFunc = pFuncTable->ReleaseWriteLockFunc;
  126. pHashTable->ReleaseReadLockFunc = pFuncTable->ReleaseReadLockFunc;
  127. }
  128. if(pFuncTable->NumBuckets)
  129. {
  130. pHashTable->NumBuckets = pFuncTable->NumBuckets;
  131. }
  132. else
  133. {
  134. pHashTable->NumBuckets = SHASH_DEFAULT_HASH_SIZE;
  135. }
  136. //test to see if buckets is a power of 2
  137. if((pHashTable->NumBuckets & (pHashTable->NumBuckets - 1)) == 0)
  138. {
  139. pHashTable->Flags = SHASH_CAP_POWER_OF2;
  140. }
  141. pHashTable->Flags |= pFuncTable->Flags;
  142. return STATUS_SUCCESS;
  143. }
  144. ULONG
  145. ShashComputeNearestPowerOfTwo(ULONG Value)
  146. {
  147. ULONG PowerOf2 = Value;
  148. while( PowerOf2 & (PowerOf2 - 1) )
  149. {
  150. PowerOf2 = PowerOf2 & (PowerOf2 - 1) ;
  151. }
  152. PowerOf2 <<= 1 ;
  153. return PowerOf2;
  154. }
  155. NTSTATUS
  156. ShashInitHashTable(
  157. PSHASH_TABLE *ppHashTable,
  158. PSHASH_FUNCTABLE pFuncTable)
  159. {
  160. PSHASH_TABLE pHashTable = NULL;
  161. ULONG cbHashTable = 0;
  162. DWORD LoopVar = 0;
  163. ULONG NumBuckets = SHASH_DEFAULT_HASH_SIZE;
  164. NTSTATUS Status = STATUS_SUCCESS;
  165. PFNALLOC lAllocFunc = SHashAllocate;
  166. PFNFREE lFreeFunc = SHashFree;
  167. if(pFuncTable && pFuncTable->NumBuckets)
  168. {
  169. NumBuckets = pFuncTable->NumBuckets;
  170. }
  171. if(pFuncTable)
  172. {
  173. if ((pFuncTable->AllocFunc != NULL) &&
  174. (pFuncTable->FreeFunc != NULL))
  175. {
  176. lAllocFunc = pFuncTable->AllocFunc;
  177. lFreeFunc = pFuncTable->FreeFunc;
  178. }
  179. }
  180. cbHashTable = sizeof(SHASH_TABLE) + NumBuckets * sizeof(SHASH_ENTRY);
  181. pHashTable = lAllocFunc (cbHashTable);
  182. if (pHashTable == NULL)
  183. {
  184. Status = STATUS_INSUFFICIENT_RESOURCES;
  185. goto Exit;
  186. }
  187. Status = ShashInitializeFunctionTable(pHashTable,
  188. pFuncTable);
  189. if (Status != STATUS_SUCCESS)
  190. {
  191. lFreeFunc(pHashTable);
  192. goto Exit;
  193. }
  194. pHashTable->pLock = pHashTable->AllocLockFunc();
  195. if(pHashTable->pLock == NULL)
  196. {
  197. lFreeFunc (pHashTable);
  198. Status = STATUS_INSUFFICIENT_RESOURCES;
  199. goto Exit;
  200. }
  201. RtlZeroMemory(&pHashTable->HashBuckets[0], NumBuckets * sizeof(SHASH_ENTRY));
  202. *ppHashTable = pHashTable;
  203. for(LoopVar = 0; LoopVar < NumBuckets; LoopVar++)
  204. {
  205. InitializeListHead(&pHashTable->HashBuckets[LoopVar].ListHead);
  206. }
  207. Exit:
  208. return Status;
  209. }
  210. void
  211. ShashTerminateHashTable(
  212. PSHASH_TABLE pHashTable
  213. )
  214. {
  215. PFNFREE FreeFunc = NULL;
  216. if(pHashTable != NULL)
  217. {
  218. pHashTable->FreeLockFunc( pHashTable->pLock);
  219. FreeFunc = pHashTable->FreeFunc;
  220. FreeFunc (pHashTable);
  221. }
  222. }
  223. NTSTATUS
  224. SHashInsertKey(IN PSHASH_TABLE pTable,
  225. IN void * pData,
  226. IN void * pvKeyIn,
  227. IN DWORD InsertFlag)
  228. {
  229. ULONG dwHash = 0;
  230. NTSTATUS Status = STATUS_SUCCESS;
  231. PLIST_ENTRY ple = NULL;
  232. PLIST_ENTRY pleRemove = NULL;
  233. PLIST_ENTRY pleStart = NULL;
  234. PSHASH_HEADER pEntry = NULL;
  235. PSHASH_HEADER pHeader = NULL;
  236. int iSign = 0;
  237. BOOLEAN LockAquired = TRUE;
  238. dwHash = pTable->HashFunc(pvKeyIn) ;
  239. if(pTable->Flags & SHASH_CAP_POWER_OF2)
  240. {
  241. dwHash = dwHash & (pTable->NumBuckets - 1);
  242. }
  243. else
  244. {
  245. dwHash = dwHash % pTable->NumBuckets;
  246. }
  247. LockAquired = pTable->WriteLockFunc(pTable);
  248. if(!LockAquired)
  249. {
  250. Status = STATUS_LOCK_NOT_GRANTED;
  251. goto Exit;
  252. }
  253. pHeader = (PSHASH_HEADER) pData;
  254. pHeader->dwHash = dwHash;
  255. pleStart = &pTable->HashBuckets[dwHash].ListHead ;
  256. ple = pleStart->Flink ;
  257. while( ple != pleStart )
  258. {
  259. pEntry = CONTAINING_RECORD(ple, SHASH_HEADER, ListEntry);
  260. iSign = pTable->CompareFunc(pEntry->pvKey, pvKeyIn ) ;
  261. if( iSign > 0 )
  262. {
  263. break ;
  264. }
  265. else if((InsertFlag == SHASH_FAIL_IFFOUND) && (iSign == 0))
  266. {
  267. Status = STATUS_OBJECT_NAME_COLLISION;
  268. goto Exit;
  269. }
  270. else if((InsertFlag == SHASH_REPLACE_IFFOUND) && (iSign == 0))
  271. {
  272. pEntry->Flags |= SHASH_FLAG_DELETE_PENDING;
  273. if(InterlockedDecrement(&pEntry->RefCount) == 0)
  274. {
  275. pleRemove = ple;
  276. ple = ple->Flink;
  277. RemoveEntryList( pleRemove ) ;
  278. InterlockedDecrement(&pTable->HashBuckets[dwHash].Count);
  279. InterlockedDecrement(&pTable->TotalItems);
  280. pTable->FreeHashEntryFunc(pEntry);
  281. }
  282. break;
  283. }
  284. ple = ple->Flink ;
  285. }
  286. InterlockedIncrement(&pHeader->RefCount);
  287. InsertTailList( ple, &pHeader->ListEntry ) ;
  288. InterlockedIncrement(&pTable->HashBuckets[dwHash].Count);
  289. InterlockedIncrement(&pTable->TotalItems);
  290. Exit:
  291. if(LockAquired)
  292. {
  293. pTable->ReleaseWriteLockFunc(pTable);
  294. }
  295. return Status;
  296. }
  297. //
  298. // Remove an item from the hash table
  299. //
  300. NTSTATUS
  301. SHashRemoveKey( IN PSHASH_TABLE pTable,
  302. IN void * pvKeyIn,
  303. IN PFNREMOVEKEY pRemoveFunc
  304. )
  305. {
  306. NTSTATUS Status = STATUS_SUCCESS;
  307. PLIST_ENTRY pleStart = NULL ;
  308. PLIST_ENTRY ple= NULL ;
  309. PSHASH_HEADER pEntry = NULL;
  310. int iSign = 0;
  311. DWORD dwHash = 0;
  312. BOOLEAN LockAquired = TRUE;
  313. dwHash = pTable->HashFunc(pvKeyIn) ;
  314. if(pTable->Flags & SHASH_CAP_POWER_OF2)
  315. {
  316. dwHash= dwHash & (pTable->NumBuckets - 1);
  317. }
  318. else
  319. {
  320. dwHash = dwHash % pTable->NumBuckets;
  321. }
  322. LockAquired = pTable->WriteLockFunc(pTable);
  323. if(!LockAquired)
  324. {
  325. Status = STATUS_LOCK_NOT_GRANTED;
  326. goto Exit;
  327. }
  328. pleStart = &pTable->HashBuckets[dwHash].ListHead ;
  329. ple = pleStart->Flink ;
  330. while( ple != pleStart )
  331. {
  332. pEntry = (PSHASH_HEADER) CONTAINING_RECORD(ple, SHASH_HEADER, ListEntry);
  333. iSign = pTable->CompareFunc( pEntry->pvKey, pvKeyIn ) ;
  334. if( iSign == 0 )
  335. {
  336. if(pRemoveFunc)
  337. {
  338. pRemoveFunc(pEntry);
  339. }
  340. pEntry->Flags |= SHASH_FLAG_DELETE_PENDING;
  341. if(InterlockedDecrement(&pEntry->RefCount) == 0)
  342. {
  343. RemoveEntryList( ple ) ;
  344. InterlockedDecrement(&pTable->HashBuckets[dwHash].Count);
  345. InterlockedDecrement(&pTable->TotalItems);
  346. pTable->FreeHashEntryFunc(pEntry);
  347. }
  348. break;
  349. }
  350. else if( iSign > 0 )
  351. {
  352. break ;
  353. }
  354. ple = ple->Flink ;
  355. }
  356. Exit:
  357. if(LockAquired)
  358. {
  359. pTable->ReleaseWriteLockFunc(pTable);
  360. }
  361. return Status;
  362. }
  363. PSHASH_HEADER
  364. SHashLookupKeyEx( IN PSHASH_TABLE pTable,
  365. IN void* pvKeyIn
  366. )
  367. {
  368. PSHASH_HEADER pEntry = NULL;
  369. PLIST_ENTRY pleStart = NULL ;
  370. PLIST_ENTRY ple= NULL ;
  371. int iSign = 0;
  372. DWORD dwHash = 0;
  373. BOOLEAN LockAquired = TRUE;
  374. dwHash = pTable->HashFunc(pvKeyIn) ;
  375. if(pTable->Flags & SHASH_CAP_POWER_OF2)
  376. {
  377. dwHash= dwHash & (pTable->NumBuckets - 1);
  378. }
  379. else
  380. {
  381. dwHash = dwHash % pTable->NumBuckets;
  382. }
  383. LockAquired = pTable->ReadLockFunc(pTable);
  384. if(!LockAquired)
  385. {
  386. goto Exit;
  387. }
  388. pleStart = &pTable->HashBuckets[dwHash].ListHead ;
  389. ple = pleStart->Flink ;
  390. while( ple != pleStart )
  391. {
  392. pEntry = (PSHASH_HEADER)CONTAINING_RECORD(ple, SHASH_HEADER, ListEntry);
  393. iSign = pTable->CompareFunc( pEntry->pvKey, pvKeyIn ) ;
  394. if( iSign == 0 )
  395. {
  396. InterlockedIncrement(&pEntry->RefCount);
  397. pTable->ReleaseReadLockFunc(pTable);
  398. return pEntry;
  399. }
  400. else if( iSign > 0 )
  401. {
  402. break ;
  403. }
  404. ple = ple->Flink ;
  405. }
  406. Exit:
  407. if(LockAquired)
  408. {
  409. pTable->ReleaseReadLockFunc(pTable);
  410. }
  411. return NULL;
  412. }
  413. NTSTATUS
  414. SHashIsKeyInTable( IN PSHASH_TABLE pTable,
  415. IN void* pvKeyIn
  416. )
  417. {
  418. NTSTATUS Status = STATUS_OBJECT_PATH_NOT_FOUND;
  419. PSHASH_HEADER pEntry = NULL;
  420. pEntry = SHashLookupKeyEx(pTable, pvKeyIn);
  421. if(pEntry != NULL)
  422. {
  423. SHashReleaseReference(pTable, pEntry);
  424. Status = STATUS_SUCCESS;
  425. }
  426. return Status;
  427. }
  428. NTSTATUS
  429. SHashGetDataFromTable( IN PSHASH_TABLE pTable,
  430. IN void* pvKeyIn,
  431. IN void ** ppData
  432. )
  433. {
  434. NTSTATUS Status = STATUS_OBJECT_PATH_NOT_FOUND;
  435. PSHASH_HEADER pEntry = NULL;
  436. pEntry = SHashLookupKeyEx(pTable, pvKeyIn);
  437. if(pEntry != NULL)
  438. {
  439. *ppData = (void *)pEntry;
  440. Status = STATUS_SUCCESS;
  441. }
  442. return Status;
  443. }
  444. NTSTATUS
  445. SHashReleaseReference( IN PSHASH_TABLE pTable,
  446. IN PSHASH_HEADER pData
  447. )
  448. {
  449. if(InterlockedDecrement(&pData->RefCount) == 0)
  450. {
  451. pTable->WriteLockFunc(pTable);
  452. InterlockedDecrement(&pTable->TotalItems);
  453. RemoveEntryList( &pData->ListEntry ) ;
  454. InterlockedDecrement(&pTable->HashBuckets[pData->dwHash].Count);
  455. pTable->FreeHashEntryFunc(pData);
  456. pTable->ReleaseWriteLockFunc(pTable);
  457. }
  458. return STATUS_SUCCESS;
  459. }
  460. NTSTATUS
  461. SHashMarkForDeletion( IN PSHASH_TABLE pTable,
  462. IN PSHASH_HEADER pData
  463. )
  464. {
  465. NTSTATUS Status = STATUS_LOCK_NOT_GRANTED;
  466. BOOLEAN LockAquired = TRUE;
  467. LockAquired = pTable->WriteLockFunc(pTable);
  468. if(LockAquired)
  469. {
  470. pData->Flags |= SHASH_FLAG_DELETE_PENDING;
  471. if(InterlockedDecrement(&pData->RefCount) == 0)
  472. {
  473. InterlockedDecrement(&pTable->TotalItems);
  474. RemoveEntryList( &pData->ListEntry ) ;
  475. InterlockedDecrement(&pTable->HashBuckets[pData->dwHash].Count);
  476. pTable->FreeHashEntryFunc(pData);
  477. }
  478. pTable->ReleaseWriteLockFunc(pTable);
  479. Status = STATUS_SUCCESS;
  480. }
  481. return Status;
  482. }
  483. #if 0
  484. NTSTATUS
  485. SHashReplaceInTable(IN PSHASH_TABLE pTable,
  486. IN void* pvKeyIn,
  487. IN OUT PVOID *ppData
  488. )
  489. {
  490. NTSTATUS Status = STATUS_SUCCESS;
  491. Status = STATUS_OBJECT_PATH_NOT_FOUND;
  492. return Status;
  493. }
  494. #endif
  495. NTSTATUS
  496. ShashEnumerateItems(IN PSHASH_TABLE pTable,
  497. IN PFNENUMERATEKEY pfnCallback,
  498. IN LPVOID lpvClientContext
  499. )
  500. {
  501. NTSTATUS Status = STATUS_SUCCESS;
  502. PLIST_ENTRY pCur = NULL ;
  503. PLIST_ENTRY pNext = NULL;
  504. PLIST_ENTRY ListHead = NULL;
  505. PSHASH_HEADER pEntry = NULL;
  506. DWORD i = 0;
  507. BOOLEAN LockAquired = TRUE;
  508. LockAquired = pTable->ReadLockFunc(pTable);
  509. if(!LockAquired)
  510. {
  511. Status = STATUS_LOCK_NOT_GRANTED;
  512. goto Exit;
  513. }
  514. for( i = 0; i < pTable->NumBuckets && Status == STATUS_SUCCESS; i++ )
  515. {
  516. ListHead = &pTable->HashBuckets[i].ListHead ;
  517. pCur = ListHead->Flink ;
  518. while( pCur != ListHead && Status == STATUS_SUCCESS )
  519. {
  520. //
  521. // save the next pointer now,
  522. //
  523. pNext = pCur->Flink ;
  524. pEntry = CONTAINING_RECORD( pCur, SHASH_HEADER, ListEntry ) ;
  525. if ((pEntry->Flags & SHASH_FLAG_DELETE_PENDING) == 0)
  526. {
  527. Status = pfnCallback( pEntry,
  528. lpvClientContext );
  529. }
  530. //
  531. // move to the next item
  532. //
  533. pCur = pNext ;
  534. }
  535. }
  536. Exit:
  537. if(LockAquired)
  538. {
  539. pTable->ReleaseReadLockFunc(pTable);
  540. }
  541. return Status;
  542. }