root/cherokee/trunk/cherokee/avl.c

Revision 2159, 15.0 kB (checked in by alo, 2 days ago)

--

Line 
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /* Cherokee
4  *
5  * Authors:
6  *      Alvaro Lopez Ortega <alvaro@alobbs.com>
7  *
8  * Copyright (C) 2001-2008 Alvaro Lopez Ortega
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of version 2 of the GNU General Public
12  * License as published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22  * USA
23  */
24
25 #include "common-internal.h"
26 #include "avl.h"
27
28 #define MAX_HEIGHT 45
29
30
31 struct cherokee_avl_node {         
32         /* Tree */
33         short                     balance;
34         struct cherokee_avl_node *left;
35         struct cherokee_avl_node *right;
36         cherokee_boolean_t        left_child;
37         cherokee_boolean_t        right_child;
38
39         /* Info */
40         cherokee_buffer_t         id;
41         void                     *value;
42 };
43
44
45 static cherokee_avl_node_t *node_first (cherokee_avl_t *avl);
46 static cherokee_avl_node_t *node_prev  (cherokee_avl_node_t *node);
47 static cherokee_avl_node_t *node_next  (cherokee_avl_node_t *node);
48
49
50 /* Nodes
51  */
52 static ret_t
53 node_new (cherokee_avl_node_t **node, cherokee_buffer_t *key, void *value)
54 {
55         CHEROKEE_NEW_STRUCT (n, avl_node);
56
57         n->balance     = 0;
58         n->left        = NULL;
59         n->right       = NULL;
60         n->left_child  = false;
61         n->right_child = false;
62         n->value       = value;
63
64         cherokee_buffer_init (&n->id);
65         cherokee_buffer_add_buffer (&n->id, key);
66
67         *node = n;
68         return ret_ok;
69 }
70
71 static ret_t
72 node_free (cherokee_avl_node_t *node)
73 {
74         cherokee_buffer_mrproper (&node->id);
75
76         free (node);
77         return ret_ok;
78 }
79
80 /* Tree constructor / destructor
81  */
82
83 ret_t
84 cherokee_avl_init (cherokee_avl_t *avl)
85 {
86         avl->root             = NULL;
87         avl->case_insensitive = false;
88
89         return ret_ok;
90 }
91
92 CHEROKEE_ADD_FUNC_NEW (avl);
93
94
95 ret_t
96 cherokee_avl_mrproper (cherokee_avl_t *avl, cherokee_func_free_t free_func)
97 {
98         cherokee_avl_node_t *node;
99         cherokee_avl_node_t *next;
100
101         if (unlikely (avl == NULL))
102                 return ret_ok;
103
104         node = node_first (avl);
105  
106         while (node) {
107                 next = node_next (node);
108
109                 if (free_func)
110                         free_func (node->value);
111                 node_free (node);
112
113                 node = next;
114         }
115
116         return ret_ok;
117 }
118
119 ret_t
120 cherokee_avl_free (cherokee_avl_t *avl, cherokee_func_free_t free_func)
121 {
122         cherokee_avl_mrproper (avl, free_func);
123         free (avl);
124         return ret_ok;
125 }
126
127
128 /* Tree methods
129  */
130
131 static int
132 compare_buffers (cherokee_buffer_t *A,
133                  cherokee_buffer_t *B,
134                  cherokee_boolean_t case_insensitive)
135 {
136         if (case_insensitive)
137                 return cherokee_buffer_cmp_buf (A, B);
138         else
139                 return cherokee_buffer_case_cmp_buf (A, B);
140 }
141
142
143 ret_t
144 cherokee_avl_set_case (cherokee_avl_t *avl, cherokee_boolean_t case_insensitive)
145 {
146         avl->case_insensitive = case_insensitive;
147         return ret_ok;
148 }
149
150
151 static cherokee_avl_node_t *
152 node_first (cherokee_avl_t *avl)
153 {
154         cherokee_avl_node_t *tmp;
155
156         if (!avl->root)
157                 return NULL;
158
159         tmp = avl->root;
160
161         while (tmp->left_child)
162                 tmp = tmp->left;
163
164         return tmp;
165 }
166
167 static cherokee_avl_node_t *
168 node_next (cherokee_avl_node_t *node)
169 {
170         cherokee_avl_node_t *tmp = node->right;
171
172         if (node->right_child)
173                 while (tmp->left_child)
174                         tmp = tmp->left;
175         return tmp;
176 }
177
178 static cherokee_avl_node_t *
179 node_prev (cherokee_avl_node_t *node)
180 {
181         cherokee_avl_node_t *tmp = node->left;
182
183         if (node->left_child)
184                 while (tmp->right_child)
185                         tmp = tmp->right;
186         return tmp;
187 }
188
189
190 static cherokee_avl_node_t *
191 node_rotate_left (cherokee_avl_node_t *node)
192 {
193         cherokee_avl_node_t *right;
194         cint_t               a_bal;
195         cint_t               b_bal;
196  
197         right = node->right;
198
199         if (right->left_child)
200                 node->right = right->left;
201         else {
202                 node->right_child = FALSE;
203                 node->right = right;
204                 right->left_child = TRUE;
205         }
206         right->left = node;
207
208         a_bal = node->balance;
209         b_bal = right->balance;
210
211         if (b_bal <= 0) {
212                 if (a_bal >= 1)
213                         right->balance = b_bal - 1;
214                 else
215                         right->balance = a_bal + b_bal - 2;
216                 node->balance = a_bal - 1;
217         } else {
218                 if (a_bal <= b_bal)
219                         right->balance = a_bal - 2;
220                 else
221                         right->balance = b_bal - 1;
222                 node->balance = a_bal - b_bal - 1;
223         }
224
225         return right;
226 }
227
228 static cherokee_avl_node_t *
229 node_rotate_right (cherokee_avl_node_t *node)
230 {
231         cherokee_avl_node_t *left;
232         cint_t               a_bal;
233         cint_t               b_bal;
234
235         left = node->left;
236        
237         if (left->right_child)
238                 node->left = left->right;
239         else {
240                 node->left_child = FALSE;
241                 node->left = left;
242                 left->right_child = TRUE;
243         }
244         left->right = node;
245
246         a_bal = node->balance;
247         b_bal = left->balance;
248
249         if (b_bal <= 0) {
250                 if (b_bal > a_bal)
251                         left->balance = b_bal + 1;
252                 else
253                         left->balance = a_bal + 2;
254                 node->balance = a_bal - b_bal + 1;
255         } else {
256                 if (a_bal <= -1)
257                         left->balance = b_bal + 1;
258                 else
259                         left->balance = a_bal + b_bal + 2;
260                 node->balance = a_bal + 1;
261         }
262
263         return left;
264 }
265
266
267 static cherokee_avl_node_t *
268 node_balance (cherokee_avl_node_t *node)
269 {
270         if (node->balance < -1) {
271                 if (node->left->balance > 0)
272                         node->left = node_rotate_left (node->left);
273                 node = node_rotate_right (node);
274
275         } else if (node->balance > 1) {
276                 if (node->right->balance < 0)
277                         node->right = node_rotate_right (node->right);
278                 node = node_rotate_left (node);
279         }
280
281         return node;
282 }
283
284
285
286 static ret_t
287 node_add (cherokee_avl_t *tree, cherokee_avl_node_t *child)
288 {
289         short                re;                 
290         cherokee_boolean_t   is_left;
291         cherokee_avl_node_t *path[MAX_HEIGHT];
292         cherokee_avl_node_t *node   = tree->root;
293         cherokee_avl_node_t *parent = NULL;
294         cint_t               idx    = 1;
295
296         path[0] = NULL;
297
298         /* If the tree is empty..
299          */
300         if (unlikely (tree->root == NULL)) {
301                 tree->root = child;
302                 return ret_ok;
303         }
304
305         /* Insert the node
306          */
307         while (true) {
308                 re = compare_buffers (&child->id, &node->id, tree->case_insensitive);
309
310                 if (re < 0) {
311                         /* Insert it on the left */
312                         if (node->left_child) {
313                                 path[idx++] = node;
314                                 node        = node->left;
315                         } else {
316                                 child->left      = node->left;
317                                 child->right     = node;
318                                 node->left       = child;
319                                 node->left_child = true;
320                                 node->balance   -= 1;
321                                 break;
322                         }
323
324                 } else if (re > 0) {
325                         /* Insert it on the left */
326                         if (node->right_child) {
327                                 path[idx++] = node;
328                                 node        = node->right;
329                         } else {
330                                 child->right      = node->right;
331                                 child->left       = node;
332                                 node->right       = child;
333                                 node->right_child = true;
334                                 node->balance    += 1;
335                                 break;
336                         }
337
338                 } else {
339                         node_free (child);
340                         return ret_error;
341                 }
342         }
343
344         /* Rebalance the tree
345          */
346         while (true) {
347                 parent = path[--idx];
348                 is_left = (parent && (parent->left == node));
349                
350                 if ((node->balance < -1) ||
351                     (node->balance >  1))
352                 {
353                         node = node_balance (node);
354                         if (parent == NULL)
355                                 tree->root = node;
356                         else if (is_left)
357                                 parent->left = node;
358                         else
359                                 parent->right = node;
360                 }
361
362                 if ((node->balance == 0) || (parent == NULL))
363                         break;
364                
365                 if (is_left)
366                         parent->balance -= 1;
367                 else
368                         parent->balance += 1;
369                
370                 node = parent;
371         }
372
373         return ret_ok;
374 }
375
376
377 ret_t
378 cherokee_avl_add (cherokee_avl_t *avl, cherokee_buffer_t *key, void *value)
379 {
380         ret_t                ret;
381         cherokee_avl_node_t *n = NULL;
382
383         if (unlikely (cherokee_buffer_is_empty(key)))
384                 return ret_error;
385
386         /* Create the new AVL node
387          */
388         ret = node_new (&n, key, value);
389         if (unlikely (ret != ret_ok))
390                 return ret;
391
392         /* Add th node to the tree
393          */
394         ret = node_add (avl, n);
395         if (unlikely (ret != ret_ok))
396                 return ret;
397
398         return ret_ok;
399 }
400
401
402 ret_t
403 cherokee_avl_del (cherokee_avl_t *avl, cherokee_buffer_t *key, void **value)
404 {
405         short                re;
406         cherokee_boolean_t   is_left;
407         cherokee_avl_node_t *path[MAX_HEIGHT];
408         cherokee_avl_node_t *parent;
409         cherokee_avl_node_t *pbalance;
410         cherokee_avl_node_t *node     = avl->root;
411         cint_t               idx      = 1;
412
413         if (unlikely (cherokee_buffer_is_empty(key)))
414                 return ret_error;
415        
416         if (avl->root == NULL)
417                 return ret_not_found;
418
419         path[0] = NULL;
420
421         while (true) {
422                 re = compare_buffers (key, &node->id, avl->case_insensitive);
423                 if (re == 0) {
424                         if (value)
425                                 *value = node->value;
426                         break;
427                 } else if (re < 0) {
428                         if (!node->left_child)
429                                 return ret_not_found;
430                         path[idx++] = node;
431                         node = node->left;
432                 } else {
433                         if (!node->right_child)
434                                 return ret_not_found;
435                         path[idx++] = node;
436                         node = node->right;
437                 }
438         }
439
440         pbalance = path[idx-1];
441         parent   = pbalance;
442         idx     -= 1;
443         is_left  = (parent && (node == parent->left));
444
445         if (!node->left_child) {
446                 if (!node->right_child) {
447                         if (!parent)
448                                 avl->root = NULL;
449                         else if (is_left) {
450                                 parent->left_child  = false;
451                                 parent->left        = node->left;
452                                 parent->balance   += 1;
453                         } else {
454                                 parent->right_child = false;
455                                 parent->right       = node->right;
456                                 parent->balance     -= 1;
457                         }
458
459                 } else { /* right child */
460                         cherokee_avl_node_t *tmp = node_next (node);
461                         tmp->left = node->left;
462
463                         if (!parent)
464                                 avl->root = node->right;
465                         else if (is_left) {
466                                 parent->left     = node->right;
467                                 parent->balance += 1;
468                         } else {
469                                 parent->right    = node->right;
470                                 parent->balance -= 1;
471                         }
472                 }
473
474         } else { /* left child */
475                 if (!node->right_child) {
476                         cherokee_avl_node_t *tmp = node_prev(node);
477                         tmp->right = node->right;
478
479                         if (parent == NULL)
480                                 avl->root = node->left;
481                         else if (is_left) {
482                                 parent->left     = node->left;
483                                 parent->balance += 1;
484                         } else {
485                                 parent->right    = node->left;
486                                 parent->balance -= 1;
487                         }
488                 } else { /* both children */
489                         cherokee_avl_node_t *prev    = node->left;
490                         cherokee_avl_node_t *next    = node->right;
491                         cherokee_avl_node_t *nextp   = node;
492                         cint_t               old_idx = idx + 1;
493                         idx++;
494
495                         /* find the immediately next node (and its parent) */
496                         while (next->left_child) {
497                                 path[++idx] = nextp = next;
498                                 next = next->left;
499                         }
500          
501                         path[old_idx] = next;
502                         pbalance      = path[idx];
503          
504                         /* remove 'next' from the tree */
505                         if (nextp != node) {
506                                 if (next->right_child)
507                                         nextp->left = next->right;
508                                 else
509                                         nextp->left_child = FALSE;
510
511                                 nextp->balance    += 1;
512                                 next->right_child  = TRUE;
513                                 next->right        = node->right;
514
515                         } else {
516                                 node->balance -= 1;
517                         }
518            
519                         /* set the prev to point to the right place */
520                         while (prev->right_child)
521                                 prev = prev->right;
522                         prev->right = next;
523            
524                         /* prepare 'next' to replace 'node' */
525                         next->left_child = TRUE;
526                         next->left = node->left;
527                         next->balance = node->balance;
528          
529                         if (!parent)
530                                 avl->root = next;
531                         else if (is_left)
532                                 parent->left = next;
533                         else
534                                 parent->right = next;
535                 }
536         }
537        
538         /* restore balance */
539         if (pbalance) {
540                 while (true) {
541                         cherokee_avl_node_t *bparent = path[--idx];
542                         is_left = (bparent && (pbalance == bparent->left));
543                              
544                         if(pbalance->balance < -1 || pbalance->balance > 1) {
545                                 pbalance = node_balance (pbalance);
546                                 if (!bparent)
547                                         avl->root = pbalance;
548                                 else if (is_left)
549                                         bparent->left = pbalance;
550                                 else
551                                         bparent->right = pbalance;
552                         }
553        
554                         if (pbalance->balance != 0 || !bparent)
555                                 break;
556        
557                         if (is_left)
558                                 bparent->balance += 1;
559                         else
560                                 bparent->balance -= 1;
561                        
562                         pbalance = bparent;
563                 }
564         }
565        
566         node_free (node);
567         return ret_ok;
568 }
569
570
571 ret_t
572 cherokee_avl_get (cherokee_avl_t *avl, cherokee_buffer_t *key, void **value)
573 {
574         short                re;
575         cherokee_avl_node_t *node;
576
577         if (unlikely (cherokee_buffer_is_empty(key)))
578                 return ret_error;
579        
580         node = avl->root;
581         if (!node)
582                 return ret_not_found;
583
584         while (true) {
585                 re = compare_buffers (key, &node->id, avl->case_insensitive);
586                 if (re == 0) {
587                         if (value)
588                                 *value = node->value;
589                         return ret_ok;
590
591                 } else if (re < 0) {
592                         if (!node->left_child)
593                                 return ret_not_found;
594                         node = node->left;
595
596                 } else {
597                         if (!node->right_child)
598                                 return ret_not_found;
599                         node = node->right;
600                 }
601         }
602
603         SHOULDNT_HAPPEN;
604         return ret_error;
605 }
606
607
608 /* Commodity methods
609  */
610
611 ret_t
612 cherokee_avl_get_ptr (cherokee_avl_t *avl, const char *key, void **value)
613 {
614         cherokee_buffer_t tmp_key;
615
616         cherokee_buffer_fake (&tmp_key, (const char *)key, strlen(key));
617         return cherokee_avl_get (avl, &tmp_key, value);
618 }
619
620
621 ret_t
622 cherokee_avl_add_ptr (cherokee_avl_t *avl, const char *key, void *value)
623 {
624         cherokee_buffer_t tmp_key;
625
626         cherokee_buffer_fake (&tmp_key, (const char *)key, strlen(key));
627         return cherokee_avl_add (avl, &tmp_key, value);
628 }
629
630
631 ret_t
632 cherokee_avl_del_ptr (cherokee_avl_t *avl, const char *key, void **value)
633 {
634         cherokee_buffer_t tmp_key;
635
636         cherokee_buffer_fake (&tmp_key, (const char *)key, strlen(key));
637         return cherokee_avl_del (avl, &tmp_key, value);
638 }
639
640
641 ret_t
642 cherokee_avl_while (cherokee_avl_t *avl, cherokee_avl_while_func_t func, void *param, cherokee_buffer_t **key, void **value)
643 {
644         ret_t                ret;
645         cherokee_avl_node_t *node = avl->root;
646
647         if (avl->root == NULL)
648                 return ret_ok;
649
650         node = node_first (avl);
651         while (node) {
652                 if (key)
653                         *key = &node->id;
654                 if (value)
655                         *value = &node->value;
656
657                 ret = func (&node->id, node->value, param);
658                 if (ret != ret_ok) return ret;
659      
660                 node = node_next (node);
661         }
662
663         return ret_ok;
664 }
665
666 static ret_t
667 func_len_each (cherokee_buffer_t *key, void *value, void *param)
668 {
669         UNUSED(key);
670         UNUSED(value);
671
672         *((size_t *)param) += 1;
673         return ret_ok;
674 }
675
676 ret_t
677 cherokee_avl_len (cherokee_avl_t *avl, size_t *len)
678 {
679         *len = 0;
680         return cherokee_avl_while (avl, func_len_each, len, NULL, NULL);
681 }
682
683
684 /* Debugging
685  */
686
687 static void
688 print_node (cherokee_avl_node_t *node)
689 {
690         if (! node) {
691                 printf ("<null />\n");
692                 return;
693         }
694
695         printf ("<node key=\"%s\" value=\"%p\">\n", node->id.buf, node->value);
696         if (node->left_child)
697                 print_node (node->left); 
698         if (node->left_child)
699                 print_node (node->right);
700         printf ("</node>\n");
701 }
702
703 ret_t
704 cherokee_avl_print (cherokee_avl_t *avl)
705 {
706         print_node (avl->root);
707         return ret_ok;
708 }
709
710
711 static cint_t
712 node_height (cherokee_avl_node_t *node)
713 {
714         cint_t left_height;
715         cint_t right_height;
716
717         if (node) {
718                 left_height = 0;
719                 right_height = 0;
720
721                 if (node->left_child)
722                         left_height = node_height (node->left);
723
724                 if (node->right_child)
725                         right_height = node_height (node->right);
726
727                 return MAX (left_height, right_height) + 1;
728         }
729         return 0;
730 }
731
732
733 static ret_t
734 node_check (cherokee_avl_node_t *node)
735 {
736         cint_t               left_height;
737         cint_t               right_height;
738         cint_t               balance;
739         cherokee_avl_node_t *tmp;
740
741         if (node) {
742                 if (node->left_child) {
743                         tmp = node_prev (node);
744                         if (tmp->right != node) {
745                                 PRINT_ERROR_S ("previous");
746                                 return ret_error;
747                         }
748                 }
749
750                 if (node->right_child) {
751                         tmp = node_next (node);
752                         if (tmp->left != node) {
753                                 PRINT_ERROR_S ("next");
754                                 return ret_error;
755                         }
756                 }
757
758                 left_height  = 0;
759                 right_height = 0;
760      
761                 if (node->left_child)
762                         left_height  = node_height (node->left);
763                 if (node->right_child)
764                         right_height = node_height (node->right);
765      
766                 balance = right_height - left_height;
767                 if (balance != node->balance) {
768                         PRINT_ERROR_S ("Balance");
769                         return ret_error;
770                 }
771
772                 if (node->left_child)
773                         node_check (node->left);
774                 if (node->right_child)
775                         node_check (node->right);
776         }
777        
778         return ret_ok;
779 }
780
781 ret_t
782 cherokee_avl_check (cherokee_avl_t *avl)
783 {
784         return node_check (avl->root);
785 }
786
Note: See TracBrowser for help on using the browser.