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.

643 lines
15 KiB

  1. /* hash.cpp
  2. *
  3. * Copyright (c) 1996 by Microsoft Corporation
  4. *
  5. * Written by: Christos Tsollis
  6. *
  7. * Revisions:
  8. *
  9. * Abstract:
  10. *
  11. * This is the implementation of a dictionary data structure.
  12. *
  13. */
  14. #include "precomp.h"
  15. DEBUG_FILEZONE(ZONE_T120_UTILITY);
  16. #define MyMalloc(size) new BYTE[size]
  17. #define MyFree(ptr) (delete [] (BYTE *) (ptr))
  18. #define Max(a,b) (((a) > (b)) ? (a) : (b))
  19. /* Function: Constructor
  20. *
  21. * Parameters:
  22. * num_of_buckets: Number of buckets in the dictionary
  23. * dtype: Dictionary type
  24. *
  25. * Return value:
  26. * None
  27. */
  28. DictionaryClass::DictionaryClass (ULong num_of_buckets, DictionaryType dtype) :
  29. Type (dtype), NumOfExternItems (0)
  30. {
  31. DWORD i;
  32. PDICTIONARY_ITEM p; // Goes through the initially allocated dictionary items to initialize the stack
  33. NumOfBuckets = Max (num_of_buckets, DEFAULT_NUMBER_OF_BUCKETS);
  34. /* Allocate the space needed for the dictionary */
  35. dwNormalSize = NumOfBuckets * (4 * sizeof (PDICTIONARY_ITEM) + 3 * sizeof (DICTIONARY_ITEM));
  36. Buckets = (PDICTIONARY_ITEM *) MyMalloc (dwNormalSize);
  37. if (Buckets == NULL)
  38. return;
  39. /* Initialize the Buckets */
  40. for (i = 0; i < NumOfBuckets; i++)
  41. Buckets[i] = NULL;
  42. // Initialize the class iterator
  43. pCurrent = NULL;
  44. /* Initialize the Dictionary items array.
  45. * This is a stack of pointers to the real dictionary items. The stack is initialized with
  46. * the addresses of the dictionary items
  47. */
  48. ItemArray = (PDICTIONARY_ITEM *) ((PBYTE) Buckets + NumOfBuckets * sizeof (PDICTIONARY_ITEM));
  49. ItemCount = 3 * NumOfBuckets;
  50. p = (PDICTIONARY_ITEM) (ItemArray + ItemCount);
  51. for (i = 0; i < ItemCount; i++)
  52. ItemArray[i] = p++;
  53. }
  54. /* Function: Copy Constructor
  55. *
  56. * Parameters:
  57. * original: The original dictionary to make a copy of
  58. *
  59. * Return value:
  60. * None
  61. *
  62. * Note:
  63. * This copy constructor will work ONLY for DWORD_DICTIONARY dictionaries.
  64. * It will NOT work for the string dictionary types.
  65. */
  66. DictionaryClass::DictionaryClass (const DictionaryClass& original)
  67. {
  68. DWORD i;
  69. PDICTIONARY_ITEM p, q, r;
  70. NumOfBuckets = original.NumOfBuckets;
  71. Type = original.Type;
  72. NumOfExternItems = original.NumOfExternItems;
  73. // Allocate the space needed for the dictionary
  74. dwNormalSize = original.dwNormalSize;
  75. Buckets = (PDICTIONARY_ITEM *) MyMalloc (dwNormalSize);
  76. if (Buckets == NULL)
  77. return;
  78. // Initialize the class iterator
  79. pCurrent = NULL;
  80. /* Initialize the Dictionary items array */
  81. ItemArray = (PDICTIONARY_ITEM *) ((PBYTE) Buckets + NumOfBuckets * sizeof (PDICTIONARY_ITEM));
  82. ItemCount = 3 * NumOfBuckets;
  83. // Traverse the whole original hash sturcture to create the copy
  84. // p: goes through the original items
  85. // q: goes through current instance's items and initializes them
  86. // r: remembers the previous item in the new instance so that its "next" field could be set
  87. q = (PDICTIONARY_ITEM) (ItemArray + ItemCount);
  88. for (q--, i = 0; i < NumOfBuckets; i++) {
  89. for (r = NULL, p = original.Buckets[i]; p != NULL; p = p->next) {
  90. // Check if there are unused items in the current dictionary
  91. if (ItemCount <= 0) {
  92. q = (PDICTIONARY_ITEM) MyMalloc (sizeof (DICTIONARY_ITEM));
  93. if (q == NULL)
  94. break;
  95. }
  96. else {
  97. ItemCount--;
  98. q++;
  99. }
  100. q->value = p->value;
  101. q->key = p->key;
  102. if (p == original.Buckets[i])
  103. Buckets[i] = q;
  104. else
  105. r->next = q;
  106. r = q;
  107. }
  108. // Set the "next" field for the last item in the bucket
  109. if (r == NULL)
  110. Buckets[i] = NULL;
  111. else
  112. r->next = NULL;
  113. }
  114. // Initialize the rest of the ItemArray array
  115. for (i = 0; i < ItemCount; i++)
  116. ItemArray[i] = q++;
  117. }
  118. /* Function: Destructor
  119. *
  120. * Parameters:
  121. * None.
  122. *
  123. * Return value:
  124. * None
  125. *
  126. */
  127. DictionaryClass::~DictionaryClass ()
  128. {
  129. DWORD i;
  130. DWORD dwOffset; // Offset of the dictionary item. If the offset does not indicate
  131. // that the item is from the initially allocated array, it has to
  132. // be freed.
  133. PDICTIONARY_ITEM p, q;
  134. if (Buckets != NULL) {
  135. // Go through the buckets to free the non-native items
  136. for (i = 0; i < NumOfBuckets; i++)
  137. for (p = Buckets[i]; p != NULL; ) {
  138. if (Type >= STRING_DICTIONARY)
  139. MyFree (p->key);
  140. dwOffset = (PBYTE) p - (PBYTE) Buckets;
  141. if (dwOffset >= 0 && dwOffset < dwNormalSize)
  142. p = p->next;
  143. else {
  144. // if the item was not allocated in the initialization, we should free it.
  145. q = p;
  146. p = p->next;
  147. MyFree (q);
  148. }
  149. }
  150. MyFree (Buckets);
  151. Buckets = NULL;
  152. }
  153. }
  154. /* Function: hashFunction
  155. *
  156. * Parameters:
  157. * key: The key value
  158. *
  159. * Return value:
  160. * An integer in the range [0..NumOfBuckets-1] that indicates the bucket used for the "key".
  161. *
  162. */
  163. DWORD DictionaryClass::hashFunction (DWORD key)
  164. {
  165. if (Type < STRING_DICTIONARY)
  166. return (key % NumOfBuckets);
  167. else
  168. return (*((unsigned char *) key) % NumOfBuckets);
  169. }
  170. /* Function: LengthStrcmp
  171. *
  172. * Parameters:
  173. * DictionaryKey: Pointer to dictionary storage that keeps a length-sensitive string (which
  174. * is a length followed by a string of that length.
  175. * ChallengeKey: Pointer to the length-sensitive key that is compared with the "DictionaryKey"
  176. * length: The length of the "ChallengeKey" string
  177. *
  178. * Return value:
  179. * 0 if the "DictionaryKey" and "ChallengeKey" strings are the same. 1, otherwise.
  180. *
  181. * Note:
  182. * This function is only used for dictionaries of type LENGTH_STRING_DICTIONARY.
  183. */
  184. int DictionaryClass::LengthStrcmp (DWORD DictionaryKey, DWORD ChallengeKey, ULong length)
  185. {
  186. ULong i;
  187. char *pDictionaryChar; // Goes through the dictionary key string
  188. char *pChallengeChar; // Goes through the challenge string
  189. // Compare the lengths first
  190. if (length != * (ULong *) DictionaryKey)
  191. return 1;
  192. // Now, compare the strings themselves
  193. pDictionaryChar = (char *) (DictionaryKey + sizeof (ULong));
  194. pChallengeChar = (char *) ChallengeKey;
  195. for (i = 0; i < length; i++)
  196. if (*pDictionaryChar++ != *pChallengeChar++)
  197. return 1;
  198. return 0;
  199. }
  200. /* Function: insert
  201. * Inserts (key, value) pair in the dictionary
  202. *
  203. * Parameters:
  204. * new_key: The new key that has to be inserted in the dictionary
  205. * new_value: The value associated with the "new_key"
  206. * length: Used only for LENGTH_STRING_DICTIONARY dictionaries; specifies the length of the new key
  207. *
  208. * Return value:
  209. * TRUE, if the operation succeeds, FALSE, otherwise.
  210. *
  211. * Note:
  212. * If the "new_key" is already in the dictionary, the (new_key, new_value) pair is NOT
  213. * inserted (the dictionary remains the same), and the return value is TRUE.
  214. */
  215. BOOL DictionaryClass::insert (DWORD new_key, DWORD new_value, ULong length)
  216. {
  217. PDICTIONARY_ITEM pNewItem; // Points to the allocated new dictionary item
  218. PDICTIONARY_ITEM p; // Goes through the bucket for the "new_key", searching for "new_key"
  219. DWORD hash_val; // The bucket ID for "new_key"
  220. BOOL bIsNative = TRUE; // TRUE, if the new allocated dictionary item is from the cache, FALSE otherwise
  221. if (Buckets == NULL)
  222. return FALSE;
  223. // Find if the item is already in the bucket, and if it's not, where it will get added.
  224. p = Buckets[hash_val = hashFunction (new_key)];
  225. ASSERT (hash_val >= 0 && hash_val < NumOfBuckets);
  226. if (p != NULL) {
  227. switch (Type) {
  228. #if 0
  229. case STRING_DICTIONARY:
  230. ASSERT (length == 0);
  231. for (; lstrcmp ((LPCTSTR) p->key, (LPCTSTR) new_key) && p->next != NULL; p = p->next);
  232. if (0 == lstrcmp ((LPCTSTR) p->key, (LPCTSTR) new_key)) {
  233. // the key already exists; no need to add it
  234. return TRUE;
  235. }
  236. break;
  237. #endif
  238. case LENGTH_STRING_DICTIONARY:
  239. ASSERT (length > 0);
  240. for (; LengthStrcmp (p->key, new_key, length) && p->next != NULL; p = p->next);
  241. if (0 == LengthStrcmp (p->key, new_key, length)) {
  242. // the key already exists; no need to add it
  243. return TRUE;
  244. }
  245. break;
  246. default:
  247. ASSERT (length == 0);
  248. for (; p->key != new_key && p->next != NULL; p = p->next);
  249. if (p->key == new_key) {
  250. // the key already exists; no need to add it
  251. return TRUE;
  252. }
  253. break;
  254. }
  255. }
  256. // Allocate the new item
  257. if (ItemCount > 0)
  258. pNewItem = ItemArray[--ItemCount]; // from the cache
  259. else { // the cache is empty, we have to malloc the new item
  260. pNewItem = (PDICTIONARY_ITEM) MyMalloc (sizeof (DICTIONARY_ITEM));
  261. if (pNewItem != NULL) {
  262. bIsNative = FALSE;
  263. NumOfExternItems++;
  264. }
  265. else {
  266. return FALSE;
  267. }
  268. }
  269. ASSERT (pNewItem != NULL);
  270. // Fill in the "key" field of the new item
  271. switch (Type) {
  272. #if 0
  273. case STRING_DICTIONARY:
  274. ASSERT (length == 0);
  275. pNewItem->key = (DWORD) MyMalloc ((lstrlen ((LPCTSTR) new_key) + 1) * sizeof(TCHAR));
  276. if (pNewItem->key == (DWORD) NULL) {
  277. if (bIsNative == FALSE) {
  278. // We have to free the allocated hash item
  279. MyFree (pNewItem);
  280. NumOfExternItems--;
  281. }
  282. else
  283. ItemCount++;
  284. return FALSE;
  285. }
  286. lstrcpy ((LPTSTR) pNewItem->key, (LPCTSTR) new_key);
  287. break;
  288. #endif
  289. case LENGTH_STRING_DICTIONARY:
  290. ASSERT (length > 0);
  291. pNewItem->key = (DWORD) MyMalloc (sizeof (ULong) + length * sizeof (TCHAR));
  292. if (pNewItem->key != (DWORD) NULL) {
  293. * ((ULong *) (pNewItem->key)) = length;
  294. memcpy ((void *) (pNewItem->key + sizeof (ULong)), (void *) new_key, length * sizeof (TCHAR));
  295. }
  296. else {
  297. if (bIsNative == FALSE) {
  298. // We have to free the allocated hash item
  299. MyFree (pNewItem);
  300. NumOfExternItems--;
  301. }
  302. else
  303. ItemCount++;
  304. return FALSE;
  305. }
  306. break;
  307. default:
  308. ASSERT (length == 0);
  309. pNewItem->key = new_key;
  310. break;
  311. }
  312. // Fill in the rest of the fields of the new item
  313. pNewItem->value = new_value;
  314. pNewItem->next = NULL;
  315. // Insert the item in its bucket
  316. if (p == NULL)
  317. Buckets[hash_val] = pNewItem;
  318. else
  319. p->next = pNewItem;
  320. return TRUE;
  321. }
  322. /* Function: remove
  323. * Removes (key, value) pair from the dictionary
  324. *
  325. * Parameters:
  326. * Key: The key that has to be removed from the dictionary
  327. * length: Used only for LENGTH_STRING_DICTIONARY dictionaries; specifies the length of the "Key"
  328. *
  329. * Return value:
  330. * None.
  331. *
  332. */
  333. BOOL DictionaryClass::remove (DWORD Key, ULong length)
  334. {
  335. PDICTIONARY_ITEM p, q; // They go through the dictionary items in "Key"'s bucket.
  336. // p: points to the current dictionary item in the bucket
  337. // q: points to the previous item
  338. DWORD hash_val; // The bucket ID for "Key"
  339. if (Buckets != NULL) {
  340. // Find the item in the dictionary
  341. p = Buckets [hash_val = hashFunction (Key)];
  342. ASSERT (hash_val >= 0 && hash_val < NumOfBuckets);
  343. switch (Type) {
  344. #if 0
  345. case STRING_DICTIONARY:
  346. ASSERT (length == 0);
  347. for (q = NULL; p != NULL && lstrcmp ((LPCTSTR) p->key, (LPCTSTR) Key); p = (q = p)->next) ;
  348. break;
  349. #endif
  350. case LENGTH_STRING_DICTIONARY:
  351. ASSERT (length > 0);
  352. for (q = NULL; p != NULL && LengthStrcmp (p->key, Key, length); p = (q = p)->next) ;
  353. break;
  354. default:
  355. ASSERT (length == 0);
  356. for (q = NULL; p != NULL && p->key != Key; p = (q = p)->next);
  357. break;
  358. }
  359. // Remove the item
  360. if (p != NULL) {
  361. if (q == NULL)
  362. Buckets[hash_val] = p->next;
  363. else
  364. q->next = p->next;
  365. // Free the item found
  366. ASSERT (p != NULL);
  367. if (Type >= STRING_DICTIONARY)
  368. MyFree (p->key);
  369. hash_val = (PBYTE) p - (PBYTE) Buckets;
  370. if (hash_val >= 0 && hash_val < dwNormalSize)
  371. ItemArray[ItemCount++] = p;
  372. else {
  373. MyFree (p);
  374. NumOfExternItems--;
  375. }
  376. return TRUE;
  377. }
  378. }
  379. return FALSE;
  380. }
  381. /* Function: find
  382. * Looks up the key in the dictionary
  383. *
  384. * Parameters
  385. * Key: The key to lookup
  386. * pValue: Pointer to receive the value associated with "Key"
  387. * It can be NULL, if we just try to find whether "Key" is in the dictionary
  388. * length: Used only for LENGTH_STRING_DICTIONARY dictionaries; specifies the length of "Key"
  389. *
  390. * Return value:
  391. * FALSE, if "Key" is not found in the dictionary
  392. * TRUE, otherwise.
  393. */
  394. BOOL DictionaryClass::find (DWORD Key, LPDWORD pValue, ULong length)
  395. {
  396. PDICTIONARY_ITEM p; // Goes through the dictionary items in "Key"'s bucket.
  397. if (Buckets != NULL) {
  398. // Find the item in the dictionary
  399. p = Buckets [hashFunction (Key)];
  400. switch (Type) {
  401. #if 0
  402. case STRING_DICTIONARY:
  403. ASSERT (length == 0);
  404. for (; p != NULL && lstrcmp ((LPCTSTR) p->key, (LPCTSTR) Key); p = p->next) ;
  405. break;
  406. #endif
  407. case LENGTH_STRING_DICTIONARY:
  408. ASSERT (length > 0);
  409. for (; p != NULL && LengthStrcmp (p->key, Key, length); p = p->next) ;
  410. break;
  411. default:
  412. ASSERT (length == 0);
  413. for (; p != NULL && p->key != Key; p = p->next);
  414. break;
  415. }
  416. if (p != NULL) {
  417. // "Key" was found
  418. if (pValue != NULL)
  419. *pValue = p->value;
  420. return TRUE;
  421. }
  422. }
  423. if (pValue != NULL)
  424. *pValue = 0;
  425. return FALSE;
  426. }
  427. LPVOID DictionaryClass::Find(DWORD Key, UINT length)
  428. {
  429. LPVOID Val;
  430. return find(Key, (LPDWORD) &Val, (ULONG) length) ? Val : NULL;
  431. }
  432. /* Function: iterate
  433. * Iterates through the items of a dictionary. It remembers where it has
  434. * stopped during the last call and starts from there.
  435. *
  436. * Parameters
  437. * pValue: Pointer to DWORD that will hold the next value from the dictionary.
  438. * It can be NULL.
  439. * pKey: Pointer to DWORD or unsigned short value to receive the key associated with the value.
  440. * It can be NULL.
  441. *
  442. * Return value:
  443. * FALSE, when we reach the end of the dictionary
  444. * TRUE, otherwise. Then, *pKey and *pValue are valid
  445. *
  446. */
  447. BOOL DictionaryClass::iterate (LPDWORD pValue, LPDWORD pKey)
  448. {
  449. if (Buckets != NULL) {
  450. if (pCurrent) {
  451. pCurrent = pCurrent->next;
  452. }
  453. else {
  454. // start from the 1st item in the dictionary
  455. pCurrent = Buckets[ulCurrentBucket = 0];
  456. }
  457. // Advance "pCurrent" until it's not NULL, or we reach the end of the dictionary.
  458. for (; ulCurrentBucket < NumOfBuckets; pCurrent = Buckets[++ulCurrentBucket]) {
  459. if (pCurrent != NULL) {
  460. // we found the next item
  461. if (pKey)
  462. *pKey = pCurrent->key;
  463. if (pValue)
  464. *pValue = pCurrent->value;
  465. return TRUE;
  466. }
  467. }
  468. }
  469. pCurrent = NULL;
  470. return FALSE;
  471. }
  472. LPVOID DictionaryClass::Iterate(LPDWORD pKey)
  473. {
  474. LPVOID Val;
  475. return iterate((LPDWORD) &Val, pKey) ? Val : NULL;
  476. }
  477. /* Function: isEmpty
  478. *
  479. * Parameters
  480. * None.
  481. *
  482. * Return value:
  483. * TRUE, if the dictionary is empty. FALSE, otherwise.
  484. *
  485. */
  486. BOOL DictionaryClass::isEmpty (void)
  487. {
  488. DWORD i;
  489. if (Buckets != NULL) {
  490. for (i = 0; i < NumOfBuckets; i++)
  491. if (Buckets[i] != NULL)
  492. return FALSE;
  493. }
  494. return TRUE;
  495. }
  496. /* Function: clear
  497. * Clears up the dictionary. No (key, value) pairs are left in the dictionary.
  498. *
  499. * Parameters:
  500. * None.
  501. *
  502. * Return value:
  503. * None.
  504. *
  505. */
  506. void DictionaryClass::clear (void)
  507. {
  508. DWORD i; // Goes through the "Buckets" array
  509. DWORD dwOffset; // The offset of a dictionary item is used to determine
  510. // whether it's a native item (that needs to be returned to the cache),
  511. // or not (and needs to be freed).
  512. PDICTIONARY_ITEM p, q; // Go through the items in a bucket
  513. if (Buckets != NULL) {
  514. // Go through the buckets to free the non-native items
  515. for (i = 0; i < NumOfBuckets; i++) {
  516. for (p = Buckets[i]; p != NULL; ) {
  517. if (Type >= STRING_DICTIONARY)
  518. MyFree (p->key);
  519. // Compute the offset of the current dictionary item
  520. dwOffset = (PBYTE) p - (PBYTE) Buckets;
  521. if (dwOffset >= 0 && dwOffset < dwNormalSize)
  522. p = p->next;
  523. else {
  524. // if the item was not allocated in the initialization, we should free it.
  525. q = p;
  526. p = p->next;
  527. MyFree (q);
  528. }
  529. }
  530. Buckets[i] = NULL;
  531. }
  532. // Initialize the class iterator
  533. pCurrent = NULL;
  534. /* Initialize the Dictionary items array */
  535. ItemCount = 3 * NumOfBuckets;
  536. p = (PDICTIONARY_ITEM) (ItemArray + ItemCount);
  537. for (i = 0; i < ItemCount; i++)
  538. ItemArray[i] = p++;
  539. NumOfExternItems = 0;
  540. }
  541. }