/*
* Copyright (c) 2004 Sendmail, Inc. and its suppliers.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*/
#include "sm/generic.h"
SM_RCSID("@(#)$Id: example-bsd-tree-list.c,v 1.3 2004/12/26 04:15:55 ca Exp $")
/*
** Example program how to use RB_ tree as imported from OpenBSD tree(3)
** All nodes in a RB_ tree must have unique keys, however, this
** is an extended version in which each node is a list of entries
** with the same key. This is similar to a hash table where colliding
** entries are put into a list.
**
** XXX NOT YET COMPLETE!
** Which APIs do we want here? Check application requirements!
**
** Current problem: tnode and node are different, should a tnode be
** integrated into node?
** How to minmize the overhead? RB_ENTRY is 3 pointers + 1 int!
** Make it a union with list pointers? doesn't help much...: list head
*/
#include "sm/assert.h"
#include "sm/error.h"
#include "sm/memops.h"
#include "sm/heap.h"
#include "sm/bsd-tree.h"
#include "sm/queue.h"
#include <stdio.h>
/* forward declaration of tree node struct (for tree) */
typedef struct tnode_S tnode_T, *tnode_P;
/* forward declaration of node struct (in list) */
typedef struct node_S node_T, *node_P;
/* tree itself, this struct is defined in the include file */
typedef struct tree_S tree_T, *tree_P;
/* declare tree structure with reference to tree node */
RB_HEAD(tree_S, tnode_S);
/*
** tree node struct:
** first entry required by RB_,
** next entry for list,
*/
typedef TAILQ_HEAD(, node_S) tn_hd_T, *tn_hd_P;
struct tnode_S
{
RB_ENTRY(tnode_S) tn_entry; /* tree organization data */
tn_hd_T tn_hd; /* head of list */
};
/*
** tree node struct:
** next entry for list,
** rest application specific
*/
struct node_S
{
TAILQ_ENTRY(node_S) n_link; /* link to next elem */
uint n_key; /* key (does not need to be unique) */
long n_value; /* some values ... */
};
/*
Summary: a tree consists of tree nodes (tnode_T)
tree
tnode_T tnode_T
tnode_T tnode_T tnode_T
a tnode_T is the head of a list.
Design decisions:
the list organization depends on the access features that are
required by the application. it doesn't seem to be simple to
write a generic abstraction for this...
can we at least write an abstraction for one type?
*/
/* comparison function for tree nodes, required */
static int
compare_tnodes(tnode_P tnode_a, tnode_P tnode_b)
{
uint a;
uint b;
a = TAILQ_FIRST(&(tnode_a->tn_hd))->n_key;
b = TAILQ_FIRST(&(tnode_b->tn_hd))->n_key;
if (a < b)
return -1;
else if (a == b)
return 0;
else
return 1;
}
/* comparison function for nodes, required */
static int
compare_nodes(node_P node_a, node_P node_b)
{
uint a;
uint b;
a = node_a->n_key;
b = node_b->n_key;
if (a < b)
return -1;
else if (a == b)
return 0;
else
return 1;
}
/*
** Generate code for tree: tree struct, tree node struct,
** RB_ENTRY in tree node struct, comparison function.
** For an include file, use RB_PROTOTYPE() with the same arguments
** to specify the functions prototypes.
*/
RB_GENERATE(tree_S, tnode_S, tn_entry, compare_tnodes)
/* turn the following functions into macros? */
/* find a node in a tree */
static node_P
tl_find(tree_P tree, node_P node)
{
tnode_T tnode;
tnode_P tnode_ret;
node_P node_h;
SM_REQUIRE(tree != NULL);
SM_REQUIRE(node != NULL);
/* create a tree node, add the node which contains the key to it */
sm_memzero(&tnode, sizeof(tnode));
TAILQ_INIT(&(tnode.tn_hd));
TAILQ_INSERT_TAIL(&(tnode.tn_hd), node, n_link);
tnode_ret = RB_FIND(tree_S, tree, &tnode);
if (tnode_ret == NULL)
return NULL;
/* loop through list to find match */
TAILQ_FOREACH(node_h, &(tnode.tn_hd), n_link)
{
if (compare_nodes(node, node_h) == 0)
return node_h;
}
return NULL;
}
/* insert a node in a tree */
static sm_ret_T
tl_insert(tree_P tree, node_P node)
{
tnode_T tnode;
tnode_P tnode_ret;
SM_REQUIRE(tree != NULL);
SM_REQUIRE(node != NULL);
/* create a tree node, add the node which contains the key to it */
sm_memzero(&tnode, sizeof(tnode));
TAILQ_INIT(&(tnode.tn_hd));
TAILQ_INSERT_TAIL(&(tnode.tn_hd), node, n_link);
tnode_ret = RB_FIND(tree_S, tree, &tnode);
if (tnode_ret == NULL)
{
tnode_P tnode_h;
tnode_h = (tnode_P) sm_zalloc(sizeof(*tnode_h));
if (tnode_h == NULL)
return ENOMEM;
tnode_ret = RB_INSERT(tree_S, tree, tnode_h);
if (tnode_ret == NULL)
return EINVAL; /* OOPS! */
return SM_SUCCESS;
}
/* just append... all keys are identical in this list */
/* later enhancement: secondary keys... */
TAILQ_INSERT_TAIL(&(tnode_ret->tn_hd), node, n_link);
return SM_SUCCESS;
}
static sm_ret_T
tl_rm(tree_P tree, node_P node)
{
tnode_T tnode;
tnode_P tnode_ret;
SM_REQUIRE(tree != NULL);
SM_REQUIRE(node != NULL);
/* create a tree node, add the node which contains the key to it */
sm_memzero(&tnode, sizeof(tnode));
TAILQ_INIT(&(tnode.tn_hd));
TAILQ_INSERT_TAIL(&(tnode.tn_hd), node, n_link);
tnode_ret = RB_FIND(tree_S, tree, &tnode);
if (tnode_ret == NULL)
return sm_err_perm(SM_E_NOTFOUND);
TAILQ_REMOVE(&(tnode_ret->tn_hd), node, n_link);
sm_free(node);
if (TAILQ_EMPTY(&(tnode_ret->tn_hd)))
{
if (RB_REMOVE(tree_S, tree, tnode_ret) == NULL)
{
/* shouldn't happen! */
SM_ASSERT(tnode_ret == NULL);
return sm_err_perm(EINVAL);
}
sm_free(tnode_ret);
}
return SM_SUCCESS;
}
int
main(int argc, char **argv)
{
sm_ret_T ret;
tree_T tree; /* a tree */
tnode_P tnode;
tnode_T telm;
node_P node;
node_T elm;
/* initialize tree */
RB_INIT(&tree);
/*
** Find a node in a tree: tree struct, head of tree, node.
** Note: this requires a full node, even though only the key
** is used for comparisons!
*/
node = tl_find(&tree, &elm);
if (node == NULL)
{
/*
** Insert a node in a tree: tree struct, head of tree, node.
*/
ret = tl_insert(&tree, node);
}
ret = tl_rm(&tree, node);
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1