nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 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, see <http://www.gnu.org/licenses/>. |
||
16 | */ |
||
17 | |||
18 | /* |
||
19 | * Modified by the GLib Team and others 1997-2000. See the AUTHORS |
||
20 | * file for a list of people on the GLib Team. See the ChangeLog |
||
21 | * files for a list of changes. These files are distributed with |
||
22 | * GLib at ftp://ftp.gtk.org/pub/gtk/. |
||
23 | */ |
||
24 | |||
25 | /* |
||
26 | * MT safe |
||
27 | */ |
||
28 | |||
29 | #include "config.h" |
||
30 | |||
31 | #include "gtree.h" |
||
32 | |||
33 | #include "gatomic.h" |
||
34 | #include "gtestutils.h" |
||
35 | #include "gslice.h" |
||
36 | |||
37 | /** |
||
38 | * SECTION:trees-binary |
||
39 | * @title: Balanced Binary Trees |
||
40 | * @short_description: a sorted collection of key/value pairs optimized |
||
41 | * for searching and traversing in order |
||
42 | * |
||
43 | * The #GTree structure and its associated functions provide a sorted |
||
44 | * collection of key/value pairs optimized for searching and traversing |
||
45 | * in order. |
||
46 | * |
||
47 | * To create a new #GTree use g_tree_new(). |
||
48 | * |
||
49 | * To insert a key/value pair into a #GTree use g_tree_insert(). |
||
50 | * |
||
51 | * To lookup the value corresponding to a given key, use |
||
52 | * g_tree_lookup() and g_tree_lookup_extended(). |
||
53 | * |
||
54 | * To find out the number of nodes in a #GTree, use g_tree_nnodes(). To |
||
55 | * get the height of a #GTree, use g_tree_height(). |
||
56 | * |
||
57 | * To traverse a #GTree, calling a function for each node visited in |
||
58 | * the traversal, use g_tree_foreach(). |
||
59 | * |
||
60 | * To remove a key/value pair use g_tree_remove(). |
||
61 | * |
||
62 | * To destroy a #GTree, use g_tree_destroy(). |
||
63 | **/ |
||
64 | |||
65 | #undef G_TREE_DEBUG |
||
66 | |||
67 | #define MAX_GTREE_HEIGHT 40 |
||
68 | |||
69 | typedef struct _GTreeNode GTreeNode; |
||
70 | |||
71 | /** |
||
72 | * GTree: |
||
73 | * |
||
74 | * The GTree struct is an opaque data structure representing a |
||
75 | * [balanced binary tree][glib-Balanced-Binary-Trees]. It should be |
||
76 | * accessed only by using the following functions. |
||
77 | */ |
||
78 | struct _GTree |
||
79 | { |
||
80 | GTreeNode *root; |
||
81 | GCompareDataFunc key_compare; |
||
82 | GDestroyNotify key_destroy_func; |
||
83 | GDestroyNotify value_destroy_func; |
||
84 | gpointer key_compare_data; |
||
85 | guint nnodes; |
||
86 | gint ref_count; |
||
87 | }; |
||
88 | |||
89 | struct _GTreeNode |
||
90 | { |
||
91 | gpointer key; /* key for this node */ |
||
92 | gpointer value; /* value stored at this node */ |
||
93 | GTreeNode *left; /* left subtree */ |
||
94 | GTreeNode *right; /* right subtree */ |
||
95 | gint8 balance; /* height (right) - height (left) */ |
||
96 | guint8 left_child; |
||
97 | guint8 right_child; |
||
98 | }; |
||
99 | |||
100 | |||
101 | static GTreeNode* g_tree_node_new (gpointer key, |
||
102 | gpointer value); |
||
103 | static void g_tree_insert_internal (GTree *tree, |
||
104 | gpointer key, |
||
105 | gpointer value, |
||
106 | gboolean replace); |
||
107 | static gboolean g_tree_remove_internal (GTree *tree, |
||
108 | gconstpointer key, |
||
109 | gboolean steal); |
||
110 | static GTreeNode* g_tree_node_balance (GTreeNode *node); |
||
111 | static GTreeNode *g_tree_find_node (GTree *tree, |
||
112 | gconstpointer key); |
||
113 | static gint g_tree_node_pre_order (GTreeNode *node, |
||
114 | GTraverseFunc traverse_func, |
||
115 | gpointer data); |
||
116 | static gint g_tree_node_in_order (GTreeNode *node, |
||
117 | GTraverseFunc traverse_func, |
||
118 | gpointer data); |
||
119 | static gint g_tree_node_post_order (GTreeNode *node, |
||
120 | GTraverseFunc traverse_func, |
||
121 | gpointer data); |
||
122 | static gpointer g_tree_node_search (GTreeNode *node, |
||
123 | GCompareFunc search_func, |
||
124 | gconstpointer data); |
||
125 | static GTreeNode* g_tree_node_rotate_left (GTreeNode *node); |
||
126 | static GTreeNode* g_tree_node_rotate_right (GTreeNode *node); |
||
127 | #ifdef G_TREE_DEBUG |
||
128 | static void g_tree_node_check (GTreeNode *node); |
||
129 | #endif |
||
130 | |||
131 | |||
132 | static GTreeNode* |
||
133 | g_tree_node_new (gpointer key, |
||
134 | gpointer value) |
||
135 | { |
||
136 | GTreeNode *node = g_slice_new (GTreeNode); |
||
137 | |||
138 | node->balance = 0; |
||
139 | node->left = NULL; |
||
140 | node->right = NULL; |
||
141 | node->left_child = FALSE; |
||
142 | node->right_child = FALSE; |
||
143 | node->key = key; |
||
144 | node->value = value; |
||
145 | |||
146 | return node; |
||
147 | } |
||
148 | |||
149 | /** |
||
150 | * g_tree_new: |
||
151 | * @key_compare_func: the function used to order the nodes in the #GTree. |
||
152 | * It should return values similar to the standard strcmp() function - |
||
153 | * 0 if the two arguments are equal, a negative value if the first argument |
||
154 | * comes before the second, or a positive value if the first argument comes |
||
155 | * after the second. |
||
156 | * |
||
157 | * Creates a new #GTree. |
||
158 | * |
||
159 | * Returns: a newly allocated #GTree |
||
160 | */ |
||
161 | GTree * |
||
162 | g_tree_new (GCompareFunc key_compare_func) |
||
163 | { |
||
164 | g_return_val_if_fail (key_compare_func != NULL, NULL); |
||
165 | |||
166 | return g_tree_new_full ((GCompareDataFunc) key_compare_func, NULL, |
||
167 | NULL, NULL); |
||
168 | } |
||
169 | |||
170 | /** |
||
171 | * g_tree_new_with_data: |
||
172 | * @key_compare_func: qsort()-style comparison function |
||
173 | * @key_compare_data: data to pass to comparison function |
||
174 | * |
||
175 | * Creates a new #GTree with a comparison function that accepts user data. |
||
176 | * See g_tree_new() for more details. |
||
177 | * |
||
178 | * Returns: a newly allocated #GTree |
||
179 | */ |
||
180 | GTree * |
||
181 | g_tree_new_with_data (GCompareDataFunc key_compare_func, |
||
182 | gpointer key_compare_data) |
||
183 | { |
||
184 | g_return_val_if_fail (key_compare_func != NULL, NULL); |
||
185 | |||
186 | return g_tree_new_full (key_compare_func, key_compare_data, |
||
187 | NULL, NULL); |
||
188 | } |
||
189 | |||
190 | /** |
||
191 | * g_tree_new_full: |
||
192 | * @key_compare_func: qsort()-style comparison function |
||
193 | * @key_compare_data: data to pass to comparison function |
||
194 | * @key_destroy_func: a function to free the memory allocated for the key |
||
195 | * used when removing the entry from the #GTree or %NULL if you don't |
||
196 | * want to supply such a function |
||
197 | * @value_destroy_func: a function to free the memory allocated for the |
||
198 | * value used when removing the entry from the #GTree or %NULL if you |
||
199 | * don't want to supply such a function |
||
200 | * |
||
201 | * Creates a new #GTree like g_tree_new() and allows to specify functions |
||
202 | * to free the memory allocated for the key and value that get called when |
||
203 | * removing the entry from the #GTree. |
||
204 | * |
||
205 | * Returns: a newly allocated #GTree |
||
206 | */ |
||
207 | GTree * |
||
208 | g_tree_new_full (GCompareDataFunc key_compare_func, |
||
209 | gpointer key_compare_data, |
||
210 | GDestroyNotify key_destroy_func, |
||
211 | GDestroyNotify value_destroy_func) |
||
212 | { |
||
213 | GTree *tree; |
||
214 | |||
215 | g_return_val_if_fail (key_compare_func != NULL, NULL); |
||
216 | |||
217 | tree = g_slice_new (GTree); |
||
218 | tree->root = NULL; |
||
219 | tree->key_compare = key_compare_func; |
||
220 | tree->key_destroy_func = key_destroy_func; |
||
221 | tree->value_destroy_func = value_destroy_func; |
||
222 | tree->key_compare_data = key_compare_data; |
||
223 | tree->nnodes = 0; |
||
224 | tree->ref_count = 1; |
||
225 | |||
226 | return tree; |
||
227 | } |
||
228 | |||
229 | static inline GTreeNode * |
||
230 | g_tree_first_node (GTree *tree) |
||
231 | { |
||
232 | GTreeNode *tmp; |
||
233 | |||
234 | if (!tree->root) |
||
235 | return NULL; |
||
236 | |||
237 | tmp = tree->root; |
||
238 | |||
239 | while (tmp->left_child) |
||
240 | tmp = tmp->left; |
||
241 | |||
242 | return tmp; |
||
243 | } |
||
244 | |||
245 | static inline GTreeNode * |
||
246 | g_tree_node_previous (GTreeNode *node) |
||
247 | { |
||
248 | GTreeNode *tmp; |
||
249 | |||
250 | tmp = node->left; |
||
251 | |||
252 | if (node->left_child) |
||
253 | while (tmp->right_child) |
||
254 | tmp = tmp->right; |
||
255 | |||
256 | return tmp; |
||
257 | } |
||
258 | |||
259 | static inline GTreeNode * |
||
260 | g_tree_node_next (GTreeNode *node) |
||
261 | { |
||
262 | GTreeNode *tmp; |
||
263 | |||
264 | tmp = node->right; |
||
265 | |||
266 | if (node->right_child) |
||
267 | while (tmp->left_child) |
||
268 | tmp = tmp->left; |
||
269 | |||
270 | return tmp; |
||
271 | } |
||
272 | |||
273 | static void |
||
274 | g_tree_remove_all (GTree *tree) |
||
275 | { |
||
276 | GTreeNode *node; |
||
277 | GTreeNode *next; |
||
278 | |||
279 | g_return_if_fail (tree != NULL); |
||
280 | |||
281 | node = g_tree_first_node (tree); |
||
282 | |||
283 | while (node) |
||
284 | { |
||
285 | next = g_tree_node_next (node); |
||
286 | |||
287 | if (tree->key_destroy_func) |
||
288 | tree->key_destroy_func (node->key); |
||
289 | if (tree->value_destroy_func) |
||
290 | tree->value_destroy_func (node->value); |
||
291 | g_slice_free (GTreeNode, node); |
||
292 | |||
293 | node = next; |
||
294 | } |
||
295 | |||
296 | tree->root = NULL; |
||
297 | tree->nnodes = 0; |
||
298 | } |
||
299 | |||
300 | /** |
||
301 | * g_tree_ref: |
||
302 | * @tree: a #GTree |
||
303 | * |
||
304 | * Increments the reference count of @tree by one. |
||
305 | * |
||
306 | * It is safe to call this function from any thread. |
||
307 | * |
||
308 | * Returns: the passed in #GTree |
||
309 | * |
||
310 | * Since: 2.22 |
||
311 | */ |
||
312 | GTree * |
||
313 | g_tree_ref (GTree *tree) |
||
314 | { |
||
315 | g_return_val_if_fail (tree != NULL, NULL); |
||
316 | |||
317 | g_atomic_int_inc (&tree->ref_count); |
||
318 | |||
319 | return tree; |
||
320 | } |
||
321 | |||
322 | /** |
||
323 | * g_tree_unref: |
||
324 | * @tree: a #GTree |
||
325 | * |
||
326 | * Decrements the reference count of @tree by one. |
||
327 | * If the reference count drops to 0, all keys and values will |
||
328 | * be destroyed (if destroy functions were specified) and all |
||
329 | * memory allocated by @tree will be released. |
||
330 | * |
||
331 | * It is safe to call this function from any thread. |
||
332 | * |
||
333 | * Since: 2.22 |
||
334 | */ |
||
335 | void |
||
336 | g_tree_unref (GTree *tree) |
||
337 | { |
||
338 | g_return_if_fail (tree != NULL); |
||
339 | |||
340 | if (g_atomic_int_dec_and_test (&tree->ref_count)) |
||
341 | { |
||
342 | g_tree_remove_all (tree); |
||
343 | g_slice_free (GTree, tree); |
||
344 | } |
||
345 | } |
||
346 | |||
347 | /** |
||
348 | * g_tree_destroy: |
||
349 | * @tree: a #GTree |
||
350 | * |
||
351 | * Removes all keys and values from the #GTree and decreases its |
||
352 | * reference count by one. If keys and/or values are dynamically |
||
353 | * allocated, you should either free them first or create the #GTree |
||
354 | * using g_tree_new_full(). In the latter case the destroy functions |
||
355 | * you supplied will be called on all keys and values before destroying |
||
356 | * the #GTree. |
||
357 | */ |
||
358 | void |
||
359 | g_tree_destroy (GTree *tree) |
||
360 | { |
||
361 | g_return_if_fail (tree != NULL); |
||
362 | |||
363 | g_tree_remove_all (tree); |
||
364 | g_tree_unref (tree); |
||
365 | } |
||
366 | |||
367 | /** |
||
368 | * g_tree_insert: |
||
369 | * @tree: a #GTree |
||
370 | * @key: the key to insert |
||
371 | * @value: the value corresponding to the key |
||
372 | * |
||
373 | * Inserts a key/value pair into a #GTree. |
||
374 | * |
||
375 | * If the given key already exists in the #GTree its corresponding value |
||
376 | * is set to the new value. If you supplied a @value_destroy_func when |
||
377 | * creating the #GTree, the old value is freed using that function. If |
||
378 | * you supplied a @key_destroy_func when creating the #GTree, the passed |
||
379 | * key is freed using that function. |
||
380 | * |
||
381 | * The tree is automatically 'balanced' as new key/value pairs are added, |
||
382 | * so that the distance from the root to every leaf is as small as possible. |
||
383 | */ |
||
384 | void |
||
385 | g_tree_insert (GTree *tree, |
||
386 | gpointer key, |
||
387 | gpointer value) |
||
388 | { |
||
389 | g_return_if_fail (tree != NULL); |
||
390 | |||
391 | g_tree_insert_internal (tree, key, value, FALSE); |
||
392 | |||
393 | #ifdef G_TREE_DEBUG |
||
394 | g_tree_node_check (tree->root); |
||
395 | #endif |
||
396 | } |
||
397 | |||
398 | /** |
||
399 | * g_tree_replace: |
||
400 | * @tree: a #GTree |
||
401 | * @key: the key to insert |
||
402 | * @value: the value corresponding to the key |
||
403 | * |
||
404 | * Inserts a new key and value into a #GTree similar to g_tree_insert(). |
||
405 | * The difference is that if the key already exists in the #GTree, it gets |
||
406 | * replaced by the new key. If you supplied a @value_destroy_func when |
||
407 | * creating the #GTree, the old value is freed using that function. If you |
||
408 | * supplied a @key_destroy_func when creating the #GTree, the old key is |
||
409 | * freed using that function. |
||
410 | * |
||
411 | * The tree is automatically 'balanced' as new key/value pairs are added, |
||
412 | * so that the distance from the root to every leaf is as small as possible. |
||
413 | */ |
||
414 | void |
||
415 | g_tree_replace (GTree *tree, |
||
416 | gpointer key, |
||
417 | gpointer value) |
||
418 | { |
||
419 | g_return_if_fail (tree != NULL); |
||
420 | |||
421 | g_tree_insert_internal (tree, key, value, TRUE); |
||
422 | |||
423 | #ifdef G_TREE_DEBUG |
||
424 | g_tree_node_check (tree->root); |
||
425 | #endif |
||
426 | } |
||
427 | |||
428 | /* internal insert routine */ |
||
429 | static void |
||
430 | g_tree_insert_internal (GTree *tree, |
||
431 | gpointer key, |
||
432 | gpointer value, |
||
433 | gboolean replace) |
||
434 | { |
||
435 | GTreeNode *node; |
||
436 | GTreeNode *path[MAX_GTREE_HEIGHT]; |
||
437 | int idx; |
||
438 | |||
439 | g_return_if_fail (tree != NULL); |
||
440 | |||
441 | if (!tree->root) |
||
442 | { |
||
443 | tree->root = g_tree_node_new (key, value); |
||
444 | tree->nnodes++; |
||
445 | return; |
||
446 | } |
||
447 | |||
448 | idx = 0; |
||
449 | path[idx++] = NULL; |
||
450 | node = tree->root; |
||
451 | |||
452 | while (1) |
||
453 | { |
||
454 | int cmp = tree->key_compare (key, node->key, tree->key_compare_data); |
||
455 | |||
456 | if (cmp == 0) |
||
457 | { |
||
458 | if (tree->value_destroy_func) |
||
459 | tree->value_destroy_func (node->value); |
||
460 | |||
461 | node->value = value; |
||
462 | |||
463 | if (replace) |
||
464 | { |
||
465 | if (tree->key_destroy_func) |
||
466 | tree->key_destroy_func (node->key); |
||
467 | |||
468 | node->key = key; |
||
469 | } |
||
470 | else |
||
471 | { |
||
472 | /* free the passed key */ |
||
473 | if (tree->key_destroy_func) |
||
474 | tree->key_destroy_func (key); |
||
475 | } |
||
476 | |||
477 | return; |
||
478 | } |
||
479 | else if (cmp < 0) |
||
480 | { |
||
481 | if (node->left_child) |
||
482 | { |
||
483 | path[idx++] = node; |
||
484 | node = node->left; |
||
485 | } |
||
486 | else |
||
487 | { |
||
488 | GTreeNode *child = g_tree_node_new (key, value); |
||
489 | |||
490 | child->left = node->left; |
||
491 | child->right = node; |
||
492 | node->left = child; |
||
493 | node->left_child = TRUE; |
||
494 | node->balance -= 1; |
||
495 | |||
496 | tree->nnodes++; |
||
497 | |||
498 | break; |
||
499 | } |
||
500 | } |
||
501 | else |
||
502 | { |
||
503 | if (node->right_child) |
||
504 | { |
||
505 | path[idx++] = node; |
||
506 | node = node->right; |
||
507 | } |
||
508 | else |
||
509 | { |
||
510 | GTreeNode *child = g_tree_node_new (key, value); |
||
511 | |||
512 | child->right = node->right; |
||
513 | child->left = node; |
||
514 | node->right = child; |
||
515 | node->right_child = TRUE; |
||
516 | node->balance += 1; |
||
517 | |||
518 | tree->nnodes++; |
||
519 | |||
520 | break; |
||
521 | } |
||
522 | } |
||
523 | } |
||
524 | |||
525 | /* Restore balance. This is the goodness of a non-recursive |
||
526 | * implementation, when we are done with balancing we 'break' |
||
527 | * the loop and we are done. |
||
528 | */ |
||
529 | while (1) |
||
530 | { |
||
531 | GTreeNode *bparent = path[--idx]; |
||
532 | gboolean left_node = (bparent && node == bparent->left); |
||
533 | g_assert (!bparent || bparent->left == node || bparent->right == node); |
||
534 | |||
535 | if (node->balance < -1 || node->balance > 1) |
||
536 | { |
||
537 | node = g_tree_node_balance (node); |
||
538 | if (bparent == NULL) |
||
539 | tree->root = node; |
||
540 | else if (left_node) |
||
541 | bparent->left = node; |
||
542 | else |
||
543 | bparent->right = node; |
||
544 | } |
||
545 | |||
546 | if (node->balance == 0 || bparent == NULL) |
||
547 | break; |
||
548 | |||
549 | if (left_node) |
||
550 | bparent->balance -= 1; |
||
551 | else |
||
552 | bparent->balance += 1; |
||
553 | |||
554 | node = bparent; |
||
555 | } |
||
556 | } |
||
557 | |||
558 | /** |
||
559 | * g_tree_remove: |
||
560 | * @tree: a #GTree |
||
561 | * @key: the key to remove |
||
562 | * |
||
563 | * Removes a key/value pair from a #GTree. |
||
564 | * |
||
565 | * If the #GTree was created using g_tree_new_full(), the key and value |
||
566 | * are freed using the supplied destroy functions, otherwise you have to |
||
567 | * make sure that any dynamically allocated values are freed yourself. |
||
568 | * If the key does not exist in the #GTree, the function does nothing. |
||
569 | * |
||
570 | * Returns: %TRUE if the key was found (prior to 2.8, this function |
||
571 | * returned nothing) |
||
572 | */ |
||
573 | gboolean |
||
574 | g_tree_remove (GTree *tree, |
||
575 | gconstpointer key) |
||
576 | { |
||
577 | gboolean removed; |
||
578 | |||
579 | g_return_val_if_fail (tree != NULL, FALSE); |
||
580 | |||
581 | removed = g_tree_remove_internal (tree, key, FALSE); |
||
582 | |||
583 | #ifdef G_TREE_DEBUG |
||
584 | g_tree_node_check (tree->root); |
||
585 | #endif |
||
586 | |||
587 | return removed; |
||
588 | } |
||
589 | |||
590 | /** |
||
591 | * g_tree_steal: |
||
592 | * @tree: a #GTree |
||
593 | * @key: the key to remove |
||
594 | * |
||
595 | * Removes a key and its associated value from a #GTree without calling |
||
596 | * the key and value destroy functions. |
||
597 | * |
||
598 | * If the key does not exist in the #GTree, the function does nothing. |
||
599 | * |
||
600 | * Returns: %TRUE if the key was found (prior to 2.8, this function |
||
601 | * returned nothing) |
||
602 | */ |
||
603 | gboolean |
||
604 | g_tree_steal (GTree *tree, |
||
605 | gconstpointer key) |
||
606 | { |
||
607 | gboolean removed; |
||
608 | |||
609 | g_return_val_if_fail (tree != NULL, FALSE); |
||
610 | |||
611 | removed = g_tree_remove_internal (tree, key, TRUE); |
||
612 | |||
613 | #ifdef G_TREE_DEBUG |
||
614 | g_tree_node_check (tree->root); |
||
615 | #endif |
||
616 | |||
617 | return removed; |
||
618 | } |
||
619 | |||
620 | /* internal remove routine */ |
||
621 | static gboolean |
||
622 | g_tree_remove_internal (GTree *tree, |
||
623 | gconstpointer key, |
||
624 | gboolean steal) |
||
625 | { |
||
626 | GTreeNode *node, *parent, *balance; |
||
627 | GTreeNode *path[MAX_GTREE_HEIGHT]; |
||
628 | int idx; |
||
629 | gboolean left_node; |
||
630 | |||
631 | g_return_val_if_fail (tree != NULL, FALSE); |
||
632 | |||
633 | if (!tree->root) |
||
634 | return FALSE; |
||
635 | |||
636 | idx = 0; |
||
637 | path[idx++] = NULL; |
||
638 | node = tree->root; |
||
639 | |||
640 | while (1) |
||
641 | { |
||
642 | int cmp = tree->key_compare (key, node->key, tree->key_compare_data); |
||
643 | |||
644 | if (cmp == 0) |
||
645 | break; |
||
646 | else if (cmp < 0) |
||
647 | { |
||
648 | if (!node->left_child) |
||
649 | return FALSE; |
||
650 | |||
651 | path[idx++] = node; |
||
652 | node = node->left; |
||
653 | } |
||
654 | else |
||
655 | { |
||
656 | if (!node->right_child) |
||
657 | return FALSE; |
||
658 | |||
659 | path[idx++] = node; |
||
660 | node = node->right; |
||
661 | } |
||
662 | } |
||
663 | |||
664 | /* The following code is almost equal to g_tree_remove_node, |
||
665 | * except that we do not have to call g_tree_node_parent. |
||
666 | */ |
||
667 | balance = parent = path[--idx]; |
||
668 | g_assert (!parent || parent->left == node || parent->right == node); |
||
669 | left_node = (parent && node == parent->left); |
||
670 | |||
671 | if (!node->left_child) |
||
672 | { |
||
673 | if (!node->right_child) |
||
674 | { |
||
675 | if (!parent) |
||
676 | tree->root = NULL; |
||
677 | else if (left_node) |
||
678 | { |
||
679 | parent->left_child = FALSE; |
||
680 | parent->left = node->left; |
||
681 | parent->balance += 1; |
||
682 | } |
||
683 | else |
||
684 | { |
||
685 | parent->right_child = FALSE; |
||
686 | parent->right = node->right; |
||
687 | parent->balance -= 1; |
||
688 | } |
||
689 | } |
||
690 | else /* node has a right child */ |
||
691 | { |
||
692 | GTreeNode *tmp = g_tree_node_next (node); |
||
693 | tmp->left = node->left; |
||
694 | |||
695 | if (!parent) |
||
696 | tree->root = node->right; |
||
697 | else if (left_node) |
||
698 | { |
||
699 | parent->left = node->right; |
||
700 | parent->balance += 1; |
||
701 | } |
||
702 | else |
||
703 | { |
||
704 | parent->right = node->right; |
||
705 | parent->balance -= 1; |
||
706 | } |
||
707 | } |
||
708 | } |
||
709 | else /* node has a left child */ |
||
710 | { |
||
711 | if (!node->right_child) |
||
712 | { |
||
713 | GTreeNode *tmp = g_tree_node_previous (node); |
||
714 | tmp->right = node->right; |
||
715 | |||
716 | if (parent == NULL) |
||
717 | tree->root = node->left; |
||
718 | else if (left_node) |
||
719 | { |
||
720 | parent->left = node->left; |
||
721 | parent->balance += 1; |
||
722 | } |
||
723 | else |
||
724 | { |
||
725 | parent->right = node->left; |
||
726 | parent->balance -= 1; |
||
727 | } |
||
728 | } |
||
729 | else /* node has a both children (pant, pant!) */ |
||
730 | { |
||
731 | GTreeNode *prev = node->left; |
||
732 | GTreeNode *next = node->right; |
||
733 | GTreeNode *nextp = node; |
||
734 | int old_idx = idx + 1; |
||
735 | idx++; |
||
736 | |||
737 | /* path[idx] == parent */ |
||
738 | /* find the immediately next node (and its parent) */ |
||
739 | while (next->left_child) |
||
740 | { |
||
741 | path[++idx] = nextp = next; |
||
742 | next = next->left; |
||
743 | } |
||
744 | |||
745 | path[old_idx] = next; |
||
746 | balance = path[idx]; |
||
747 | |||
748 | /* remove 'next' from the tree */ |
||
749 | if (nextp != node) |
||
750 | { |
||
751 | if (next->right_child) |
||
752 | nextp->left = next->right; |
||
753 | else |
||
754 | nextp->left_child = FALSE; |
||
755 | nextp->balance += 1; |
||
756 | |||
757 | next->right_child = TRUE; |
||
758 | next->right = node->right; |
||
759 | } |
||
760 | else |
||
761 | node->balance -= 1; |
||
762 | |||
763 | /* set the prev to point to the right place */ |
||
764 | while (prev->right_child) |
||
765 | prev = prev->right; |
||
766 | prev->right = next; |
||
767 | |||
768 | /* prepare 'next' to replace 'node' */ |
||
769 | next->left_child = TRUE; |
||
770 | next->left = node->left; |
||
771 | next->balance = node->balance; |
||
772 | |||
773 | if (!parent) |
||
774 | tree->root = next; |
||
775 | else if (left_node) |
||
776 | parent->left = next; |
||
777 | else |
||
778 | parent->right = next; |
||
779 | } |
||
780 | } |
||
781 | |||
782 | /* restore balance */ |
||
783 | if (balance) |
||
784 | while (1) |
||
785 | { |
||
786 | GTreeNode *bparent = path[--idx]; |
||
787 | g_assert (!bparent || bparent->left == balance || bparent->right == balance); |
||
788 | left_node = (bparent && balance == bparent->left); |
||
789 | |||
790 | if(balance->balance < -1 || balance->balance > 1) |
||
791 | { |
||
792 | balance = g_tree_node_balance (balance); |
||
793 | if (!bparent) |
||
794 | tree->root = balance; |
||
795 | else if (left_node) |
||
796 | bparent->left = balance; |
||
797 | else |
||
798 | bparent->right = balance; |
||
799 | } |
||
800 | |||
801 | if (balance->balance != 0 || !bparent) |
||
802 | break; |
||
803 | |||
804 | if (left_node) |
||
805 | bparent->balance += 1; |
||
806 | else |
||
807 | bparent->balance -= 1; |
||
808 | |||
809 | balance = bparent; |
||
810 | } |
||
811 | |||
812 | if (!steal) |
||
813 | { |
||
814 | if (tree->key_destroy_func) |
||
815 | tree->key_destroy_func (node->key); |
||
816 | if (tree->value_destroy_func) |
||
817 | tree->value_destroy_func (node->value); |
||
818 | } |
||
819 | |||
820 | g_slice_free (GTreeNode, node); |
||
821 | |||
822 | tree->nnodes--; |
||
823 | |||
824 | return TRUE; |
||
825 | } |
||
826 | |||
827 | /** |
||
828 | * g_tree_lookup: |
||
829 | * @tree: a #GTree |
||
830 | * @key: the key to look up |
||
831 | * |
||
832 | * Gets the value corresponding to the given key. Since a #GTree is |
||
833 | * automatically balanced as key/value pairs are added, key lookup |
||
834 | * is O(log n) (where n is the number of key/value pairs in the tree). |
||
835 | * |
||
836 | * Returns: the value corresponding to the key, or %NULL |
||
837 | * if the key was not found |
||
838 | */ |
||
839 | gpointer |
||
840 | g_tree_lookup (GTree *tree, |
||
841 | gconstpointer key) |
||
842 | { |
||
843 | GTreeNode *node; |
||
844 | |||
845 | g_return_val_if_fail (tree != NULL, NULL); |
||
846 | |||
847 | node = g_tree_find_node (tree, key); |
||
848 | |||
849 | return node ? node->value : NULL; |
||
850 | } |
||
851 | |||
852 | /** |
||
853 | * g_tree_lookup_extended: |
||
854 | * @tree: a #GTree |
||
855 | * @lookup_key: the key to look up |
||
856 | * @orig_key: (optional) (nullable): returns the original key |
||
857 | * @value: (optional) (nullable): returns the value associated with the key |
||
858 | * |
||
859 | * Looks up a key in the #GTree, returning the original key and the |
||
860 | * associated value. This is useful if you need to free the memory |
||
861 | * allocated for the original key, for example before calling |
||
862 | * g_tree_remove(). |
||
863 | * |
||
864 | * Returns: %TRUE if the key was found in the #GTree |
||
865 | */ |
||
866 | gboolean |
||
867 | g_tree_lookup_extended (GTree *tree, |
||
868 | gconstpointer lookup_key, |
||
869 | gpointer *orig_key, |
||
870 | gpointer *value) |
||
871 | { |
||
872 | GTreeNode *node; |
||
873 | |||
874 | g_return_val_if_fail (tree != NULL, FALSE); |
||
875 | |||
876 | node = g_tree_find_node (tree, lookup_key); |
||
877 | |||
878 | if (node) |
||
879 | { |
||
880 | if (orig_key) |
||
881 | *orig_key = node->key; |
||
882 | if (value) |
||
883 | *value = node->value; |
||
884 | return TRUE; |
||
885 | } |
||
886 | else |
||
887 | return FALSE; |
||
888 | } |
||
889 | |||
890 | /** |
||
891 | * g_tree_foreach: |
||
892 | * @tree: a #GTree |
||
893 | * @func: the function to call for each node visited. |
||
894 | * If this function returns %TRUE, the traversal is stopped. |
||
895 | * @user_data: user data to pass to the function |
||
896 | * |
||
897 | * Calls the given function for each of the key/value pairs in the #GTree. |
||
898 | * The function is passed the key and value of each pair, and the given |
||
899 | * @data parameter. The tree is traversed in sorted order. |
||
900 | * |
||
901 | * The tree may not be modified while iterating over it (you can't |
||
902 | * add/remove items). To remove all items matching a predicate, you need |
||
903 | * to add each item to a list in your #GTraverseFunc as you walk over |
||
904 | * the tree, then walk the list and remove each item. |
||
905 | */ |
||
906 | void |
||
907 | g_tree_foreach (GTree *tree, |
||
908 | GTraverseFunc func, |
||
909 | gpointer user_data) |
||
910 | { |
||
911 | GTreeNode *node; |
||
912 | |||
913 | g_return_if_fail (tree != NULL); |
||
914 | |||
915 | if (!tree->root) |
||
916 | return; |
||
917 | |||
918 | node = g_tree_first_node (tree); |
||
919 | |||
920 | while (node) |
||
921 | { |
||
922 | if ((*func) (node->key, node->value, user_data)) |
||
923 | break; |
||
924 | |||
925 | node = g_tree_node_next (node); |
||
926 | } |
||
927 | } |
||
928 | |||
929 | /** |
||
930 | * g_tree_traverse: |
||
931 | * @tree: a #GTree |
||
932 | * @traverse_func: the function to call for each node visited. If this |
||
933 | * function returns %TRUE, the traversal is stopped. |
||
934 | * @traverse_type: the order in which nodes are visited, one of %G_IN_ORDER, |
||
935 | * %G_PRE_ORDER and %G_POST_ORDER |
||
936 | * @user_data: user data to pass to the function |
||
937 | * |
||
938 | * Calls the given function for each node in the #GTree. |
||
939 | * |
||
940 | * Deprecated:2.2: The order of a balanced tree is somewhat arbitrary. |
||
941 | * If you just want to visit all nodes in sorted order, use |
||
942 | * g_tree_foreach() instead. If you really need to visit nodes in |
||
943 | * a different order, consider using an [n-ary tree][glib-N-ary-Trees]. |
||
944 | */ |
||
945 | /** |
||
946 | * GTraverseFunc: |
||
947 | * @key: a key of a #GTree node |
||
948 | * @value: the value corresponding to the key |
||
949 | * @data: user data passed to g_tree_traverse() |
||
950 | * |
||
951 | * Specifies the type of function passed to g_tree_traverse(). It is |
||
952 | * passed the key and value of each node, together with the @user_data |
||
953 | * parameter passed to g_tree_traverse(). If the function returns |
||
954 | * %TRUE, the traversal is stopped. |
||
955 | * |
||
956 | * Returns: %TRUE to stop the traversal |
||
957 | */ |
||
958 | void |
||
959 | g_tree_traverse (GTree *tree, |
||
960 | GTraverseFunc traverse_func, |
||
961 | GTraverseType traverse_type, |
||
962 | gpointer user_data) |
||
963 | { |
||
964 | g_return_if_fail (tree != NULL); |
||
965 | |||
966 | if (!tree->root) |
||
967 | return; |
||
968 | |||
969 | switch (traverse_type) |
||
970 | { |
||
971 | case G_PRE_ORDER: |
||
972 | g_tree_node_pre_order (tree->root, traverse_func, user_data); |
||
973 | break; |
||
974 | |||
975 | case G_IN_ORDER: |
||
976 | g_tree_node_in_order (tree->root, traverse_func, user_data); |
||
977 | break; |
||
978 | |||
979 | case G_POST_ORDER: |
||
980 | g_tree_node_post_order (tree->root, traverse_func, user_data); |
||
981 | break; |
||
982 | |||
983 | case G_LEVEL_ORDER: |
||
984 | g_warning ("g_tree_traverse(): traverse type G_LEVEL_ORDER isn't implemented."); |
||
985 | break; |
||
986 | } |
||
987 | } |
||
988 | |||
989 | /** |
||
990 | * g_tree_search: |
||
991 | * @tree: a #GTree |
||
992 | * @search_func: a function used to search the #GTree |
||
993 | * @user_data: the data passed as the second argument to @search_func |
||
994 | * |
||
995 | * Searches a #GTree using @search_func. |
||
996 | * |
||
997 | * The @search_func is called with a pointer to the key of a key/value |
||
998 | * pair in the tree, and the passed in @user_data. If @search_func returns |
||
999 | * 0 for a key/value pair, then the corresponding value is returned as |
||
1000 | * the result of g_tree_search(). If @search_func returns -1, searching |
||
1001 | * will proceed among the key/value pairs that have a smaller key; if |
||
1002 | * @search_func returns 1, searching will proceed among the key/value |
||
1003 | * pairs that have a larger key. |
||
1004 | * |
||
1005 | * Returns: the value corresponding to the found key, or %NULL |
||
1006 | * if the key was not found |
||
1007 | */ |
||
1008 | gpointer |
||
1009 | g_tree_search (GTree *tree, |
||
1010 | GCompareFunc search_func, |
||
1011 | gconstpointer user_data) |
||
1012 | { |
||
1013 | g_return_val_if_fail (tree != NULL, NULL); |
||
1014 | |||
1015 | if (tree->root) |
||
1016 | return g_tree_node_search (tree->root, search_func, user_data); |
||
1017 | else |
||
1018 | return NULL; |
||
1019 | } |
||
1020 | |||
1021 | /** |
||
1022 | * g_tree_height: |
||
1023 | * @tree: a #GTree |
||
1024 | * |
||
1025 | * Gets the height of a #GTree. |
||
1026 | * |
||
1027 | * If the #GTree contains no nodes, the height is 0. |
||
1028 | * If the #GTree contains only one root node the height is 1. |
||
1029 | * If the root node has children the height is 2, etc. |
||
1030 | * |
||
1031 | * Returns: the height of @tree |
||
1032 | */ |
||
1033 | gint |
||
1034 | g_tree_height (GTree *tree) |
||
1035 | { |
||
1036 | GTreeNode *node; |
||
1037 | gint height; |
||
1038 | |||
1039 | g_return_val_if_fail (tree != NULL, 0); |
||
1040 | |||
1041 | if (!tree->root) |
||
1042 | return 0; |
||
1043 | |||
1044 | height = 0; |
||
1045 | node = tree->root; |
||
1046 | |||
1047 | while (1) |
||
1048 | { |
||
1049 | height += 1 + MAX(node->balance, 0); |
||
1050 | |||
1051 | if (!node->left_child) |
||
1052 | return height; |
||
1053 | |||
1054 | node = node->left; |
||
1055 | } |
||
1056 | } |
||
1057 | |||
1058 | /** |
||
1059 | * g_tree_nnodes: |
||
1060 | * @tree: a #GTree |
||
1061 | * |
||
1062 | * Gets the number of nodes in a #GTree. |
||
1063 | * |
||
1064 | * Returns: the number of nodes in @tree |
||
1065 | */ |
||
1066 | gint |
||
1067 | g_tree_nnodes (GTree *tree) |
||
1068 | { |
||
1069 | g_return_val_if_fail (tree != NULL, 0); |
||
1070 | |||
1071 | return tree->nnodes; |
||
1072 | } |
||
1073 | |||
1074 | static GTreeNode * |
||
1075 | g_tree_node_balance (GTreeNode *node) |
||
1076 | { |
||
1077 | if (node->balance < -1) |
||
1078 | { |
||
1079 | if (node->left->balance > 0) |
||
1080 | node->left = g_tree_node_rotate_left (node->left); |
||
1081 | node = g_tree_node_rotate_right (node); |
||
1082 | } |
||
1083 | else if (node->balance > 1) |
||
1084 | { |
||
1085 | if (node->right->balance < 0) |
||
1086 | node->right = g_tree_node_rotate_right (node->right); |
||
1087 | node = g_tree_node_rotate_left (node); |
||
1088 | } |
||
1089 | |||
1090 | return node; |
||
1091 | } |
||
1092 | |||
1093 | static GTreeNode * |
||
1094 | g_tree_find_node (GTree *tree, |
||
1095 | gconstpointer key) |
||
1096 | { |
||
1097 | GTreeNode *node; |
||
1098 | gint cmp; |
||
1099 | |||
1100 | node = tree->root; |
||
1101 | if (!node) |
||
1102 | return NULL; |
||
1103 | |||
1104 | while (1) |
||
1105 | { |
||
1106 | cmp = tree->key_compare (key, node->key, tree->key_compare_data); |
||
1107 | if (cmp == 0) |
||
1108 | return node; |
||
1109 | else if (cmp < 0) |
||
1110 | { |
||
1111 | if (!node->left_child) |
||
1112 | return NULL; |
||
1113 | |||
1114 | node = node->left; |
||
1115 | } |
||
1116 | else |
||
1117 | { |
||
1118 | if (!node->right_child) |
||
1119 | return NULL; |
||
1120 | |||
1121 | node = node->right; |
||
1122 | } |
||
1123 | } |
||
1124 | } |
||
1125 | |||
1126 | static gint |
||
1127 | g_tree_node_pre_order (GTreeNode *node, |
||
1128 | GTraverseFunc traverse_func, |
||
1129 | gpointer data) |
||
1130 | { |
||
1131 | if ((*traverse_func) (node->key, node->value, data)) |
||
1132 | return TRUE; |
||
1133 | |||
1134 | if (node->left_child) |
||
1135 | { |
||
1136 | if (g_tree_node_pre_order (node->left, traverse_func, data)) |
||
1137 | return TRUE; |
||
1138 | } |
||
1139 | |||
1140 | if (node->right_child) |
||
1141 | { |
||
1142 | if (g_tree_node_pre_order (node->right, traverse_func, data)) |
||
1143 | return TRUE; |
||
1144 | } |
||
1145 | |||
1146 | return FALSE; |
||
1147 | } |
||
1148 | |||
1149 | static gint |
||
1150 | g_tree_node_in_order (GTreeNode *node, |
||
1151 | GTraverseFunc traverse_func, |
||
1152 | gpointer data) |
||
1153 | { |
||
1154 | if (node->left_child) |
||
1155 | { |
||
1156 | if (g_tree_node_in_order (node->left, traverse_func, data)) |
||
1157 | return TRUE; |
||
1158 | } |
||
1159 | |||
1160 | if ((*traverse_func) (node->key, node->value, data)) |
||
1161 | return TRUE; |
||
1162 | |||
1163 | if (node->right_child) |
||
1164 | { |
||
1165 | if (g_tree_node_in_order (node->right, traverse_func, data)) |
||
1166 | return TRUE; |
||
1167 | } |
||
1168 | |||
1169 | return FALSE; |
||
1170 | } |
||
1171 | |||
1172 | static gint |
||
1173 | g_tree_node_post_order (GTreeNode *node, |
||
1174 | GTraverseFunc traverse_func, |
||
1175 | gpointer data) |
||
1176 | { |
||
1177 | if (node->left_child) |
||
1178 | { |
||
1179 | if (g_tree_node_post_order (node->left, traverse_func, data)) |
||
1180 | return TRUE; |
||
1181 | } |
||
1182 | |||
1183 | if (node->right_child) |
||
1184 | { |
||
1185 | if (g_tree_node_post_order (node->right, traverse_func, data)) |
||
1186 | return TRUE; |
||
1187 | } |
||
1188 | |||
1189 | if ((*traverse_func) (node->key, node->value, data)) |
||
1190 | return TRUE; |
||
1191 | |||
1192 | return FALSE; |
||
1193 | } |
||
1194 | |||
1195 | static gpointer |
||
1196 | g_tree_node_search (GTreeNode *node, |
||
1197 | GCompareFunc search_func, |
||
1198 | gconstpointer data) |
||
1199 | { |
||
1200 | gint dir; |
||
1201 | |||
1202 | if (!node) |
||
1203 | return NULL; |
||
1204 | |||
1205 | while (1) |
||
1206 | { |
||
1207 | dir = (* search_func) (node->key, data); |
||
1208 | if (dir == 0) |
||
1209 | return node->value; |
||
1210 | else if (dir < 0) |
||
1211 | { |
||
1212 | if (!node->left_child) |
||
1213 | return NULL; |
||
1214 | |||
1215 | node = node->left; |
||
1216 | } |
||
1217 | else |
||
1218 | { |
||
1219 | if (!node->right_child) |
||
1220 | return NULL; |
||
1221 | |||
1222 | node = node->right; |
||
1223 | } |
||
1224 | } |
||
1225 | } |
||
1226 | |||
1227 | static GTreeNode * |
||
1228 | g_tree_node_rotate_left (GTreeNode *node) |
||
1229 | { |
||
1230 | GTreeNode *right; |
||
1231 | gint a_bal; |
||
1232 | gint b_bal; |
||
1233 | |||
1234 | right = node->right; |
||
1235 | |||
1236 | if (right->left_child) |
||
1237 | node->right = right->left; |
||
1238 | else |
||
1239 | { |
||
1240 | node->right_child = FALSE; |
||
1241 | right->left_child = TRUE; |
||
1242 | } |
||
1243 | right->left = node; |
||
1244 | |||
1245 | a_bal = node->balance; |
||
1246 | b_bal = right->balance; |
||
1247 | |||
1248 | if (b_bal <= 0) |
||
1249 | { |
||
1250 | if (a_bal >= 1) |
||
1251 | right->balance = b_bal - 1; |
||
1252 | else |
||
1253 | right->balance = a_bal + b_bal - 2; |
||
1254 | node->balance = a_bal - 1; |
||
1255 | } |
||
1256 | else |
||
1257 | { |
||
1258 | if (a_bal <= b_bal) |
||
1259 | right->balance = a_bal - 2; |
||
1260 | else |
||
1261 | right->balance = b_bal - 1; |
||
1262 | node->balance = a_bal - b_bal - 1; |
||
1263 | } |
||
1264 | |||
1265 | return right; |
||
1266 | } |
||
1267 | |||
1268 | static GTreeNode * |
||
1269 | g_tree_node_rotate_right (GTreeNode *node) |
||
1270 | { |
||
1271 | GTreeNode *left; |
||
1272 | gint a_bal; |
||
1273 | gint b_bal; |
||
1274 | |||
1275 | left = node->left; |
||
1276 | |||
1277 | if (left->right_child) |
||
1278 | node->left = left->right; |
||
1279 | else |
||
1280 | { |
||
1281 | node->left_child = FALSE; |
||
1282 | left->right_child = TRUE; |
||
1283 | } |
||
1284 | left->right = node; |
||
1285 | |||
1286 | a_bal = node->balance; |
||
1287 | b_bal = left->balance; |
||
1288 | |||
1289 | if (b_bal <= 0) |
||
1290 | { |
||
1291 | if (b_bal > a_bal) |
||
1292 | left->balance = b_bal + 1; |
||
1293 | else |
||
1294 | left->balance = a_bal + 2; |
||
1295 | node->balance = a_bal - b_bal + 1; |
||
1296 | } |
||
1297 | else |
||
1298 | { |
||
1299 | if (a_bal <= -1) |
||
1300 | left->balance = b_bal + 1; |
||
1301 | else |
||
1302 | left->balance = a_bal + b_bal + 2; |
||
1303 | node->balance = a_bal + 1; |
||
1304 | } |
||
1305 | |||
1306 | return left; |
||
1307 | } |
||
1308 | |||
1309 | #ifdef G_TREE_DEBUG |
||
1310 | static gint |
||
1311 | g_tree_node_height (GTreeNode *node) |
||
1312 | { |
||
1313 | gint left_height; |
||
1314 | gint right_height; |
||
1315 | |||
1316 | if (node) |
||
1317 | { |
||
1318 | left_height = 0; |
||
1319 | right_height = 0; |
||
1320 | |||
1321 | if (node->left_child) |
||
1322 | left_height = g_tree_node_height (node->left); |
||
1323 | |||
1324 | if (node->right_child) |
||
1325 | right_height = g_tree_node_height (node->right); |
||
1326 | |||
1327 | return MAX (left_height, right_height) + 1; |
||
1328 | } |
||
1329 | |||
1330 | return 0; |
||
1331 | } |
||
1332 | |||
1333 | static void |
||
1334 | g_tree_node_check (GTreeNode *node) |
||
1335 | { |
||
1336 | gint left_height; |
||
1337 | gint right_height; |
||
1338 | gint balance; |
||
1339 | GTreeNode *tmp; |
||
1340 | |||
1341 | if (node) |
||
1342 | { |
||
1343 | if (node->left_child) |
||
1344 | { |
||
1345 | tmp = g_tree_node_previous (node); |
||
1346 | g_assert (tmp->right == node); |
||
1347 | } |
||
1348 | |||
1349 | if (node->right_child) |
||
1350 | { |
||
1351 | tmp = g_tree_node_next (node); |
||
1352 | g_assert (tmp->left == node); |
||
1353 | } |
||
1354 | |||
1355 | left_height = 0; |
||
1356 | right_height = 0; |
||
1357 | |||
1358 | if (node->left_child) |
||
1359 | left_height = g_tree_node_height (node->left); |
||
1360 | if (node->right_child) |
||
1361 | right_height = g_tree_node_height (node->right); |
||
1362 | |||
1363 | balance = right_height - left_height; |
||
1364 | g_assert (balance == node->balance); |
||
1365 | |||
1366 | if (node->left_child) |
||
1367 | g_tree_node_check (node->left); |
||
1368 | if (node->right_child) |
||
1369 | g_tree_node_check (node->right); |
||
1370 | } |
||
1371 | } |
||
1372 | |||
1373 | static void |
||
1374 | g_tree_node_dump (GTreeNode *node, |
||
1375 | gint indent) |
||
1376 | { |
||
1377 | g_print ("%*s%c\n", indent, "", *(char *)node->key); |
||
1378 | |||
1379 | if (node->left_child) |
||
1380 | g_tree_node_dump (node->left, indent + 2); |
||
1381 | else if (node->left) |
||
1382 | g_print ("%*s<%c\n", indent + 2, "", *(char *)node->left->key); |
||
1383 | |||
1384 | if (node->right_child) |
||
1385 | g_tree_node_dump (node->right, indent + 2); |
||
1386 | else if (node->right) |
||
1387 | g_print ("%*s>%c\n", indent + 2, "", *(char *)node->right->key); |
||
1388 | } |
||
1389 | |||
1390 | |||
1391 | void |
||
1392 | g_tree_dump (GTree *tree) |
||
1393 | { |
||
1394 | if (tree->root) |
||
1395 | g_tree_node_dump (tree->root, 0); |
||
1396 | } |
||
1397 | #endif |