Home | History | Annotate | Line # | Download | only in libldap
      1 /*	$NetBSD: avl.c,v 1.3 2025/09/05 21:16:21 christos Exp $	*/
      2 
      3 /* avl.c - routines to implement an avl tree */
      4 /* $OpenLDAP$ */
      5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      6  *
      7  * Copyright 1998-2024 The OpenLDAP Foundation.
      8  * All rights reserved.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted only as authorized by the OpenLDAP
     12  * Public License.
     13  *
     14  * A copy of this license is available in the file LICENSE in the
     15  * top-level directory of the distribution or, alternatively, at
     16  * <http://www.OpenLDAP.org/license.html>.
     17  */
     18 /* Portions Copyright (c) 1993 Regents of the University of Michigan.
     19  * All rights reserved.
     20  *
     21  * Redistribution and use in source and binary forms are permitted
     22  * provided that this notice is preserved and that due credit is given
     23  * to the University of Michigan at Ann Arbor. The name of the University
     24  * may not be used to endorse or promote products derived from this
     25  * software without specific prior written permission. This software
     26  * is provided ``as is'' without express or implied warranty.
     27  */
     28 /* ACKNOWLEDGEMENTS:
     29  * This work was originally developed by the University of Michigan
     30  * (as part of U-MICH LDAP).  Additional significant contributors
     31  * include:
     32  *   Howard Y. Chu
     33  *   Hallvard B. Furuseth
     34  *   Kurt D. Zeilenga
     35  */
     36 
     37 #include <sys/cdefs.h>
     38 __RCSID("$NetBSD: avl.c,v 1.3 2025/09/05 21:16:21 christos Exp $");
     39 
     40 #include "portable.h"
     41 
     42 #include <limits.h>
     43 #include <stdio.h>
     44 #include <ac/stdlib.h>
     45 
     46 #ifdef CSRIMALLOC
     47 #define ber_memalloc malloc
     48 #define ber_memrealloc realloc
     49 #define ber_memfree free
     50 #else
     51 #include "lber.h"
     52 #endif
     53 
     54 #define AVL_INTERNAL
     55 #include "ldap_avl.h"
     56 
     57 /* Maximum tree depth this host's address space could support */
     58 #define MAX_TREE_DEPTH	(sizeof(void *) * CHAR_BIT)
     59 
     60 static const int avl_bfs[] = {LH, RH};
     61 
     62 /*
     63  * ldap_avl_insert -- insert a node containing data data into the avl tree
     64  * with root root.  fcmp is a function to call to compare the data portion
     65  * of two nodes.  it should take two arguments and return <, >, or == 0,
     66  * depending on whether its first argument is <, >, or == its second
     67  * argument (like strcmp, e.g.).  fdup is a function to call when a duplicate
     68  * node is inserted.  it should return 0, or -1 and its return value
     69  * will be the return value from ldap_avl_insert in the case of a duplicate node.
     70  * the function will be called with the original node's data as its first
     71  * argument and with the incoming duplicate node's data as its second
     72  * argument.  this could be used, for example, to keep a count with each
     73  * node.
     74  *
     75  * NOTE: this routine may malloc memory
     76  */
     77 int
     78 ldap_avl_insert( Avlnode ** root, void *data, AVL_CMP fcmp, AVL_DUP fdup )
     79 {
     80     Avlnode *t, *p, *s, *q, *r;
     81     int a, cmp, ncmp;
     82 
     83 	if ( *root == NULL ) {
     84 		if (( r = (Avlnode *) ber_memalloc( sizeof( Avlnode ))) == NULL ) {
     85 			return( -1 );
     86 		}
     87 		r->avl_link[0] = r->avl_link[1] = NULL;
     88 		r->avl_data = data;
     89 		r->avl_bits[0] = r->avl_bits[1] = AVL_CHILD;
     90 		r->avl_bf = EH;
     91 		*root = r;
     92 
     93 		return( 0 );
     94 	}
     95 
     96     t = NULL;
     97     s = p = *root;
     98 
     99 	/* find insertion point */
    100     while (1) {
    101 		cmp = fcmp( data, p->avl_data );
    102 		if ( cmp == 0 )
    103 			return (*fdup)( p->avl_data, data );
    104 
    105 		cmp = (cmp > 0);
    106 		q = p->avl_link[cmp];
    107 		if (q == NULL) {
    108 			/* insert */
    109 			if (( q = (Avlnode *) ber_memalloc( sizeof( Avlnode ))) == NULL ) {
    110 				return( -1 );
    111 			}
    112 			q->avl_link[0] = q->avl_link[1] = NULL;
    113 			q->avl_data = data;
    114 			q->avl_bits[0] = q->avl_bits[1] = AVL_CHILD;
    115 			q->avl_bf = EH;
    116 
    117 			p->avl_link[cmp] = q;
    118 			break;
    119 		} else if ( q->avl_bf ) {
    120 			t = p;
    121 			s = q;
    122 		}
    123 		p = q;
    124     }
    125 
    126     /* adjust balance factors */
    127     cmp = fcmp( data, s->avl_data ) > 0;
    128 	r = p = s->avl_link[cmp];
    129 	a = avl_bfs[cmp];
    130 
    131 	while ( p != q ) {
    132 		cmp = fcmp( data, p->avl_data ) > 0;
    133 		p->avl_bf = avl_bfs[cmp];
    134 		p = p->avl_link[cmp];
    135 	}
    136 
    137 	/* checks and balances */
    138 
    139 	if ( s->avl_bf == EH ) {
    140 		s->avl_bf = a;
    141 		return 0;
    142 	} else if ( s->avl_bf == -a ) {
    143 		s->avl_bf = EH;
    144 		return 0;
    145     } else if ( s->avl_bf == a ) {
    146 		cmp = (a > 0);
    147 		ncmp = !cmp;
    148 		if ( r->avl_bf == a ) {
    149 			/* single rotation */
    150 			p = r;
    151 			s->avl_link[cmp] = r->avl_link[ncmp];
    152 			r->avl_link[ncmp] = s;
    153 			s->avl_bf = 0;
    154 			r->avl_bf = 0;
    155 		} else if ( r->avl_bf == -a ) {
    156 			/* double rotation */
    157 			p = r->avl_link[ncmp];
    158 			r->avl_link[ncmp] = p->avl_link[cmp];
    159 			p->avl_link[cmp] = r;
    160 			s->avl_link[cmp] = p->avl_link[ncmp];
    161 			p->avl_link[ncmp] = s;
    162 
    163 			if ( p->avl_bf == a ) {
    164 				s->avl_bf = -a;
    165 				r->avl_bf = 0;
    166 			} else if ( p->avl_bf == -a ) {
    167 				s->avl_bf = 0;
    168 				r->avl_bf = a;
    169 			} else {
    170 				s->avl_bf = 0;
    171 				r->avl_bf = 0;
    172 			}
    173 			p->avl_bf = 0;
    174 		}
    175 		/* Update parent */
    176 		if ( t == NULL )
    177 			*root = p;
    178 		else if ( s == t->avl_right )
    179 			t->avl_right = p;
    180 		else
    181 			t->avl_left = p;
    182     }
    183 
    184   return 0;
    185 }
    186 
    187 void*
    188 ldap_avl_delete( Avlnode **root, void* data, AVL_CMP fcmp )
    189 {
    190 	Avlnode *p, *q, *r, *top;
    191 	int side, side_bf, shorter, nside;
    192 
    193 	/* parent stack */
    194 	Avlnode *pptr[MAX_TREE_DEPTH];
    195 	unsigned char pdir[MAX_TREE_DEPTH];
    196 	int depth = 0;
    197 
    198 	if ( *root == NULL )
    199 		return NULL;
    200 
    201 	p = *root;
    202 
    203 	while (1) {
    204 		side = fcmp( data, p->avl_data );
    205 		if ( !side )
    206 			break;
    207 		side = ( side > 0 );
    208 		pdir[depth] = side;
    209 		pptr[depth++] = p;
    210 
    211 		p = p->avl_link[side];
    212 		if ( p == NULL )
    213 			return p;
    214 	}
    215 	data = p->avl_data;
    216 
    217 	/* If this node has two children, swap so we are deleting a node with
    218 	 * at most one child.
    219 	 */
    220 	if ( p->avl_link[0] && p->avl_link[1] ) {
    221 
    222 		/* find the immediate predecessor <q> */
    223 		q = p->avl_link[0];
    224 		side = depth;
    225 		pdir[depth++] = 0;
    226 		while (q->avl_link[1]) {
    227 			pdir[depth] = 1;
    228 			pptr[depth++] = q;
    229 			q = q->avl_link[1];
    230 		}
    231 		/* swap links */
    232 		r = p->avl_link[0];
    233 		p->avl_link[0] = q->avl_link[0];
    234 		q->avl_link[0] = r;
    235 
    236 		q->avl_link[1] = p->avl_link[1];
    237 		p->avl_link[1] = NULL;
    238 
    239 		q->avl_bf = p->avl_bf;
    240 
    241 		/* fix stack positions: old parent of p points to q */
    242 		pptr[side] = q;
    243 		if ( side ) {
    244 			r = pptr[side-1];
    245 			r->avl_link[pdir[side-1]] = q;
    246 		} else {
    247 			*root = q;
    248 		}
    249 		/* new parent of p points to p */
    250 		if ( depth-side > 1 ) {
    251 			r = pptr[depth-1];
    252 			r->avl_link[1] = p;
    253 		} else {
    254 			q->avl_link[0] = p;
    255 		}
    256 	}
    257 
    258 	/* now <p> has at most one child, get it */
    259 	q = p->avl_link[0] ? p->avl_link[0] : p->avl_link[1];
    260 
    261 	ber_memfree( p );
    262 
    263 	if ( !depth ) {
    264 		*root = q;
    265 		return data;
    266 	}
    267 
    268 	/* set the child into p's parent */
    269 	depth--;
    270 	p = pptr[depth];
    271 	side = pdir[depth];
    272 	p->avl_link[side] = q;
    273 
    274 	top = NULL;
    275 	shorter = 1;
    276 
    277 	while ( shorter ) {
    278 		p = pptr[depth];
    279 		side = pdir[depth];
    280 		nside = !side;
    281 		side_bf = avl_bfs[side];
    282 
    283 		/* case 1: height unchanged */
    284 		if ( p->avl_bf == EH ) {
    285 			/* Tree is now heavier on opposite side */
    286 			p->avl_bf = avl_bfs[nside];
    287 			shorter = 0;
    288 
    289 		} else if ( p->avl_bf == side_bf ) {
    290 		/* case 2: taller subtree shortened, height reduced */
    291 			p->avl_bf = EH;
    292 		} else {
    293 		/* case 3: shorter subtree shortened */
    294 			if ( depth )
    295 				top = pptr[depth-1]; /* p->parent; */
    296 			else
    297 				top = NULL;
    298 			/* set <q> to the taller of the two subtrees of <p> */
    299 			q = p->avl_link[nside];
    300 			if ( q->avl_bf == EH ) {
    301 				/* case 3a: height unchanged, single rotate */
    302 				p->avl_link[nside] = q->avl_link[side];
    303 				q->avl_link[side] = p;
    304 				shorter = 0;
    305 				q->avl_bf = side_bf;
    306 				p->avl_bf = (- side_bf);
    307 
    308 			} else if ( q->avl_bf == p->avl_bf ) {
    309 				/* case 3b: height reduced, single rotate */
    310 				p->avl_link[nside] = q->avl_link[side];
    311 				q->avl_link[side] = p;
    312 				shorter = 1;
    313 				q->avl_bf = EH;
    314 				p->avl_bf = EH;
    315 
    316 			} else {
    317 				/* case 3c: height reduced, balance factors opposite */
    318 				r = q->avl_link[side];
    319 				q->avl_link[side] = r->avl_link[nside];
    320 				r->avl_link[nside] = q;
    321 
    322 				p->avl_link[nside] = r->avl_link[side];
    323 				r->avl_link[side] = p;
    324 
    325 				if ( r->avl_bf == side_bf ) {
    326 					q->avl_bf = (- side_bf);
    327 					p->avl_bf = EH;
    328 				} else if ( r->avl_bf == (- side_bf)) {
    329 					q->avl_bf = EH;
    330 					p->avl_bf = side_bf;
    331 				} else {
    332 					q->avl_bf = EH;
    333 					p->avl_bf = EH;
    334 				}
    335 				r->avl_bf = EH;
    336 				q = r;
    337 			}
    338 			/* a rotation has caused <q> (or <r> in case 3c) to become
    339 			 * the root.  let <p>'s former parent know this.
    340 			 */
    341 			if ( top == NULL ) {
    342 				*root = q;
    343 			} else if (top->avl_link[0] == p) {
    344 				top->avl_link[0] = q;
    345 			} else {
    346 				top->avl_link[1] = q;
    347 			}
    348 			/* end case 3 */
    349 			p = q;
    350 		}
    351 		if ( !depth )
    352 			break;
    353 		depth--;
    354 	} /* end while(shorter) */
    355 
    356 	return data;
    357 }
    358 
    359 static int
    360 avl_inapply( Avlnode *root, AVL_APPLY fn, void* arg, int stopflag )
    361 {
    362 	if ( root == 0 )
    363 		return( AVL_NOMORE );
    364 
    365 	if ( root->avl_left != 0 )
    366 		if ( avl_inapply( root->avl_left, fn, arg, stopflag )
    367 		    == stopflag )
    368 			return( stopflag );
    369 
    370 	if ( (*fn)( root->avl_data, arg ) == stopflag )
    371 		return( stopflag );
    372 
    373 	if ( root->avl_right == 0 )
    374 		return( AVL_NOMORE );
    375 	else
    376 		return( avl_inapply( root->avl_right, fn, arg, stopflag ) );
    377 }
    378 
    379 static int
    380 avl_postapply( Avlnode *root, AVL_APPLY fn, void* arg, int stopflag )
    381 {
    382 	if ( root == 0 )
    383 		return( AVL_NOMORE );
    384 
    385 	if ( root->avl_left != 0 )
    386 		if ( avl_postapply( root->avl_left, fn, arg, stopflag )
    387 		    == stopflag )
    388 			return( stopflag );
    389 
    390 	if ( root->avl_right != 0 )
    391 		if ( avl_postapply( root->avl_right, fn, arg, stopflag )
    392 		    == stopflag )
    393 			return( stopflag );
    394 
    395 	return( (*fn)( root->avl_data, arg ) );
    396 }
    397 
    398 static int
    399 avl_preapply( Avlnode *root, AVL_APPLY fn, void* arg, int stopflag )
    400 {
    401 	if ( root == 0 )
    402 		return( AVL_NOMORE );
    403 
    404 	if ( (*fn)( root->avl_data, arg ) == stopflag )
    405 		return( stopflag );
    406 
    407 	if ( root->avl_left != 0 )
    408 		if ( avl_preapply( root->avl_left, fn, arg, stopflag )
    409 		    == stopflag )
    410 			return( stopflag );
    411 
    412 	if ( root->avl_right == 0 )
    413 		return( AVL_NOMORE );
    414 	else
    415 		return( avl_preapply( root->avl_right, fn, arg, stopflag ) );
    416 }
    417 
    418 /*
    419  * ldap_avl_apply -- avl tree root is traversed, function fn is called with
    420  * arguments arg and the data portion of each node.  if fn returns stopflag,
    421  * the traversal is cut short, otherwise it continues.  Do not use -6 as
    422  * a stopflag, as this is what is used to indicate the traversal ran out
    423  * of nodes.
    424  */
    425 
    426 int
    427 ldap_avl_apply( Avlnode *root, AVL_APPLY fn, void* arg, int stopflag, int type )
    428 {
    429 	switch ( type ) {
    430 	case AVL_INORDER:
    431 		return( avl_inapply( root, fn, arg, stopflag ) );
    432 	case AVL_PREORDER:
    433 		return( avl_preapply( root, fn, arg, stopflag ) );
    434 	case AVL_POSTORDER:
    435 		return( avl_postapply( root, fn, arg, stopflag ) );
    436 	default:
    437 		fprintf( stderr, "Invalid traversal type %d\n", type );
    438 		return( -1 );
    439 	}
    440 
    441 	/* NOTREACHED */
    442 }
    443 
    444 /*
    445  * ldap_avl_prefixapply - traverse avl tree root, applying function fprefix
    446  * to any nodes that match.  fcmp is called with data as its first arg
    447  * and the current node's data as its second arg.  it should return
    448  * 0 if they match, < 0 if data is less, and > 0 if data is greater.
    449  * the idea is to efficiently find all nodes that are prefixes of
    450  * some key...  Like ldap_avl_apply, this routine also takes a stopflag
    451  * and will return prematurely if fmatch returns this value.  Otherwise,
    452  * AVL_NOMORE is returned.
    453  */
    454 
    455 int
    456 ldap_avl_prefixapply(
    457     Avlnode	*root,
    458     void*	data,
    459     AVL_CMP		fmatch,
    460     void*	marg,
    461     AVL_CMP		fcmp,
    462     void*	carg,
    463     int		stopflag
    464 )
    465 {
    466 	int	cmp;
    467 
    468 	if ( root == 0 )
    469 		return( AVL_NOMORE );
    470 
    471 	cmp = (*fcmp)( data, root->avl_data /* , carg */);
    472 	if ( cmp == 0 ) {
    473 		if ( (*fmatch)( root->avl_data, marg ) == stopflag )
    474 			return( stopflag );
    475 
    476 		if ( root->avl_left != 0 )
    477 			if ( ldap_avl_prefixapply( root->avl_left, data, fmatch,
    478 			    marg, fcmp, carg, stopflag ) == stopflag )
    479 				return( stopflag );
    480 
    481 		if ( root->avl_right != 0 )
    482 			return( ldap_avl_prefixapply( root->avl_right, data, fmatch,
    483 			    marg, fcmp, carg, stopflag ) );
    484 		else
    485 			return( AVL_NOMORE );
    486 
    487 	} else if ( cmp < 0 ) {
    488 		if ( root->avl_left != 0 )
    489 			return( ldap_avl_prefixapply( root->avl_left, data, fmatch,
    490 			    marg, fcmp, carg, stopflag ) );
    491 	} else {
    492 		if ( root->avl_right != 0 )
    493 			return( ldap_avl_prefixapply( root->avl_right, data, fmatch,
    494 			    marg, fcmp, carg, stopflag ) );
    495 	}
    496 
    497 	return( AVL_NOMORE );
    498 }
    499 
    500 /*
    501  * ldap_avl_free -- traverse avltree root, freeing the memory it is using.
    502  * the dfree() is called to free the data portion of each node.  The
    503  * number of items actually freed is returned.
    504  */
    505 
    506 int
    507 ldap_avl_free( Avlnode *root, AVL_FREE dfree )
    508 {
    509 	int	nleft, nright;
    510 
    511 	if ( root == 0 )
    512 		return( 0 );
    513 
    514 	nleft = nright = 0;
    515 	if ( root->avl_left != 0 )
    516 		nleft = ldap_avl_free( root->avl_left, dfree );
    517 
    518 	if ( root->avl_right != 0 )
    519 		nright = ldap_avl_free( root->avl_right, dfree );
    520 
    521 	if ( dfree )
    522 		(*dfree)( root->avl_data );
    523 	ber_memfree( root );
    524 
    525 	return( nleft + nright + 1 );
    526 }
    527 
    528 /*
    529  * ldap_avl_find -- search avltree root for a node with data data.  the function
    530  * cmp is used to compare things.  it is called with data as its first arg
    531  * and the current node data as its second.  it should return 0 if they match,
    532  * < 0 if arg1 is less than arg2 and > 0 if arg1 is greater than arg2.
    533  */
    534 
    535 Avlnode *
    536 ldap_avl_find2( Avlnode *root, const void *data, AVL_CMP fcmp )
    537 {
    538 	int	cmp;
    539 
    540 	while ( root != 0 && (cmp = (*fcmp)( data, root->avl_data )) != 0 ) {
    541 		cmp = cmp > 0;
    542 		root = root->avl_link[cmp];
    543 	}
    544 	return root;
    545 }
    546 
    547 void*
    548 ldap_avl_find( Avlnode *root, const void* data, AVL_CMP fcmp )
    549 {
    550 	int	cmp;
    551 
    552 	while ( root != 0 && (cmp = (*fcmp)( data, root->avl_data )) != 0 ) {
    553 		cmp = cmp > 0;
    554 		root = root->avl_link[cmp];
    555 	}
    556 
    557 	return( root ? root->avl_data : 0 );
    558 }
    559 
    560 /*
    561  * ldap_avl_find_lin -- search avltree root linearly for a node with data data.
    562  * the function cmp is used to compare things.  it is called with data as its
    563  * first arg and the current node data as its second.  it should return 0 if
    564  * they match, non-zero otherwise.
    565  */
    566 
    567 void*
    568 ldap_avl_find_lin( Avlnode *root, const void* data, AVL_CMP fcmp )
    569 {
    570 	void*	res;
    571 
    572 	if ( root == 0 )
    573 		return( NULL );
    574 
    575 	if ( (*fcmp)( data, root->avl_data ) == 0 )
    576 		return( root->avl_data );
    577 
    578 	if ( root->avl_left != 0 )
    579 		if ( (res = ldap_avl_find_lin( root->avl_left, data, fcmp ))
    580 		    != NULL )
    581 			return( res );
    582 
    583 	if ( root->avl_right == 0 )
    584 		return( NULL );
    585 	else
    586 		return( ldap_avl_find_lin( root->avl_right, data, fcmp ) );
    587 }
    588 
    589 /* NON-REENTRANT INTERFACE */
    590 
    591 static void*	*avl_list;
    592 static int	avl_maxlist;
    593 static int	ldap_avl_nextlist;
    594 
    595 #define AVL_GRABSIZE	100
    596 
    597 /* ARGSUSED */
    598 static int
    599 avl_buildlist( void* data, void* arg )
    600 {
    601 	static int	slots;
    602 
    603 	if ( avl_list == (void* *) 0 ) {
    604 		avl_list = (void* *) ber_memalloc(AVL_GRABSIZE * sizeof(void*));
    605 		slots = AVL_GRABSIZE;
    606 		avl_maxlist = 0;
    607 	} else if ( avl_maxlist == slots ) {
    608 		slots += AVL_GRABSIZE;
    609 		avl_list = (void* *) ber_memrealloc( (char *) avl_list,
    610 		    (unsigned) slots * sizeof(void*));
    611 	}
    612 
    613 	avl_list[ avl_maxlist++ ] = data;
    614 
    615 	return( 0 );
    616 }
    617 
    618 /*
    619  * ldap_avl_getfirst() and ldap_avl_getnext() are provided as alternate tree
    620  * traversal methods, to be used when a single function cannot be
    621  * provided to be called with every node in the tree.  ldap_avl_getfirst()
    622  * traverses the tree and builds a linear list of all the nodes,
    623  * returning the first node.  ldap_avl_getnext() returns the next thing
    624  * on the list built by ldap_avl_getfirst().  This means that ldap_avl_getfirst()
    625  * can take a while, and that the tree should not be messed with while
    626  * being traversed in this way, and that multiple traversals (even of
    627  * different trees) cannot be active at once.
    628  */
    629 
    630 void*
    631 ldap_avl_getfirst( Avlnode *root )
    632 {
    633 	if ( avl_list ) {
    634 		ber_memfree( (char *) avl_list);
    635 		avl_list = (void* *) 0;
    636 	}
    637 	avl_maxlist = 0;
    638 	ldap_avl_nextlist = 0;
    639 
    640 	if ( root == 0 )
    641 		return( 0 );
    642 
    643 	(void) ldap_avl_apply( root, avl_buildlist, (void*) 0, -1, AVL_INORDER );
    644 
    645 	return( avl_list[ ldap_avl_nextlist++ ] );
    646 }
    647 
    648 void*
    649 ldap_avl_getnext( void )
    650 {
    651 	if ( avl_list == 0 )
    652 		return( 0 );
    653 
    654 	if ( ldap_avl_nextlist == avl_maxlist ) {
    655 		ber_memfree( (void*) avl_list);
    656 		avl_list = (void* *) 0;
    657 		return( 0 );
    658 	}
    659 
    660 	return( avl_list[ ldap_avl_nextlist++ ] );
    661 }
    662 
    663 /* end non-reentrant code */
    664 
    665 
    666 int
    667 ldap_avl_dup_error( void* left, void* right )
    668 {
    669 	return( -1 );
    670 }
    671 
    672 int
    673 ldap_avl_dup_ok( void* left, void* right )
    674 {
    675 	return( 0 );
    676 }
    677