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.

624 lines
16 KiB

  1. #include "precomp.h"
  2. DEBUG_FILEZONE(ZONE_T120_T123PSTN);
  3. /* hash.cpp
  4. *
  5. * Copyright (c) 1996 by Microsoft Corporation
  6. *
  7. * Written by: Christos Tsollis
  8. *
  9. * Revisions:
  10. *
  11. * Abstract:
  12. *
  13. * This is the implementation of a dictionary data structure.
  14. *
  15. */
  16. #define MyMalloc(size) LocalAlloc (LMEM_FIXED, (size))
  17. #define MyFree(ptr) LocalFree ((HLOCAL) (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_PTR 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 < 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_PTR key)
  164. {
  165. if (Type >= STRING_DICTIONARY)
  166. return (*((unsigned char *) key) % NumOfBuckets);
  167. return (DWORD)(key % NumOfBuckets);
  168. }
  169. /* Function: LengthStrcmp
  170. *
  171. * Parameters:
  172. * DictionaryKey: Pointer to dictionary storage that keeps a length-sensitive string (which
  173. * is a length followed by a string of that length.
  174. * ChallengeKey: Pointer to the length-sensitive key that is compared with the "DictionaryKey"
  175. * length: The length of the "ChallengeKey" string
  176. *
  177. * Return value:
  178. * 0 if the "DictionaryKey" and "ChallengeKey" strings are the same. 1, otherwise.
  179. *
  180. * Note:
  181. * This function is only used for dictionaries of type LENGTH_STRING_DICTIONARY.
  182. */
  183. int DictionaryClass::LengthStrcmp (DWORD_PTR DictionaryKey, DWORD_PTR ChallengeKey, ULONG length)
  184. {
  185. ULONG i;
  186. char *pDictionaryChar; // Goes through the dictionary key string
  187. char *pChallengeChar; // Goes through the challenge string
  188. // Compare the lengths first
  189. if (length != * (ULONG *) DictionaryKey)
  190. return 1;
  191. // Now, compare the strings themselves
  192. pDictionaryChar = (char *) (DictionaryKey + sizeof (ULONG));
  193. pChallengeChar = (char *) ChallengeKey;
  194. for (i = 0; i < length; i++)
  195. if (*pDictionaryChar++ != *pChallengeChar++)
  196. return 1;
  197. return 0;
  198. }
  199. /* Function: insert
  200. * Inserts (key, value) pair in the dictionary
  201. *
  202. * Parameters:
  203. * new_key: The new key that has to be inserted in the dictionary
  204. * new_value: The value associated with the "new_key"
  205. * length: Used only for LENGTH_STRING_DICTIONARY dictionaries; specifies the length of the new key
  206. *
  207. * Return value:
  208. * TRUE, if the operation succeeds, FALSE, otherwise.
  209. *
  210. * Note:
  211. * If the "new_key" is already in the dictionary, the (new_key, new_value) pair is NOT
  212. * inserted (the dictionary remains the same), and the return value is TRUE.
  213. */
  214. BOOL DictionaryClass::insert (DWORD_PTR new_key, DWORD_PTR new_value, ULONG length)
  215. {
  216. PDICTIONARY_ITEM pNewItem; // Points to the allocated new dictionary item
  217. PDICTIONARY_ITEM p; // Goes through the bucket for the "new_key", searching for "new_key"
  218. DWORD hash_val; // The bucket ID for "new_key"
  219. BOOL bIsNative = TRUE; // TRUE, if the new allocated dictionary item is from the cache, FALSE otherwise
  220. if (Buckets == NULL)
  221. return FALSE;
  222. // Find if the item is already in the bucket, and if it's not, where it will get added.
  223. p = Buckets[hash_val = hashFunction (new_key)];
  224. ASSERT (hash_val < NumOfBuckets);
  225. if (p != NULL) {
  226. switch (Type) {
  227. case STRING_DICTIONARY:
  228. ASSERT (length == 0);
  229. for (; lstrcmp ((LPCTSTR) p->key, (LPCTSTR) new_key) && p->next != NULL; p = p->next);
  230. if (0 == lstrcmp ((LPCTSTR) p->key, (LPCTSTR) new_key)) {
  231. // the key already exists; no need to add it
  232. return TRUE;
  233. }
  234. break;
  235. case LENGTH_STRING_DICTIONARY:
  236. ASSERT (length > 0);
  237. for (; LengthStrcmp (p->key, new_key, length) && p->next != NULL; p = p->next);
  238. if (0 == LengthStrcmp (p->key, new_key, length)) {
  239. // the key already exists; no need to add it
  240. return TRUE;
  241. }
  242. break;
  243. default:
  244. ASSERT (length == 0);
  245. for (; p->key != new_key && p->next != NULL; p = p->next);
  246. if (p->key == new_key) {
  247. // the key already exists; no need to add it
  248. return TRUE;
  249. }
  250. break;
  251. }
  252. }
  253. // Allocate the new item
  254. if (ItemCount > 0)
  255. pNewItem = ItemArray[--ItemCount]; // from the cache
  256. else { // the cache is empty, we have to malloc the new item
  257. pNewItem = (PDICTIONARY_ITEM) MyMalloc (sizeof (DICTIONARY_ITEM));
  258. if (pNewItem == NULL)
  259. return FALSE;
  260. bIsNative = FALSE;
  261. NumOfExternItems++;
  262. }
  263. ASSERT (pNewItem != NULL);
  264. // Fill in the "key" field of the new item
  265. switch (Type) {
  266. case STRING_DICTIONARY:
  267. ASSERT (length == 0);
  268. pNewItem->key = (DWORD_PTR) MyMalloc ((lstrlen ((LPCTSTR) new_key) + 1) * sizeof(TCHAR));
  269. if (pNewItem->key == (DWORD) NULL) {
  270. if (bIsNative == FALSE) {
  271. // We have to free the allocated hash item
  272. MyFree (pNewItem);
  273. NumOfExternItems--;
  274. }
  275. else
  276. ItemCount++;
  277. return FALSE;
  278. }
  279. lstrcpy ((LPTSTR) pNewItem->key, (LPCTSTR) new_key);
  280. break;
  281. case LENGTH_STRING_DICTIONARY:
  282. ASSERT (length > 0);
  283. pNewItem->key = (DWORD_PTR) MyMalloc (sizeof (ULONG) + length * sizeof (TCHAR));
  284. if (pNewItem->key == (DWORD) NULL) {
  285. if (bIsNative == FALSE) {
  286. // We have to free the allocated hash item
  287. MyFree (pNewItem);
  288. NumOfExternItems--;
  289. }
  290. else
  291. ItemCount++;
  292. return FALSE;
  293. }
  294. * ((ULONG *) (pNewItem->key)) = length;
  295. memcpy ((void *) (pNewItem->key + sizeof (ULONG)), (void *) new_key, length * sizeof (TCHAR));
  296. break;
  297. default:
  298. ASSERT (length == 0);
  299. pNewItem->key = new_key;
  300. break;
  301. }
  302. // Fill in the rest of the fields of the new item
  303. pNewItem->value = new_value;
  304. pNewItem->next = NULL;
  305. // Insert the item in its bucket
  306. if (p == NULL) {
  307. Buckets[hash_val] = pNewItem;
  308. return TRUE;
  309. }
  310. p->next = pNewItem;
  311. return TRUE;
  312. }
  313. /* Function: remove
  314. * Removes (key, value) pair from the dictionary
  315. *
  316. * Parameters:
  317. * Key: The key that has to be removed from the dictionary
  318. * length: Used only for LENGTH_STRING_DICTIONARY dictionaries; specifies the length of the "Key"
  319. *
  320. * Return value:
  321. * None.
  322. *
  323. */
  324. void DictionaryClass::remove (DWORD_PTR Key, ULONG length)
  325. {
  326. PDICTIONARY_ITEM p, q; // They go through the dictionary items in "Key"'s bucket.
  327. // p: points to the current dictionary item in the bucket
  328. // q: points to the previous item
  329. DWORD hash_val; // The bucket ID for "Key"
  330. if (Buckets == NULL)
  331. return;
  332. // Find the item in the dictionary
  333. p = Buckets [hash_val = hashFunction (Key)];
  334. ASSERT (hash_val < NumOfBuckets);
  335. switch (Type) {
  336. case STRING_DICTIONARY:
  337. ASSERT (length == 0);
  338. for (q = NULL; p != NULL && lstrcmp ((LPCTSTR) p->key, (LPCTSTR) Key); q = p, p = p->next) ;
  339. break;
  340. case LENGTH_STRING_DICTIONARY:
  341. ASSERT (length > 0);
  342. for (q = NULL; p != NULL && LengthStrcmp (p->key, Key, length); q = p, p = p->next) ;
  343. break;
  344. default:
  345. ASSERT (length == 0);
  346. for (q = NULL; p != NULL && p->key != Key; q = p, p = p->next);
  347. break;
  348. }
  349. // Remove the item
  350. if (p == NULL)
  351. return;
  352. if (q == NULL)
  353. Buckets[hash_val] = p->next;
  354. else
  355. q->next = p->next;
  356. // Free the item found
  357. ASSERT (p != NULL);
  358. if (Type >= STRING_DICTIONARY)
  359. MyFree (p->key);
  360. hash_val = (DWORD)((PBYTE) p - (PBYTE) Buckets);
  361. if (hash_val < dwNormalSize)
  362. ItemArray[ItemCount++] = p;
  363. else {
  364. MyFree (p);
  365. NumOfExternItems--;
  366. }
  367. }
  368. /* Function: find
  369. * Looks up the key in the dictionary
  370. *
  371. * Parameters
  372. * Key: The key to lookup
  373. * pValue: Pointer to receive the value associated with "Key"
  374. * It can be NULL, if we just try to find whether "Key" is in the dictionary
  375. * length: Used only for LENGTH_STRING_DICTIONARY dictionaries; specifies the length of "Key"
  376. *
  377. * Return value:
  378. * FALSE, if "Key" is not found in the dictionary
  379. * TRUE, otherwise.
  380. */
  381. BOOL DictionaryClass::find (DWORD_PTR Key, PDWORD_PTR pValue, ULONG length)
  382. {
  383. PDICTIONARY_ITEM p; // Goes through the dictionary items in "Key"'s bucket.
  384. if (Buckets == NULL) {
  385. if (pValue != NULL)
  386. *pValue = 0;
  387. return FALSE;
  388. }
  389. // Find the item in the dictionary
  390. p = Buckets [hashFunction (Key)];
  391. switch (Type) {
  392. case STRING_DICTIONARY:
  393. ASSERT (length == 0);
  394. for (; p != NULL && lstrcmp ((LPCTSTR) p->key, (LPCTSTR) Key); p = p->next) ;
  395. break;
  396. case LENGTH_STRING_DICTIONARY:
  397. ASSERT (length > 0);
  398. for (; p != NULL && LengthStrcmp (p->key, Key, length); p = p->next) ;
  399. break;
  400. default:
  401. ASSERT (length == 0);
  402. for (; p != NULL && p->key != Key; p = p->next);
  403. break;
  404. }
  405. if (p == NULL) {
  406. // we did not find the "Key"
  407. if (pValue != NULL)
  408. *pValue = 0;
  409. return FALSE;
  410. }
  411. // "Key" was found
  412. if (pValue != NULL)
  413. *pValue = p->value;
  414. return TRUE;
  415. }
  416. /* Function: iterate
  417. * Iterates through the items of a dictionary. It remembers where it has
  418. * stopped during the last call and starts from there.
  419. *
  420. * Parameters
  421. * pValue: Pointer to DWORD that will hold the next value from the dictionary.
  422. * It can be NULL.
  423. * pKey: Pointer to DWORD or unsigned short value to receive the key associated with the value.
  424. * It can be NULL.
  425. *
  426. * Return value:
  427. * FALSE, when we reach the end of the dictionary
  428. * TRUE, otherwise. Then, *pKey and *pValue are valid
  429. *
  430. */
  431. BOOL DictionaryClass::iterate (PDWORD_PTR pValue, PDWORD_PTR pKey)
  432. {
  433. if (Buckets == NULL)
  434. return FALSE;
  435. if (pCurrent == NULL)
  436. // start from the 1st item in the dictionary
  437. pCurrent = Buckets[ulCurrentBucket = 0];
  438. else
  439. pCurrent = pCurrent->next;
  440. // Advance "pCurrent" until it's not NULL, or we reach the end of the dictionary.
  441. for (; ulCurrentBucket < NumOfBuckets; pCurrent = Buckets[++ulCurrentBucket]) {
  442. if (pCurrent != NULL) {
  443. // we found the next item
  444. if (pKey)
  445. *pKey = pCurrent->key;
  446. if (pValue)
  447. *pValue = pCurrent->value;
  448. return TRUE;
  449. }
  450. }
  451. pCurrent = NULL;
  452. return FALSE;
  453. }
  454. /* Function: isEmpty
  455. *
  456. * Parameters
  457. * None.
  458. *
  459. * Return value:
  460. * TRUE, if the dictionary is empty. FALSE, otherwise.
  461. *
  462. */
  463. BOOL DictionaryClass::isEmpty (void)
  464. {
  465. DWORD i;
  466. if (Buckets == NULL)
  467. return TRUE;
  468. for (i = 0; i < NumOfBuckets; i++)
  469. if (Buckets[i] != NULL)
  470. return FALSE;
  471. return TRUE;
  472. }
  473. /* Function: clear
  474. * Clears up the dictionary. No (key, value) pairs are left in the dictionary.
  475. *
  476. * Parameters:
  477. * None.
  478. *
  479. * Return value:
  480. * None.
  481. *
  482. */
  483. void DictionaryClass::clear (void)
  484. {
  485. DWORD i; // Goes through the "Buckets" array
  486. DWORD_PTR dwOffset; // The offset of a dictionary item is used to determine
  487. // whether it's a native item (that needs to be returned to the cache),
  488. // or not (and needs to be freed).
  489. PDICTIONARY_ITEM p, q; // Go through the items in a bucket
  490. if (Buckets != NULL) {
  491. // Go through the buckets to free the non-native items
  492. for (i = 0; i < NumOfBuckets; i++) {
  493. for (p = Buckets[i]; p != NULL; ) {
  494. if (Type >= STRING_DICTIONARY)
  495. MyFree (p->key);
  496. // Compute the offset of the current dictionary item
  497. dwOffset = (PBYTE) p - (PBYTE) Buckets;
  498. if (dwOffset < dwNormalSize)
  499. p = p->next;
  500. else {
  501. // if the item was not allocated in the initialization, we should free it.
  502. q = p;
  503. p = p->next;
  504. MyFree (q);
  505. }
  506. }
  507. Buckets[i] = NULL;
  508. }
  509. // Initialize the class iterator
  510. pCurrent = NULL;
  511. /* Initialize the Dictionary items array */
  512. ItemCount = 3 * NumOfBuckets;
  513. p = (PDICTIONARY_ITEM) (ItemArray + ItemCount);
  514. for (i = 0; i < ItemCount; i++)
  515. ItemArray[i] = p++;
  516. NumOfExternItems = 0;
  517. }
  518. }
  519.