windows-nt/Source/XPSP1/NT/sdktools/gutils/tree.c
2020-09-26 16:20:57 +08:00

434 lines
11 KiB
C

/*
* 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);
}