tinyproxy/src/ternary.c
Robert James Kaes 4ac03908fc Header reorganization. Basically all system headers are now included in
tinyproxy.h and all the other files include the tinyproxy.h header. This
moves all the dependancy issues into one file.
2001-10-25 17:27:39 +00:00

399 lines
9.6 KiB
C

/* $Id: ternary.c,v 1.11 2001-10-25 17:27:39 rjkaes Exp $
*
* This module creates a Ternary Search Tree which can store both string
* keys, and arbitrary data for each key. It works similar to a hash, and
* a binary search tree. The idea (and some code) is taken from Dr. Dobb's
* Journal, April 1998 "Ternary Search Trees", Jon Bentley and Bob
* Sedgewick, pg 20-25. The supplied code was then made "production" ready.
* Hopefully, the comments in the code should make the API self
* explanatory.
*
* Copyright (C) 2000 Robert James Kaes (rjkaes@flarenet.com)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#include "tinyproxy.h"
#include "log.h"
#include "ternary.h"
#include "utils.h"
/*
* Macros for the tree structures (limits)
*/
#define MAXTREES 128 /* max. number of trees */
/*
* The structure for each individual node of the ternary tree.
*/
typedef struct tnode {
char splitchar;
struct tnode *lokid, *eqkid, *hikid;
} Tnode;
/*
* The structure for each root of a ternary tree.
*/
#define BUFSIZE 512
#define BUFARRAY 512
typedef struct ttree {
TERNARY token; /* contains unique ternary tree ID */
Tnode *tree_root;
Tnode *buf;
int bufn, freen;
Tnode *freearr[BUFARRAY];
} Ttree;
/*
* variables shared by library routines.
*/
static Ttree *trees[MAXTREES]; /* the array of trees */
/*
* nonce generator -- this MUST be non-zero _always_
*/
#define IOFFSET 0x1221 /* used to hide index number in token */
#define NOFFSET 0x0502 /* initial nonce */
static unsigned int noncectr = NOFFSET;
/*
* Contains any error messages from the functions.
*/
char te_errbuf[256];
/*
* Generate a token; this is an integer: index number + OFFSET,,none
* WARNING: Each quantity must fix into 16 bits
*
* Parameters: int index index number
* Returned: TERNARY token of corresponding tree
* Errors: TE_INTINCON * index + OFFSET is too big
* * nonce is too big
* * index is out of range
* (te_errbuf has disambiguating string)
* Exceptions: none
*/
static TERNARY create_token_ref(unsigned int ind)
{
unsigned int high; /* high 16 bits of token (index) */
unsigned int low; /* low 16 bits of token (nonce) */
/*
* Sanity check argument; called internally...
*/
if (ind > MAXTREES) {
ERRBUF3("create_token_ref: index %u exceeds %d", ind, MAXTREES);
return TE_INTINCON;
}
/*
* Get the high part of the token (index into tree array, offset by
* some arbitrary amount)
* SANITY CHECK: Be sure index + OFFSET fits into 16 bits as positive.
*/
high = (ind + IOFFSET) & 0x7fff;
if (high != ind + IOFFSET) {
ERRBUF3("create_token_ref: index %u larger than %u", ind,
0x7fff - IOFFSET);
return TE_INTINCON;
}
/*
* Get the low part of the token (nonce)
* SANITY CHECK: Be sure nonce fits into 16 bits
*/
low = noncectr & 0xffff;
if ((low != noncectr++) || low == 0) {
ERRBUF3("create_token_ref: generation number %u exceeds %u",
noncectr - 1, 0xffff - NOFFSET);
return TE_INTINCON;
}
return (TERNARY)((high << 16) | low);
}
/*
* Check a token number and turn it into an index.
*
* Parameters: TERNARY tno ternary token from the user
* Returned: int index from the ticket
* Errors: TE_BADTOKEN ternary token is invalid because:
* * index out of range [0 .. MAXTREES]
* * index is for unused slot
* * nonce is of old tree
* (te_errbuf has disambiguating string)
* TE_INTINCON tree is internally inconsistent because:
* * nonce is 0
* (te_errbuf has disambiguating string)
* EXCEPTIONS: none
*/
static int read_token_ref(TERNARY tno)
{
unsigned int ind; /* index of current tree */
/*
* Get the index number and check it for validity
*/
ind = ((tno >> 16) & 0xffff) - IOFFSET;
if (ind >= MAXTREES) {
ERRBUF3("read_token_ref: index %u exceeds %d", ind, MAXTREES);
return TE_BADTOKEN;
}
if (trees[ind] == NULL) {
ERRBUF2("readbuf: token refers to unused tree index %u", ind);
return TE_BADTOKEN;
}
/*
* We have a valid index; now validate the nonce; note we store the
* token in the tree, so just check that (same thing)
*/
if (trees[ind]->token != tno) {
ERRBUF3("readbuf: token refers to old tree (new=%u, old=%u)",
(unsigned int)((trees[ind]->token) & 0xffff) - IOFFSET,
(unsigned int)(tno & 0xffff) - NOFFSET);
return TE_BADTOKEN;
}
/*
* Check for internal consistencies
*/
if ((trees[ind]->token & 0xffff) == 0) {
ERRBUF("read_token_ref: internal inconsistency: nonce = 0");
return TE_INTINCON;
}
return ind;
}
/*
* Create a new tree
*
* Parameters: none
* Returned: TERNARY token (if > 0); error number (if < 0)
* Errors: TE_BADPARAM parameter is NULL
* TE_TOOMANYTS too many trees allocated already
* TE_NOROOM no memory to allocate new trees
* (te_errbuf has descriptive string)
* Exceptions: none
*/
TERNARY ternary_new(void)
{
int cur; /* index of current tree */
TERNARY token; /* new token for current tree */
/*
* Check for array full
*/
for (cur = 0; cur < MAXTREES; cur++)
if (trees[cur] == NULL)
break;
if (cur == MAXTREES) {
ERRBUF2("ternary_new: too many trees (max %d)", MAXTREES);
return TE_TOOMANYTS;
}
/*
* Allocate a new tree
*/
if ((trees[cur] = safemalloc(sizeof(Ttree))) == NULL) {
ERRBUF("ternary_new: malloc: no more memory");
return TE_NOROOM;
}
/*
* Generate token
*/
if (TE_ISERROR(token = create_token_ref(cur))) {
/* error in token generation -- clean up and return */
safefree(trees[cur]);
trees[cur] = NULL;
return token;
}
/*
* Now initialize tree entry
*/
trees[cur]->token = token;
trees[cur]->tree_root = trees[cur]->buf = NULL;
trees[cur]->bufn = trees[cur]->freen = 0;
return token;
}
/*
* Delete an exisiting tree
*
* Parameters: TERNARY tno token for the tree to be deleted
* Returned: uint16_t error code
* Errors: TE_BADPARAM parameter refers to deleted, unallocated,
* or invalid tree (from read_token_ref()).
* TE_INTINCON tree is internally inconsistent (from
* read_token_ref()).
* Exceptions: none
*/
int ternary_destroy(TERNARY tno, void (*freeptr)(void *))
{
int cur; /* index of current tree */
unsigned int i, j;
Tnode *ptr;
/*
* Check that tno refers to an existing tree;
* read_token_ref sets error code
*/
if (TE_ISERROR(cur = read_token_ref(tno)))
return cur;
for (i = 0; i < trees[cur]->freen; i++) {
for (j = 0; j < BUFSIZE; j++) {
DEBUG2("first: j = %d, i = %d", j, i);
ptr = (trees[cur]->freearr[i] + j);
if (ptr->splitchar == 0)
if (freeptr)
(*freeptr)(ptr->eqkid);
}
safefree(trees[cur]->freearr[i]);
}
/* Remove the tree and NULL it's position in the array */
safefree(trees[cur]);
return TE_NONE;
}
/*
* Insert the key/value into an existing tree
*
* Parameters: TERNARY tno tree to insert data into
* char *s NULL terminated string (key)
* void *data data to associate with key (can be NULL)
* Returned: int error code
* Errors: TE_BADPARAM parameter refers to deleted, unallocated, or
* invalid tree (from read_token_ref()).
* TE_INTINCON tree is internally inconsistent (from
* read_token_ref()).
* TE_TOOFULL tree is full, so no new elements can be added.
* Exceptions: none
*/
int ternary_insert_replace(TERNARY tno, const char *s, void *data,
short int replace)
{
int cur; /* index of current tree */
Ttree *tree; /* pointer to tree structure */
int d;
Tnode *pp, **p;
/*
* Check that tno refers to an existing tree; read_token_ref sets error
* code.
*/
if (TE_ISERROR(cur = read_token_ref(tno)))
return cur;
tree = trees[cur];
p = &(tree->tree_root);
while ((pp = *p)) {
if ((d = *s - pp->splitchar) == 0) {
if (*s++ == 0) {
if (!replace)
return TE_EXISTS;
else {
free(pp->eqkid);
pp->eqkid = (Tnode *)data;
return TE_NONE;
}
}
p = &(pp->eqkid);
} else if (d < 0)
p = &(pp->lokid);
else
p = &(pp->hikid);
}
for (;;) {
if (tree->bufn-- == 0) {
tree->buf = safecalloc(BUFSIZE, sizeof(Tnode));
if (!tree->buf) {
ERRBUF("ternary_insert: malloc: no more memory");
return TE_NOROOM;
}
if (tree->freen == BUFARRAY - 1) {
ERRBUF3("ternary_insert: freen %u equals %u",
tree->freen,
BUFARRAY - 1);
return TE_TOOFULL;
}
tree->freearr[tree->freen++] = tree->buf;
tree->bufn = BUFSIZE - 1;
}
*p = tree->buf++;
pp = *p;
pp->splitchar = *s;
pp->lokid = pp->eqkid = pp->hikid = NULL;
if (*s++ == 0) {
pp->eqkid = (Tnode *) data;
return TE_NONE;
}
p = &(pp->eqkid);
}
}
/*
* Return the data stored with the string
*
* Parameters: TERNARY tno token for the tree involved
* char *s the name the data is stored under
* void **d the data (or NULL if you don't care about data)
* Returned: int error codes
* Errors:
* Exceptions:
*/
int ternary_search(TERNARY tno, const char *s, void **data)
{
int cur;
Tnode *p;
/*
* Check that tno refers to an existing tree; read_token_ref sets error
* code
*/
if (TE_ISERROR(cur = read_token_ref(tno)))
return cur;
p = trees[cur]->tree_root;
while (p) {
if (toupper(*s) < toupper(p->splitchar))
p = p->lokid;
else if (toupper(*s) == toupper(p->splitchar)) {
if (*s++ == 0) {
if (data)
*data = (void *)p->eqkid;
return TE_NONE;
}
p = p->eqkid;
} else
p = p->hikid;
}
if (data)
*data = (void *)NULL;
return TE_EMPTY;
}