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.

320 lines
7.3 KiB

  1. /*++
  2. Copyright (c) 1989-2001 Microsoft Corporation
  3. Module Name:
  4. hash.c
  5. Abstract:
  6. Abstract Data Type: Hash
  7. Author:
  8. Jiandong Ruan
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. #include "hash.h"
  13. typedef struct SMB_HASH_TABLE {
  14. KSPIN_LOCK Lock;
  15. DWORD NumberOfBuckets;
  16. PSMB_HASH HashFunc; // Hash function
  17. PSMB_HASH_CMP CmpFunc; // Compare function
  18. PSMB_HASH_DEL DelFunc; // Delete function: called before an entry is removed from the hash table
  19. PSMB_HASH_ADD AddFunc; // Add function: called before an entry is added into the hash table
  20. PSMB_HASH_REFERENCE RefFunc;
  21. PSMB_HASH_DEREFERENCE DerefFunc;
  22. PSMB_HASH_GET_KEY GetKeyFunc;
  23. LIST_ENTRY Buckets[0];
  24. } SMB_HASH_TABLE, *PSMB_HASH_TABLE;
  25. VOID __inline
  26. SmbLockHashTable(
  27. PSMB_HASH_TABLE HashTbl,
  28. KIRQL *Irql
  29. )
  30. {
  31. KeAcquireSpinLock(&HashTbl->Lock, Irql);
  32. }
  33. VOID __inline
  34. SmbUnlockHashTable(
  35. PSMB_HASH_TABLE HashTbl,
  36. KIRQL Irql
  37. )
  38. {
  39. KeReleaseSpinLock(&HashTbl->Lock, Irql);
  40. }
  41. PSMB_HASH_TABLE
  42. SmbCreateHashTable(
  43. DWORD NumberOfBuckets,
  44. PSMB_HASH HashFunc,
  45. PSMB_HASH_GET_KEY GetKeyFunc,
  46. PSMB_HASH_CMP CmpFunc,
  47. PSMB_HASH_ADD AddFunc, // optional
  48. PSMB_HASH_DEL DelFunc, // optional
  49. PSMB_HASH_REFERENCE RefFunc, // optional
  50. PSMB_HASH_DEREFERENCE DerefFunc // optional
  51. )
  52. {
  53. PSMB_HASH_TABLE HashTbl = NULL;
  54. DWORD i, Size = 0;
  55. //
  56. // To avoid overflow below, set a upperbound on the number of buckets
  57. // 65536 is large enough!!!
  58. //
  59. if (0 == NumberOfBuckets || NumberOfBuckets >= 0x10000) {
  60. goto error;
  61. }
  62. if (NULL == HashFunc || NULL == CmpFunc || NULL == GetKeyFunc) {
  63. goto error;
  64. }
  65. //
  66. // Allocate memory
  67. //
  68. Size = sizeof(SMB_HASH_TABLE) + sizeof(LIST_ENTRY)*NumberOfBuckets;
  69. HashTbl = ExAllocatePoolWithTag(NonPagedPool, Size, 'HBMS');
  70. if (NULL == HashTbl) {
  71. goto error;
  72. }
  73. RtlZeroMemory(HashTbl, Size);
  74. //
  75. // Initialize
  76. //
  77. KeInitializeSpinLock(&HashTbl->Lock);
  78. HashTbl->NumberOfBuckets = NumberOfBuckets;
  79. HashTbl->HashFunc = HashFunc;
  80. HashTbl->CmpFunc = CmpFunc;
  81. HashTbl->GetKeyFunc = GetKeyFunc;
  82. HashTbl->DelFunc = DelFunc;
  83. HashTbl->AddFunc = AddFunc;
  84. HashTbl->RefFunc = RefFunc;
  85. HashTbl->DerefFunc = DerefFunc;
  86. for (i = 0; i < NumberOfBuckets; i++) {
  87. InitializeListHead(&HashTbl->Buckets[i]);
  88. }
  89. error:
  90. return HashTbl;
  91. }
  92. VOID
  93. SmbDestroyHashTable(
  94. PSMB_HASH_TABLE HashTbl
  95. )
  96. {
  97. KIRQL Irql = 0;
  98. DWORD i = 0;
  99. PLIST_ENTRY entry = NULL;
  100. if (NULL == HashTbl) {
  101. goto error;
  102. }
  103. for (i = 0; i < HashTbl->NumberOfBuckets; i++) {
  104. SmbLockHashTable(HashTbl, &Irql);
  105. while(!IsListEmpty(&HashTbl->Buckets[i])) {
  106. entry = RemoveHeadList(&HashTbl->Buckets[i]);
  107. InitializeListHead(entry);
  108. SmbUnlockHashTable(HashTbl, Irql);
  109. if (HashTbl->DelFunc) {
  110. HashTbl->DelFunc(entry);
  111. }
  112. SmbLockHashTable(HashTbl, &Irql);
  113. }
  114. SmbUnlockHashTable(HashTbl, Irql);
  115. }
  116. ExFreePool(HashTbl);
  117. error:
  118. return;
  119. }
  120. PLIST_ENTRY
  121. SmbLookupLockedHashTable(
  122. PSMB_HASH_TABLE HashTbl,
  123. PVOID Key,
  124. DWORD Hash
  125. )
  126. /*++
  127. Routine Description:
  128. Look up hash table
  129. Note: spinlock should held
  130. Arguments:
  131. Return Value:
  132. NULL Not found
  133. --*/
  134. {
  135. PLIST_ENTRY pHead = NULL, FoundEntry = NULL;
  136. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  137. ASSERT (Hash < HashTbl->NumberOfBuckets);
  138. pHead = HashTbl->Buckets[Hash].Flink;
  139. while(pHead != &HashTbl->Buckets[Hash]) {
  140. if (HashTbl->CmpFunc(pHead, Key) == 0) {
  141. FoundEntry = pHead;
  142. break;
  143. }
  144. pHead = pHead->Flink;
  145. }
  146. return FoundEntry;
  147. }
  148. PLIST_ENTRY
  149. SmbLookupHashTable(
  150. PSMB_HASH_TABLE HashTbl,
  151. PVOID Key
  152. )
  153. {
  154. DWORD dwHash = 0;
  155. KIRQL Irql = 0;
  156. PLIST_ENTRY FoundEntry = NULL;
  157. dwHash = HashTbl->HashFunc(Key) % HashTbl->NumberOfBuckets;
  158. SmbLockHashTable(HashTbl, &Irql);
  159. FoundEntry = SmbLookupLockedHashTable(HashTbl, Key, dwHash);
  160. SmbUnlockHashTable(HashTbl, Irql);
  161. return FoundEntry;
  162. }
  163. PLIST_ENTRY
  164. SmbLookupHashTableAndReference(
  165. PSMB_HASH_TABLE HashTbl,
  166. PVOID Key
  167. )
  168. {
  169. DWORD dwHash = 0;
  170. KIRQL Irql = 0;
  171. PLIST_ENTRY FoundEntry = NULL;
  172. if (NULL == HashTbl->RefFunc) {
  173. return NULL;
  174. }
  175. dwHash = HashTbl->HashFunc(Key) % HashTbl->NumberOfBuckets;
  176. SmbLockHashTable(HashTbl, &Irql);
  177. FoundEntry = SmbLookupLockedHashTable(HashTbl, Key, dwHash);
  178. if (FoundEntry) {
  179. LONG RefCount = 0;
  180. RefCount = HashTbl->RefFunc(FoundEntry);
  181. ASSERT(RefCount > 1);
  182. }
  183. SmbUnlockHashTable(HashTbl, Irql);
  184. return FoundEntry;
  185. }
  186. PLIST_ENTRY
  187. SmbAddToHashTable(
  188. PSMB_HASH_TABLE HashTbl,
  189. PLIST_ENTRY Entry
  190. )
  191. /*++
  192. Routine Description:
  193. Add the entry into hash table if it isn't in the hash table
  194. Otherwise, increase the RefCount of the existing entry if RefFunc is set
  195. Arguments:
  196. Return Value:
  197. --*/
  198. {
  199. DWORD dwHash = 0;
  200. KIRQL Irql = 0;
  201. PVOID Key = NULL;
  202. PLIST_ENTRY FoundEntry = NULL;
  203. Key = HashTbl->GetKeyFunc(Entry);
  204. dwHash = HashTbl->HashFunc(Key) % HashTbl->NumberOfBuckets;
  205. SmbLockHashTable(HashTbl, &Irql);
  206. FoundEntry = SmbLookupLockedHashTable(HashTbl, Key, dwHash);
  207. if (NULL == FoundEntry) {
  208. if (HashTbl->AddFunc) {
  209. HashTbl->AddFunc(Entry);
  210. }
  211. InsertTailList(&HashTbl->Buckets[dwHash], Entry);
  212. FoundEntry = Entry;
  213. } else {
  214. if (HashTbl->RefFunc) {
  215. HashTbl->RefFunc(FoundEntry);
  216. }
  217. }
  218. SmbUnlockHashTable(HashTbl, Irql);
  219. return FoundEntry;
  220. }
  221. PLIST_ENTRY
  222. SmbRemoveFromHashTable(
  223. PSMB_HASH_TABLE HashTbl,
  224. PLIST_ENTRY Key
  225. )
  226. /*++
  227. Routine Description:
  228. Decrement the refcount of the entry. If the refoucne is zero, delete it.
  229. Arguments:
  230. Return Value:
  231. --*/
  232. {
  233. DWORD dwHash = 0;
  234. KIRQL Irql = 0;
  235. PLIST_ENTRY FoundEntry = NULL;
  236. dwHash = HashTbl->HashFunc(Key) % HashTbl->NumberOfBuckets;
  237. SmbLockHashTable(HashTbl, &Irql);
  238. FoundEntry = SmbLookupLockedHashTable(HashTbl, Key, dwHash);
  239. if (FoundEntry) {
  240. if (HashTbl->DerefFunc) {
  241. LONG RefCount;
  242. RefCount = HashTbl->DerefFunc(FoundEntry);
  243. ASSERT(RefCount >= 0);
  244. if (RefCount) {
  245. FoundEntry = NULL;
  246. }
  247. }
  248. if (FoundEntry) {
  249. RemoveEntryList(FoundEntry);
  250. InitializeListHead(FoundEntry);
  251. if (HashTbl->DelFunc) {
  252. HashTbl->DelFunc(FoundEntry);
  253. }
  254. }
  255. }
  256. SmbUnlockHashTable(HashTbl, Irql);
  257. return FoundEntry;
  258. }