source: trunk/third/glib2/glib/gtree.c @ 18159

Revision 18159, 26.6 KB checked in by ghudson, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18158, which included commits to RCS files with non-trunk default branches.
Line 
1/* GLIB - Library of useful routines for C programming
2 * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
3 *
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.
8 *
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.
13 *
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.
18 */
19
20/*
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/.
25 */
26
27/*
28 * MT safe
29 */
30
31#include "config.h"
32
33#include "glib.h"
34
35
36typedef struct _GTreeNode  GTreeNode;
37
38struct _GTree
39{
40  GTreeNode *root;
41  GCompareDataFunc key_compare;
42  GDestroyNotify   key_destroy_func;
43  GDestroyNotify   value_destroy_func;
44  gpointer         key_compare_data;
45};
46
47struct _GTreeNode
48{
49  gint balance;      /* height (left) - height (right) */
50  GTreeNode *left;   /* left subtree */
51  GTreeNode *right;  /* right subtree */
52  gpointer key;      /* key for this node */
53  gpointer value;    /* value stored at this node */
54};
55
56
57static GTreeNode* g_tree_node_new                   (gpointer          key,
58                                                     gpointer          value);
59static void       g_tree_node_destroy               (GTreeNode        *node,
60                                                     GDestroyNotify    key_destroy_func,
61                                                     GDestroyNotify    value_destroy_func);
62static GTreeNode* g_tree_node_insert                (GTree            *tree,
63                                                     GTreeNode        *node,
64                                                     gpointer          key,
65                                                     gpointer          value,
66                                                     gboolean          replace,
67                                                     gboolean         *inserted);
68static GTreeNode* g_tree_node_remove                (GTree            *tree,
69                                                     GTreeNode        *node,
70                                                     gconstpointer     key,
71                                                     gboolean          notify);
72static GTreeNode* g_tree_node_balance               (GTreeNode        *node);
73static GTreeNode* g_tree_node_remove_leftmost       (GTreeNode        *node,
74                                                     GTreeNode       **leftmost);
75static GTreeNode* g_tree_node_restore_left_balance  (GTreeNode        *node,
76                                                     gint              old_balance);
77static GTreeNode* g_tree_node_restore_right_balance (GTreeNode        *node,
78                                                     gint              old_balance);
79static GTreeNode* g_tree_node_lookup                (GTreeNode        *node,
80                                                     GCompareDataFunc  compare,
81                                                     gpointer          comp_data,
82                                                     gconstpointer     key);
83static gint       g_tree_node_count                 (GTreeNode        *node);
84static gint       g_tree_node_pre_order             (GTreeNode        *node,
85                                                     GTraverseFunc     traverse_func,
86                                                     gpointer          data);
87static gint       g_tree_node_in_order              (GTreeNode        *node,
88                                                     GTraverseFunc     traverse_func,
89                                                     gpointer          data);
90static gint       g_tree_node_post_order            (GTreeNode        *node,
91                                                     GTraverseFunc     traverse_func,
92                                                     gpointer          data);
93static gpointer   g_tree_node_search                (GTreeNode        *node,
94                                                     GCompareFunc      search_func,
95                                                     gconstpointer     data);
96static gint       g_tree_node_height                (GTreeNode        *node);
97static GTreeNode* g_tree_node_rotate_left           (GTreeNode        *node);
98static GTreeNode* g_tree_node_rotate_right          (GTreeNode        *node);
99static void       g_tree_node_check                 (GTreeNode        *node);
100
101
102G_LOCK_DEFINE_STATIC (g_tree_global);
103static GMemChunk *node_mem_chunk = NULL;
104static GTreeNode *node_free_list = NULL;
105
106
107static GTreeNode*
108g_tree_node_new (gpointer key,
109                 gpointer value)
110{
111  GTreeNode *node;
112
113  G_LOCK (g_tree_global);
114  if (node_free_list)
115    {
116      node = node_free_list;
117      node_free_list = node->right;
118    }
119  else
120    {
121      if (!node_mem_chunk)
122        node_mem_chunk = g_mem_chunk_new ("GLib GTreeNode mem chunk",
123                                          sizeof (GTreeNode),
124                                          1024,
125                                          G_ALLOC_ONLY);
126
127      node = g_chunk_new (GTreeNode, node_mem_chunk);
128   }
129  G_UNLOCK (g_tree_global);
130
131  node->balance = 0;
132  node->left = NULL;
133  node->right = NULL;
134  node->key = key;
135  node->value = value;
136
137  return node;
138}
139
140static void
141g_tree_node_destroy (GTreeNode      *node,
142                     GDestroyNotify  key_destroy_func,
143                     GDestroyNotify  value_destroy_func)
144{
145  if (node)
146    {
147      g_tree_node_destroy (node->right,
148                           key_destroy_func, value_destroy_func);
149      g_tree_node_destroy (node->left,
150                           key_destroy_func, value_destroy_func);
151
152      if (key_destroy_func)
153        key_destroy_func (node->key);
154      if (value_destroy_func)
155        value_destroy_func (node->value);
156     
157#ifdef ENABLE_GC_FRIENDLY
158      node->left = NULL;
159      node->key = NULL;
160      node->value = NULL;
161#endif /* ENABLE_GC_FRIENDLY */
162
163      G_LOCK (g_tree_global);
164      node->right = node_free_list;
165      node_free_list = node;
166      G_UNLOCK (g_tree_global);
167   }
168}
169
170/**
171 * g_tree_new:
172 * @key_compare_func: the function used to order the nodes in the #GTree.
173 *   It should return values similar to the standard
174 *   <function>strcmp()</function> function -
175 *   0 if the two arguments are equal, a negative value if the first argument
176 *   comes before the second, or a positive value if the first argument comes
177 *   after the second.
178 *
179 * Creates a new #GTree.
180 *
181 * Return value: a new #GTree.
182 **/
183GTree*
184g_tree_new (GCompareFunc key_compare_func)
185{
186  g_return_val_if_fail (key_compare_func != NULL, NULL);
187
188  return g_tree_new_full ((GCompareDataFunc) key_compare_func, NULL,
189                          NULL, NULL);
190}
191
192/**
193 * g_tree_new_with_data:
194 * @key_compare_func: <function>qsort()</function>-style comparison function.
195 * @key_compare_data: data to pass to comparison function.
196 *
197 * Creates a new #GTree with a comparison function that accepts user data.
198 * See g_tree_new() for more details.
199 *
200 * Return value: a new #GTree.
201 **/
202GTree*
203g_tree_new_with_data (GCompareDataFunc key_compare_func,
204                      gpointer         key_compare_data)
205{
206  g_return_val_if_fail (key_compare_func != NULL, NULL);
207 
208  return g_tree_new_full (key_compare_func, key_compare_data,
209                          NULL, NULL);
210}
211
212/**
213 * g_tree_new_full:
214 * @key_compare_func: <function>qsort()</function>-style comparison function.
215 * @key_compare_data: data to pass to comparison function.
216 * @key_destroy_func: a function to free the memory allocated for the key
217 *   used when removing the entry from the #GTree or %NULL if you don't
218 *   want to supply such a function.
219 * @value_destroy_func: a function to free the memory allocated for the
220 *   value used when removing the entry from the #GTree or %NULL if you
221 *   don't want to supply such a function.
222 *
223 * Creates a new #GTree like g_tree_new() and allows to specify functions
224 * to free the memory allocated for the key and value that get called when
225 * removing the entry from the #GTree.
226 *
227 * Return value: a new #GTree.
228 **/
229GTree*   
230g_tree_new_full (GCompareDataFunc key_compare_func,
231                 gpointer         key_compare_data,             
232                 GDestroyNotify   key_destroy_func,
233                 GDestroyNotify   value_destroy_func)
234{
235  GTree *tree;
236 
237  g_return_val_if_fail (key_compare_func != NULL, NULL);
238 
239  tree = g_new (GTree, 1);
240  tree->root               = NULL;
241  tree->key_compare        = key_compare_func;
242  tree->key_destroy_func   = key_destroy_func;
243  tree->value_destroy_func = value_destroy_func;
244  tree->key_compare_data   = key_compare_data;
245 
246  return tree;
247}
248
249/**
250 * g_tree_destroy:
251 * @tree: a #GTree.
252 *
253 * Destroys the #GTree. If keys and/or values are dynamically allocated, you
254 * should either free them first or create the #GTree using g_tree_new_full().
255 * In the latter case the destroy functions you supplied will be called on
256 * all keys and values before destroying the #GTree.
257 **/
258void
259g_tree_destroy (GTree *tree)
260{
261  g_return_if_fail (tree != NULL);
262
263  g_tree_node_destroy (tree->root,
264                       tree->key_destroy_func,
265                       tree->value_destroy_func);
266
267  g_free (tree);
268}
269
270/**
271 * g_tree_insert:
272 * @tree: a #GTree.
273 * @key: the key to insert.
274 * @value: the value corresponding to the key.
275 *
276 * Inserts a key/value pair into a #GTree. If the given key already exists
277 * in the #GTree its corresponding value is set to the new value. If you
278 * supplied a value_destroy_func when creating the #GTree, the old value is
279 * freed using that function. If you supplied a @key_destroy_func when
280 * creating the #GTree, the passed key is freed using that function.
281 *
282 * The tree is automatically 'balanced' as new key/value pairs are added,
283 * so that the distance from the root to every leaf is as small as possible.
284 **/
285void
286g_tree_insert (GTree    *tree,
287               gpointer  key,
288               gpointer  value)
289{
290  gboolean   inserted;
291
292  g_return_if_fail (tree != NULL);
293
294  inserted = FALSE;
295  tree->root = g_tree_node_insert (tree,
296                                   tree->root,
297                                   key, value,
298                                   FALSE, &inserted);
299}
300
301/**
302 * g_tree_replace:
303 * @tree: a #GTree.
304 * @key: the key to insert.
305 * @value: the value corresponding to the key.
306 *
307 * Inserts a new key and value into a #GTree similar to g_tree_insert().
308 * The difference is that if the key already exists in the #GTree, it gets
309 * replaced by the new key. If you supplied a @value_destroy_func when
310 * creating the #GTree, the old value is freed using that function. If you
311 * supplied a @key_destroy_func when creating the #GTree, the old key is
312 * freed using that function.
313 *
314 * The tree is automatically 'balanced' as new key/value pairs are added,
315 * so that the distance from the root to every leaf is as small as possible.
316 **/
317void
318g_tree_replace (GTree    *tree,
319                gpointer  key,
320                gpointer  value)
321{
322  gboolean   inserted;
323
324  g_return_if_fail (tree != NULL);
325
326  inserted = FALSE;
327  tree->root = g_tree_node_insert (tree,
328                                   tree->root,
329                                   key, value,
330                                   TRUE, &inserted);
331}
332
333/**
334 * g_tree_remove:
335 * @tree: a #GTree.
336 * @key: the key to remove.
337 *
338 * Removes a key/value pair from a #GTree.
339 *
340 * If the #GTree was created using g_tree_new_full(), the key and value
341 * are freed using the supplied destroy functions, otherwise you have to
342 * make sure that any dynamically allocated values are freed yourself.
343 **/
344void
345g_tree_remove (GTree         *tree,
346               gconstpointer  key)
347{
348  g_return_if_fail (tree != NULL);
349
350  tree->root = g_tree_node_remove (tree, tree->root, key, TRUE);
351}
352
353/**
354 * g_tree_steal:
355 * @tree: a #GTree.
356 * @key: the key to remove.
357 *
358 * Removes a key and its associated value from a #GTree without calling
359 * the key and value destroy functions.
360 **/
361void
362g_tree_steal (GTree         *tree,
363              gconstpointer  key)
364{
365  g_return_if_fail (tree != NULL);
366
367  tree->root = g_tree_node_remove (tree, tree->root, key, FALSE);
368}
369
370/**
371 * g_tree_lookup:
372 * @tree: a #GTree.
373 * @key: the key to look up.
374 *
375 * Gets the value corresponding to the given key. Since a #GTree is
376 * automatically balanced as key/value pairs are added, key lookup is very
377 * fast.
378 *
379 * Return value: the value corresponding to the key.
380 **/
381gpointer
382g_tree_lookup (GTree         *tree,
383               gconstpointer  key)
384{
385  GTreeNode *node;
386
387  g_return_val_if_fail (tree != NULL, NULL);
388
389  node = g_tree_node_lookup (tree->root,
390                             tree->key_compare, tree->key_compare_data, key);
391
392  return node ? node->value : NULL;
393}
394
395/**
396 * g_tree_lookup_extended:
397 * @tree: a #GTree.
398 * @lookup_key: the key to look up.
399 * @orig_key: returns the original key.
400 * @value: returns the value associated with the key.
401 *
402 * Looks up a key in the #GTree, returning the original key and the
403 * associated value and a #gboolean which is %TRUE if the key was found. This
404 * is useful if you need to free the memory allocated for the original key,
405 * for example before calling g_tree_remove().
406 *
407 * Return value: %TRUE if the key was found in the #GTree.
408 **/
409gboolean
410g_tree_lookup_extended (GTree         *tree,
411                        gconstpointer  lookup_key,
412                        gpointer      *orig_key,
413                        gpointer      *value)
414{
415  GTreeNode *node;
416 
417  g_return_val_if_fail (tree != NULL, FALSE);
418 
419  node = g_tree_node_lookup (tree->root,
420                             tree->key_compare, tree->key_compare_data, lookup_key);
421
422  if (node)
423    {
424      if (orig_key)
425        *orig_key = node->key;
426      if (value)
427        *value = node->value;
428      return TRUE;
429    }
430  else
431    return FALSE;
432}
433
434/**
435 * g_tree_foreach:
436 * @tree: a #GTree.
437 * @func: the function to call for each node visited. If this function
438 *   returns %TRUE, the traversal is stopped.
439 * @user_data: user data to pass to the function.
440 *
441 * Calls the given function for each of the key/value pairs in the #GTree.
442 * The function is passed the key and value of each pair, and the given
443 * @data parameter. The tree is traversed in sorted order.
444 *
445 * The tree may not be modified while iterating over it (you can't
446 * add/remove items). To remove all items matching a predicate, you need
447 * to add each item to a list in your #GTraverseFunc as you walk over
448 * the tree, then walk the list and remove each item.
449 **/
450void
451g_tree_foreach (GTree         *tree,
452                GTraverseFunc  func,
453                gpointer       user_data)
454{
455  g_return_if_fail (tree != NULL);
456 
457  if (!tree->root)
458    return;
459
460  g_tree_node_in_order (tree->root, func, user_data);
461}
462
463/**
464 * g_tree_traverse:
465 * @tree: a #GTree.
466 * @traverse_func: the function to call for each node visited. If this
467 *   function returns %TRUE, the traversal is stopped.
468 * @traverse_type: the order in which nodes are visited, one of %G_IN_ORDER,
469 *   %G_PRE_ORDER and %G_POST_ORDER.
470 * @user_data: user data to pass to the function.
471 *
472 * Calls the given function for each node in the #GTree.
473 *
474 * Deprecated: The order of a balanced tree is somewhat arbitrary. If you
475 * just want to visit all nodes in sorted order, use g_tree_foreach()
476 * instead. If you really need to visit nodes in a different order, consider
477 * using an <link linkend="glib-N-ary-Trees">N-ary Tree</link>.
478 **/
479void
480g_tree_traverse (GTree         *tree,
481                 GTraverseFunc  traverse_func,
482                 GTraverseType  traverse_type,
483                 gpointer       user_data)
484{
485  g_return_if_fail (tree != NULL);
486
487  if (!tree->root)
488    return;
489
490  switch (traverse_type)
491    {
492    case G_PRE_ORDER:
493      g_tree_node_pre_order (tree->root, traverse_func, user_data);
494      break;
495
496    case G_IN_ORDER:
497      g_tree_node_in_order (tree->root, traverse_func, user_data);
498      break;
499
500    case G_POST_ORDER:
501      g_tree_node_post_order (tree->root, traverse_func, user_data);
502      break;
503   
504    case G_LEVEL_ORDER:
505      g_warning ("g_tree_traverse(): traverse type G_LEVEL_ORDER isn't implemented.");
506      break;
507    }
508}
509
510/**
511 * g_tree_search:
512 * @tree: a #GTree.
513 * @search_func: a function used to search the #GTree.
514 * @user_data: the data passed as the second argument to the @search_func
515 * function.
516 *
517 * Searches a #GTree using @search_func.
518 *
519 * The @search_func is called with a pointer to the key of a key/value pair in the tree,
520 * and the passed in @user_data. If @search_func returns 0 for a key/value pair, then
521 * g_tree_search_func() will return the value of that pair. If @search_func returns -1,
522 * searching will proceed among the key/value pairs that have a smaller key; if @search_func
523 * returns 1, searching will proceed among the key/value pairs that have a larger key.
524 *
525 * Return value: the value corresponding to the found key, or %NULL if the key
526 * was not found.
527 **/
528gpointer
529g_tree_search (GTree         *tree,
530               GCompareFunc   search_func,
531               gconstpointer  user_data)
532{
533  g_return_val_if_fail (tree != NULL, NULL);
534
535  if (tree->root)
536    return g_tree_node_search (tree->root, search_func, user_data);
537  else
538    return NULL;
539}
540
541/**
542 * g_tree_height:
543 * @tree: a #GTree.
544 *
545 * Gets the height of a #GTree.
546 *
547 * If the #GTree contains no nodes, the height is 0.
548 * If the #GTree contains only one root node the height is 1.
549 * If the root node has children the height is 2, etc.
550 *
551 * Return value: the height of the #GTree.
552 **/
553gint
554g_tree_height (GTree *tree)
555{
556  g_return_val_if_fail (tree != NULL, 0);
557
558  if (tree->root)
559    return g_tree_node_height (tree->root);
560  else
561    return 0;
562}
563
564/**
565 * g_tree_nnodes:
566 * @tree: a #GTree.
567 *
568 * Gets the number of nodes in a #GTree.
569 *
570 * Return value: the number of nodes in the #GTree.
571 **/
572gint
573g_tree_nnodes (GTree *tree)
574{
575  g_return_val_if_fail (tree != NULL, 0);
576
577  if (tree->root)
578    return g_tree_node_count (tree->root);
579  else
580    return 0;
581}
582
583static GTreeNode*
584g_tree_node_insert (GTree     *tree,
585                    GTreeNode *node,
586                    gpointer   key,
587                    gpointer   value,
588                    gboolean   replace,
589                    gboolean  *inserted)
590{
591  gint  old_balance;
592  gint  cmp;
593
594  if (!node)
595    {
596      *inserted = TRUE;
597      return g_tree_node_new (key, value);
598    }
599
600  cmp = tree->key_compare (key, node->key, tree->key_compare_data);
601  if (cmp == 0)
602    {
603      *inserted = FALSE;
604
605      if (tree->value_destroy_func)
606        tree->value_destroy_func (node->value);
607
608      node->value = value;
609     
610      if (replace)
611        {
612          if (tree->key_destroy_func)
613            tree->key_destroy_func (node->key);
614
615          node->key = key;
616        }
617      else
618        {
619          /* free the passed key */
620          if (tree->key_destroy_func)
621            tree->key_destroy_func (key);
622        }
623
624      return node;
625    }
626
627  if (cmp < 0)
628    {
629      if (node->left)
630        {
631          old_balance = node->left->balance;
632          node->left = g_tree_node_insert (tree,
633                                           node->left,
634                                           key, value,
635                                           replace, inserted);
636
637          if ((old_balance != node->left->balance) && node->left->balance)
638            node->balance -= 1;
639        }
640      else
641        {
642          *inserted = TRUE;
643          node->left = g_tree_node_new (key, value);
644          node->balance -= 1;
645        }
646    }
647  else if (cmp > 0)
648    {
649      if (node->right)
650        {
651          old_balance = node->right->balance;
652          node->right = g_tree_node_insert (tree,
653                                            node->right,
654                                            key, value,
655                                            replace, inserted);
656
657          if ((old_balance != node->right->balance) && node->right->balance)
658            node->balance += 1;
659        }
660      else
661        {
662          *inserted = TRUE;
663          node->right = g_tree_node_new (key, value);
664          node->balance += 1;
665        }
666    }
667
668  if (*inserted)
669    {
670      if ((node->balance < -1) || (node->balance > 1))
671        node = g_tree_node_balance (node);
672    }
673
674  return node;
675}
676
677static GTreeNode*
678g_tree_node_remove (GTree         *tree,
679                    GTreeNode     *node,
680                    gconstpointer  key,
681                    gboolean       notify)
682{
683  GTreeNode *new_root;
684  gint old_balance;
685  gint cmp;
686
687  if (!node)
688    return NULL;
689
690  cmp = tree->key_compare (key, node->key, tree->key_compare_data);
691  if (cmp == 0)
692    {
693      GTreeNode *garbage;
694
695      garbage = node;
696
697      if (!node->right)
698        {
699          node = node->left;
700        }
701      else
702        {
703          old_balance = node->right->balance;
704          node->right = g_tree_node_remove_leftmost (node->right, &new_root);
705          new_root->left = node->left;
706          new_root->right = node->right;
707          new_root->balance = node->balance;
708          node = g_tree_node_restore_right_balance (new_root, old_balance);
709        }
710
711      if (notify)
712        {
713          if (tree->key_destroy_func)
714            tree->key_destroy_func (garbage->key);
715          if (tree->value_destroy_func)
716            tree->value_destroy_func (garbage->value);
717        }
718
719#ifdef ENABLE_GC_FRIENDLY
720      garbage->left = NULL;
721      garbage->key = NULL;
722      garbage->value = NULL;
723#endif /* ENABLE_GC_FRIENDLY */
724
725      G_LOCK (g_tree_global);
726      garbage->right = node_free_list;
727      node_free_list = garbage;
728      G_UNLOCK (g_tree_global);
729   }
730  else if (cmp < 0)
731    {
732      if (node->left)
733        {
734          old_balance = node->left->balance;
735          node->left = g_tree_node_remove (tree, node->left, key, notify);
736          node = g_tree_node_restore_left_balance (node, old_balance);
737        }
738    }
739  else if (cmp > 0)
740    {
741      if (node->right)
742        {
743          old_balance = node->right->balance;
744          node->right = g_tree_node_remove (tree, node->right, key, notify);
745          node = g_tree_node_restore_right_balance (node, old_balance);
746        }
747    }
748
749  return node;
750}
751
752static GTreeNode*
753g_tree_node_balance (GTreeNode *node)
754{
755  if (node->balance < -1)
756    {
757      if (node->left->balance > 0)
758        node->left = g_tree_node_rotate_left (node->left);
759      node = g_tree_node_rotate_right (node);
760    }
761  else if (node->balance > 1)
762    {
763      if (node->right->balance < 0)
764        node->right = g_tree_node_rotate_right (node->right);
765      node = g_tree_node_rotate_left (node);
766    }
767
768  return node;
769}
770
771static GTreeNode*
772g_tree_node_remove_leftmost (GTreeNode  *node,
773                             GTreeNode **leftmost)
774{
775  gint old_balance;
776
777  if (!node->left)
778    {
779      *leftmost = node;
780      return node->right;
781    }
782
783  old_balance = node->left->balance;
784  node->left = g_tree_node_remove_leftmost (node->left, leftmost);
785  return g_tree_node_restore_left_balance (node, old_balance);
786}
787
788static GTreeNode*
789g_tree_node_restore_left_balance (GTreeNode *node,
790                                  gint       old_balance)
791{
792  if (!node->left)
793    node->balance += 1;
794  else if ((node->left->balance != old_balance) &&
795           (node->left->balance == 0))
796    node->balance += 1;
797
798  if (node->balance > 1)
799    return g_tree_node_balance (node);
800  return node;
801}
802
803static GTreeNode*
804g_tree_node_restore_right_balance (GTreeNode *node,
805                                   gint       old_balance)
806{
807  if (!node->right)
808    node->balance -= 1;
809  else if ((node->right->balance != old_balance) &&
810           (node->right->balance == 0))
811    node->balance -= 1;
812
813  if (node->balance < -1)
814    return g_tree_node_balance (node);
815  return node;
816}
817
818static GTreeNode *
819g_tree_node_lookup (GTreeNode        *node,
820                    GCompareDataFunc  compare,
821                    gpointer          compare_data,
822                    gconstpointer     key)
823{
824  gint cmp;
825
826  if (!node)
827    return NULL;
828
829  cmp = (* compare) (key, node->key, compare_data);
830  if (cmp == 0)
831    return node;
832
833  if (cmp < 0)
834    {
835      if (node->left)
836        return g_tree_node_lookup (node->left, compare, compare_data, key);
837    }
838  else if (cmp > 0)
839    {
840      if (node->right)
841        return g_tree_node_lookup (node->right, compare, compare_data, key);
842    }
843
844  return NULL;
845}
846
847static gint
848g_tree_node_count (GTreeNode *node)
849{
850  gint count;
851
852  count = 1;
853  if (node->left)
854    count += g_tree_node_count (node->left);
855  if (node->right)
856    count += g_tree_node_count (node->right);
857
858  return count;
859}
860
861static gint
862g_tree_node_pre_order (GTreeNode     *node,
863                       GTraverseFunc  traverse_func,
864                       gpointer       data)
865{
866  if ((*traverse_func) (node->key, node->value, data))
867    return TRUE;
868  if (node->left)
869    {
870      if (g_tree_node_pre_order (node->left, traverse_func, data))
871        return TRUE;
872    }
873  if (node->right)
874    {
875      if (g_tree_node_pre_order (node->right, traverse_func, data))
876        return TRUE;
877    }
878
879  return FALSE;
880}
881
882static gint
883g_tree_node_in_order (GTreeNode     *node,
884                      GTraverseFunc  traverse_func,
885                      gpointer       data)
886{
887  if (node->left)
888    {
889      if (g_tree_node_in_order (node->left, traverse_func, data))
890        return TRUE;
891    }
892  if ((*traverse_func) (node->key, node->value, data))
893    return TRUE;
894  if (node->right)
895    {
896      if (g_tree_node_in_order (node->right, traverse_func, data))
897        return TRUE;
898    }
899
900  return FALSE;
901}
902
903static gint
904g_tree_node_post_order (GTreeNode     *node,
905                        GTraverseFunc  traverse_func,
906                        gpointer       data)
907{
908  if (node->left)
909    {
910      if (g_tree_node_post_order (node->left, traverse_func, data))
911        return TRUE;
912    }
913  if (node->right)
914    {
915      if (g_tree_node_post_order (node->right, traverse_func, data))
916        return TRUE;
917    }
918  if ((*traverse_func) (node->key, node->value, data))
919    return TRUE;
920
921  return FALSE;
922}
923
924static gpointer
925g_tree_node_search (GTreeNode     *node,
926                    GCompareFunc   search_func,
927                    gconstpointer  data)
928{
929  gint dir;
930
931  if (!node)
932    return NULL;
933
934  do {
935    dir = (* search_func) (node->key, data);
936    if (dir == 0)
937      return node->value;
938
939    if (dir < 0)
940      node = node->left;
941    else if (dir > 0)
942      node = node->right;
943  } while (node);
944
945  return NULL;
946}
947
948static gint
949g_tree_node_height (GTreeNode *node)
950{
951  gint left_height;
952  gint right_height;
953
954  if (node)
955    {
956      left_height = 0;
957      right_height = 0;
958
959      if (node->left)
960        left_height = g_tree_node_height (node->left);
961
962      if (node->right)
963        right_height = g_tree_node_height (node->right);
964
965      return MAX (left_height, right_height) + 1;
966    }
967
968  return 0;
969}
970
971static GTreeNode*
972g_tree_node_rotate_left (GTreeNode *node)
973{
974  GTreeNode *right;
975  gint a_bal;
976  gint b_bal;
977
978  right = node->right;
979
980  node->right = right->left;
981  right->left = node;
982
983  a_bal = node->balance;
984  b_bal = right->balance;
985
986  if (b_bal <= 0)
987    {
988      if (a_bal >= 1)
989        right->balance = b_bal - 1;
990      else
991        right->balance = a_bal + b_bal - 2;
992      node->balance = a_bal - 1;
993    }
994  else
995    {
996      if (a_bal <= b_bal)
997        right->balance = a_bal - 2;
998      else
999        right->balance = b_bal - 1;
1000      node->balance = a_bal - b_bal - 1;
1001    }
1002
1003  return right;
1004}
1005
1006static GTreeNode*
1007g_tree_node_rotate_right (GTreeNode *node)
1008{
1009  GTreeNode *left;
1010  gint a_bal;
1011  gint b_bal;
1012
1013  left = node->left;
1014
1015  node->left = left->right;
1016  left->right = node;
1017
1018  a_bal = node->balance;
1019  b_bal = left->balance;
1020
1021  if (b_bal <= 0)
1022    {
1023      if (b_bal > a_bal)
1024        left->balance = b_bal + 1;
1025      else
1026        left->balance = a_bal + 2;
1027      node->balance = a_bal - b_bal + 1;
1028    }
1029  else
1030    {
1031      if (a_bal <= -1)
1032        left->balance = b_bal + 1;
1033      else
1034        left->balance = a_bal + b_bal + 2;
1035      node->balance = a_bal + 1;
1036    }
1037
1038  return left;
1039}
1040
1041static void
1042g_tree_node_check (GTreeNode *node)
1043{
1044  gint left_height;
1045  gint right_height;
1046  gint balance;
1047 
1048  if (node)
1049    {
1050      left_height = 0;
1051      right_height = 0;
1052     
1053      if (node->left)
1054        left_height = g_tree_node_height (node->left);
1055      if (node->right)
1056        right_height = g_tree_node_height (node->right);
1057     
1058      balance = right_height - left_height;
1059      if (balance != node->balance)
1060        g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO,
1061               "g_tree_node_check: failed: %d ( %d )\n",
1062               balance, node->balance);
1063     
1064      if (node->left)
1065        g_tree_node_check (node->left);
1066      if (node->right)
1067        g_tree_node_check (node->right);
1068    }
1069}
Note: See TracBrowser for help on using the repository browser.