|
|
/*
* tree.c * * data type providing a map between a KEY and a VALUE. The KEY is a * 32-bit DWORD, and the VALUE is any arbitrary area of storage. * * memory is allocated from gmem_get, using hHeap as the heap handle. * hHeap must be declared and initialised elsewhere. * * currently implemented as a unbalanced binary tree. * * Geraint Davies, July 92 */
#include <windows.h>
#include <stdlib.h>
#include <memory.h>
#include "gutils.h"
#include "tree.h"
/* -- data types ----------------------------------------------- */
/* on creating a tree, we return a TREE handle. This is in fact a pointer
* to a struct tree, defined here. */ struct tree { HANDLE hHeap; TREEITEM first; };
/* each element in the tree is stored in a TREEITEM. a TREEITEM handle
* is a pointer to a struct treeitem, defined here */ struct treeitem { TREE root; TREEKEY key; TREEITEM left, right; UINT length; /* length of the user's data */ LPVOID data; /* pointer to our copy of the users data */ };
/* -- internal functions ---------------------------------------------*/
/* free up an element of the tree. recursively calls itself to
* free left and right children */ void tree_delitem(TREEITEM item) { if (item == NULL) { return; } if (item->left != NULL) { tree_delitem(item->left); } if (item->right != NULL) { tree_delitem(item->right); } if (item->data != NULL) { gmem_free(item->root->hHeap, item->data, item->length); }
gmem_free(item->root->hHeap, (LPSTR) item, sizeof(struct treeitem)); }
/* create a new treeitem, with a data block of length bytes.
* if the value pointer is not NULL, initialise the data block with * the contents of value. */ TREEITEM tree_newitem(TREE root, TREEKEY key, LPVOID value, UINT length) { TREEITEM item;
item = (TREEITEM) gmem_get(root->hHeap, sizeof(struct treeitem));
item->root = root; item->key = key; item->left = NULL; item->right = NULL; item->length = length; item->data = gmem_get(root->hHeap, length); if (value != NULL) { memcpy(item->data, value, length); }
return(item); }
/* find the item with the given key. if it does not exist, return
* the parent item to which it would be attached. returns NULL if * no items in the tree */ TREEITEM tree_getitem(TREE tree, TREEKEY key) { TREEITEM item, prev;
prev = NULL; for (item = tree->first; item != NULL; ) {
if (item->key == key) { return(item); }
/* not this item - go on to the correct child item.
* remember this item as if the child is NULL, this item * will be the correct insertion point for the new item */ prev = item;
if (key < item->key) { item = item->left; } else { item = item->right; } } /* prev is the parent - or null if nothing in tree */ return(prev); }
/* --- external functions ------------------------------------------ */
/*
* create an empty tree. hHeap is the handle to use for all * memory allocations for this tree. */ TREE APIENTRY tree_create(HANDLE hHeap) { TREE tree;
tree = (TREE) gmem_get(hHeap, sizeof(struct tree)); tree->first = NULL; tree->hHeap = hHeap; return(tree); }
/*
* delete an entire tree, including all the user data */ void APIENTRY tree_delete(TREE tree) {
tree_delitem(tree->first);
gmem_free(tree->hHeap, (LPSTR) tree, sizeof(struct tree)); }
/*
* add a new element to the tree, mapping the key given to the value given. * The value is a block of storage: a copy of this is inserted into the tree. * we return a pointer to the copy of the data in the tree. * * the value pointer can be NULL: in this case, we insert a block of * length bytes, but don't initialise it. you get a pointer to it and * can initialise it yourself. * * if the key already exists, the value will be replaced with the new data. */ LPVOID APIENTRY tree_update(TREE tree, TREEKEY key, LPVOID value, UINT length) { TREEITEM item;
/* find the place in the tree for this key to go */ item = tree_getitem(tree, key);
if (item == NULL) { /* there is nothing in the tree: this item should
* go at the top */ tree->first = tree_newitem(tree, key, value, length); return(tree->first->data); }
/* is this the same key ? */ if (item->key == key) {
/* this key already inserted. re-alloc the data */ if (length != item->length) { gmem_free(tree->hHeap, item->data, item->length); item->data = gmem_get(tree->hHeap, length); } /* don't initialise block if no pointer passed */ if (value != NULL) { memcpy(item->data, value, length); } return(item->data); }
/* not the same key - getitem returned the parent for
* the new tree. insert it as a child of item. */ return(tree_addafter(tree, &item, key, value, length)); }
/*
* return a pointer to the value (data block) for a given key. returns * null if not found. */ LPVOID APIENTRY tree_find(TREE tree, TREEKEY key) { TREEITEM item;
/* find the correct place in the tree */ item = tree_getitem(tree, key);
if (item == NULL) { /* nothing in the tree */ return(NULL); }
if (item->key != key) { /* this key not in. getitem has returned parent */ return(NULL); }
/* found the right element - return pointer to the
* data block */ return(item->data); }
/*
* next two routines are an optimisation for a common tree operation. in * this case, the user will want to insert a new element only if * the key is not there. if it is there, he will want to modify the * existing value (increment a reference count, for example). * * if tree_search fails to find the key, it will return a TREEITEM handle * for the parent. This can be passed to tree_addafter to insert the * new element without re-searching the tree. */
/*
* find an element. if not, find it's correct parent item */ LPVOID APIENTRY tree_search(TREE tree, TREEKEY key, PTREEITEM pplace) { TREEITEM item;
item = tree_getitem(tree, key);
if (item == NULL) { /* no items in tree. set placeholder to NULL to
* indicate insert at top of tree */ *pplace = NULL;
/* return NULL to indicate key not found */ return(NULL); }
if (item->key == key) { /* found the key already there -
* set pplace to null just for safety */ *pplace = NULL;
/* give the user a pointer to his data */ return(item->data); }
/* key was not found - getitem has returned the parent
* - set this as the place for new insertions */ *pplace = item;
/* return NULL to indicate that the key was not found */ return(NULL); }
/*
* insert a key in the position already found by tree_search. * * return a pointer to the user's data in the tree. if the value * pointer passed in is null, then we allocate the block, but don't * initialise it to anything. */ LPVOID APIENTRY tree_addafter(TREE tree, PTREEITEM place, TREEKEY key, LPVOID value, UINT length) { TREEITEM item, child;
item = *place; if (item == NULL) { tree->first = tree_newitem(tree, key, value, length); return (tree->first->data); }
child = tree_newitem(tree, key, value, length); if (child->key < item->key ) { /* should go on left leg */ if (item->left != NULL) { Trace_Error(NULL, "TREE: left leaf leg not free", FALSE);
} item->left = child; } else { if (item->right != NULL) { Trace_Error(NULL, "TREE: right leaf leg not free", FALSE); } item->right = child; } return(child->data); }
/* --- ctree ------------------------------------------------------*/
/*
* ctree is a class of tree built on top of the tree interface. a * ctree keeps count of the number of insertions of identical keys. * * we do this be adding a long counter to the beginning of the user * data before inserting into the tree. if the key is not found, we set * this to one. If the key was already there, we *do not* insert the * data (data is always from the first insertion) - we simply increment * the count. */
/*
* create a tree for use by CTREE - same as an ordinary tree */ TREE APIENTRY ctree_create(HANDLE hHeap) { return(tree_create(hHeap)); }
/*
* delete a ctree - same as for TREE */ void APIENTRY ctree_delete(TREE tree) { tree_delete(tree); }
/* insert an element in the tree. if the element is not there,
* insert the data and set the reference count for this key to 1. * if the key was there already, don't change the data, just increment * the reference count * * if the value pointer is not null, we initialise the value block * in the tree to contain this. * * we return a pointer to the users data in the tree */ LPVOID APIENTRY ctree_update(TREE tree, TREEKEY key, LPVOID value, UINT length) { TREEITEM item; LONG_PTR FAR * pcounter; LPVOID datacopy;
pcounter = tree_search(tree, key, &item);
if (pcounter == NULL) { /* element not found - insert a new one
* the data block for this element should be * the user's block with our reference count at * the beginning */ pcounter = tree_addafter(tree, &item, key, NULL, length + sizeof(LONG_PTR)); *pcounter = 1; /* add on size of one long to get the start of the user
* data */ datacopy = pcounter + 1; if (value != NULL) { memcpy(datacopy, value, length); } return(datacopy); }
/* key was already there - increment reference count and
* return pointer to data */
(*pcounter)++;
/* add on size of one long to get the start of the user
* data */ datacopy = pcounter + 1; return(datacopy); }
/* return the reference count for this key */ long APIENTRY ctree_getcount(TREE tree, TREEKEY key) { LONG_PTR FAR * pcounter;
pcounter = tree_find(tree, key); if (pcounter == NULL) { return(0); } return((long)*pcounter); }
/* return a pointer to the user's data block for this key,
* or NULL if key not present */ LPVOID APIENTRY ctree_find(TREE tree, TREEKEY key) { LONG_PTR FAR * pcounter;
pcounter = tree_find(tree, key); if (pcounter == NULL) { return(0); }
/* increment pointer by size of 1 long to point to
* user's datablock */ return(pcounter+1); }
|