Source code of Windows XP (NT5)
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.

572 lines
10 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. hash.c
  5. Abstract:
  6. This is a fairly generic hash table implementation. This is used to form
  7. a lookup table for mapping pointers to dwords, so we can send dwords over
  8. the wire. This is for sundown.
  9. Author:
  10. Ken Peery (kpeery) 26-Feb-1999
  11. Revision History:
  12. --*/
  13. #include "clusrtlp.h"
  14. //
  15. // PLIST_ENTRY
  16. // NextListEntry(
  17. // PLIST_ENTRY ListHead
  18. // );
  19. //
  20. #define NextListEntry(ListHead) (ListHead)->Flink
  21. //
  22. // local routines
  23. //
  24. DWORD
  25. ClRtlFindUniqueIdHashUnSafe(
  26. IN PCL_HASH pTable,
  27. OUT PDWORD pId
  28. );
  29. PVOID
  30. ClRtlGetEntryHashUnSafe(
  31. IN PCL_HASH pTable,
  32. IN DWORD Id
  33. );
  34. VOID
  35. ClRtlInitializeHash(
  36. PCL_HASH pTable
  37. )
  38. /*++
  39. Routine Description:
  40. Initializes a hash table for use.
  41. Arguments:
  42. pTable - Supplies a pointer to a hash table structure to initialize
  43. Return Value:
  44. None.
  45. --*/
  46. {
  47. DWORD index;
  48. if (NULL == pTable) {
  49. //
  50. // We should never call this routine with NULL
  51. //
  52. return;
  53. }
  54. ZeroMemory(pTable,sizeof(CL_HASH));
  55. for(index=0; index < MAX_CL_HASH; index++) {
  56. InitializeListHead(&pTable->Head[index].ListHead);
  57. }
  58. InitializeCriticalSection(&pTable->Lock);
  59. }
  60. VOID
  61. ClRtlDeleteHash(
  62. IN PCL_HASH pTable
  63. )
  64. /*++
  65. Routine Description:
  66. Releases all resources used by a hash table
  67. Arguments:
  68. pTable - supplies the hash table to be deleted
  69. Return Value:
  70. None.
  71. --*/
  72. {
  73. DWORD index;
  74. PLIST_ENTRY pItem;
  75. if (NULL == pTable)
  76. return;
  77. EnterCriticalSection(&pTable->Lock);
  78. for(index=0; index < MAX_CL_HASH; index++) {
  79. while(!IsListEmpty(&pTable->Head[index].ListHead)) {
  80. pItem=RemoveHeadList(&pTable->Head[index].ListHead);
  81. LocalFree(pItem);
  82. }
  83. }
  84. LeaveCriticalSection(&pTable->Lock);
  85. DeleteCriticalSection(&pTable->Lock);
  86. }
  87. PVOID
  88. ClRtlRemoveEntryHash(
  89. IN PCL_HASH pTable,
  90. IN DWORD Id
  91. )
  92. /*++
  93. Routine Description:
  94. Removes the item specified by the Id from the list. If the item is
  95. not there then return NULL. Then save off the pData field and delete this
  96. entry from the list. Then return the pData field.
  97. Arguments:
  98. Id - the id for the entry to remove
  99. pTable - the hash table to search
  100. Return Value:
  101. The pData field of the entry with the matching id, or NULL.
  102. --*/
  103. {
  104. DWORD index;
  105. PVOID pData;
  106. PCL_HASH_ITEM pItem;
  107. pData=NULL;
  108. pItem=NULL;
  109. if ((Id == 0) || (pTable == NULL)) {
  110. SetLastError(ERROR_INVALID_PARAMETER);
  111. return NULL;
  112. }
  113. index = Id % MAX_CL_HASH;
  114. //
  115. // Lock the table for the search
  116. //
  117. EnterCriticalSection(&pTable->Lock);
  118. if (pTable->Head[index].Id == Id) {
  119. //
  120. // if the entry is in the head
  121. //
  122. pData=pTable->Head[index].pData;
  123. if (IsListEmpty(&pTable->Head[index].ListHead)) {
  124. //
  125. // there are no other entries so just zero this one out
  126. //
  127. pTable->Head[index].Id = 0;
  128. pTable->Head[index].pData = NULL;
  129. } else {
  130. //
  131. // if there is at least one other entry move that one into the
  132. // head and delete it
  133. //
  134. pItem=(PCL_HASH_ITEM)RemoveHeadList(&pTable->Head[index].ListHead);
  135. pTable->Head[index].Id = pItem->Id;
  136. pTable->Head[index].pData = pItem->pData;
  137. LocalFree(pItem);
  138. }
  139. } else {
  140. pItem=(PCL_HASH_ITEM)NextListEntry(&pTable->Head[index].ListHead);
  141. do
  142. {
  143. if (pItem->Id == Id)
  144. {
  145. pData=pItem->pData;
  146. RemoveEntryList(&pItem->ListHead);
  147. LocalFree(pItem);
  148. break;
  149. }
  150. pItem=(PCL_HASH_ITEM)NextListEntry(&pItem->ListHead);
  151. } while(pItem != &pTable->Head[index]);
  152. }
  153. // cache the now free value
  154. pTable->CacheFreeId[index]=Id;
  155. LeaveCriticalSection(&pTable->Lock);
  156. return(pData);
  157. }
  158. DWORD
  159. ClRtlFindUniqueIdHashUnSafe(
  160. IN PCL_HASH pTable,
  161. OUT PDWORD pId
  162. )
  163. /*++
  164. Routine Description:
  165. If the tables last id value should rollover we have to make sure that the
  166. id choosen is unique. This should only happen under extreme conditions
  167. but even still we must find a unique id as quickly as possible, the calling
  168. routine should already have the critical section at this point.
  169. Arguments:
  170. pTable - Supplies the hash table to search
  171. pId - sideffect to hold the id or 0 on error
  172. Return Value:
  173. ERROR_SUCCESS or the appropate Win32 error code.
  174. NOTENOTE:
  175. This algorithm is fairly slow essentially it is a sequential search with
  176. a small cache for previously freed values. We would do better if we kept
  177. a ranged free list somewhere so that if we rollover we pick from the list.
  178. The free list would have to be maintained even before we rollover to make
  179. sure we had all the available values.
  180. --*/
  181. {
  182. DWORD OldId;
  183. DWORD dwErr, index;
  184. BOOL bFoundUniqueId;
  185. PCL_HASH_ITEM pItem;
  186. dwErr=ERROR_INVALID_PARAMETER;
  187. bFoundUniqueId=FALSE;
  188. *pId=0;
  189. OldId=pTable->LastId;
  190. do
  191. {
  192. index=pTable->LastId % MAX_CL_HASH;
  193. //
  194. // first check to see if there is a free value in the cache
  195. //
  196. if (pTable->CacheFreeId[index] != 0)
  197. {
  198. bFoundUniqueId=TRUE;
  199. *pId=pTable->CacheFreeId[index];
  200. pTable->CacheFreeId[index]=0;
  201. break;
  202. }
  203. //
  204. // if the cache is empty at this index, determine if this value
  205. // is in use.
  206. //
  207. if (NULL == ClRtlGetEntryHashUnSafe(pTable, pTable->LastId)) {
  208. bFoundUniqueId=TRUE;
  209. *pId=pTable->LastId;
  210. break;
  211. }
  212. //
  213. // ok, this id is in use and nothing in the cache, try the next id
  214. //
  215. pTable->LastId++;
  216. if (pTable->LastId == 0) {
  217. pTable->LastId++;
  218. }
  219. } while(!bFoundUniqueId && (OldId != pTable->LastId));
  220. if (bFoundUniqueId) {
  221. dwErr=ERROR_SUCCESS;
  222. }
  223. return(dwErr);
  224. }
  225. DWORD
  226. ClRtlInsertTailHash(
  227. IN PCL_HASH pTable,
  228. IN PVOID pData,
  229. OUT PDWORD pId
  230. )
  231. /*++
  232. Routine Description:
  233. Inserts a new pData value into the tail of one of the entries for the
  234. hash table. The unique id for this entry is returned or 0 on failure.
  235. Arguments:
  236. pTable - Supplies the hash table to add the entry.
  237. pData - Supplies the data entry to be added to the table.
  238. pId - sideffect to hold the id or 0 on error
  239. Return Value:
  240. ERROR_SUCCESS or the appropate Win32 error code.
  241. --*/
  242. {
  243. DWORD index;
  244. DWORD dwErr;
  245. PCL_HASH_ITEM pItem;
  246. *pId=0;
  247. dwErr=ERROR_SUCCESS;
  248. if (pTable == NULL) {
  249. return(ERROR_INVALID_PARAMETER);
  250. }
  251. EnterCriticalSection(&pTable->Lock);
  252. pTable->LastId++;
  253. if (pTable->LastId == 0) {
  254. pTable->bRollover = TRUE;
  255. pTable->LastId++;
  256. }
  257. index=pTable->LastId % MAX_CL_HASH;
  258. *pId=pTable->LastId;
  259. if (pTable->Head[index].Id == 0) {
  260. //
  261. // if the first entry then add it to the head
  262. //
  263. // if we rollover, but the head is empty then the id is unique.
  264. //
  265. pTable->Head[index].Id = *pId;
  266. pTable->Head[index].pData = pData;
  267. if (pTable->CacheFreeId[index] == *pId) {
  268. pTable->CacheFreeId[index]=0;
  269. }
  270. } else {
  271. // if this is not the first entry then add it to the end.
  272. pItem=(PCL_HASH_ITEM)LocalAlloc(LMEM_FIXED,sizeof(CL_HASH_ITEM));
  273. if (NULL == pItem) {
  274. dwErr=ERROR_NOT_ENOUGH_MEMORY;
  275. } else {
  276. if (pTable->bRollover) {
  277. dwErr=ClRtlFindUniqueIdHashUnSafe(pTable, pId);
  278. }
  279. if (dwErr == ERROR_SUCCESS)
  280. {
  281. pItem->Id = *pId;
  282. pItem->pData = pData;
  283. index= *pId % MAX_CL_HASH;
  284. if (pTable->CacheFreeId[index] == *pId) {
  285. pTable->CacheFreeId[index]=0;
  286. }
  287. InsertTailList(&pTable->Head[index].ListHead,&pItem->ListHead);
  288. }
  289. else
  290. {
  291. LocalFree(pItem);
  292. }
  293. }
  294. }
  295. LeaveCriticalSection(&pTable->Lock);
  296. return(dwErr);
  297. }
  298. PVOID
  299. ClRtlGetEntryHash(
  300. IN PCL_HASH pTable,
  301. IN DWORD Id
  302. )
  303. /*++
  304. Routine Description:
  305. Gets the data portion of the item specified by the Id from the hash table.
  306. If the item is not there then return NULL.
  307. Arguments:
  308. Id - the id for the entry to find
  309. pTable - the hash table to search
  310. Return Value:
  311. The pData field of the entry with the matching id, or NULL.
  312. --*/
  313. {
  314. PVOID pData;
  315. pData=NULL;
  316. if ((Id == 0) || (pTable == NULL)) {
  317. SetLastError(ERROR_INVALID_PARAMETER);
  318. return NULL;
  319. }
  320. //
  321. // Lock the table for the search
  322. //
  323. EnterCriticalSection(&pTable->Lock);
  324. pData=ClRtlGetEntryHashUnSafe(pTable,Id);
  325. LeaveCriticalSection(&pTable->Lock);
  326. if (pData == NULL) {
  327. SetLastError(ERROR_INVALID_PARAMETER);
  328. }
  329. return(pData);
  330. }
  331. PVOID
  332. ClRtlGetEntryHashUnSafe(
  333. IN PCL_HASH pTable,
  334. IN DWORD Id
  335. )
  336. /*++
  337. Routine Description:
  338. Gets the data portion of the item specified by the Id from the hash table.
  339. If the item is not there then return NULL.
  340. Arguments:
  341. Id - the id for the entry to find
  342. pTable - the hash table to search
  343. Return Value:
  344. The pData field of the entry with the matching id, or NULL.
  345. --*/
  346. {
  347. DWORD index;
  348. PVOID pData;
  349. PCL_HASH_ITEM pItem;
  350. pData=NULL;
  351. pItem=NULL;
  352. if (Id == 0) {
  353. return NULL;
  354. }
  355. index = Id % MAX_CL_HASH;
  356. if (pTable->Head[index].Id == Id) {
  357. //
  358. // if the entry is in the head
  359. //
  360. pData=pTable->Head[index].pData;
  361. } else {
  362. pItem=(PCL_HASH_ITEM)NextListEntry(&pTable->Head[index].ListHead);
  363. do
  364. {
  365. if (pItem->Id == Id) {
  366. pData=pItem->pData;
  367. break;
  368. }
  369. pItem=(PCL_HASH_ITEM)NextListEntry(&pItem->ListHead);
  370. } while(pItem != &pTable->Head[index]);
  371. }
  372. return(pData);
  373. }