123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671 |
- /* avl.c - routines to implement an avl tree */
- /* $OpenLDAP$ */
- /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
- *
- * Copyright 1998-2022 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
- * <http://www.OpenLDAP.org/license.html>.
- */
- /* 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 <limits.h>
- #include <stdio.h>
- #include <ac/stdlib.h>
- #ifdef CSRIMALLOC
- #define ber_memalloc malloc
- #define ber_memrealloc realloc
- #define ber_memfree free
- #else
- #include "lber.h"
- #endif
- #define AVL_INTERNAL
- #include "ldap_avl.h"
- /* Maximum tree depth this host's address space could support */
- #define MAX_TREE_DEPTH (sizeof(void *) * CHAR_BIT)
- static const int avl_bfs[] = {LH, RH};
- /*
- * ldap_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 ldap_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
- ldap_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_bits[0] = r->avl_bits[1] = AVL_CHILD;
- 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_bits[0] = q->avl_bits[1] = AVL_CHILD;
- 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*
- ldap_avl_delete( Avlnode **root, void* data, AVL_CMP fcmp )
- {
- Avlnode *p, *q, *r, *top;
- int side, side_bf, shorter, nside;
- /* parent stack */
- Avlnode *pptr[MAX_TREE_DEPTH];
- unsigned char pdir[MAX_TREE_DEPTH];
- 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> */
- 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 <p> 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 <q> to the taller of the two subtrees of <p> */
- 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 <q> (or <r> in case 3c) to become
- * the root. let <p>'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 ) );
- }
- /*
- * ldap_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
- ldap_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 */
- }
- /*
- * ldap_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 ldap_avl_apply, this routine also takes a stopflag
- * and will return prematurely if fmatch returns this value. Otherwise,
- * AVL_NOMORE is returned.
- */
- int
- ldap_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 ( ldap_avl_prefixapply( root->avl_left, data, fmatch,
- marg, fcmp, carg, stopflag ) == stopflag )
- return( stopflag );
- if ( root->avl_right != 0 )
- return( ldap_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( ldap_avl_prefixapply( root->avl_left, data, fmatch,
- marg, fcmp, carg, stopflag ) );
- } else {
- if ( root->avl_right != 0 )
- return( ldap_avl_prefixapply( root->avl_right, data, fmatch,
- marg, fcmp, carg, stopflag ) );
- }
- return( AVL_NOMORE );
- }
- /*
- * ldap_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
- ldap_avl_free( Avlnode *root, AVL_FREE dfree )
- {
- int nleft, nright;
- if ( root == 0 )
- return( 0 );
- nleft = nright = 0;
- if ( root->avl_left != 0 )
- nleft = ldap_avl_free( root->avl_left, dfree );
- if ( root->avl_right != 0 )
- nright = ldap_avl_free( root->avl_right, dfree );
- if ( dfree )
- (*dfree)( root->avl_data );
- ber_memfree( root );
- return( nleft + nright + 1 );
- }
- /*
- * ldap_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 *
- ldap_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*
- ldap_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 );
- }
- /*
- * ldap_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*
- ldap_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 = ldap_avl_find_lin( root->avl_left, data, fcmp ))
- != NULL )
- return( res );
- if ( root->avl_right == 0 )
- return( NULL );
- else
- return( ldap_avl_find_lin( root->avl_right, data, fcmp ) );
- }
- /* NON-REENTRANT INTERFACE */
- static void* *avl_list;
- static int avl_maxlist;
- static int ldap_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 );
- }
- /*
- * ldap_avl_getfirst() and ldap_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. ldap_avl_getfirst()
- * traverses the tree and builds a linear list of all the nodes,
- * returning the first node. ldap_avl_getnext() returns the next thing
- * on the list built by ldap_avl_getfirst(). This means that ldap_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*
- ldap_avl_getfirst( Avlnode *root )
- {
- if ( avl_list ) {
- ber_memfree( (char *) avl_list);
- avl_list = (void* *) 0;
- }
- avl_maxlist = 0;
- ldap_avl_nextlist = 0;
- if ( root == 0 )
- return( 0 );
- (void) ldap_avl_apply( root, avl_buildlist, (void*) 0, -1, AVL_INORDER );
- return( avl_list[ ldap_avl_nextlist++ ] );
- }
- void*
- ldap_avl_getnext( void )
- {
- if ( avl_list == 0 )
- return( 0 );
- if ( ldap_avl_nextlist == avl_maxlist ) {
- ber_memfree( (void*) avl_list);
- avl_list = (void* *) 0;
- return( 0 );
- }
- return( avl_list[ ldap_avl_nextlist++ ] );
- }
- /* end non-reentrant code */
- int
- ldap_avl_dup_error( void* left, void* right )
- {
- return( -1 );
- }
- int
- ldap_avl_dup_ok( void* left, void* right )
- {
- return( 0 );
- }
|