Current section

Files

Jump to
penelope c_src deps crfsuite lib crf src rumavl.c
Raw

c_src/deps/crfsuite/lib/crf/src/rumavl.c

/*----------------------------------------------------------------------------
* RumAVL - Threaded AVL Tree Implementation
*
* Copyright (c) 2005-2007 Jesse Long <jpl@unknown.za.net>
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* 1. The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* 2. The origin of the Software must not be misrepresented; you must not
* claim that you wrote the original Software.
* 3. Altered source versions of the Software must be plainly marked as
* such, and must not be misrepresented as being the original Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*--------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
* Although not required by the license, I would appreciate it if you would
* send me a mail notifying me of bugfixes and enhancements you make to this
* code. My email address is <jpl@unknown.za.net>
*--------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
* DEVELOPEMENT NOTES
*
* Links
* Each node has two links, link[0] is the left child, and link[1] is the
* right child. When a link points to a node that is actually below it in
* the BST, the respective thread flag is marked 0. When the link is a
* thread, the respective thread flag is marked 1, or 2 if the thread is
* to the opposite edge of the BST.
*
* Direction
* In RumAVL we use the numbers -1 (RUMAVL_DESC) and +1 (RUMAVL_ASC) to
* indicate direction, where -1 (RUMAVL_DESC) means left or descending in
* value, and +1 (RUMAVL_ASC) means right or ascending in value.
*
* Threads
* In RumAVL, the threads (non-bst links of leaves) are implemented in a
* sort of circular list. It is important to note that you cannot go
* through the entire list by following the same link, as you would when
* going through a linked list. Draw an example threaded AVL tree on paper
* and see why.
*
*--------------------------------------------------------------------------*/
#include <stdlib.h>
#include <string.h>
#include "rumavl.h"
/* For memory allocation debugging
#ifdef USE_MEMBUG
#define MEMBUG_DEFINES
#include <membug.h>
#endif */
/*****************************************************************************
*
* MACROS - to make readability better
*
****************************************************************************/
/* Link numbers */
#define LEFT (0)
#define RIGHT (1)
/* Direction to link no, expects RUMAVL_DESC or RUMAVL_ASC */
#define LINK_NO(i) (((i) + 1) / 2) /* -1 => 0; 1 => 1 */
/* Get opposite link number, expects LEFT or RIGHT */
#define OTHER_LINK(i) ((i) ^ 1) /* 1 => 0; 0 => 1 */
/* link no to direction, expects LEFT or RIGHT */
#define DIR_NO(i) (((i) * 2) - 1) /* 0 => -1; 1 => 1 */
/* opposite direction, expects RUMAVL_DESC or RUMAVL_ASC */
#define OTHER_DIR(i) ((i) * -1) /* -1 => 1; 1 => -1 */
/* Memory allocation functions */
#define mem_alloc(tree, bytes) mem_mgr((tree), NULL, (bytes))
#define mem_free(tree, ptr) mem_mgr((tree), (ptr), 0)
#define mem_relloc(tree, ptr, bytes) mem_mgr((tree), (ptr), (bytes))
/*****************************************************************************
*
* DATA TYPES
*
****************************************************************************/
/*
* RUMAVL - the handle on the tree
*
* All settings for a tree are in the RUMAVL object, including memory
* management, delete and overwrite callback functions, and the record
* comparison function pointer.
*/
struct rumavl {
RUMAVL_NODE *root; /* root node in tree */
size_t reclen; /* length of records */
int (*cmp)(const void *, /* function to compare records */
const void *,
size_t,
void *);
int (*owcb)(RUMAVL *, RUMAVL_NODE *, void *, const void *, void *);
int (*delcb)(RUMAVL *, RUMAVL_NODE *, void *, void *);
void *(*alloc)(void *, size_t, void *);
void *udata; /* user data for callbacks */
};
/*
* RUMAVL_NODE - the node structure
*
* RUMAVL_NODE's contain all information about a specific node, including
* links to the right and left children of the node, and flags (thread)
* indicating whether or not the links are threads or not, and the balance
* factor of the node.
*
* The record associated with each node is allocated along with the node,
* and can be found directly after the node, by using the NODE_REC() macro.
*/
struct rumavl_node {
RUMAVL_NODE *link[2]; /* links to child nodes */
char thread[2]; /* flags for links, normal link or thread? */
signed char balance; /* balance factor for node */
void *rec;
#define NODE_REC(node) ((node)->rec)
};
/*
* RUMAVL_STACK - a stack of nodes forming a path to a node
*
* RUMAVL_STACK's are used while deleting and inserting nodes, where effects
* could be felt by all parents of the node. RUMAVL_STACK's are implemented
* in a singly linked list. This is a change from the method used by most AVL
* trees, where a static array node pointers are allocated. Linked lists allow
* fo an unlimited height in the AVL tree.
*
* node is a pointer to the parent node's pointer to the node in question.
* dir is the direction of the descent from this node.
*/
typedef struct rumavl_stack RUMAVL_STACK;
struct rumavl_stack {
RUMAVL_STACK *next;
RUMAVL_NODE **node;
int dir;
};
/* various other RumAVL specific structs defined in rumavl.h */
/*****************************************************************************
*
* FORWARD DECLERATIONS
*
****************************************************************************/
static RUMAVL_NODE *seq_next (RUMAVL_NODE *node, int dir);
static RUMAVL_NODE *node_new(RUMAVL *tree, const void *record);
static void node_destroy (RUMAVL *tree, RUMAVL_NODE *node);
static int stack_push (RUMAVL *tree, RUMAVL_STACK **stack, RUMAVL_NODE **node,
int dir);
static void stack_destroy(RUMAVL *tree, RUMAVL_STACK *stack);
static void stack_update(RUMAVL *tree, RUMAVL_STACK *stack, signed char diff);
static signed char balance (RUMAVL_NODE **node, int dir);
static signed char rotate (RUMAVL_NODE **node, int dir);
static void *mem_mgr (RUMAVL *tree, void *ptr, size_t size);
static int rec_cmp (RUMAVL *tree, const void *reca, const void *recb);
static int my_cmp (const void *a, const void *b, size_t n, void *udata);
static int insert_cb (RUMAVL *t, RUMAVL_NODE *n, void *r1, const void *r2,
void *udata);
/*****************************************************************************
*
* PUBLIC FUNCTIONS
*
****************************************************************************/
/*----------------------------------------------------------------------------
* rumavl_new - allocates a new RUMAVL object, and initialises it. This is the
* only time the user gets to set the record length and record comparison
* function, to avoid data loss.
*--------------------------------------------------------------------------*/
RUMAVL *rumavl_new (size_t reclen,
int (*cmp)(const void *, const void *, size_t, void *),
void *(*alloc)(void *, size_t, void *),
void *udata)
{
RUMAVL *tree;
if (reclen < 1)
return NULL;
if (alloc == NULL)
tree = malloc(sizeof(RUMAVL));
else
tree = alloc(NULL, sizeof(RUMAVL), udata);
if (tree == NULL)
return NULL;
tree->root = NULL;
tree->owcb = NULL;
tree->delcb = NULL;
tree->alloc = alloc;
tree->reclen = reclen;
tree->udata = udata;
if (cmp == NULL)
tree->cmp = my_cmp;
else
tree->cmp = cmp;
return tree;
}
/*----------------------------------------------------------------------------
* rumavl_destroy - cleanly frees all memory used by the RUMAVL, as well as
* all nodes. All nodes are passed to the delete callback function in case the
* user has a special way of destroying nodes. The return value of the delete
* callback function is ignored, because once we start destroying we cant
* simply undestroy half the nodes.
*--------------------------------------------------------------------------*/
void rumavl_destroy (RUMAVL *tree)
{
RUMAVL_NODE *node, *tmp;
if (tree->root != NULL){
/* walk through tree deleting all */
node = tree->root;
while (node->thread[LEFT] == 0) /* move to bottom left most node */
node = node->link[LEFT];
while (node != NULL){
tmp = seq_next(node, RUMAVL_ASC);
if (tree->delcb != NULL){
tree->delcb(tree, node, NODE_REC(node), tree->udata);
}
node_destroy(tree, node);
node = tmp;
}
}
if (tree->alloc == NULL)
free(tree);
else
tree->alloc(tree, 0, tree->udata);
}
/*---------------------------------------------------------------------------
* rumavl_udata - get a pointer to the tree's user pointer
*-------------------------------------------------------------------------*/
void **rumavl_udata (RUMAVL *tree)
{
return &tree->udata;
}
int (**rumavl_owcb(RUMAVL *tree))(RUMAVL *, RUMAVL_NODE *, void *,
const void *, void *)
{
return &tree->owcb;
}
int (**rumavl_delcb(RUMAVL *tree))(RUMAVL *, RUMAVL_NODE *, void *, void *)
{
return &tree->delcb;
}
/*----------------------------------------------------------------------------
* rumavl_set - set a node, overwriting if necessary, or creating if the node
* does not exist
*--------------------------------------------------------------------------*/
int rumavl_set (RUMAVL *tree, const void *record)
{
RUMAVL_NODE **node, *tmp;
RUMAVL_STACK *stack;
int ln;
if (tree->root == NULL){
/* This is the first node in the tree */
if ((tree->root = node_new(tree, record)) == NULL)
return RUMAVL_ERR_NOMEM;
tree->root->link[LEFT] = tree->root;
tree->root->link[RIGHT] = tree->root;
tree->root->thread[LEFT] = 2;
tree->root->thread[RIGHT] = 2;
return 0;
}
/* Since the tree is not empty, we must descend towards the nodes ideal
* possition, and we may even find an existing node with the same record.
* We keep a list parents for the eventual node position, because these
* parents may become inbalanced by a new insertion. */
stack = NULL;
node = &tree->root;
for (;;){
if ((ln = rec_cmp(tree, record, NODE_REC(*node))) == 0){
/* OK, we found the exact node we wish to set, and we now
* overwrite it. No change happens to the tree structure */
stack_destroy(tree, stack);
if (tree->owcb != NULL &&
(ln = tree->owcb(tree, *node, NODE_REC(*node),
record, tree->udata)) != 0){
return ln;
}
memcpy(NODE_REC(*node), record, tree->reclen);
return 0;
}
/* *node is not the node we seek */
if (stack_push(tree, &stack, node, ln)){
stack_destroy(tree, stack);
return RUMAVL_ERR_NOMEM;
}
ln = LINK_NO(ln);
if ((*node)->thread[ln] > 0){
/* This is as close to the correct node as we can get. We will
* now break and add the new node as a leaf */
break;
}
node = &(*node)->link[ln];
}
/* we have reached a leaf, add new node here */
if ((tmp = node_new(tree, record)) == NULL){
stack_destroy(tree, stack);
return RUMAVL_ERR_NOMEM;
}
/* new child inherits parent thread */
tmp->link[ln] = (*node)->link[ln];
tmp->thread[ln] = (*node)->thread[ln];
if (tmp->thread[ln] == 2)
tmp->link[ln]->link[OTHER_LINK(ln)] = tmp;
tmp->link[OTHER_LINK(ln)] = *node;
tmp->thread[OTHER_LINK(ln)] = 1;
(*node)->link[ln] = tmp;
(*node)->thread[ln] = 0;
/* all parentage is now one level heavier - balance where necessary */
stack_update(tree, stack, +1);
return 0;
}
/*----------------------------------------------------------------------------
* rumavl_insert - like rumavl_set, but only works if the node does not
* exist. Temporarily replaces overwrite callback with a function that
* always prevents overwrite, and calls rumavl_set()
*--------------------------------------------------------------------------*/
int rumavl_insert (RUMAVL *tree, const void *record)
{
int retv;
int (*tmp)(RUMAVL *, RUMAVL_NODE *, void *, const void *, void *);
tmp = tree->owcb;
tree->owcb = insert_cb;
retv = rumavl_set(tree, record);
tree->owcb = tmp;
return retv;
}
/*----------------------------------------------------------------------------
* rumavl_delete - deletes a node. Beware! this function is the worst part of
* the library. Think (and draw pictures) when you edit this function.
*--------------------------------------------------------------------------*/
int rumavl_delete (RUMAVL *tree, const void *record)
{
RUMAVL_NODE **node, *tmpnode;
RUMAVL_STACK *stack;
int dir, ln;
if (tree->root == NULL) /* tree is empty */
return RUMAVL_ERR_NOENT;
stack = NULL;
node = &tree->root;
/* Find desired node */
while ((dir = rec_cmp(tree, record, NODE_REC(*node))) != 0){
if (stack_push(tree, &stack, node, dir) != 0)
goto nomemout;
if ((*node)->thread[LINK_NO(dir)] > 0){
/* desired node does not exist */
stack_destroy(tree, stack);
return RUMAVL_ERR_NOENT;
}
node = &(*node)->link[LINK_NO(dir)];
}
/* OK, we got the node to be deleted, now get confirmation from user */
if (tree->delcb != NULL &&
(ln = tree->delcb(tree, *node, NODE_REC(*node), tree->udata))
!= 0){
stack_destroy(tree, stack);
return ln;
}
if ((*node)->thread[LEFT] > 0){
if ((*node)->thread[RIGHT] > 0){
/* ooh look, we're a leaf */
tmpnode = *node;
if (stack != NULL){
/* This node has a parent, which will need to take over a
* thread from the node being deleted. First we work out
* which (left/right) child we are of parent, then give
* parent the respective thread. If the thread destination
* points back to us (edge of tree thread), update it to
* point to our parent. */
ln = LINK_NO(stack->dir);
(*stack->node)->link[ln] = tmpnode->link[ln];
(*stack->node)->thread[ln] = tmpnode->thread[ln];
if ((*stack->node)->thread[ln] == 2)
(*stack->node)->link[ln]->link[OTHER_LINK(ln)] =
*stack->node;
}else{
/*
* the only time stack will == NULL is when we are
* deleting the root of the tree. We already know that
* this is a leaf, so we will be leaving the tree empty.
*/
tree->root = NULL;
}
node_destroy(tree, tmpnode);
}else{
/* *node has only one child, and can be pruned by replacing
* *node with its only child. This block of code and the next
* should be identical, except that all directions and link
* numbers are opposite.
*
* Let node being deleted = DELNODE for this comment.
* DELNODE only has one child (the right child). The left
* most descendant of DELNODE will have a thread (left thread)
* pointing to DELNODE. This thread must be updated to point
* to the node currently pointed to by DELNODE's left thread.
*
* DELNODE's left thread may point to the opposite edge of the
* BST. In this case, the destination of the thread will have
* a thread back to DELNODE. This will need to be updated to
* point back to the leftmost descendant of DELNODE.
*/
tmpnode = *node; /* node being deleted */
*node = (*node)->link[RIGHT]; /* right child */
/* find left most descendant */
while ((*node)->thread[LEFT] == 0)
node = &(*node)->link[LEFT];
/* inherit thread from node being deleted */
(*node)->link[LEFT] = tmpnode->link[LEFT];
(*node)->thread[LEFT] = tmpnode->thread[LEFT];
/* update reverse thread if necessary */
if ((*node)->thread[LEFT] == 2)
(*node)->link[LEFT]->link[RIGHT] = *node;
node_destroy(tree, tmpnode);
}
}else if ((*node)->thread[RIGHT] > 0){
/* see above */
tmpnode = *node;
*node = (*node)->link[LEFT];
while ((*node)->thread[RIGHT] == 0)
node = &(*node)->link[RIGHT];
(*node)->link[RIGHT] = tmpnode->link[RIGHT];
(*node)->thread[RIGHT] = tmpnode->thread[RIGHT];
if ((*node)->thread[RIGHT] == 2)
(*node)->link[RIGHT]->link[LEFT] = *node;
node_destroy(tree, tmpnode);
}else{
/* Delete a node with children on both sides. We do this by replacing
* the node to be deleted (delnode) with its inner most child
* on the heavier side (repnode). This in place replacement is quicker
* than the previously used method of rotating delnode until it is a
* (semi) leaf.
*
* At this point node points to delnode's parent's link to delnode. */
RUMAVL_NODE *repnode, *parent;
int outdir, outln;
/* find heaviest subtree */
if ((*node)->balance > 0){
outdir = +1; /* outter direction */
dir = -1; /* inner direction */
outln = 1; /* outer link number */
ln = 0; /* inner link number */
}else{
outdir = -1; /* same as above, but opposite subtree */
dir = +1;
outln = 0;
ln = 1;
}
/* Add node to be deleted to the list of nodes to be rebalanced.
* Rememer that the replacement node will actually be acted apon,
* and that the replacement node should feel the effect of its own
* move */
if (stack_push(tree, &stack, node, outdir) != 0)
goto nomemout;
parent = *node;
repnode = parent->link[outln];
if (repnode->thread[ln] != 0){
/* repnode inherits delnode's lighter tree, and balance, and gets
* balance readjusted below */
repnode->link[ln] = (*node)->link[ln];
repnode->thread[ln] = (*node)->thread[ln];
repnode->balance = (*node)->balance;
}else{
/* Now we add delnodes direct child to the list of "to update".
* We pass a pointer to delnode's link to its direct child to
* stack_push(), but that pointer is invalid, because when
* stack_update() tries to access the link, delnode would have
* been destroyed. So, we remember the stack position at which
* we passed the faulty pointer to stack_push, and update its
* node pointer when we find repnode to point to repnodes
* link on the same side */
RUMAVL_STACK *tmpstack;
if (stack_push(tree, &stack, &parent->link[outln], dir) != 0)
goto nomemout;
tmpstack = stack;
parent = repnode;
repnode = repnode->link[ln];
/* move towards the innermost child of delnode */
while (repnode->thread[ln] == 0){
if (stack_push(tree, &stack, &parent->link[ln], dir) != 0)
goto nomemout;
parent = repnode;
repnode = repnode->link[ln];
}
if (repnode->thread[outln] == 0){
/* repnode's parent inherits repnodes only child */
parent->link[ln] = repnode->link[outln];
}else{
/* parent already has a link to repnode, but it must now be
* marked as a thread */
parent->thread[ln] = 1;
}
repnode->link[0] = (*node)->link[0];
repnode->thread[0] = (*node)->thread[0];
repnode->link[1] = (*node)->link[1];
repnode->thread[1] = (*node)->thread[1];
repnode->balance = (*node)->balance;
/* see comment above */
tmpstack->node = &repnode->link[outln];
}
node_destroy(tree, *node);
*node = repnode;
/* innermost child in lighter tree has an invalid thread to delnode,
* update it to point to repnode */
repnode = seq_next(repnode, dir);
repnode->link[outln] = *node;
}
/* update parents' balances */
stack_update(tree, stack, -1);
return 0;
nomemout:
stack_destroy(tree, stack);
return RUMAVL_ERR_NOMEM;
}
/*----------------------------------------------------------------------------
* rumavl_find
*
* Returns a pointer to the record that matches "record".
*--------------------------------------------------------------------------*/
void *rumavl_find (RUMAVL *tree, const void *find)
{
void *record;
rumavl_node_find(tree, find, &record);
return record;
}
void *(**rumavl_alloc(RUMAVL *tree))(void *ptr, size_t size, void *udata)
{
return &tree->alloc;
}
/*----------------------------------------------------------------------------
* rumavl_record_size - returns size of all records in a tree
*--------------------------------------------------------------------------*/
size_t rumavl_record_size (RUMAVL *tree)
{
return tree->reclen;
}
/*----------------------------------------------------------------------------
* rumavl_node_find
*
* Returns a pointer to the node that matches "record".
*--------------------------------------------------------------------------*/
RUMAVL_NODE *rumavl_node_find (RUMAVL *tree, const void *find, void **record)
{
RUMAVL_NODE *node;
int ln;
if (find == NULL || tree->root == NULL)
goto fail;
node = tree->root;
for (;;){
if ((ln = rec_cmp(tree, find, NODE_REC(node))) == 0){
if (record != NULL)
*record = NODE_REC(node);
return node;
}
ln = LINK_NO(ln);
if (node->thread[ln] > 0)
break;
node = node->link[ln];
}
/* we didn't find the desired node */
fail:
if (record != NULL)
*record = NULL;
return NULL;
}
/*----------------------------------------------------------------------------
* rumavl_node_next - find next node
*--------------------------------------------------------------------------*/
RUMAVL_NODE *rumavl_node_next (RUMAVL *tree, RUMAVL_NODE *node, int dir,
void **record)
{
/* make sure `dir' is either RUMAVL_ASC or RUMAVL_DESC */
if (dir == 0)
goto fail;
else if (dir > 0)
dir = RUMAVL_ASC;
else
dir = RUMAVL_DESC;
/* if node is uninitialised, start with first possible node in `dir'
* direction */
if (node == NULL){
/* unless the tree is empty of course */
if (tree->root == NULL)
goto fail;
dir = OTHER_LINK(LINK_NO(dir));
node = tree->root;
while (node->thread[dir] == 0){
node = node->link[dir];
}
goto found;
}
if ((node = seq_next(node, dir)) == NULL)
goto fail;
/* fall through */
found:
if (record != NULL)
*record = NODE_REC(node);
return node;
fail:
if (record != NULL)
*record = NULL;
return NULL;
}
/*----------------------------------------------------------------------------
* rumavl_node_record - returns a pointer to the record stored in a node
*--------------------------------------------------------------------------*/
void *rumavl_node_record (RUMAVL_NODE *node)
{
return NODE_REC(node);
}
/*----------------------------------------------------------------------------
* rumavl_foreach - loop through entire tree, using temporary iterator
*--------------------------------------------------------------------------*/
extern int rumavl_foreach (RUMAVL *tree, int dir,
int (*cbfn)(RUMAVL *, void *, void *), void *udata)
{
RUMAVL_NODE *node;
int retv;
void *record;
if (cbfn == NULL)
return RUMAVL_ERR_INVAL;
retv = RUMAVL_ERR_NOENT;
node = NULL;
while ((node = rumavl_node_next(tree, node, dir, &record)) != NULL){
if ((retv = cbfn(tree, record, udata)) != 0)
break;
}
return retv;
}
/*----------------------------------------------------------------------------
* rumavl_strerror - return string description of RumAVL error code
*--------------------------------------------------------------------------*/
const char *rumavl_strerror (int errno)
{
switch (errno){
case 0:
return "Operation successful";
case RUMAVL_ERR_INVAL:
return "Invalid argument to function";
case RUMAVL_ERR_NOMEM:
return "Insufficient memory to complete operation";
case RUMAVL_ERR_NOENT:
return "Entry does not exist";
case RUMAVL_ERR_EORNG:
return "No more entries in range";
case RUMAVL_ERR_EXIST:
return "Entry already exists";
}
return "UNKNOWN ERROR";
}
/*****************************************************************************
*
* PRIVATE FUNCTIONS
*
****************************************************************************/
/*----------------------------------------------------------------------------
* insert_cb - used by rumavl_insert() to disallow any overwriting by
* rumavl_set()
*--------------------------------------------------------------------------*/
static int insert_cb (RUMAVL *t, RUMAVL_NODE *n, void *r1, const void *r2,
void *udata)
{
(void) t; (void) r1; (void) r2; (void) udata; (void) n;
return RUMAVL_ERR_EXIST;
}
/*----------------------------------------------------------------------------
* seq_next - return a pointer to the next node in sequence
*--------------------------------------------------------------------------*/
static RUMAVL_NODE *seq_next (RUMAVL_NODE *node, int dir)
{
int ln;
ln = LINK_NO(dir);
if (node->thread[ln] == 2){
return NULL;
}else if (node->thread[ln] == 1){
return node->link[ln];
}
node = node->link[ln];
ln = OTHER_LINK(ln);
while (node->thread[ln] == 0){
node = node->link[ln];
}
return node;
}
/*----------------------------------------------------------------------------
* node_new - create a new node. MUST update link[] and thread[] after calling
* this function
*--------------------------------------------------------------------------*/
static RUMAVL_NODE *node_new(RUMAVL *tree, const void *record)
{
RUMAVL_NODE *node;
if ((node = mem_alloc(tree, sizeof(RUMAVL_NODE))) == NULL)
return NULL;
if ((node->rec = mem_alloc(tree, tree->reclen)) == NULL){
mem_free(tree, node);
return NULL;
}
memcpy(node->rec, record, tree->reclen);
node->balance = 0;
node->link[0] = NULL;
node->link[1] = NULL;
node->thread[0] = 0;
node->thread[1] = 0;
return node;
}
/*----------------------------------------------------------------------------
* node_destroy - cleanly destroy node
*--------------------------------------------------------------------------*/
static void node_destroy (RUMAVL *tree, RUMAVL_NODE *node)
{
mem_free(tree, node->rec);
mem_free(tree, node);
}
/*----------------------------------------------------------------------------
* stack_push - push a node entry onto stack, for rumavl_set() and
* rumavl_delete(). If this is the first entry, *stack should == NULL
*--------------------------------------------------------------------------*/
static int stack_push(RUMAVL *tree, RUMAVL_STACK **stack, RUMAVL_NODE **node,
int dir)
{
RUMAVL_STACK *tmp;
if ((tmp = mem_alloc(tree, sizeof(RUMAVL_STACK))) == NULL)
return -1;
tmp->next = *stack;
*stack = tmp;
tmp->node = node;
tmp->dir = dir;
return 0;
}
/*----------------------------------------------------------------------------
* stack_destroy - free up a stack
*--------------------------------------------------------------------------*/
static void stack_destroy(RUMAVL *tree, RUMAVL_STACK *stack)
{
RUMAVL_STACK *tmp;
while (stack != NULL){
tmp = stack;
stack = stack->next;
mem_free(tree, tmp);
}
}
/*----------------------------------------------------------------------------
* stack_update - goes up stack readjusting balance as needed. This function
* serves as a testiment to the philosophy of commenting while you code, 'cos
* hell if I can remember how I got to this. I think is has something to do
* with the varying effects on tree height, depending on exactly which sub
* tree, or sub-sub tree was modified. TODO study and comment
*--------------------------------------------------------------------------*/
static void stack_update(RUMAVL *tree, RUMAVL_STACK *stack, signed char diff)
{
RUMAVL_STACK *tmpstack;
/* if diff becomes 0, we quit, because no further change to ancestors
* can be made */
while (stack != NULL && diff != 0){
signed char ob, nb;
ob = (*stack->node)->balance;
(*stack->node)->balance += diff * (signed char)stack->dir;
nb = (*stack->node)->balance;
if (diff < 0){
if (stack->dir == -1 && ob < 0){
if (nb > 0)
nb = 0;
diff = (nb - ob) * -1;
}else if (stack->dir == 1 && ob > 0){
if (nb < 0)
nb = 0;
diff = nb - ob;
}else{
diff = 0;
}
}else{
if (stack->dir == -1 && nb < 0){
if (ob > 0)
ob = 0;
diff = (nb - ob) * -1;
}else if (stack->dir == 1 && nb > 0){
if (ob < 0)
ob = 0;
diff = nb - ob;
}else{
diff = 0;
}
}
while ((*stack->node)->balance > 1){
diff += balance(stack->node, -1);
}
while ((*stack->node)->balance < -1){
diff += balance(stack->node, 1);
}
tmpstack = stack;
stack = stack->next;
mem_free(tree, tmpstack);
}
/* we may exit early if diff becomes 0. We still need to free all stack
* entries */
while (stack != NULL){
tmpstack = stack;
stack = stack->next;
mem_free(tree, tmpstack);
}
}
/*----------------------------------------------------------------------------
* my_cmp - a wrapper around memcmp() for default record comparison function.
*--------------------------------------------------------------------------*/
static int my_cmp (const void *a, const void *b, size_t n, void *udata)
{
(void) udata;
return memcmp(a, b, n);
}
/*----------------------------------------------------------------------------
* rec_cmp - a wrapper around the record comparison function, that only
* returns 0, RUMAVL_ASC or RUMAVL_DESC.
*--------------------------------------------------------------------------*/
static int rec_cmp (RUMAVL *tree, const void *reca, const void *recb)
{
int retv;
retv = tree->cmp(reca, recb, tree->reclen, tree->udata);
if (retv < 0)
return RUMAVL_DESC;
if (retv > 0)
return RUMAVL_ASC;
return 0;
}
/*----------------------------------------------------------------------------
* Balance - rotate or double rotate as needed. Sometimes simply rotating a
* tree is inefficient, as it leaves the tree as inbalanced as it was before
* the rotate. To rectify this, we first rotate the heavier child so that the
* heavier grandchild is on the outside, then rotate as per normal.
*
* TODO Check all callers, and make sure that they call this function sanely,
* and then remove unnecessary checks.
*--------------------------------------------------------------------------*/
static signed char balance (RUMAVL_NODE **node, int dir)
{
int ln;
signed char retv;
if (node == NULL || *node == NULL || (dir * dir) != 1)
return 0;
ln = OTHER_LINK(LINK_NO(dir)); /* link number of new root */
/* new root must exist */
if ((*node)->thread[ln] > 0)
return 0;
retv = 0;
if ((*node)->link[ln]->balance == (char) dir &&
(*node)->link[ln]->thread[OTHER_LINK(ln)] == 0){
/* double rotate if inner grandchild is heaviest */
retv = rotate (&((*node)->link[ln]), OTHER_DIR(dir));
}
return retv + rotate (node, dir);
}
/*----------------------------------------------------------------------------
* rotate
*
* rotates a tree rooted at *node. dir determines the direction of the rotate,
* dir < 0 -> left rotate; dir >= 0 -> right rotate
*
* TODO How sure are we that all callers pass decent `dir' values?
* TODO Restudy the tree height modification and balance factor algorithms,
* and document them.
*--------------------------------------------------------------------------*/
static signed char rotate (RUMAVL_NODE **node, int dir)
{
RUMAVL_NODE *tmp;
signed char a, b, ad, bd, retv;
int ln;
/* force |dir| to be either -1 or +1 */
if (node == NULL || *node == NULL || (dir * dir) != 1)
return 0;
ln = LINK_NO(dir);
ln = OTHER_LINK(ln); /* link number of new root */
/* new root must exist */
if ((*node)->thread[ln] > 0)
return 0;
/* calculate effect on tree height */
if ((dir == 1 && (*node)->balance < 0 && (*node)->link[0]->balance >= 0)||
(dir == -1 && (*node)->balance > 0 && (*node)->link[1]->balance <= 0)){
retv = 0;
}else{
if (dir == 1){
if ((*node)->balance < -1)
retv = -1;
else if ((*node)->balance == -1)
retv = 0;
else
retv = +1;
}else{
if ((*node)->balance > 1)
retv = -1;
else if ((*node)->balance == 1)
retv = 0;
else
retv = +1;
}
}
/* rotate tree */
tmp = *node;
*node = tmp->link[ln];
if ((*node)->thread[OTHER_LINK(ln)] > 0){
tmp->thread[ln] = 1;
}else{
tmp->link[ln] = (*node)->link[OTHER_LINK(ln)];
tmp->thread[ln] = 0;
}
(*node)->link[OTHER_LINK(ln)] = tmp;
(*node)->thread[OTHER_LINK(ln)] = 0;
/* rebalance factors after rotate matrix */
a = tmp->balance;
b = (*node)->balance;
if (a > 0)
ad = 1;
else if (a < 0)
ad = -1;
else
ad = 0;
if (b > 0)
bd = 1;
else if (b < 0)
bd = -1;
else
bd = 0;
if (ad == OTHER_DIR(dir)){
if (bd == OTHER_DIR(dir)){
tmp->balance += (b * -1) + dir;
if (tmp->balance * dir > 0)
(*node)->balance = (tmp->balance - (b * -1)) + dir;
else
(*node)->balance += dir;
}else{
tmp->balance += dir;
(*node)->balance += dir;
}
}else{
if (bd == OTHER_DIR(dir)){
tmp->balance += (b * -1) + dir;
(*node)->balance += dir + tmp->balance;
}else{
tmp->balance += dir;
(*node)->balance += dir + tmp->balance;
}
}
return retv;
}
/*----------------------------------------------------------------------------
* mem_alloc
*
* default memory allocation function (malloc wrapper)
*--------------------------------------------------------------------------*/
static void *mem_mgr (RUMAVL *tree, void *ptr, size_t size)
{
if (tree->alloc != NULL)
return tree->alloc(ptr, size, tree->udata);
return realloc(ptr, size);
}