]> git.sur5r.net Git - openldap/blob - libraries/liblutil/tavl.c
Add threaded AVL functions
[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, *top;
189         int side, side_bf, shorter, cmp;
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                 cmp = fcmp( data, p->avl_data );
203                 if ( !cmp )
204                         break;
205                 cmp = ( cmp > 0 );
206                 pdir[depth] = cmp;
207                 pptr[depth++] = p;
208
209                 p = avl_child( p, cmp );
210                 if ( p == NULL )
211                         return NULL;
212         }
213
214         /* If this node has two children, swap so we are deleting a node with
215          * at most one child.
216          */
217         if ( p->avl_bits[0] == AVL_CHILD && p->avl_bits[1] == AVL_CHILD &&
218                 p->avl_link[0] && p->avl_link[1] ) {
219                 void *temp;
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                 temp = p->avl_data;
232                 p->avl_data = q->avl_data;
233                 q->avl_data = temp;
234                 p = q;
235         }
236
237         /* now <p> has at most one child, get it */
238         if ( p->avl_link[0] && p->avl_bits[0] == AVL_CHILD ) {
239                 q = p->avl_link[0];
240         } else if ( p->avl_link[1] && p->avl_bits[1] == AVL_CHILD ) {
241                 q = p->avl_link[1];
242         } else {
243                 q = NULL;
244         }
245
246         data = p->avl_data;
247         ber_memfree( p );
248
249         if ( !depth ) {
250                 *root = q;
251                 return data;
252         }
253
254         /* set the child into p's parent */
255         depth--;
256         side = pdir[depth];
257         p = pptr[depth];
258         p->avl_link[side] = q;
259         side_bf = avl_bfs[side];
260         top = NULL;
261
262         /* Update child thread */
263         if ( q ) {
264                 while ( q->avl_bits[!side] == AVL_CHILD )
265                         q = q->avl_link[!side];
266                 q->avl_link[!side] = p;
267         }
268
269         shorter = 1;
270   
271         while ( shorter ) {
272                 /* case 1: height unchanged */
273                 if ( p->avl_bf == EH ) {
274                         /* Tree is now heavier on opposite side */
275                         p->avl_bf = avl_bfs[!side];
276                         shorter = 0;
277                   
278                 } else if ( p->avl_bf == side_bf ) {
279                 /* case 2: taller subtree shortened, height reduced */
280                         p->avl_bf = EH;
281                 } else {
282                 /* case 3: shorter subtree shortened */
283                         if ( depth )
284                                 top = pptr[depth-1]; /* p->parent; */
285                         else
286                                 top = NULL;
287                         /* set <q> to the taller of the two subtrees of <p> */
288                         q = p->avl_link[!side];
289                         if ( q->avl_bf == EH ) {
290                                 /* case 3a: height unchanged, single rotate */
291                                 if ( q->avl_bits[side] == AVL_THREAD ) {
292                                         q->avl_bits[side] = AVL_CHILD;
293                                         p->avl_bits[!side] = AVL_THREAD;
294                                 } else {
295                                         p->avl_link[!side] = q->avl_link[side];
296                                         q->avl_link[side] = p;
297                                 }
298                                 shorter = 0;
299                                 q->avl_bf = side_bf;
300                                 p->avl_bf = (- side_bf);
301
302                         } else if ( q->avl_bf == p->avl_bf ) {
303                                 /* case 3b: height reduced, single rotate */
304                                 if ( q->avl_bits[side] == AVL_THREAD ) {
305                                         q->avl_bits[side] = AVL_CHILD;
306                                         p->avl_bits[!side] = AVL_THREAD;
307                                 } else {
308                                         p->avl_link[!side] = q->avl_link[side];
309                                         q->avl_link[side] = p;
310                                 }
311                                 shorter = 1;
312                                 q->avl_bf = EH;
313                                 p->avl_bf = EH;
314
315                         } else {
316                                 /* case 3c: height reduced, balance factors opposite */
317                                 r = q->avl_link[side];
318                                 if ( r->avl_bits[!side] == AVL_THREAD ) {
319                                         r->avl_bits[!side] = AVL_CHILD;
320                                         q->avl_bits[side] = AVL_THREAD;
321                                 } else {
322                                         q->avl_link[side] = r->avl_link[!side];
323                                         r->avl_link[!side] = q;
324                                 }
325
326                                 if ( r->avl_bits[side] == AVL_THREAD ) {
327                                         r->avl_bits[side] = AVL_CHILD;
328                                         p->avl_bits[!side] = AVL_THREAD;
329                                         p->avl_link[!side] = r;
330                                 } else {
331                                         p->avl_link[!side] = r->avl_link[side];
332                                         r->avl_link[side] = p;
333                                 }
334
335                                 if ( r->avl_bf == side_bf ) {
336                                         q->avl_bf = (- side_bf);
337                                         p->avl_bf = EH;
338                                 } else if ( r->avl_bf == (- side_bf)) {
339                                         q->avl_bf = EH;
340                                         p->avl_bf = side_bf;
341                                 } else {
342                                         q->avl_bf = EH;
343                                         p->avl_bf = EH;
344                                 }
345                                 r->avl_bf = EH;
346                                 q = r;
347                         }
348                         /* a rotation has caused <q> (or <r> in case 3c) to become
349                          * the root.  let <p>'s former parent know this.
350                          */
351                         if ( top == NULL ) {
352                                 *root = q;
353                         } else if (top->avl_link[0] == p) {
354                                 top->avl_link[0] = q;
355                         } else {
356                                 top->avl_link[1] = q;
357                         }
358                         /* end case 3 */
359                         p = q;
360                 }
361                 if ( !depth )
362                         break;
363                 depth--;
364                 p = pptr[depth];
365                 side = pdir[depth];
366                 side_bf = avl_bfs[side];
367         } /* end while(shorter) */
368
369         return data;
370 }
371
372 /*
373  * tavl_free -- traverse avltree root, freeing the memory it is using.
374  * the dfree() is called to free the data portion of each node.  The
375  * number of items actually freed is returned.
376  */
377
378 int
379 tavl_free( Avlnode *root, AVL_FREE dfree )
380 {
381         int     nleft, nright;
382
383         if ( root == 0 )
384                 return( 0 );
385
386         nleft = tavl_free( avl_lchild( root ), dfree );
387
388         nright = tavl_free( avl_rchild( root ), dfree );
389
390         if ( dfree )
391                 (*dfree)( root->avl_data );
392         ber_memfree( root );
393
394         return( nleft + nright + 1 );
395 }
396
397 /*
398  * tavl_find -- search avltree root for a node with data data.  the function
399  * cmp is used to compare things.  it is called with data as its first arg 
400  * and the current node data as its second.  it should return 0 if they match,
401  * < 0 if arg1 is less than arg2 and > 0 if arg1 is greater than arg2.
402  */
403
404 Avlnode *
405 tavl_find2( Avlnode *root, const void *data, AVL_CMP fcmp )
406 {
407         int     cmp;
408
409         while ( root != 0 && (cmp = (*fcmp)( data, root->avl_data )) != 0 ) {
410                 if ( cmp < 0 )
411                         root = avl_lchild( root );
412                 else
413                         root = avl_rchild( root );
414         }
415         return root;
416 }
417
418 void*
419 tavl_find( Avlnode *root, const void* data, AVL_CMP fcmp )
420 {
421         int     cmp;
422
423         while ( root != 0 && (cmp = (*fcmp)( data, root->avl_data )) != 0 ) {
424                 if ( cmp < 0 )
425                         root = avl_lchild( root );
426                 else
427                         root = avl_rchild( root );
428         }
429
430         return( root ? root->avl_data : 0 );
431 }