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.

433 lines
11 KiB

  1. /*
  2. * tree.c
  3. *
  4. * data type providing a map between a KEY and a VALUE. The KEY is a
  5. * 32-bit DWORD, and the VALUE is any arbitrary area of storage.
  6. *
  7. * memory is allocated from gmem_get, using hHeap as the heap handle.
  8. * hHeap must be declared and initialised elsewhere.
  9. *
  10. * currently implemented as a unbalanced binary tree.
  11. *
  12. * Geraint Davies, July 92
  13. */
  14. #include <windows.h>
  15. #include <stdlib.h>
  16. #include <memory.h>
  17. #include "gutils.h"
  18. #include "tree.h"
  19. /* -- data types ----------------------------------------------- */
  20. /* on creating a tree, we return a TREE handle. This is in fact a pointer
  21. * to a struct tree, defined here.
  22. */
  23. struct tree {
  24. HANDLE hHeap;
  25. TREEITEM first;
  26. };
  27. /* each element in the tree is stored in a TREEITEM. a TREEITEM handle
  28. * is a pointer to a struct treeitem, defined here
  29. */
  30. struct treeitem {
  31. TREE root;
  32. TREEKEY key;
  33. TREEITEM left, right;
  34. UINT length; /* length of the user's data */
  35. LPVOID data; /* pointer to our copy of the users data */
  36. };
  37. /* -- internal functions ---------------------------------------------*/
  38. /* free up an element of the tree. recursively calls itself to
  39. * free left and right children
  40. */
  41. void
  42. tree_delitem(TREEITEM item)
  43. {
  44. if (item == NULL) {
  45. return;
  46. }
  47. if (item->left != NULL) {
  48. tree_delitem(item->left);
  49. }
  50. if (item->right != NULL) {
  51. tree_delitem(item->right);
  52. }
  53. if (item->data != NULL) {
  54. gmem_free(item->root->hHeap, item->data, item->length);
  55. }
  56. gmem_free(item->root->hHeap, (LPSTR) item, sizeof(struct treeitem));
  57. }
  58. /* create a new treeitem, with a data block of length bytes.
  59. * if the value pointer is not NULL, initialise the data block with
  60. * the contents of value.
  61. */
  62. TREEITEM
  63. tree_newitem(TREE root, TREEKEY key, LPVOID value, UINT length)
  64. {
  65. TREEITEM item;
  66. item = (TREEITEM) gmem_get(root->hHeap, sizeof(struct treeitem));
  67. item->root = root;
  68. item->key = key;
  69. item->left = NULL;
  70. item->right = NULL;
  71. item->length = length;
  72. item->data = gmem_get(root->hHeap, length);
  73. if (value != NULL) {
  74. memcpy(item->data, value, length);
  75. }
  76. return(item);
  77. }
  78. /* find the item with the given key. if it does not exist, return
  79. * the parent item to which it would be attached. returns NULL if
  80. * no items in the tree
  81. */
  82. TREEITEM
  83. tree_getitem(TREE tree, TREEKEY key)
  84. {
  85. TREEITEM item, prev;
  86. prev = NULL;
  87. for (item = tree->first; item != NULL; ) {
  88. if (item->key == key) {
  89. return(item);
  90. }
  91. /* not this item - go on to the correct child item.
  92. * remember this item as if the child is NULL, this item
  93. * will be the correct insertion point for the new item
  94. */
  95. prev = item;
  96. if (key < item->key) {
  97. item = item->left;
  98. } else {
  99. item = item->right;
  100. }
  101. }
  102. /* prev is the parent - or null if nothing in tree */
  103. return(prev);
  104. }
  105. /* --- external functions ------------------------------------------ */
  106. /*
  107. * create an empty tree. hHeap is the handle to use for all
  108. * memory allocations for this tree.
  109. */
  110. TREE APIENTRY
  111. tree_create(HANDLE hHeap)
  112. {
  113. TREE tree;
  114. tree = (TREE) gmem_get(hHeap, sizeof(struct tree));
  115. tree->first = NULL;
  116. tree->hHeap = hHeap;
  117. return(tree);
  118. }
  119. /*
  120. * delete an entire tree, including all the user data
  121. */
  122. void APIENTRY
  123. tree_delete(TREE tree)
  124. {
  125. tree_delitem(tree->first);
  126. gmem_free(tree->hHeap, (LPSTR) tree, sizeof(struct tree));
  127. }
  128. /*
  129. * add a new element to the tree, mapping the key given to the value given.
  130. * The value is a block of storage: a copy of this is inserted into the tree.
  131. * we return a pointer to the copy of the data in the tree.
  132. *
  133. * the value pointer can be NULL: in this case, we insert a block of
  134. * length bytes, but don't initialise it. you get a pointer to it and
  135. * can initialise it yourself.
  136. *
  137. * if the key already exists, the value will be replaced with the new data.
  138. */
  139. LPVOID APIENTRY
  140. tree_update(TREE tree, TREEKEY key, LPVOID value, UINT length)
  141. {
  142. TREEITEM item;
  143. /* find the place in the tree for this key to go */
  144. item = tree_getitem(tree, key);
  145. if (item == NULL) {
  146. /* there is nothing in the tree: this item should
  147. * go at the top
  148. */
  149. tree->first = tree_newitem(tree, key, value, length);
  150. return(tree->first->data);
  151. }
  152. /* is this the same key ? */
  153. if (item->key == key) {
  154. /* this key already inserted. re-alloc the data */
  155. if (length != item->length) {
  156. gmem_free(tree->hHeap, item->data, item->length);
  157. item->data = gmem_get(tree->hHeap, length);
  158. }
  159. /* don't initialise block if no pointer passed */
  160. if (value != NULL) {
  161. memcpy(item->data, value, length);
  162. }
  163. return(item->data);
  164. }
  165. /* not the same key - getitem returned the parent for
  166. * the new tree. insert it as a child of item.
  167. */
  168. return(tree_addafter(tree, &item, key, value, length));
  169. }
  170. /*
  171. * return a pointer to the value (data block) for a given key. returns
  172. * null if not found.
  173. */
  174. LPVOID APIENTRY
  175. tree_find(TREE tree, TREEKEY key)
  176. {
  177. TREEITEM item;
  178. /* find the correct place in the tree */
  179. item = tree_getitem(tree, key);
  180. if (item == NULL) {
  181. /* nothing in the tree */
  182. return(NULL);
  183. }
  184. if (item->key != key) {
  185. /* this key not in. getitem has returned parent */
  186. return(NULL);
  187. }
  188. /* found the right element - return pointer to the
  189. * data block
  190. */
  191. return(item->data);
  192. }
  193. /*
  194. * next two routines are an optimisation for a common tree operation. in
  195. * this case, the user will want to insert a new element only if
  196. * the key is not there. if it is there, he will want to modify the
  197. * existing value (increment a reference count, for example).
  198. *
  199. * if tree_search fails to find the key, it will return a TREEITEM handle
  200. * for the parent. This can be passed to tree_addafter to insert the
  201. * new element without re-searching the tree.
  202. */
  203. /*
  204. * find an element. if not, find it's correct parent item
  205. */
  206. LPVOID APIENTRY
  207. tree_search(TREE tree, TREEKEY key, PTREEITEM pplace)
  208. {
  209. TREEITEM item;
  210. item = tree_getitem(tree, key);
  211. if (item == NULL) {
  212. /* no items in tree. set placeholder to NULL to
  213. * indicate insert at top of tree
  214. */
  215. *pplace = NULL;
  216. /* return NULL to indicate key not found */
  217. return(NULL);
  218. }
  219. if (item->key == key) {
  220. /* found the key already there -
  221. * set pplace to null just for safety
  222. */
  223. *pplace = NULL;
  224. /* give the user a pointer to his data */
  225. return(item->data);
  226. }
  227. /* key was not found - getitem has returned the parent
  228. * - set this as the place for new insertions
  229. */
  230. *pplace = item;
  231. /* return NULL to indicate that the key was not found */
  232. return(NULL);
  233. }
  234. /*
  235. * insert a key in the position already found by tree_search.
  236. *
  237. * return a pointer to the user's data in the tree. if the value
  238. * pointer passed in is null, then we allocate the block, but don't
  239. * initialise it to anything.
  240. */
  241. LPVOID APIENTRY
  242. tree_addafter(TREE tree, PTREEITEM place, TREEKEY key, LPVOID value, UINT length)
  243. {
  244. TREEITEM item, child;
  245. item = *place;
  246. if (item == NULL) {
  247. tree->first = tree_newitem(tree, key, value, length);
  248. return (tree->first->data);
  249. }
  250. child = tree_newitem(tree, key, value, length);
  251. if (child->key < item->key ) {
  252. /* should go on left leg */
  253. if (item->left != NULL) {
  254. Trace_Error(NULL, "TREE: left leaf leg not free", FALSE);
  255. }
  256. item->left = child;
  257. } else {
  258. if (item->right != NULL) {
  259. Trace_Error(NULL, "TREE: right leaf leg not free", FALSE);
  260. }
  261. item->right = child;
  262. }
  263. return(child->data);
  264. }
  265. /* --- ctree ------------------------------------------------------*/
  266. /*
  267. * ctree is a class of tree built on top of the tree interface. a
  268. * ctree keeps count of the number of insertions of identical keys.
  269. *
  270. * we do this be adding a long counter to the beginning of the user
  271. * data before inserting into the tree. if the key is not found, we set
  272. * this to one. If the key was already there, we *do not* insert the
  273. * data (data is always from the first insertion) - we simply increment
  274. * the count.
  275. */
  276. /*
  277. * create a tree for use by CTREE - same as an ordinary tree
  278. */
  279. TREE APIENTRY
  280. ctree_create(HANDLE hHeap)
  281. {
  282. return(tree_create(hHeap));
  283. }
  284. /*
  285. * delete a ctree - same as for TREE
  286. */
  287. void APIENTRY
  288. ctree_delete(TREE tree)
  289. {
  290. tree_delete(tree);
  291. }
  292. /* insert an element in the tree. if the element is not there,
  293. * insert the data and set the reference count for this key to 1.
  294. * if the key was there already, don't change the data, just increment
  295. * the reference count
  296. *
  297. * if the value pointer is not null, we initialise the value block
  298. * in the tree to contain this.
  299. *
  300. * we return a pointer to the users data in the tree
  301. */
  302. LPVOID APIENTRY
  303. ctree_update(TREE tree, TREEKEY key, LPVOID value, UINT length)
  304. {
  305. TREEITEM item;
  306. LONG_PTR FAR * pcounter;
  307. LPVOID datacopy;
  308. pcounter = tree_search(tree, key, &item);
  309. if (pcounter == NULL) {
  310. /* element not found - insert a new one
  311. * the data block for this element should be
  312. * the user's block with our reference count at
  313. * the beginning
  314. */
  315. pcounter = tree_addafter(tree, &item, key, NULL,
  316. length + sizeof(LONG_PTR));
  317. *pcounter = 1;
  318. /* add on size of one long to get the start of the user
  319. * data
  320. */
  321. datacopy = pcounter + 1;
  322. if (value != NULL) {
  323. memcpy(datacopy, value, length);
  324. }
  325. return(datacopy);
  326. }
  327. /* key was already there - increment reference count and
  328. * return pointer to data
  329. */
  330. (*pcounter)++;
  331. /* add on size of one long to get the start of the user
  332. * data
  333. */
  334. datacopy = pcounter + 1;
  335. return(datacopy);
  336. }
  337. /* return the reference count for this key */
  338. long APIENTRY
  339. ctree_getcount(TREE tree, TREEKEY key)
  340. {
  341. LONG_PTR FAR * pcounter;
  342. pcounter = tree_find(tree, key);
  343. if (pcounter == NULL) {
  344. return(0);
  345. }
  346. return((long)*pcounter);
  347. }
  348. /* return a pointer to the user's data block for this key,
  349. * or NULL if key not present
  350. */
  351. LPVOID APIENTRY
  352. ctree_find(TREE tree, TREEKEY key)
  353. {
  354. LONG_PTR FAR * pcounter;
  355. pcounter = tree_find(tree, key);
  356. if (pcounter == NULL) {
  357. return(0);
  358. }
  359. /* increment pointer by size of 1 long to point to
  360. * user's datablock
  361. */
  362. return(pcounter+1);
  363. }