/* avl.c - routines to implement an avl tree */ /* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * * Copyright 1998-2005 The OpenLDAP Foundation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted only as authorized by the OpenLDAP * Public License. * * A copy of this license is available in the file LICENSE in the * top-level directory of the distribution or, alternatively, at * . */ /* Portions Copyright (c) 1993 Regents of the University of Michigan. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that this notice is preserved and that due credit is given * to the University of Michigan at Ann Arbor. The name of the University * may not be used to endorse or promote products derived from this * software without specific prior written permission. This software * is provided ``as is'' without express or implied warranty. */ /* ACKNOWLEDGEMENTS: * This work was originally developed by the University of Michigan * (as part of U-MICH LDAP). Additional significant contributors * include: * Howard Y. Chu * Hallvard B. Furuseth * Kurt D. Zeilenga */ #include "portable.h" #include #include #ifdef CSRIMALLOC #define ber_memalloc malloc #define ber_memrealloc realloc #define ber_memfree free #else #include "lber.h" #endif #define AVL_INTERNAL #include "avl.h" static const int avl_bfs[] = {LH, RH}; /* * avl_insert -- insert a node containing data data into the avl tree * with root root. fcmp is a function to call to compare the data portion * of two nodes. it should take two arguments and return <, >, or == 0, * depending on whether its first argument is <, >, or == its second * argument (like strcmp, e.g.). fdup is a function to call when a duplicate * node is inserted. it should return 0, or -1 and its return value * will be the return value from avl_insert in the case of a duplicate node. * the function will be called with the original node's data as its first * argument and with the incoming duplicate node's data as its second * argument. this could be used, for example, to keep a count with each * node. * * NOTE: this routine may malloc memory */ int avl_insert( Avlnode ** root, void *data, AVL_CMP fcmp, AVL_DUP fdup ) { Avlnode *t, *p, *s, *q, *r; int a, cmp, ncmp; if ( *root == NULL ) { if (( r = (Avlnode *) ber_memalloc( sizeof( Avlnode ))) == NULL ) { return( -1 ); } r->avl_link[0] = r->avl_link[1] = NULL; r->avl_data = data; r->avl_bf = EH; *root = r; return( 0 ); } t = NULL; s = p = *root; /* find insertion point */ while (1) { cmp = fcmp( data, p->avl_data ); if ( cmp == 0 ) return (*fdup)( p->avl_data, data ); cmp = (cmp > 0); q = p->avl_link[cmp]; if (q == NULL) { /* insert */ if (( q = (Avlnode *) ber_memalloc( sizeof( Avlnode ))) == NULL ) { return( -1 ); } q->avl_link[0] = q->avl_link[1] = NULL; q->avl_data = data; q->avl_bf = EH; p->avl_link[cmp] = q; break; } else if ( q->avl_bf ) { t = p; s = q; } p = q; } /* adjust balance factors */ cmp = fcmp( data, s->avl_data ) > 0; r = p = s->avl_link[cmp]; a = avl_bfs[cmp]; while ( p != q ) { cmp = fcmp( data, p->avl_data ) > 0; p->avl_bf = avl_bfs[cmp]; p = p->avl_link[cmp]; } /* checks and balances */ if ( s->avl_bf == EH ) { s->avl_bf = a; return 0; } else if ( s->avl_bf == -a ) { s->avl_bf = EH; return 0; } else if ( s->avl_bf == a ) { cmp = (a > 0); ncmp = !cmp; if ( r->avl_bf == a ) { /* single rotation */ p = r; s->avl_link[cmp] = r->avl_link[ncmp]; r->avl_link[ncmp] = s; s->avl_bf = 0; r->avl_bf = 0; } else if ( r->avl_bf == -a ) { /* double rotation */ p = r->avl_link[ncmp]; r->avl_link[ncmp] = p->avl_link[cmp]; p->avl_link[cmp] = r; s->avl_link[cmp] = p->avl_link[ncmp]; p->avl_link[ncmp] = s; if ( p->avl_bf == a ) { s->avl_bf = -a; r->avl_bf = 0; } else if ( p->avl_bf == -a ) { s->avl_bf = 0; r->avl_bf = a; } else { s->avl_bf = 0; r->avl_bf = 0; } p->avl_bf = 0; } /* Update parent */ if ( t == NULL ) *root = p; else if ( s == t->avl_right ) t->avl_right = p; else t->avl_left = p; } return 0; } void* avl_delete( Avlnode **root, void* data, AVL_CMP fcmp ) { Avlnode *p, *q, *r, *top; int side, side_bf, shorter, nside; /* parent stack */ Avlnode *pptr[sizeof(void *)*8]; unsigned char pdir[sizeof(void *)*8]; int depth = 0; if ( *root == NULL ) return NULL; p = *root; while (1) { side = fcmp( data, p->avl_data ); if ( !side ) break; side = ( side > 0 ); pdir[depth] = side; pptr[depth++] = p; p = p->avl_link[side]; if ( p == NULL ) return p; } data = p->avl_data; /* If this node has two children, swap so we are deleting a node with * at most one child. */ if ( p->avl_link[0] && p->avl_link[1] ) { /* find the immediate predecessor */ q = p->avl_link[0]; side = depth; pdir[depth++] = 0; while (q->avl_link[1]) { pdir[depth] = 1; pptr[depth++] = q; q = q->avl_link[1]; } /* swap links */ r = p->avl_link[0]; p->avl_link[0] = q->avl_link[0]; q->avl_link[0] = r; q->avl_link[1] = p->avl_link[1]; p->avl_link[1] = NULL; q->avl_bf = p->avl_bf; /* fix stack positions: old parent of p points to q */ pptr[side] = q; if ( side ) { r = pptr[side-1]; r->avl_link[pdir[side-1]] = q; } else { *root = q; } /* new parent of p points to p */ if ( depth-side > 1 ) { r = pptr[depth-1]; r->avl_link[1] = p; } else { q->avl_link[0] = p; } } /* now

