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.

509 lines
19 KiB

  1. #include <precomp.h>
  2. #include "ErrCtrl.h"
  3. #include "utils.h"
  4. #include "hash.h"
  5. //~~~~~~~~~~~~~~~~~~~~~~~~ PRIVATE HASH FUNCTIONS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  6. //
  7. // Matches the keys one against the other.
  8. UINT // [RET] number of matching chars
  9. HshPrvMatchKeys(
  10. LPWSTR wszKey1, // [IN] key 1
  11. LPWSTR wszKey2) // [IN] key 2
  12. {
  13. UINT i = 0;
  14. while (*wszKey1 != L'\0' && *wszKey1 == *wszKey2)
  15. {
  16. wszKey1++; wszKey2++;
  17. i++;
  18. }
  19. return i;
  20. }
  21. // deletes all the pHash tree - doesn't touch the pObjects from
  22. // within (if any)
  23. VOID
  24. HshDestructor(
  25. PHASH_NODE pHash) // [IN] hash tree to delete
  26. {
  27. // pHash should not be NULL -- but who knows what the caller is doing!
  28. if (pHash != NULL)
  29. {
  30. while(!IsListEmpty(&(pHash->lstDown)))
  31. {
  32. PHASH_NODE pChild;
  33. pChild = CONTAINING_RECORD(pHash->lstDown.Flink, HASH_NODE, lstHoriz);
  34. HshDestructor(pChild);
  35. }
  36. RemoveEntryList(&(pHash->lstHoriz));
  37. MemFree(pHash->wszKey);
  38. MemFree(pHash);
  39. }
  40. }
  41. //~~~~~~~~~~~~~~~~~~~~~~~~ PUBLIC HASH FUNCTIONS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  42. //
  43. // Initializes a HASH structure
  44. DWORD
  45. HshInitialize(PHASH pHash)
  46. {
  47. DWORD dwErr = ERROR_SUCCESS;
  48. if (pHash != NULL)
  49. {
  50. __try
  51. {
  52. InitializeCriticalSection(&(pHash->csMutex));
  53. pHash->bValid = TRUE;
  54. }
  55. __except(EXCEPTION_EXECUTE_HANDLER)
  56. {
  57. dwErr = GetExceptionCode();
  58. }
  59. pHash->pRoot = NULL;
  60. }
  61. else
  62. dwErr = ERROR_INVALID_PARAMETER;
  63. SetLastError(dwErr);
  64. return dwErr;
  65. }
  66. // Cleans all resources referenced by a HASH structures
  67. VOID
  68. HshDestroy(PHASH pHash)
  69. {
  70. HshDestructor(pHash->pRoot);
  71. if (pHash->bValid)
  72. {
  73. DeleteCriticalSection(&(pHash->csMutex));
  74. pHash->bValid = FALSE;
  75. }
  76. }
  77. // Inserts an opaque object into the cache. The object is keyed on a wstring
  78. // The call could alter the structure of the hash, hence it returns the reference
  79. // to the updated hash.
  80. DWORD // [RET] win32 error code
  81. HshInsertObjectRef(
  82. PHASH_NODE pHash, // [IN] hash to operate on
  83. LPWSTR wszKey, // [IN] key of the object to insert
  84. LPVOID pObject, // [IN] object itself to insert in the cache
  85. PHASH_NODE *ppOutHash) // [OUT] pointer to the updated hash
  86. {
  87. DWORD dwErr = ERROR_SUCCESS;
  88. _Asrt(ppOutHash != NULL, L"No output hash expected??");
  89. _Asrt(wszKey != NULL, L"Key info should not be NULL");
  90. // if the node passed in is NULL this means a new node
  91. // has to be created
  92. if (pHash == NULL)
  93. {
  94. // this node is definitely not in the hash
  95. // allocate the new node
  96. pHash = MemCAlloc(sizeof(HASH_NODE));
  97. if (pHash == NULL)
  98. {
  99. dwErr = GetLastError();
  100. goto exit;
  101. }
  102. // allocate mem for the new key
  103. pHash->wszKey = MemCAlloc(sizeof(WCHAR)*(wcslen(wszKey)+1));
  104. if (pHash->wszKey == NULL)
  105. {
  106. dwErr = GetLastError();
  107. MemFree(pHash);
  108. goto exit;
  109. }
  110. // copy the new key
  111. wcscpy(pHash->wszKey, wszKey);
  112. // initialize the horizontal and down lists
  113. InitializeListHead(&(pHash->lstHoriz));
  114. InitializeListHead(&(pHash->lstDown));
  115. // copy the reference to the object associated with the key
  116. pHash->pObject = pObject;
  117. // at this point we have a standalone newly created node:
  118. // no links are defined either on the horizontal, downwards or upwards
  119. // these will be set up (if needed) by the caller.
  120. *ppOutHash = pHash;
  121. }
  122. // if the node passed in is not NULL, we need to match as many
  123. // characters between wszKey and the node's key. Based on who's shorter
  124. // we decide to either set the reference (if keys identical) or
  125. // break the branch (if wszKey is shorter than the current one) or
  126. // to recurse down the insertion.
  127. else
  128. {
  129. UINT nMatch;
  130. PHASH_NODE pChild;
  131. enum { MATCH, SUBSET, SUPERSET, UNDECIDED, RECURSE} nAnalysis;
  132. nMatch = HshPrvMatchKeys(wszKey, pHash->wszKey);
  133. // analyze the given key with respect to the current node;
  134. if (wszKey[nMatch] == L'\0' && pHash->wszKey[nMatch] == L'\0')
  135. // the key matches the current node
  136. nAnalysis = MATCH;
  137. else if (wszKey[nMatch] == L'\0')
  138. // the key is a subset of the current node
  139. nAnalysis = SUBSET;
  140. else
  141. {
  142. // so far undecided - further see if this translates to SUPERSET
  143. // or even better, if SUPERSET can be handled by a child, hence
  144. // RECURSE.
  145. nAnalysis = UNDECIDED;
  146. if (pHash->wszKey[nMatch] == L'\0')
  147. {
  148. PLIST_ENTRY pEntry;
  149. // the new key is a superset of the current nod
  150. nAnalysis = SUPERSET;
  151. // the new key could be covered by one of the existent children.
  152. // check then if it is the case to send the work down to some child
  153. for (pEntry = pHash->lstDown.Flink;
  154. pEntry != &(pHash->lstDown);
  155. pEntry = pEntry->Flink)
  156. {
  157. pChild = CONTAINING_RECORD(pEntry, HASH_NODE, lstHoriz);
  158. if (wszKey[nMatch] == pChild->wszKey[0])
  159. {
  160. // the child to follow up has been located and saved
  161. // in pChild. Set nAnalysis to UNDECIDED and break;
  162. nAnalysis = RECURSE;
  163. break;
  164. }
  165. }
  166. }
  167. }
  168. // if the key matches exactly the current node
  169. // then is only a matter of setting the object's reference
  170. if (nAnalysis == MATCH)
  171. {
  172. // if the node is already referencing an object..
  173. if (pHash->pObject != NULL)
  174. {
  175. // signal out ERROR_DUPLICATE_TAG
  176. dwErr = ERROR_DUPLICATE_TAG;
  177. }
  178. else
  179. {
  180. // just insert the reference and get out with success.
  181. pHash->pObject = pObject;
  182. // save the Out hash pointer
  183. *ppOutHash = pHash;
  184. }
  185. }
  186. // if a child has been identified, let pChild (saved previously)
  187. // to follow up
  188. else if (nAnalysis == RECURSE)
  189. {
  190. dwErr = HshInsertObjectRef(
  191. pChild,
  192. wszKey+nMatch,
  193. pObject,
  194. ppOutHash);
  195. if (dwErr == ERROR_SUCCESS)
  196. *ppOutHash = pHash;
  197. }
  198. // if any of SUBSET, SUPERSET or UNDECIDED
  199. else
  200. {
  201. PHASH_NODE pParent = NULL;
  202. LPWSTR wszTrailKey = NULL;
  203. UINT nTrailLen = 0;
  204. // [C = common part; c = current key; n = new key]
  205. // (SUBSET) (UNDECIDED)
  206. // current: CCCCCccc or current: CCCCCccc
  207. // new: CCCCC new: CCCCCnnnnn
  208. //
  209. // In both cases, the current node splits.
  210. // Create first a new node, containing just CCCCC
  211. if (nAnalysis != SUPERSET)
  212. {
  213. // get the number of chars in pHash's key that are not matching
  214. nTrailLen = wcslen(pHash->wszKey) - nMatch;
  215. // create first the trailing part of the key (allocate the number of
  216. // chars from the current node that didn't match)
  217. wszTrailKey = MemCAlloc(sizeof(WCHAR)*(nTrailLen+1));
  218. if (wszTrailKey == NULL)
  219. {
  220. // if anything went wrong, just go out with the error.
  221. // hash hasn't been modified.
  222. dwErr = GetLastError();
  223. goto exit;
  224. }
  225. // wcsncpy doesn't append the null terminator but the string
  226. // is already nulled out and it has the right size
  227. wcsncpy(wszTrailKey, pHash->wszKey+nMatch, nTrailLen);
  228. // create then the node that will act as the common parent
  229. pHash->wszKey[nMatch] = L'\0';
  230. dwErr = HshInsertObjectRef(
  231. NULL, // request a new node to be created
  232. pHash->wszKey, // common part of the current keys
  233. NULL, // this node is not referencing any object
  234. &pParent); // get back the newly created pointer.
  235. pHash->wszKey[nMatch] = wszTrailKey[0];
  236. // in case anything went wrong, the hash has not been altered,
  237. // we just need to bubble up the error.
  238. if (dwErr != ERROR_SUCCESS)
  239. {
  240. MemFree(wszTrailKey);
  241. goto exit;
  242. }
  243. // set the new parent up link
  244. pParent->pUpLink = pHash->pUpLink;
  245. }
  246. // (SUPERSET) (UNDECIDED)
  247. // current: CCCCC or current: CCCCCccccc
  248. // new: CCCCCnnn new: CCCCCnnn
  249. // In both cases a new node has to be created for the "nnn" part.
  250. if (nAnalysis != SUBSET)
  251. {
  252. dwErr = HshInsertObjectRef(
  253. NULL,
  254. wszKey + nMatch,
  255. pObject,
  256. &pChild);
  257. if (dwErr != ERROR_SUCCESS)
  258. {
  259. // second creation failed, clean up everything and bail out
  260. MemFree(wszTrailKey);
  261. if (pParent != NULL)
  262. {
  263. MemFree(pParent->wszKey);
  264. MemFree(pParent);
  265. }
  266. // hash structure is not altered at this point.
  267. goto exit;
  268. }
  269. // link it up to the corresponding parent.
  270. pChild->pUpLink = (nAnalysis == SUPERSET) ? pHash : pParent;
  271. }
  272. // NO WAY BACK FROM NOW ON - hash is about to be altered
  273. // success is guaranteed
  274. // at this point, pChild is a non null pointer, with all the
  275. // LIST_ENTRIES from within the HASH_NODE initialized correctly.
  276. // (SUBSET) (UNDECIDED)
  277. // current: CCCCCccccc or current: CCCCCccccc
  278. // new: CCCCC new: CCCCCnnn
  279. // if the node has split, put the new parent in its place
  280. if (nAnalysis != SUPERSET)
  281. {
  282. // set the current key to the shrinked unmatched trailing part
  283. MemFree(pHash->wszKey);
  284. pHash->wszKey = wszTrailKey;
  285. // set the current upLink to the new pParent node
  286. pHash->pUpLink = pParent;
  287. // insert the new parent into its place
  288. InsertHeadList(&(pHash->lstHoriz), &(pParent->lstHoriz));
  289. // remove the node from its previous parent
  290. RemoveEntryList(&(pHash->lstHoriz));
  291. // reset the current node's list
  292. InitializeListHead(&(pHash->lstHoriz));
  293. // insert the node to its new parent down list.
  294. InsertHeadList(&(pParent->lstDown), &(pHash->lstHoriz));
  295. }
  296. // (SUPERSET) (UNDECIDED)
  297. // current: CCCCC or current: CCCCCccccc
  298. // new: CCCCCnnn new: CCCCCnnn
  299. // if a new child node has been created, link it in the hash
  300. if (nAnalysis != SUBSET)
  301. {
  302. // if the given key was either a superset of the
  303. // current key or derived from the current key,
  304. // there is a separated node for it. Insert it in the down list
  305. if (nAnalysis == SUPERSET)
  306. {
  307. InsertTailList(&(pHash->lstDown), &(pChild->lstHoriz));
  308. }
  309. else
  310. {
  311. InsertTailList(&(pParent->lstDown), &(pChild->lstHoriz));
  312. }
  313. }
  314. else
  315. {
  316. // set the new parent's reference to this data.
  317. pParent->pObject = pObject;
  318. }
  319. *ppOutHash = (nAnalysis == SUPERSET) ? pHash : pParent;
  320. // and that's all - we're done successfully!
  321. }
  322. }
  323. exit:
  324. SetLastError(dwErr);
  325. return dwErr;
  326. }
  327. // Retrieves an object from the hash. The hash structure is not touched in
  328. // any manner.
  329. DWORD // [RET] win32 error code
  330. HshQueryObjectRef(
  331. PHASH_NODE pHash, // [IN] hash to operate on
  332. LPWSTR wszKey, // [IN] key of the object to retrieve
  333. PHASH_NODE *ppHashNode) // [OUT] hash node referencing the queried object
  334. {
  335. DWORD dwErr = ERROR_FILE_NOT_FOUND;
  336. INT nDiff;
  337. if (wszKey == NULL)
  338. {
  339. dwErr = ERROR_INVALID_PARAMETER;
  340. goto exit;
  341. }
  342. if (pHash == NULL)
  343. {
  344. dwErr = ERROR_FILE_NOT_FOUND;
  345. goto exit;
  346. }
  347. nDiff = wcscmp(wszKey, pHash->wszKey);
  348. if (nDiff == 0)
  349. {
  350. // The key is identical with the one in this node
  351. if (pHash->pObject != NULL)
  352. {
  353. if (ppHashNode != NULL)
  354. {
  355. *ppHashNode = pHash;
  356. }
  357. dwErr = ERROR_SUCCESS;
  358. }
  359. // If there is no object in this node, this means
  360. // the query failed with FILE_NOT_FOUND
  361. }
  362. else if (nDiff > 0)
  363. {
  364. // The key is larger than the current node's key
  365. UINT nTrail = wcslen(pHash->wszKey);
  366. PLIST_ENTRY pEntry;
  367. // The trailing part of the key could be covered by one of
  368. // the children nodes. Scan then the Down list.
  369. for (pEntry = pHash->lstDown.Flink;
  370. pEntry != &(pHash->lstDown);
  371. pEntry = pEntry->Flink)
  372. {
  373. PHASH_NODE pChild;
  374. pChild = CONTAINING_RECORD(pEntry, HASH_NODE, lstHoriz);
  375. if (wszKey[nTrail] == pChild->wszKey[0])
  376. {
  377. // the child to follow up has been located and saved
  378. // in pChild. Try to match the trailing key against this node
  379. dwErr = HshQueryObjectRef(pChild, wszKey+nTrail, ppHashNode);
  380. break;
  381. }
  382. }
  383. // if no child has been located, dwErr is the default FILE_NOT_FOUND
  384. }
  385. // if nDiff < 0 - meaning key is included in the current node's key then
  386. // dwErr is the default FILE_NOT_FOUND which is good.
  387. exit:
  388. SetLastError(dwErr);
  389. return dwErr;
  390. }
  391. // Removes the object referenced by the pHash node. This could lead to one or
  392. // more hash node removals (if a leaf node on an isolated branch) but it could
  393. // also let the hash node untouched (i.e. internal node).
  394. // It is the caller's responsibility to clean up the object pointed by ppObject
  395. DWORD // [RET] win32 error code
  396. HshRemoveObjectRef(
  397. PHASH_NODE pHash, // [IN] hash to operate on
  398. PHASH_NODE pRemoveNode, // [IN] hash node to clear the reference to pObject
  399. LPVOID *ppObject, // [OUT] pointer to the object whose reference has been cleared
  400. PHASH_NODE *ppOutHash) // [OUT] pointer to the updated hash
  401. {
  402. DWORD dwErr = ERROR_SUCCESS;
  403. PHASH_NODE pNewRoot = NULL;
  404. _Asrt(ppObject != NULL, L"No output object expected??");
  405. _Asrt(ppOutHash != NULL, L"No output hash expected??");
  406. _Asrt(pRemoveNode != NULL, L"No node to remove??");
  407. // if the node contains no reference, return FILE_NOT_FOUND
  408. if (pRemoveNode->pObject == NULL)
  409. {
  410. dwErr = ERROR_FILE_NOT_FOUND;
  411. goto exit;
  412. }
  413. // remove the reference to the object at this moment
  414. *ppObject = pRemoveNode->pObject;
  415. pRemoveNode->pObject = NULL;
  416. // now climb the tree up to the root (!! - well it's a reversed tree) and merge
  417. // whatever nodes can be merged.
  418. // Merging: A node not referencing any object and having 0 or at most 1
  419. // successor can be deleted from the hash tree structure.
  420. while ((pRemoveNode != NULL) &&
  421. (pRemoveNode->pObject == NULL) &&
  422. (pRemoveNode->lstDown.Flink->Flink == &(pRemoveNode->lstDown)))
  423. {
  424. PHASH_NODE pUp = pRemoveNode->pUpLink;
  425. // if there is exactly 1 successor, its key needs to be prefixed with
  426. // the key of the node that is about to be removed. The successor also
  427. // needs to replace its parent in the hash tree structure.
  428. if (!IsListEmpty(&(pRemoveNode->lstDown)))
  429. {
  430. PHASH_NODE pDown;
  431. LPWSTR wszNewKey;
  432. pDown = CONTAINING_RECORD(pRemoveNode->lstDown.Flink, HASH_NODE, lstHoriz);
  433. wszNewKey = MemCAlloc(sizeof(WCHAR)*(wcslen(pRemoveNode->wszKey)+wcslen(pDown->wszKey)+1));
  434. if (wszNewKey == NULL)
  435. {
  436. // if the allocation failed, bail out with the error code.
  437. // the reference had already been removed, the hash might
  438. // not be compacted but it is still valid!
  439. dwErr = GetLastError();
  440. goto exit;
  441. }
  442. wcscpy(wszNewKey, pRemoveNode->wszKey);
  443. wcscat(wszNewKey, pDown->wszKey);
  444. MemFree(pDown->wszKey);
  445. pDown->wszKey = wszNewKey;
  446. // now raise the child node as a replacement of its parent
  447. pDown->pUpLink = pRemoveNode->pUpLink;
  448. InsertHeadList(&(pRemoveNode->lstHoriz), &(pDown->lstHoriz));
  449. pNewRoot = pDown;
  450. }
  451. // remove the current node
  452. RemoveEntryList(&(pRemoveNode->lstHoriz));
  453. // cleanup all its memory
  454. MemFree(pRemoveNode->wszKey);
  455. MemFree(pRemoveNode);
  456. // finally go and check the upper level node (if there is any)
  457. pRemoveNode = pUp;
  458. }
  459. // if pRemoveNode ended up to be NULL, this means either the whole hash has been cleared
  460. // or a "brother" took the role of the root. pNewRoot has the right value in both cases
  461. // if pRemoveNode is not NULL, since it walked up the chain constantly it means pHash (the
  462. // original root) was not affected. Hence, return it out.
  463. *ppOutHash = (pRemoveNode == NULL) ? pNewRoot : pHash;
  464. exit:
  465. SetLastError(dwErr);
  466. return dwErr;
  467. }