]> git.sur5r.net Git - openldap/blob - libraries/liblutil/tavl.c
923356c2ea73436f30e698d13edb0df8d97e73fc
[openldap] / libraries / liblutil / tavl.c
1 /* avl.c - routines to implement an avl tree */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2005 The OpenLDAP Foundation.
6  * Portions Copyright (c) 2005 by Howard Chu, Symas Corp.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work was initially developed by Howard Chu for inclusion
19  * in OpenLDAP software.
20  */
21
22 #include "portable.h"
23
24 #include <stdio.h>
25 #include <ac/stdlib.h>
26
27 #ifdef CSRIMALLOC
28 #define ber_memalloc malloc
29 #define ber_memrealloc realloc
30 #define ber_memfree free
31 #else
32 #include "lber.h"
33 #endif
34
35 #define AVL_INTERNAL
36 #include "avl.h"
37
38 static const int avl_bfs[] = {LH, RH};
39
40 /*
41  * Threaded AVL trees - for fast in-order traversal of nodes.
42  */
43 /*
44  * tavl_insert -- insert a node containing data data into the avl tree
45  * with root root.  fcmp is a function to call to compare the data portion
46  * of two nodes.  it should take two arguments and return <, >, or == 0,
47  * depending on whether its first argument is <, >, or == its second
48  * argument (like strcmp, e.g.).  fdup is a function to call when a duplicate
49  * node is inserted.  it should return 0, or -1 and its return value
50  * will be the return value from avl_insert in the case of a duplicate node.
51  * the function will be called with the original node's data as its first
52  * argument and with the incoming duplicate node's data as its second
53  * argument.  this could be used, for example, to keep a count with each
54  * node.
55  *
56  * NOTE: this routine may malloc memory
57  */
58 int
59 tavl_insert( Avlnode ** root, void *data, AVL_CMP fcmp, AVL_DUP fdup )
60 {
61     Avlnode *t, *p, *s, *q, *r;
62     int a, cmp, ncmp;
63
64         if ( *root == NULL ) {
65                 if (( r = (Avlnode *) ber_memalloc( sizeof( Avlnode ))) == NULL ) {
66                         return( -1 );
67                 }
68                 r->avl_link[0] = r->avl_link[1] = NULL;
69                 r->avl_data = data;
70                 r->avl_bf = EH;
71                 r->avl_bits[0] = r->avl_bits[1] = AVL_THREAD;
72                 *root = r;
73
74                 return( 0 );
75         }
76
77     t = NULL;
78     s = p = *root;
79
80         /* find insertion point */
81     while (1) {
82                 cmp = fcmp( data, p->avl_data );
83                 if ( cmp == 0 )
84                         return (*fdup)( p->avl_data, data );
85
86                 cmp = (cmp > 0);
87                 q = avl_child( p, cmp );
88                 if (q == NULL) {
89                         /* insert */
90                         if (( q = (Avlnode *) ber_memalloc( sizeof( Avlnode ))) == NULL ) {
91                                 return( -1 );
92                         }
93                         q->avl_link[cmp] = p->avl_link[cmp];
94                         q->avl_link[!cmp] = p;
95                         q->avl_data = data;
96                         q->avl_bf = EH;
97                         q->avl_bits[0] = q->avl_bits[1] = AVL_THREAD;
98
99                         p->avl_link[cmp] = q;
100                         p->avl_bits[cmp] = AVL_CHILD;
101                         break;
102                 } else if ( q->avl_bf ) {
103                         t = p;
104                         s = q;
105                 }
106                 p = q;
107     }
108
109     /* adjust balance factors */
110     cmp = fcmp( data, s->avl_data ) > 0;
111         r = p = s->avl_link[cmp];
112         a = avl_bfs[cmp];
113
114         while ( p != q ) {
115                 cmp = fcmp( data, p->avl_data ) > 0;
116                 p->avl_bf = avl_bfs[cmp];
117                 p = p->avl_link[cmp];
118         }
119
120         /* checks and balances */
121
122         if ( s->avl_bf == EH ) {
123                 s->avl_bf = a;
124                 return 0;
125         } else if ( s->avl_bf == -a ) {
126                 s->avl_bf = EH;
127                 return 0;
128     } else if ( s->avl_bf == a ) {
129                 cmp = (a > 0);
130                 ncmp = !cmp;
131                 if ( r->avl_bf == a ) {
132                         /* single rotation */
133                         p = r;
134                         if ( r->avl_bits[ncmp] == AVL_THREAD ) {
135                                 r->avl_bits[ncmp] = AVL_CHILD;
136                                 s->avl_bits[cmp] = AVL_THREAD;
137                         } else {
138                                 s->avl_link[cmp] = r->avl_link[ncmp];
139                                 r->avl_link[ncmp] = s;
140                         }
141                         s->avl_bf = 0;
142                         r->avl_bf = 0;
143                 } else if ( r->avl_bf == -a ) {
144                         /* double rotation */
145                         p = r->avl_link[ncmp];
146                         if ( p->avl_bits[cmp] == AVL_THREAD ) {
147                                 p->avl_bits[cmp] = AVL_CHILD;
148                                 r->avl_bits[ncmp] = AVL_THREAD;
149                         } else {
150                                 r->avl_link[ncmp] = p->avl_link[cmp];
151                                 p->avl_link[cmp] = r;
152                         }
153                         if ( p->avl_bits[ncmp] == AVL_THREAD ) {
154                                 p->avl_bits[ncmp] = AVL_CHILD;
155                                 s->avl_link[cmp] = p;
156                                 s->avl_bits[cmp] = AVL_THREAD;
157                         } else {
158                                 s->avl_link[cmp] = p->avl_link[ncmp];
159                                 p->avl_link[ncmp] = s;
160                         }
161                         if ( p->avl_bf == a ) {
162                                 s->avl_bf = -a;
163                                 r->avl_bf = 0;
164                         } else if ( p->avl_bf == -a ) {
165                                 s->avl_bf = 0;
166                                 r->avl_bf = a;
167                         } else {
168                                 s->avl_bf = 0;
169                                 r->avl_bf = 0;
170                         }
171                         p->avl_bf = 0;
172                 }
173                 /* Update parent */
174                 if ( t == NULL )
175                         *root = p;
176                 else if ( s == t->avl_right )
177                         t->avl_right = p;
178                 else
179                         t->avl_left = p;
180     }
181
182   return 0;
183 }
184
185 void*
186 tavl_delete( Avlnode **root, void* data, AVL_CMP fcmp )
187 {
188         Avlnode *p, *q, *r, *s, *top;
189         int side, side_bf, shorter, nside;
190
191         /* parent stack */
192         Avlnode *pptr[sizeof(void *)*8];
193         unsigned char pdir[sizeof(void *)*8];
194         int depth = 0;
195
196         if ( *root == NULL )
197                 return NULL;
198
199         p = *root;
200
201         while (1) {
202                 side = fcmp( data, p->avl_data );
203                 if ( !side )
204                         break;
205                 side = ( side > 0 );
206                 pdir[depth] = side;
207                 pptr[depth++] = p;
208
209                 if ( p->avl_bits[side] == AVL_THREAD )
210                         return NULL;
211                 p = p->avl_link[side];
212         }
213         data = p->avl_data;
214
215         /* If this node has two children, swap so we are deleting a node with
216          * at most one child.
217          */
218         if ( p->avl_bits[0] == AVL_CHILD && p->avl_bits[1] == AVL_CHILD &&
219                 p->avl_link[0] && p->avl_link[1] ) {
220
221                 /* find the immediate predecessor <q> */
222                 q = p->avl_link[0];
223                 pdir[depth] = 0;
224                 pptr[depth++] = p;
225                 while (q->avl_bits[1] == AVL_CHILD && q->avl_link[1]) {
226                         pdir[depth] = 1;
227                         pptr[depth++] = q;
228                         q = q->avl_link[1];
229                 }
230                 /* swap */
231                 p->avl_data = q->avl_data;
232                 p = q;
233         }
234
235         /* now <p> has at most one child, get it */
236         if ( p->avl_link[0] && p->avl_bits[0] == AVL_CHILD ) {
237                 q = p->avl_link[0];
238                 /* Preserve thread continuity */
239                 r = p->avl_link[1];
240                 nside = 1;
241         } else if ( p->avl_link[1] && p->avl_bits[1] == AVL_CHILD ) {
242                 q = p->avl_link[1];
243                 r = p->avl_link[0];
244                 nside = 0;
245         } else {
246                 q = NULL;
247                 if ( depth > 0 )
248                         r = p->avl_link[pdir[depth-1]];
249                 else
250                         r = NULL;
251         }
252
253         ber_memfree( p );
254
255         if ( !depth ) {
256                 *root = q;
257                 return data;
258         }
259
260         /* set the child into p's parent */
261         depth--;
262         p = pptr[depth];
263         side = pdir[depth];
264         p->avl_link[side] = q;
265
266         /* Update child thread */
267         if ( q ) {
268                 for ( ; q->avl_bits[nside] == AVL_CHILD && q->avl_link[nside];
269                         q = q->avl_link[nside] ) ;
270                 q->avl_link[nside] = r;
271         } else {
272                 p->avl_bits[side] = AVL_THREAD;
273                 p->avl_link[side] = r;
274         }
275
276         top = NULL;
277         shorter = 1;
278   
279         while ( shorter ) {
280                 p = pptr[depth];
281                 side = pdir[depth];
282                 nside = !side;
283                 side_bf = avl_bfs[side];
284
285                 /* case 1: height unchanged */
286                 if ( p->avl_bf == EH ) {
287                         /* Tree is now heavier on opposite side */
288                         p->avl_bf = avl_bfs[nside];
289                         shorter = 0;
290                   
291                 } else if ( p->avl_bf == side_bf ) {
292                 /* case 2: taller subtree shortened, height reduced */
293                         p->avl_bf = EH;
294                 } else {
295                 /* case 3: shorter subtree shortened */
296                         if ( depth )
297                                 top = pptr[depth-1]; /* p->parent; */
298                         else
299                                 top = NULL;
300                         /* set <q> to the taller of the two subtrees of <p> */
301                         q = p->avl_link[nside];
302                         if ( q->avl_bf == EH ) {
303                                 /* case 3a: height unchanged, single rotate */
304                                 if ( q->avl_bits[side] == AVL_THREAD ) {
305                                         q->avl_bits[side] = AVL_CHILD;
306                                         p->avl_bits[nside] = AVL_THREAD;
307                                 } else {
308                                         p->avl_link[nside] = q->avl_link[side];
309                                         q->avl_link[side] = p;
310                                 }
311                                 shorter = 0;
312                                 q->avl_bf = side_bf;
313                                 p->avl_bf = (- side_bf);
314
315                         } else if ( q->avl_bf == p->avl_bf ) {
316                                 /* case 3b: height reduced, single rotate */
317                                 if ( q->avl_bits[side] == AVL_THREAD ) {
318                                         q->avl_bits[side] = AVL_CHILD;
319                                         p->avl_bits[nside] = AVL_THREAD;
320                                 } else {
321                                         p->avl_link[nside] = q->avl_link[side];
322                                         q->avl_link[side] = p;
323                                 }
324                                 shorter = 1;
325                                 q->avl_bf = EH;
326                                 p->avl_bf = EH;
327
328                         } else {
329                                 /* case 3c: height reduced, balance factors opposite */
330                                 r = q->avl_link[side];
331                                 if ( r->avl_bits[nside] == AVL_THREAD ) {
332                                         r->avl_bits[nside] = AVL_CHILD;
333                                         q->avl_bits[side] = AVL_THREAD;
334                                 } else {
335                                         q->avl_link[side] = r->avl_link[nside];
336                                         r->avl_link[nside] = q;
337                                 }
338
339                                 if ( r->avl_bits[side] == AVL_THREAD ) {
340                                         r->avl_bits[side] = AVL_CHILD;
341                                         p->avl_bits[nside] = AVL_THREAD;
342                                         p->avl_link[nside] = r;
343                                 } else {
344                                         p->avl_link[nside] = r->avl_link[side];
345                                         r->avl_link[side] = p;
346                                 }
347
348                                 if ( r->avl_bf == side_bf ) {
349                                         q->avl_bf = (- side_bf);
350                                         p->avl_bf = EH;
351                                 } else if ( r->avl_bf == (- side_bf)) {
352                                         q->avl_bf = EH;
353                                         p->avl_bf = side_bf;
354                                 } else {
355                                         q->avl_bf = EH;
356                                         p->avl_bf = EH;
357                                 }
358                                 r->avl_bf = EH;
359                                 q = r;
360                         }
361                         /* a rotation has caused <q> (or <r> in case 3c) to become
362                          * the root.  let <p>'s former parent know this.
363                          */
364                         if ( top == NULL ) {
365                                 *root = q;
366                         } else if (top->avl_link[0] == p) {
367                                 top->avl_link[0] = q;
368                         } else {
369                                 top->avl_link[1] = q;
370                         }
371                         /* end case 3 */
372                         p = q;
373                 }
374                 if ( !depth )
375                         break;
376                 depth--;
377         } /* end while(shorter) */
378
379         return data;
380 }
381
382 /*
383  * tavl_free -- traverse avltree root, freeing the memory it is using.
384  * the dfree() is called to free the data portion of each node.  The
385  * number of items actually freed is returned.
386  */
387
388 int
389 tavl_free( Avlnode *root, AVL_FREE dfree )
390 {
391         int     nleft, nright;
392
393         if ( root == 0 )
394                 return( 0 );
395
396         nleft = tavl_free( avl_lchild( root ), dfree );
397
398         nright = tavl_free( avl_rchild( root ), dfree );
399
400         if ( dfree )
401                 (*dfree)( root->avl_data );
402         ber_memfree( root );
403
404         return( nleft + nright + 1 );
405 }
406
407 /*
408  * tavl_find -- search avltree root for a node with data data.  the function
409  * cmp is used to compare things.  it is called with data as its first arg 
410  * and the current node data as its second.  it should return 0 if they match,
411  * < 0 if arg1 is less than arg2 and > 0 if arg1 is greater than arg2.
412  */
413
414 Avlnode *
415 tavl_find2( Avlnode *root, const void *data, AVL_CMP fcmp )
416 {
417         int     cmp;
418
419         while ( root != 0 && (cmp = (*fcmp)( data, root->avl_data )) != 0 ) {
420                 cmp = cmp > 0;
421                 root = avl_child( root, cmp );
422         }
423         return root;
424 }
425
426 void*
427 tavl_find( Avlnode *root, const void* data, AVL_CMP fcmp )
428 {
429         int     cmp;
430
431         while ( root != 0 && (cmp = (*fcmp)( data, root->avl_data )) != 0 ) {
432                 cmp = cmp > 0;
433                 root = avl_child( root, cmp );
434         }
435
436         return( root ? root->avl_data : 0 );
437 }
438
439 /* Return the leftmost or rightmost node in the tree */
440 Avlnode *
441 tavl_end( Avlnode *root, int dir )
442 {
443         if ( root ) {
444                 while ( root->avl_bits[dir] == AVL_CHILD )
445                         root = root->avl_link[dir];
446         }
447         return root;
448 }
449
450 /* Return the next node in the given direction */
451 Avlnode *
452 tavl_next( Avlnode *root, int dir )
453 {
454         if ( root ) {
455                 int c = root->avl_bits[dir];
456
457                 root = root->avl_link[dir];
458                 if ( c == AVL_CHILD ) {
459                         dir ^= 1;
460                         while ( root->avl_bits[dir] == AVL_CHILD )
461                                 root = root->avl_link[dir];
462                 }
463         }
464         return root;
465 }