has at most one child, get it */ q = p->avl_link[0] ? p->avl_link[0] : p->avl_link[1]; ber_memfree( p ); if ( !depth ) { *root = q; return data; } /* set the child into p's parent */ depth--; p = pptr[depth]; side = pdir[depth]; p->avl_link[side] = q; top = NULL; shorter = 1; while ( shorter ) { p = pptr[depth]; side = pdir[depth]; nside = !side; side_bf = avl_bfs[side]; /* case 1: height unchanged */ if ( p->avl_bf == EH ) { /* Tree is now heavier on opposite side */ p->avl_bf = avl_bfs[nside]; shorter = 0; } else if ( p->avl_bf == side_bf ) { /* case 2: taller subtree shortened, height reduced */ p->avl_bf = EH; } else { /* case 3: shorter subtree shortened */ if ( depth ) top = pptr[depth-1]; /* p->parent; */ else top = NULL; /* set to the taller of the two subtrees of

*/ q = p->avl_link[nside]; if ( q->avl_bf == EH ) { /* case 3a: height unchanged, single rotate */ p->avl_link[nside] = q->avl_link[side]; q->avl_link[side] = p; shorter = 0; q->avl_bf = side_bf; p->avl_bf = (- side_bf); } else if ( q->avl_bf == p->avl_bf ) { /* case 3b: height reduced, single rotate */ p->avl_link[nside] = q->avl_link[side]; q->avl_link[side] = p; shorter = 1; q->avl_bf = EH; p->avl_bf = EH; } else { /* case 3c: height reduced, balance factors opposite */ r = q->avl_link[side]; q->avl_link[side] = r->avl_link[nside]; r->avl_link[nside] = q; p->avl_link[nside] = r->avl_link[side]; r->avl_link[side] = p; if ( r->avl_bf == side_bf ) { q->avl_bf = (- side_bf); p->avl_bf = EH; } else if ( r->avl_bf == (- side_bf)) { q->avl_bf = EH; p->avl_bf = side_bf; } else { q->avl_bf = EH; p->avl_bf = EH; } r->avl_bf = EH; q = r; } /* a rotation has caused (or in case 3c) to become * the root. let

's former parent know this. */ if ( top == NULL ) { *root = q; } else if (top->avl_link[0] == p) { top->avl_link[0] = q; } else { top->avl_link[1] = q; } /* end case 3 */ p = q; } if ( !depth ) break; depth--; } /* end while(shorter) */ return data; } static int avl_inapply( Avlnode *root, AVL_APPLY fn, void* arg, int stopflag ) { if ( root == 0 ) return( AVL_NOMORE ); if ( root->avl_left != 0 ) if ( avl_inapply( root->avl_left, fn, arg, stopflag ) == stopflag ) return( stopflag ); if ( (*fn)( root->avl_data, arg ) == stopflag ) return( stopflag ); if ( root->avl_right == 0 ) return( AVL_NOMORE ); else return( avl_inapply( root->avl_right, fn, arg, stopflag ) ); } static int avl_postapply( Avlnode *root, AVL_APPLY fn, void* arg, int stopflag ) { if ( root == 0 ) return( AVL_NOMORE ); if ( root->avl_left != 0 ) if ( avl_postapply( root->avl_left, fn, arg, stopflag ) == stopflag ) return( stopflag ); if ( root->avl_right != 0 ) if ( avl_postapply( root->avl_right, fn, arg, stopflag ) == stopflag ) return( stopflag ); return( (*fn)( root->avl_data, arg ) ); } static int avl_preapply( Avlnode *root, AVL_APPLY fn, void* arg, int stopflag ) { if ( root == 0 ) return( AVL_NOMORE ); if ( (*fn)( root->avl_data, arg ) == stopflag ) return( stopflag ); if ( root->avl_left != 0 ) if ( avl_preapply( root->avl_left, fn, arg, stopflag ) == stopflag ) return( stopflag ); if ( root->avl_right == 0 ) return( AVL_NOMORE ); else return( avl_preapply( root->avl_right, fn, arg, stopflag ) ); } /* * avl_apply -- avl tree root is traversed, function fn is called with * arguments arg and the data portion of each node. if fn returns stopflag, * the traversal is cut short, otherwise it continues. Do not use -6 as * a stopflag, as this is what is used to indicate the traversal ran out * of nodes. */ int avl_apply( Avlnode *root, AVL_APPLY fn, void* arg, int stopflag, int type ) { switch ( type ) { case AVL_INORDER: return( avl_inapply( root, fn, arg, stopflag ) ); case AVL_PREORDER: return( avl_preapply( root, fn, arg, stopflag ) ); case AVL_POSTORDER: return( avl_postapply( root, fn, arg, stopflag ) ); default: fprintf( stderr, "Invalid traversal type %d\n", type ); return( -1 ); } /* NOTREACHED */ } /* * avl_prefixapply - traverse avl tree root, applying function fprefix * to any nodes that match. fcmp is called with data as its first arg * and the current node's data as its second arg. it should return * 0 if they match, < 0 if data is less, and > 0 if data is greater. * the idea is to efficiently find all nodes that are prefixes of * some key... Like avl_apply, this routine also takes a stopflag * and will return prematurely if fmatch returns this value. Otherwise, * AVL_NOMORE is returned. */ int avl_prefixapply( Avlnode *root, void* data, AVL_CMP fmatch, void* marg, AVL_CMP fcmp, void* carg, int stopflag ) { int cmp; if ( root == 0 ) return( AVL_NOMORE ); cmp = (*fcmp)( data, root->avl_data /* , carg */); if ( cmp == 0 ) { if ( (*fmatch)( root->avl_data, marg ) == stopflag ) return( stopflag ); if ( root->avl_left != 0 ) if ( avl_prefixapply( root->avl_left, data, fmatch, marg, fcmp, carg, stopflag ) == stopflag ) return( stopflag ); if ( root->avl_right != 0 ) return( avl_prefixapply( root->avl_right, data, fmatch, marg, fcmp, carg, stopflag ) ); else return( AVL_NOMORE ); } else if ( cmp < 0 ) { if ( root->avl_left != 0 ) return( avl_prefixapply( root->avl_left, data, fmatch, marg, fcmp, carg, stopflag ) ); } else { if ( root->avl_right != 0 ) return( avl_prefixapply( root->avl_right, data, fmatch, marg, fcmp, carg, stopflag ) ); } return( AVL_NOMORE ); } /* * avl_free -- traverse avltree root, freeing the memory it is using. * the dfree() is called to free the data portion of each node. The * number of items actually freed is returned. */ int avl_free( Avlnode *root, AVL_FREE dfree ) { int nleft, nright; if ( root == 0 ) return( 0 ); nleft = nright = 0; if ( root->avl_left != 0 ) nleft = avl_free( root->avl_left, dfree ); if ( root->avl_right != 0 ) nright = avl_free( root->avl_right, dfree ); if ( dfree ) (*dfree)( root->avl_data ); ber_memfree( root ); return( nleft + nright + 1 ); } /* * avl_find -- search avltree root for a node with data data. the function * cmp is used to compare things. it is called with data as its first arg * and the current node data as its second. it should return 0 if they match, * < 0 if arg1 is less than arg2 and > 0 if arg1 is greater than arg2. */ Avlnode * avl_find2( Avlnode *root, const void *data, AVL_CMP fcmp ) { int cmp; while ( root != 0 && (cmp = (*fcmp)( data, root->avl_data )) != 0 ) { cmp = cmp > 0; root = root->avl_link[cmp]; } return root; } void* avl_find( Avlnode *root, const void* data, AVL_CMP fcmp ) { int cmp; while ( root != 0 && (cmp = (*fcmp)( data, root->avl_data )) != 0 ) { cmp = cmp > 0; root = root->avl_link[cmp]; } return( root ? root->avl_data : 0 ); } /* * avl_find_lin -- search avltree root linearly for a node with data data. * the function cmp is used to compare things. it is called with data as its * first arg and the current node data as its second. it should return 0 if * they match, non-zero otherwise. */ void* avl_find_lin( Avlnode *root, const void* data, AVL_CMP fcmp ) { void* res; if ( root == 0 ) return( NULL ); if ( (*fcmp)( data, root->avl_data ) == 0 ) return( root->avl_data ); if ( root->avl_left != 0 ) if ( (res = avl_find_lin( root->avl_left, data, fcmp )) != NULL ) return( res ); if ( root->avl_right == 0 ) return( NULL ); else return( avl_find_lin( root->avl_right, data, fcmp ) ); } /* NON-REENTRANT INTERFACE */ static void* *avl_list; static int avl_maxlist; static int avl_nextlist; #define AVL_GRABSIZE 100 /* ARGSUSED */ static int avl_buildlist( void* data, void* arg ) { static int slots; if ( avl_list == (void* *) 0 ) { avl_list = (void* *) ber_memalloc(AVL_GRABSIZE * sizeof(void*)); slots = AVL_GRABSIZE; avl_maxlist = 0; } else if ( avl_maxlist == slots ) { slots += AVL_GRABSIZE; avl_list = (void* *) ber_memrealloc( (char *) avl_list, (unsigned) slots * sizeof(void*)); } avl_list[ avl_maxlist++ ] = data; return( 0 ); } /* * avl_getfirst() and avl_getnext() are provided as alternate tree * traversal methods, to be used when a single function cannot be * provided to be called with every node in the tree. avl_getfirst() * traverses the tree and builds a linear list of all the nodes, * returning the first node. avl_getnext() returns the next thing * on the list built by avl_getfirst(). This means that avl_getfirst() * can take a while, and that the tree should not be messed with while * being traversed in this way, and that multiple traversals (even of * different trees) cannot be active at once. */ void* avl_getfirst( Avlnode *root ) { if ( avl_list ) { ber_memfree( (char *) avl_list); avl_list = (void* *) 0; } avl_maxlist = 0; avl_nextlist = 0; if ( root == 0 ) return( 0 ); (void) avl_apply( root, avl_buildlist, (void*) 0, -1, AVL_INORDER ); return( avl_list[ avl_nextlist++ ] ); } void* avl_getnext( void ) { if ( avl_list == 0 ) return( 0 ); if ( avl_nextlist == avl_maxlist ) { ber_memfree( (void*) avl_list); avl_list = (void* *) 0; return( 0 ); } return( avl_list[ avl_nextlist++ ] ); } /* end non-reentrant code */ int avl_dup_error( void* left, void* right ) { return( -1 ); } int avl_dup_ok( void* left, void* right ) { return( 0 ); }