Fix gccisms (pointer arithmetic on void pointer, label without statement
[glib.git] / gtree.c
blob23b3feda8a10429ce73b56204733f0f12011dbc7
1 /* GLIB - Library of useful routines for C programming
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * Modified by the GLib Team and others 1997-2000. See the AUTHORS
22 * file for a list of people on the GLib Team. See the ChangeLog
23 * files for a list of changes. These files are distributed with
24 * GLib at ftp://ftp.gtk.org/pub/gtk/.
27 /*
28 * MT safe
31 #include "glib.h"
34 typedef struct _GRealTree GRealTree;
35 typedef struct _GTreeNode GTreeNode;
37 struct _GRealTree
39 GTreeNode *root;
40 GCompareFunc key_compare;
43 struct _GTreeNode
45 gint balance; /* height (left) - height (right) */
46 GTreeNode *left; /* left subtree */
47 GTreeNode *right; /* right subtree */
48 gpointer key; /* key for this node */
49 gpointer value; /* value stored at this node */
53 static GTreeNode* g_tree_node_new (gpointer key,
54 gpointer value);
55 static void g_tree_node_destroy (GTreeNode *node);
56 static GTreeNode* g_tree_node_insert (GTreeNode *node,
57 GCompareFunc compare,
58 gpointer key,
59 gpointer value,
60 gint *inserted);
61 static GTreeNode* g_tree_node_remove (GTreeNode *node,
62 GCompareFunc compare,
63 gconstpointer key);
64 static GTreeNode* g_tree_node_balance (GTreeNode *node);
65 static GTreeNode* g_tree_node_remove_leftmost (GTreeNode *node,
66 GTreeNode **leftmost);
67 static GTreeNode* g_tree_node_restore_left_balance (GTreeNode *node,
68 gint old_balance);
69 static GTreeNode* g_tree_node_restore_right_balance (GTreeNode *node,
70 gint old_balance);
71 static gpointer g_tree_node_lookup (GTreeNode *node,
72 GCompareFunc compare,
73 gconstpointer key);
74 static gint g_tree_node_count (GTreeNode *node);
75 static gint g_tree_node_pre_order (GTreeNode *node,
76 GTraverseFunc traverse_func,
77 gpointer data);
78 static gint g_tree_node_in_order (GTreeNode *node,
79 GTraverseFunc traverse_func,
80 gpointer data);
81 static gint g_tree_node_post_order (GTreeNode *node,
82 GTraverseFunc traverse_func,
83 gpointer data);
84 static gpointer g_tree_node_search (GTreeNode *node,
85 GCompareFunc search_func,
86 gconstpointer data);
87 static gint g_tree_node_height (GTreeNode *node);
88 static GTreeNode* g_tree_node_rotate_left (GTreeNode *node);
89 static GTreeNode* g_tree_node_rotate_right (GTreeNode *node);
90 static void g_tree_node_check (GTreeNode *node);
93 G_LOCK_DEFINE_STATIC (g_tree_global);
94 static GMemChunk *node_mem_chunk = NULL;
95 static GTreeNode *node_free_list = NULL;
98 static GTreeNode*
99 g_tree_node_new (gpointer key,
100 gpointer value)
102 GTreeNode *node;
104 G_LOCK (g_tree_global);
105 if (node_free_list)
107 node = node_free_list;
108 node_free_list = node->right;
110 else
112 if (!node_mem_chunk)
113 node_mem_chunk = g_mem_chunk_new ("GLib GTreeNode mem chunk",
114 sizeof (GTreeNode),
115 1024,
116 G_ALLOC_ONLY);
118 node = g_chunk_new (GTreeNode, node_mem_chunk);
120 G_UNLOCK (g_tree_global);
122 node->balance = 0;
123 node->left = NULL;
124 node->right = NULL;
125 node->key = key;
126 node->value = value;
128 return node;
131 static void
132 g_tree_node_destroy (GTreeNode *node)
134 if (node)
136 g_tree_node_destroy (node->right);
137 g_tree_node_destroy (node->left);
139 #ifdef ENABLE_GC_FRIENDLY
140 node->left = NULL;
141 node->key = NULL;
142 node->value = NULL;
143 #endif /* ENABLE_GC_FRIENDLY */
145 G_LOCK (g_tree_global);
146 node->right = node_free_list;
147 node_free_list = node;
148 G_UNLOCK (g_tree_global);
153 GTree*
154 g_tree_new (GCompareFunc key_compare_func)
156 GRealTree *rtree;
158 g_return_val_if_fail (key_compare_func != NULL, NULL);
160 rtree = g_new (GRealTree, 1);
161 rtree->root = NULL;
162 rtree->key_compare = key_compare_func;
164 return (GTree*) rtree;
167 void
168 g_tree_destroy (GTree *tree)
170 GRealTree *rtree;
172 g_return_if_fail (tree != NULL);
174 rtree = (GRealTree*) tree;
176 g_tree_node_destroy (rtree->root);
177 g_free (rtree);
180 void
181 g_tree_insert (GTree *tree,
182 gpointer key,
183 gpointer value)
185 GRealTree *rtree;
186 gint inserted;
188 g_return_if_fail (tree != NULL);
190 rtree = (GRealTree*) tree;
192 inserted = FALSE;
193 rtree->root = g_tree_node_insert (rtree->root, rtree->key_compare,
194 key, value, &inserted);
197 void
198 g_tree_remove (GTree *tree,
199 gconstpointer key)
201 GRealTree *rtree;
203 g_return_if_fail (tree != NULL);
205 rtree = (GRealTree*) tree;
207 rtree->root = g_tree_node_remove (rtree->root, rtree->key_compare, key);
210 gpointer
211 g_tree_lookup (GTree *tree,
212 gconstpointer key)
214 GRealTree *rtree;
216 g_return_val_if_fail (tree != NULL, NULL);
218 rtree = (GRealTree*) tree;
220 return g_tree_node_lookup (rtree->root, rtree->key_compare, key);
223 void
224 g_tree_traverse (GTree *tree,
225 GTraverseFunc traverse_func,
226 GTraverseType traverse_type,
227 gpointer data)
229 GRealTree *rtree;
231 g_return_if_fail (tree != NULL);
233 rtree = (GRealTree*) tree;
235 if (!rtree->root)
236 return;
238 switch (traverse_type)
240 case G_PRE_ORDER:
241 g_tree_node_pre_order (rtree->root, traverse_func, data);
242 break;
244 case G_IN_ORDER:
245 g_tree_node_in_order (rtree->root, traverse_func, data);
246 break;
248 case G_POST_ORDER:
249 g_tree_node_post_order (rtree->root, traverse_func, data);
250 break;
252 case G_LEVEL_ORDER:
253 g_warning ("g_tree_traverse(): traverse type G_LEVEL_ORDER isn't implemented.");
254 break;
258 gpointer
259 g_tree_search (GTree *tree,
260 GCompareFunc search_func,
261 gconstpointer data)
263 GRealTree *rtree;
265 g_return_val_if_fail (tree != NULL, NULL);
267 rtree = (GRealTree*) tree;
269 if (rtree->root)
270 return g_tree_node_search (rtree->root, search_func, data);
271 else
272 return NULL;
275 gint
276 g_tree_height (GTree *tree)
278 GRealTree *rtree;
280 g_return_val_if_fail (tree != NULL, 0);
282 rtree = (GRealTree*) tree;
284 if (rtree->root)
285 return g_tree_node_height (rtree->root);
286 else
287 return 0;
290 gint
291 g_tree_nnodes (GTree *tree)
293 GRealTree *rtree;
295 g_return_val_if_fail (tree != NULL, 0);
297 rtree = (GRealTree*) tree;
299 if (rtree->root)
300 return g_tree_node_count (rtree->root);
301 else
302 return 0;
305 static GTreeNode*
306 g_tree_node_insert (GTreeNode *node,
307 GCompareFunc compare,
308 gpointer key,
309 gpointer value,
310 gint *inserted)
312 gint old_balance;
313 gint cmp;
315 if (!node)
317 *inserted = TRUE;
318 return g_tree_node_new (key, value);
321 cmp = (* compare) (key, node->key);
322 if (cmp == 0)
324 *inserted = FALSE;
325 node->value = value;
326 return node;
329 if (cmp < 0)
331 if (node->left)
333 old_balance = node->left->balance;
334 node->left = g_tree_node_insert (node->left, compare, key, value, inserted);
336 if ((old_balance != node->left->balance) && node->left->balance)
337 node->balance -= 1;
339 else
341 *inserted = TRUE;
342 node->left = g_tree_node_new (key, value);
343 node->balance -= 1;
346 else if (cmp > 0)
348 if (node->right)
350 old_balance = node->right->balance;
351 node->right = g_tree_node_insert (node->right, compare, key, value, inserted);
353 if ((old_balance != node->right->balance) && node->right->balance)
354 node->balance += 1;
356 else
358 *inserted = TRUE;
359 node->right = g_tree_node_new (key, value);
360 node->balance += 1;
364 if (*inserted)
366 if ((node->balance < -1) || (node->balance > 1))
367 node = g_tree_node_balance (node);
370 return node;
373 static GTreeNode*
374 g_tree_node_remove (GTreeNode *node,
375 GCompareFunc compare,
376 gconstpointer key)
378 GTreeNode *new_root;
379 gint old_balance;
380 gint cmp;
382 if (!node)
383 return NULL;
385 cmp = (* compare) (key, node->key);
386 if (cmp == 0)
388 GTreeNode *garbage;
390 garbage = node;
392 if (!node->right)
394 node = node->left;
396 else
398 old_balance = node->right->balance;
399 node->right = g_tree_node_remove_leftmost (node->right, &new_root);
400 new_root->left = node->left;
401 new_root->right = node->right;
402 new_root->balance = node->balance;
403 node = g_tree_node_restore_right_balance (new_root, old_balance);
406 #ifdef ENABLE_GC_FRIENDLY
407 garbage->left = NULL;
408 garbage->key = NULL;
409 garbage->value = NULL;
410 #endif /* ENABLE_GC_FRIENDLY */
412 G_LOCK (g_tree_global);
413 garbage->right = node_free_list;
414 node_free_list = garbage;
415 G_UNLOCK (g_tree_global);
417 else if (cmp < 0)
419 if (node->left)
421 old_balance = node->left->balance;
422 node->left = g_tree_node_remove (node->left, compare, key);
423 node = g_tree_node_restore_left_balance (node, old_balance);
426 else if (cmp > 0)
428 if (node->right)
430 old_balance = node->right->balance;
431 node->right = g_tree_node_remove (node->right, compare, key);
432 node = g_tree_node_restore_right_balance (node, old_balance);
436 return node;
439 static GTreeNode*
440 g_tree_node_balance (GTreeNode *node)
442 if (node->balance < -1)
444 if (node->left->balance > 0)
445 node->left = g_tree_node_rotate_left (node->left);
446 node = g_tree_node_rotate_right (node);
448 else if (node->balance > 1)
450 if (node->right->balance < 0)
451 node->right = g_tree_node_rotate_right (node->right);
452 node = g_tree_node_rotate_left (node);
455 return node;
458 static GTreeNode*
459 g_tree_node_remove_leftmost (GTreeNode *node,
460 GTreeNode **leftmost)
462 gint old_balance;
464 if (!node->left)
466 *leftmost = node;
467 return node->right;
470 old_balance = node->left->balance;
471 node->left = g_tree_node_remove_leftmost (node->left, leftmost);
472 return g_tree_node_restore_left_balance (node, old_balance);
475 static GTreeNode*
476 g_tree_node_restore_left_balance (GTreeNode *node,
477 gint old_balance)
479 if (!node->left)
480 node->balance += 1;
481 else if ((node->left->balance != old_balance) &&
482 (node->left->balance == 0))
483 node->balance += 1;
485 if (node->balance > 1)
486 return g_tree_node_balance (node);
487 return node;
490 static GTreeNode*
491 g_tree_node_restore_right_balance (GTreeNode *node,
492 gint old_balance)
494 if (!node->right)
495 node->balance -= 1;
496 else if ((node->right->balance != old_balance) &&
497 (node->right->balance == 0))
498 node->balance -= 1;
500 if (node->balance < -1)
501 return g_tree_node_balance (node);
502 return node;
505 static gpointer
506 g_tree_node_lookup (GTreeNode *node,
507 GCompareFunc compare,
508 gconstpointer key)
510 gint cmp;
512 if (!node)
513 return NULL;
515 cmp = (* compare) (key, node->key);
516 if (cmp == 0)
517 return node->value;
519 if (cmp < 0)
521 if (node->left)
522 return g_tree_node_lookup (node->left, compare, key);
524 else if (cmp > 0)
526 if (node->right)
527 return g_tree_node_lookup (node->right, compare, key);
530 return NULL;
533 static gint
534 g_tree_node_count (GTreeNode *node)
536 gint count;
538 count = 1;
539 if (node->left)
540 count += g_tree_node_count (node->left);
541 if (node->right)
542 count += g_tree_node_count (node->right);
544 return count;
547 static gint
548 g_tree_node_pre_order (GTreeNode *node,
549 GTraverseFunc traverse_func,
550 gpointer data)
552 if ((*traverse_func) (node->key, node->value, data))
553 return TRUE;
554 if (node->left)
556 if (g_tree_node_pre_order (node->left, traverse_func, data))
557 return TRUE;
559 if (node->right)
561 if (g_tree_node_pre_order (node->right, traverse_func, data))
562 return TRUE;
565 return FALSE;
568 static gint
569 g_tree_node_in_order (GTreeNode *node,
570 GTraverseFunc traverse_func,
571 gpointer data)
573 if (node->left)
575 if (g_tree_node_in_order (node->left, traverse_func, data))
576 return TRUE;
578 if ((*traverse_func) (node->key, node->value, data))
579 return TRUE;
580 if (node->right)
582 if (g_tree_node_in_order (node->right, traverse_func, data))
583 return TRUE;
586 return FALSE;
589 static gint
590 g_tree_node_post_order (GTreeNode *node,
591 GTraverseFunc traverse_func,
592 gpointer data)
594 if (node->left)
596 if (g_tree_node_post_order (node->left, traverse_func, data))
597 return TRUE;
599 if (node->right)
601 if (g_tree_node_post_order (node->right, traverse_func, data))
602 return TRUE;
604 if ((*traverse_func) (node->key, node->value, data))
605 return TRUE;
607 return FALSE;
610 static gpointer
611 g_tree_node_search (GTreeNode *node,
612 GCompareFunc search_func,
613 gconstpointer data)
615 gint dir;
617 if (!node)
618 return NULL;
620 do {
621 dir = (* search_func) (node->key, data);
622 if (dir == 0)
623 return node->value;
625 if (dir < 0)
626 node = node->left;
627 else if (dir > 0)
628 node = node->right;
629 } while (node && (dir != 0));
631 return NULL;
634 static gint
635 g_tree_node_height (GTreeNode *node)
637 gint left_height;
638 gint right_height;
640 if (node)
642 left_height = 0;
643 right_height = 0;
645 if (node->left)
646 left_height = g_tree_node_height (node->left);
648 if (node->right)
649 right_height = g_tree_node_height (node->right);
651 return MAX (left_height, right_height) + 1;
654 return 0;
657 static GTreeNode*
658 g_tree_node_rotate_left (GTreeNode *node)
660 GTreeNode *right;
661 gint a_bal;
662 gint b_bal;
664 right = node->right;
666 node->right = right->left;
667 right->left = node;
669 a_bal = node->balance;
670 b_bal = right->balance;
672 if (b_bal <= 0)
674 if (a_bal >= 1)
675 right->balance = b_bal - 1;
676 else
677 right->balance = a_bal + b_bal - 2;
678 node->balance = a_bal - 1;
680 else
682 if (a_bal <= b_bal)
683 right->balance = a_bal - 2;
684 else
685 right->balance = b_bal - 1;
686 node->balance = a_bal - b_bal - 1;
689 return right;
692 static GTreeNode*
693 g_tree_node_rotate_right (GTreeNode *node)
695 GTreeNode *left;
696 gint a_bal;
697 gint b_bal;
699 left = node->left;
701 node->left = left->right;
702 left->right = node;
704 a_bal = node->balance;
705 b_bal = left->balance;
707 if (b_bal <= 0)
709 if (b_bal > a_bal)
710 left->balance = b_bal + 1;
711 else
712 left->balance = a_bal + 2;
713 node->balance = a_bal - b_bal + 1;
715 else
717 if (a_bal <= -1)
718 left->balance = b_bal + 1;
719 else
720 left->balance = a_bal + b_bal + 2;
721 node->balance = a_bal + 1;
724 return left;
727 static void
728 g_tree_node_check (GTreeNode *node)
730 gint left_height;
731 gint right_height;
732 gint balance;
734 if (node)
736 left_height = 0;
737 right_height = 0;
739 if (node->left)
740 left_height = g_tree_node_height (node->left);
741 if (node->right)
742 right_height = g_tree_node_height (node->right);
744 balance = right_height - left_height;
745 if (balance != node->balance)
746 g_log (g_log_domain_glib, G_LOG_LEVEL_INFO,
747 "g_tree_node_check: failed: %d ( %d )\n",
748 balance, node->balance);
750 if (node->left)
751 g_tree_node_check (node->left);
752 if (node->right)
753 g_tree_node_check (node->right);