diff options
author | Tim Janik <timj@gtk.org> | 1998-07-31 20:21:10 +0000 |
---|---|---|
committer | Tim Janik <timj@src.gnome.org> | 1998-07-31 20:21:10 +0000 |
commit | 5272119ce910740d98415f70c662bbb8942a4a5f (patch) | |
tree | d982d8b5bf9347600720a85e28e734468bbe49cb | |
parent | e502a2345c2a1bb3868389578e2dc83fe0416b67 (diff) |
added a GNode test.
Fri Jul 31 22:17:05 1998 Tim Janik <timj@gtk.org>
* testglib.c (g_node_test): added a GNode test.
Fri Jul 31 09:08:16 1998 Tim Janik <timj@gtk.org>
* Makefile.am: compile gnode.c.
* glib.h:
* gnode.c: added implementation of n-way trees.
* gtree.c (g_tree_traverse): added a warning to the switch() statement
which says that G_LEVEL_ORDER is not implemented.
-rw-r--r-- | ChangeLog | 14 | ||||
-rw-r--r-- | ChangeLog.pre-2-0 | 14 | ||||
-rw-r--r-- | ChangeLog.pre-2-10 | 14 | ||||
-rw-r--r-- | ChangeLog.pre-2-12 | 14 | ||||
-rw-r--r-- | ChangeLog.pre-2-2 | 14 | ||||
-rw-r--r-- | ChangeLog.pre-2-4 | 14 | ||||
-rw-r--r-- | ChangeLog.pre-2-6 | 14 | ||||
-rw-r--r-- | ChangeLog.pre-2-8 | 14 | ||||
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | glib.h | 111 | ||||
-rw-r--r-- | glib/Makefile.am | 1 | ||||
-rw-r--r-- | glib/glib.h | 111 | ||||
-rw-r--r-- | glib/gnode.c | 887 | ||||
-rw-r--r-- | glib/gtree.c | 4 | ||||
-rw-r--r-- | gnode.c | 887 | ||||
-rw-r--r-- | gtree.c | 4 | ||||
-rw-r--r-- | testglib.c | 158 | ||||
-rw-r--r-- | tests/testglib.c | 158 |
18 files changed, 2428 insertions, 6 deletions
@@ -1,3 +1,17 @@ +Fri Jul 31 22:17:05 1998 Tim Janik <timj@gtk.org> + + * testglib.c (g_node_test): added a GNode test. + +Fri Jul 31 09:08:16 1998 Tim Janik <timj@gtk.org> + + * Makefile.am: compile gnode.c. + + * glib.h: + * gnode.c: added implementation of n-way trees. + + * gtree.c (g_tree_traverse): added a warning to the switch() statement + which says that G_LEVEL_ORDER is not implemented. + Mon Jul 27 00:17:30 CDT 1998 Shawn T. Amundson <amundson@gtk.org> * Released GLib 1.1.0 diff --git a/ChangeLog.pre-2-0 b/ChangeLog.pre-2-0 index e334e34f9..9ba7458ca 100644 --- a/ChangeLog.pre-2-0 +++ b/ChangeLog.pre-2-0 @@ -1,3 +1,17 @@ +Fri Jul 31 22:17:05 1998 Tim Janik <timj@gtk.org> + + * testglib.c (g_node_test): added a GNode test. + +Fri Jul 31 09:08:16 1998 Tim Janik <timj@gtk.org> + + * Makefile.am: compile gnode.c. + + * glib.h: + * gnode.c: added implementation of n-way trees. + + * gtree.c (g_tree_traverse): added a warning to the switch() statement + which says that G_LEVEL_ORDER is not implemented. + Mon Jul 27 00:17:30 CDT 1998 Shawn T. Amundson <amundson@gtk.org> * Released GLib 1.1.0 diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index e334e34f9..9ba7458ca 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,17 @@ +Fri Jul 31 22:17:05 1998 Tim Janik <timj@gtk.org> + + * testglib.c (g_node_test): added a GNode test. + +Fri Jul 31 09:08:16 1998 Tim Janik <timj@gtk.org> + + * Makefile.am: compile gnode.c. + + * glib.h: + * gnode.c: added implementation of n-way trees. + + * gtree.c (g_tree_traverse): added a warning to the switch() statement + which says that G_LEVEL_ORDER is not implemented. + Mon Jul 27 00:17:30 CDT 1998 Shawn T. Amundson <amundson@gtk.org> * Released GLib 1.1.0 diff --git a/ChangeLog.pre-2-12 b/ChangeLog.pre-2-12 index e334e34f9..9ba7458ca 100644 --- a/ChangeLog.pre-2-12 +++ b/ChangeLog.pre-2-12 @@ -1,3 +1,17 @@ +Fri Jul 31 22:17:05 1998 Tim Janik <timj@gtk.org> + + * testglib.c (g_node_test): added a GNode test. + +Fri Jul 31 09:08:16 1998 Tim Janik <timj@gtk.org> + + * Makefile.am: compile gnode.c. + + * glib.h: + * gnode.c: added implementation of n-way trees. + + * gtree.c (g_tree_traverse): added a warning to the switch() statement + which says that G_LEVEL_ORDER is not implemented. + Mon Jul 27 00:17:30 CDT 1998 Shawn T. Amundson <amundson@gtk.org> * Released GLib 1.1.0 diff --git a/ChangeLog.pre-2-2 b/ChangeLog.pre-2-2 index e334e34f9..9ba7458ca 100644 --- a/ChangeLog.pre-2-2 +++ b/ChangeLog.pre-2-2 @@ -1,3 +1,17 @@ +Fri Jul 31 22:17:05 1998 Tim Janik <timj@gtk.org> + + * testglib.c (g_node_test): added a GNode test. + +Fri Jul 31 09:08:16 1998 Tim Janik <timj@gtk.org> + + * Makefile.am: compile gnode.c. + + * glib.h: + * gnode.c: added implementation of n-way trees. + + * gtree.c (g_tree_traverse): added a warning to the switch() statement + which says that G_LEVEL_ORDER is not implemented. + Mon Jul 27 00:17:30 CDT 1998 Shawn T. Amundson <amundson@gtk.org> * Released GLib 1.1.0 diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index e334e34f9..9ba7458ca 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -1,3 +1,17 @@ +Fri Jul 31 22:17:05 1998 Tim Janik <timj@gtk.org> + + * testglib.c (g_node_test): added a GNode test. + +Fri Jul 31 09:08:16 1998 Tim Janik <timj@gtk.org> + + * Makefile.am: compile gnode.c. + + * glib.h: + * gnode.c: added implementation of n-way trees. + + * gtree.c (g_tree_traverse): added a warning to the switch() statement + which says that G_LEVEL_ORDER is not implemented. + Mon Jul 27 00:17:30 CDT 1998 Shawn T. Amundson <amundson@gtk.org> * Released GLib 1.1.0 diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index e334e34f9..9ba7458ca 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,17 @@ +Fri Jul 31 22:17:05 1998 Tim Janik <timj@gtk.org> + + * testglib.c (g_node_test): added a GNode test. + +Fri Jul 31 09:08:16 1998 Tim Janik <timj@gtk.org> + + * Makefile.am: compile gnode.c. + + * glib.h: + * gnode.c: added implementation of n-way trees. + + * gtree.c (g_tree_traverse): added a warning to the switch() statement + which says that G_LEVEL_ORDER is not implemented. + Mon Jul 27 00:17:30 CDT 1998 Shawn T. Amundson <amundson@gtk.org> * Released GLib 1.1.0 diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index e334e34f9..9ba7458ca 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,17 @@ +Fri Jul 31 22:17:05 1998 Tim Janik <timj@gtk.org> + + * testglib.c (g_node_test): added a GNode test. + +Fri Jul 31 09:08:16 1998 Tim Janik <timj@gtk.org> + + * Makefile.am: compile gnode.c. + + * glib.h: + * gnode.c: added implementation of n-way trees. + + * gtree.c (g_tree_traverse): added a warning to the switch() statement + which says that G_LEVEL_ORDER is not implemented. + Mon Jul 27 00:17:30 CDT 1998 Shawn T. Amundson <amundson@gtk.org> * Released GLib 1.1.0 diff --git a/Makefile.am b/Makefile.am index 7bcc118cd..50faf48ef 100644 --- a/Makefile.am +++ b/Makefile.am @@ -20,6 +20,7 @@ libglib_1_1_la_SOURCES = \ glist.c \ gmem.c \ gmessages.c \ + gnode.c \ gprimes.c \ gslist.c \ gtimer.c \ @@ -475,6 +475,7 @@ typedef union _GValue GValue; typedef struct _GCompletion GCompletion; typedef struct _GRelation GRelation; typedef struct _GTuples GTuples; +typedef struct _GNode GNode; typedef void (*GFunc) (gpointer data, @@ -488,6 +489,10 @@ typedef void (*GCacheDestroyFunc) (gpointer value); typedef gint (*GTraverseFunc) (gpointer key, gpointer value, gpointer data); +typedef gboolean (*GNodeTraverseFunc) (GNode *node, + gpointer data); +typedef void (*GNodeForeachFunc) (GNode *node, + gpointer data); typedef gint (*GSearchFunc) (gpointer key, gpointer data); typedef void (*GErrorFunc) (gchar *str); @@ -562,9 +567,18 @@ typedef enum { G_IN_ORDER, G_PRE_ORDER, - G_POST_ORDER + G_POST_ORDER, + G_LEVEL_ORDER } GTraverseType; +typedef enum +{ + G_TRAVERSE_LEAFS = 1 << 0, + G_TRAVERSE_NON_LEAFS = 1 << 1, + G_TRAVERSE_ALL = G_TRAVERSE_LEAFS | G_TRAVERSE_NON_LEAFS, + G_TRAVERSE_MASK = 0x03 +} GTraverseFlags; + /* Doubly linked lists */ GList* g_list_alloc (void); @@ -707,7 +721,7 @@ void g_cache_value_foreach (GCache *cache, gpointer user_data); -/* Trees +/* Balanced binary trees */ GTree* g_tree_new (GCompareFunc key_compare_func); void g_tree_destroy (GTree *tree); @@ -729,6 +743,99 @@ gint g_tree_height (GTree *tree); gint g_tree_nnodes (GTree *tree); + +/* N-way tree implementation + */ +struct _GNode +{ + GNode *prev; + GNode *next; + GNode *parent; + GNode *children; + + gpointer data; +}; + +#define G_NODE_IS_ROOT(node) (((GNode*) (node))->parent == NULL && \ + ((GNode*) (node))->prev == NULL && \ + ((GNode*) (node))->next == NULL) +#define G_NODE_IS_LEAF(node) (((GNode*) (node))->children == NULL) + +GNode* g_node_new (gpointer data); +void g_node_destroy (GNode *root); +void g_node_unlink (GNode *node); +void g_node_insert (GNode *parent, + gint position, + GNode *node); +void g_node_insert_before (GNode *parent, + GNode *sibling, + GNode *node); +void g_node_prepend (GNode *parent, + GNode *node); +guint g_node_n_nodes (GNode *root, + GTraverseFlags flags); +GNode* g_node_get_root (GNode *node); +gboolean g_node_is_ancestor (GNode *node, + GNode *descendant); +guint g_node_depth (GNode *node); +GNode* g_node_find (GNode *root, + GTraverseType order, + GTraverseFlags flags, + gpointer data); + +/* traversal function, assumes that `node' is root + * (only traverses `node' and its subtree). + * this function is just a high level interface to + * low level traversal functions, optimized for speed. + */ +void g_node_traverse (GNode *root, + GTraverseType order, + GTraverseFlags flags, + gint max_depth, + GNodeTraverseFunc func, + gpointer data); + +/* return the maximum tree height starting with `node', this is an expensive + * operation, since we need to visit all nodes. this could be shortened by + * adding `guint height' to struct _GNode, but then again, this is not very + * often needed, and would make g_node_insert() more time consuming. + */ +guint g_node_max_height (GNode *root); + +void g_node_children_foreach (GNode *node, + GTraverseFlags flags, + GNodeForeachFunc func, + gpointer data); +void g_node_reverse_children (GNode *node); +guint g_node_n_children (GNode *node); +GNode* g_node_nth_child (GNode *node, + guint n); +GNode* g_node_last_child (GNode *node); +GNode* g_node_find_child (GNode *node, + GTraverseFlags flags, + gpointer data); +gint g_node_child_position (GNode *node, + GNode *child); +gint g_node_child_index (GNode *node, + gpointer data); + +GNode* g_node_first_sibling (GNode *node); +GNode* g_node_last_sibling (GNode *node); + +#define g_node_prev_sibling(node) ((node) ? \ + ((GNode*) (node))->prev : NULL) +#define g_node_next_sibling(node) ((node) ? \ + ((GNode*) (node))->next : NULL) +#define g_node_first_child(node) ((node) ? \ + ((GNode*) (node))->children : NULL) +#define g_node_append(parent, node) G_STMT_START { \ + g_node_insert_before ((parent), \ + NULL, \ + (node)); \ + } G_STMT_END + + + /* Memory */ diff --git a/glib/Makefile.am b/glib/Makefile.am index 7bcc118cd..50faf48ef 100644 --- a/glib/Makefile.am +++ b/glib/Makefile.am @@ -20,6 +20,7 @@ libglib_1_1_la_SOURCES = \ glist.c \ gmem.c \ gmessages.c \ + gnode.c \ gprimes.c \ gslist.c \ gtimer.c \ diff --git a/glib/glib.h b/glib/glib.h index 69ad8adcb..b48ed1108 100644 --- a/glib/glib.h +++ b/glib/glib.h @@ -475,6 +475,7 @@ typedef union _GValue GValue; typedef struct _GCompletion GCompletion; typedef struct _GRelation GRelation; typedef struct _GTuples GTuples; +typedef struct _GNode GNode; typedef void (*GFunc) (gpointer data, @@ -488,6 +489,10 @@ typedef void (*GCacheDestroyFunc) (gpointer value); typedef gint (*GTraverseFunc) (gpointer key, gpointer value, gpointer data); +typedef gboolean (*GNodeTraverseFunc) (GNode *node, + gpointer data); +typedef void (*GNodeForeachFunc) (GNode *node, + gpointer data); typedef gint (*GSearchFunc) (gpointer key, gpointer data); typedef void (*GErrorFunc) (gchar *str); @@ -562,9 +567,18 @@ typedef enum { G_IN_ORDER, G_PRE_ORDER, - G_POST_ORDER + G_POST_ORDER, + G_LEVEL_ORDER } GTraverseType; +typedef enum +{ + G_TRAVERSE_LEAFS = 1 << 0, + G_TRAVERSE_NON_LEAFS = 1 << 1, + G_TRAVERSE_ALL = G_TRAVERSE_LEAFS | G_TRAVERSE_NON_LEAFS, + G_TRAVERSE_MASK = 0x03 +} GTraverseFlags; + /* Doubly linked lists */ GList* g_list_alloc (void); @@ -707,7 +721,7 @@ void g_cache_value_foreach (GCache *cache, gpointer user_data); -/* Trees +/* Balanced binary trees */ GTree* g_tree_new (GCompareFunc key_compare_func); void g_tree_destroy (GTree *tree); @@ -729,6 +743,99 @@ gint g_tree_height (GTree *tree); gint g_tree_nnodes (GTree *tree); + +/* N-way tree implementation + */ +struct _GNode +{ + GNode *prev; + GNode *next; + GNode *parent; + GNode *children; + + gpointer data; +}; + +#define G_NODE_IS_ROOT(node) (((GNode*) (node))->parent == NULL && \ + ((GNode*) (node))->prev == NULL && \ + ((GNode*) (node))->next == NULL) +#define G_NODE_IS_LEAF(node) (((GNode*) (node))->children == NULL) + +GNode* g_node_new (gpointer data); +void g_node_destroy (GNode *root); +void g_node_unlink (GNode *node); +void g_node_insert (GNode *parent, + gint position, + GNode *node); +void g_node_insert_before (GNode *parent, + GNode *sibling, + GNode *node); +void g_node_prepend (GNode *parent, + GNode *node); +guint g_node_n_nodes (GNode *root, + GTraverseFlags flags); +GNode* g_node_get_root (GNode *node); +gboolean g_node_is_ancestor (GNode *node, + GNode *descendant); +guint g_node_depth (GNode *node); +GNode* g_node_find (GNode *root, + GTraverseType order, + GTraverseFlags flags, + gpointer data); + +/* traversal function, assumes that `node' is root + * (only traverses `node' and its subtree). + * this function is just a high level interface to + * low level traversal functions, optimized for speed. + */ +void g_node_traverse (GNode *root, + GTraverseType order, + GTraverseFlags flags, + gint max_depth, + GNodeTraverseFunc func, + gpointer data); + +/* return the maximum tree height starting with `node', this is an expensive + * operation, since we need to visit all nodes. this could be shortened by + * adding `guint height' to struct _GNode, but then again, this is not very + * often needed, and would make g_node_insert() more time consuming. + */ +guint g_node_max_height (GNode *root); + +void g_node_children_foreach (GNode *node, + GTraverseFlags flags, + GNodeForeachFunc func, + gpointer data); +void g_node_reverse_children (GNode *node); +guint g_node_n_children (GNode *node); +GNode* g_node_nth_child (GNode *node, + guint n); +GNode* g_node_last_child (GNode *node); +GNode* g_node_find_child (GNode *node, + GTraverseFlags flags, + gpointer data); +gint g_node_child_position (GNode *node, + GNode *child); +gint g_node_child_index (GNode *node, + gpointer data); + +GNode* g_node_first_sibling (GNode *node); +GNode* g_node_last_sibling (GNode *node); + +#define g_node_prev_sibling(node) ((node) ? \ + ((GNode*) (node))->prev : NULL) +#define g_node_next_sibling(node) ((node) ? \ + ((GNode*) (node))->next : NULL) +#define g_node_first_child(node) ((node) ? \ + ((GNode*) (node))->children : NULL) +#define g_node_append(parent, node) G_STMT_START { \ + g_node_insert_before ((parent), \ + NULL, \ + (node)); \ + } G_STMT_END + + + /* Memory */ diff --git a/glib/gnode.c b/glib/gnode.c new file mode 100644 index 000000000..2279de212 --- /dev/null +++ b/glib/gnode.c @@ -0,0 +1,887 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * GNode: N-way tree implementation. + * Copyright (C) 1998 Tim Janik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include "glib.h" + + +#define KEEP_NODES (1024) + + +/* --- variables --- */ +static GMemChunk *g_tree_node_chunk = NULL; +static GNode *free_nodes = NULL; +static guint n_free_nodes = 0; + + +/* --- functions --- */ +GNode* +g_node_new (gpointer data) +{ + register GNode *node; + + if (!g_tree_node_chunk) + g_tree_node_chunk = g_mem_chunk_create (GNode, 1024, G_ALLOC_AND_FREE); + + if (n_free_nodes) + { + node = free_nodes; + free_nodes = free_nodes->next; + n_free_nodes--; + } + else + node = g_chunk_new (GNode, g_tree_node_chunk); + + node->prev = NULL; + node->next = NULL; + node->parent = NULL; + node->children = NULL; + node->data = data; + + return node; +} + +static void +g_node_free (GNode *parent) +{ + GNode *node; + + node = parent->children; + + while (node) + { + register GNode *free_node; + + free_node = node; + node = free_node->next; + g_node_free (free_node); + } + + if (n_free_nodes < KEEP_NODES) + { + parent->next = free_nodes; + free_nodes = parent; + n_free_nodes++; + } + else + g_chunk_free (parent, g_tree_node_chunk); +} + +void +g_node_destroy (GNode *root) +{ + g_return_if_fail (root != NULL); + + if (!G_NODE_IS_ROOT (root)) + g_node_unlink (root); + + g_node_free (root); +} + +void +g_node_unlink (GNode *node) +{ + g_return_if_fail (node != NULL); + + if (node->prev) + node->prev->next = node->next; + else if (node->parent) + node->parent->children = node->next; + node->parent = NULL; + if (node->next) + { + node->next->prev = node->prev; + node->next = NULL; + } + node->prev = NULL; +} + +void +g_node_insert (GNode *parent, + gint position, + GNode *node) +{ + g_return_if_fail (parent != NULL); + g_return_if_fail (node != NULL); + g_return_if_fail (G_NODE_IS_ROOT (node)); + + if (position > 0) + g_node_insert_before (parent, + g_node_nth_child (parent, position), + node); + else if (position == 0) + g_node_prepend (parent, node); + else if (position < 0) + g_node_append (parent, node); +} + +void +g_node_insert_before (GNode *parent, + GNode *sibling, + GNode *node) +{ + g_return_if_fail (parent != NULL); + g_return_if_fail (node != NULL); + g_return_if_fail (G_NODE_IS_ROOT (node)); + if (sibling) + g_return_if_fail (sibling->parent == parent); + + node->parent = parent; + + if (sibling) + { + if (sibling->prev) + { + node->prev = sibling->prev; + node->prev->next = node; + node->next = sibling; + sibling->prev = node; + } + else + { + node->parent->children = node; + node->next = sibling; + sibling->prev = node; + } + } + else + { + if (parent->children) + { + sibling = parent->children; + while (sibling->next) + sibling = sibling->next; + node->prev = sibling; + sibling->next = node; + } + else + node->parent->children = node; + } +} + +void +g_node_prepend (GNode *parent, + GNode *node) +{ + g_return_if_fail (parent != NULL); + + g_node_insert_before (parent, parent->children, node); +} + +GNode* +g_node_get_root (GNode *node) +{ + g_return_val_if_fail (node != NULL, NULL); + + while (node->parent) + node = node->parent; + + return node; +} + +gboolean +g_node_is_ancestor (GNode *node, + GNode *descendant) +{ + g_return_val_if_fail (node != NULL, FALSE); + g_return_val_if_fail (descendant != NULL, FALSE); + + while (descendant) + { + if (descendant->parent == node) + return TRUE; + + descendant = descendant->parent; + } + + return FALSE; +} + +/* returns 1 for root, 2 for first level children, + * 3 for children's children... + */ +guint +g_node_depth (GNode *node) +{ + register guint depth = 0; + + while (node) + { + depth++; + node = node->parent; + } + + return depth; +} + +void +g_node_reverse_children (GNode *node) +{ + GNode *child; + GNode *last; + + g_return_if_fail (node != NULL); + + child = node->children; + last = NULL; + while (child) + { + last = child; + child = last->next; + last->next = last->prev; + last->prev = child; + } + node->children = last; +} + +guint +g_node_max_height (GNode *root) +{ + register GNode *child; + register guint max_height = 0; + + if (!root) + return 0; + + child = root->children; + while (child) + { + register guint tmp_height; + + tmp_height = g_node_max_height (child); + if (tmp_height > max_height) + max_height = tmp_height; + child = child->next; + } + + return max_height + 1; +} + +static gboolean +g_node_traverse_pre_order (GNode *node, + GTraverseFlags flags, + GNodeTraverseFunc func, + gpointer data) +{ + if (node->children) + { + GNode *child; + + if ((flags & G_TRAVERSE_NON_LEAFS) && + func (node, data)) + return TRUE; + + child = node->children; + while (child) + { + register GNode *current; + + current = child; + child = current->next; + if (g_node_traverse_pre_order (current, flags, func, data)) + return TRUE; + } + } + else if ((flags & G_TRAVERSE_LEAFS) && + func (node, data)) + return TRUE; + + return FALSE; +} + +static gboolean +g_node_depth_traverse_pre_order (GNode *node, + GTraverseFlags flags, + guint depth, + GNodeTraverseFunc func, + gpointer data) +{ + if (node->children) + { + GNode *child; + + if ((flags & G_TRAVERSE_NON_LEAFS) && + func (node, data)) + return TRUE; + + depth--; + if (!depth) + return FALSE; + + child = node->children; + while (child) + { + register GNode *current; + + current = child; + child = current->next; + if (g_node_depth_traverse_pre_order (current, flags, depth, func, data)) + return TRUE; + } + } + else if ((flags & G_TRAVERSE_LEAFS) && + func (node, data)) + return TRUE; + + return FALSE; +} + +static gboolean +g_node_traverse_post_order (GNode *node, + GTraverseFlags flags, + GNodeTraverseFunc func, + gpointer data) +{ + if (node->children) + { + GNode *child; + + child = node->children; + while (child) + { + register GNode *current; + + current = child; + child = current->next; + if (g_node_traverse_post_order (current, flags, func, data)) + return TRUE; + } + + if ((flags & G_TRAVERSE_NON_LEAFS) && + func (node, data)) + return TRUE; + + } + else if ((flags & G_TRAVERSE_LEAFS) && + func (node, data)) + return TRUE; + + return FALSE; +} + +static gboolean +g_node_depth_traverse_post_order (GNode *node, + GTraverseFlags flags, + guint depth, + GNodeTraverseFunc func, + gpointer data) +{ + if (node->children) + { + depth--; + if (depth) + { + GNode *child; + + child = node->children; + while (child) + { + register GNode *current; + + current = child; + child = current->next; + if (g_node_depth_traverse_post_order (current, flags, depth, func, data)) + return TRUE; + } + } + + if ((flags & G_TRAVERSE_NON_LEAFS) && + func (node, data)) + return TRUE; + + } + else if ((flags & G_TRAVERSE_LEAFS) && + func (node, data)) + return TRUE; + + return FALSE; +} + +static gboolean +g_node_traverse_in_order (GNode *node, + GTraverseFlags flags, + GNodeTraverseFunc func, + gpointer data) +{ + if (node->children) + { + GNode *child; + register GNode *current; + + child = node->children; + current = child; + child = current->next; + + if (g_node_traverse_in_order (current, flags, func, data)) + return TRUE; + + if ((flags & G_TRAVERSE_NON_LEAFS) && + func (node, data)) + return TRUE; + + while (child) + { + current = child; + child = current->next; + if (g_node_traverse_in_order (current, flags, func, data)) + return TRUE; + } + } + else if ((flags & G_TRAVERSE_LEAFS) && + func (node, data)) + return TRUE; + + return FALSE; +} + +static gboolean +g_node_depth_traverse_in_order (GNode *node, + GTraverseFlags flags, + guint depth, + GNodeTraverseFunc func, + gpointer data) +{ + if (node->children) + { + depth--; + if (depth) + { + GNode *child; + register GNode *current; + + child = node->children; + current = child; + child = current->next; + + if (g_node_depth_traverse_in_order (current, flags, depth, func, data)) + return TRUE; + + if ((flags & G_TRAVERSE_NON_LEAFS) && + func (node, data)) + return TRUE; + + while (child) + { + current = child; + child = current->next; + if (g_node_depth_traverse_in_order (current, flags, depth, func, data)) + return TRUE; + } + } + else if ((flags & G_TRAVERSE_NON_LEAFS) && + func (node, data)) + return TRUE; + } + else if ((flags & G_TRAVERSE_LEAFS) && + func (node, data)) + return TRUE; + + return FALSE; +} + +static gboolean +g_node_traverse_children (GNode *node, + GTraverseFlags flags, + GNodeTraverseFunc func, + gpointer data) +{ + GNode *child; + + child = node->children; + + while (child) + { + register GNode *current; + + current = child; + child = current->next; + + if (current->children) + { + if ((flags & G_TRAVERSE_NON_LEAFS) && + func (current, data)) + return TRUE; + } + else if ((flags & G_TRAVERSE_LEAFS) && + func (current, data)) + return TRUE; + } + + child = node->children; + + while (child) + { + register GNode *current; + + current = child; + child = current->next; + + if (current->children && + g_node_traverse_children (current, flags, func, data)) + return TRUE; + } + + return FALSE; +} + +static gboolean +g_node_depth_traverse_children (GNode *node, + GTraverseFlags flags, + guint depth, + GNodeTraverseFunc func, + gpointer data) +{ + GNode *child; + + child = node->children; + + while (child) + { + register GNode *current; + + current = child; + child = current->next; + + if (current->children) + { + if ((flags & G_TRAVERSE_NON_LEAFS) && + func (current, data)) + return TRUE; + } + else if ((flags & G_TRAVERSE_LEAFS) && + func (current, data)) + return TRUE; + } + + depth--; + if (!depth) + return FALSE; + + child = node->children; + + while (child) + { + register GNode *current; + + current = child; + child = current->next; + + if (current->children && + g_node_depth_traverse_children (current, flags, depth, func, data)) + return TRUE; + } + + return FALSE; +} + +void +g_node_traverse (GNode *root, + GTraverseType order, + GTraverseFlags flags, + gint depth, + GNodeTraverseFunc func, + gpointer data) +{ + g_return_if_fail (root != NULL); + g_return_if_fail (func != NULL); + g_return_if_fail (order <= G_LEVEL_ORDER); + g_return_if_fail (flags <= G_TRAVERSE_MASK); + g_return_if_fail (depth == -1 || depth > 0); + + switch (order) + { + case G_PRE_ORDER: + if (depth < 0) + g_node_traverse_pre_order (root, flags, func, data); + else + g_node_depth_traverse_pre_order (root, flags, depth, func, data); + break; + case G_POST_ORDER: + if (depth < 0) + g_node_traverse_post_order (root, flags, func, data); + else + g_node_depth_traverse_post_order (root, flags, depth, func, data); + break; + case G_IN_ORDER: + if (depth < 0) + g_node_traverse_in_order (root, flags, func, data); + else + g_node_depth_traverse_in_order (root, flags, depth, func, data); + break; + case G_LEVEL_ORDER: + if (root->children) + { + if (!((flags & G_TRAVERSE_NON_LEAFS) && + func (root, data))) + { + if (depth < 0) + g_node_traverse_children (root, flags, func, data); + else + { + depth--; + if (depth) + g_node_depth_traverse_children (root, flags, depth, func, data); + } + } + } + else if (flags & G_TRAVERSE_LEAFS) + func (root, data); + break; + } +} + +static gboolean +g_node_find_func (GNode *node, + gpointer data) +{ + register gpointer *d = data; + + if (*d != node->data) + return FALSE; + + *(++d) = node; + + return TRUE; +} + +GNode* +g_node_find (GNode *root, + GTraverseType order, + GTraverseFlags flags, + gpointer data) +{ + gpointer d[2]; + + g_return_val_if_fail (root != NULL, NULL); + g_return_val_if_fail (order <= G_LEVEL_ORDER, NULL); + g_return_val_if_fail (flags <= G_TRAVERSE_MASK, NULL); + + d[0] = data; + d[1] = NULL; + + g_node_traverse (root, order, flags, -1, g_node_find_func, d); + + return d[1]; +} + +static void +g_node_count_func (GNode *node, + GTraverseFlags flags, + guint *n) +{ + if (node->children) + { + GNode *child; + + if (flags & G_TRAVERSE_NON_LEAFS) + (*n)++; + + child = node->children; + while (child) + { + g_node_count_func (child, flags, n); + child = child->next; + } + } + else if (flags & G_TRAVERSE_LEAFS) + (*n)++; +} + +guint +g_node_n_nodes (GNode *root, + GTraverseFlags flags) +{ + guint n = 0; + + g_return_val_if_fail (root != NULL, 0); + g_return_val_if_fail (flags <= G_TRAVERSE_MASK, 0); + + g_node_count_func (root, flags, &n); + + return n; +} + +GNode* +g_node_last_child (GNode *node) +{ + g_return_val_if_fail (node != NULL, NULL); + + node = node->children; + if (node) + while (node->next) + node = node->next; + + return node; +} + +GNode* +g_node_nth_child (GNode *node, + guint n) +{ + g_return_val_if_fail (node != NULL, NULL); + + node = node->children; + if (node) + while ((n-- > 0) && node) + node = node->next; + + return node; +} + +guint +g_node_n_children (GNode *node) +{ + guint n = 0; + + g_return_val_if_fail (node != NULL, 0); + + node = node->children; + while (node) + { + n++; + node = node->next; + } + + return n; +} + +GNode* +g_node_find_child (GNode *node, + GTraverseFlags flags, + gpointer data) +{ + g_return_val_if_fail (node != NULL, NULL); + g_return_val_if_fail (flags <= G_TRAVERSE_MASK, NULL); + + node = node->children; + while (node) + { + if (node->data == data) + { + if (G_NODE_IS_LEAF (node)) + { + if (flags & G_TRAVERSE_LEAFS) + return node; + } + else + { + if (flags & G_TRAVERSE_NON_LEAFS) + return node; + } + } + node = node->next; + } + + return NULL; +} + +gint +g_node_child_position (GNode *node, + GNode *child) +{ + register guint n = 0; + + g_return_val_if_fail (node != NULL, -1); + g_return_val_if_fail (child != NULL, -1); + g_return_val_if_fail (child->parent == node, -1); + + node = node->children; + while (node) + { + if (node == child) + return n; + n++; + node = node->next; + } + + return -1; +} + +gint +g_node_child_index (GNode *node, + gpointer data) +{ + register guint n = 0; + + g_return_val_if_fail (node != NULL, -1); + + node = node->children; + while (node) + { + if (node->data == data) + return n; + n++; + node = node->next; + } + + return -1; +} + +GNode* +g_node_first_sibling (GNode *node) +{ + g_return_val_if_fail (node != NULL, NULL); + + while (node->prev) + node = node->prev; + + return node; +} + +GNode* +g_node_last_sibling (GNode *node) +{ + g_return_val_if_fail (node != NULL, NULL); + + while (node->next) + node = node->next; + + return node; +} + +void +g_node_children_foreach (GNode *node, + GTraverseFlags flags, + GNodeForeachFunc func, + gpointer data) +{ + g_return_if_fail (node != NULL); + g_return_if_fail (flags <= G_TRAVERSE_MASK); + g_return_if_fail (func != NULL); + + node = node->children; + while (node) + { + register GNode *current; + + current = node; + node = current->next; + if (G_NODE_IS_LEAF (node)) + { + if (flags & G_TRAVERSE_LEAFS) + func (current, data); + } + else + { + if (flags & G_TRAVERSE_NON_LEAFS) + func (current, data); + } + } +} diff --git a/glib/gtree.c b/glib/gtree.c index 981ff396f..2a3dd8fa4 100644 --- a/glib/gtree.c +++ b/glib/gtree.c @@ -177,6 +177,10 @@ g_tree_traverse (GTree *tree, case G_POST_ORDER: g_tree_node_post_order (rtree->root, traverse_func, data); break; + + case G_LEVEL_ORDER: + g_warning ("g_tree_traverse(): traverse type G_LEVEL_ORDER isn't implemented."); + break; } } diff --git a/gnode.c b/gnode.c new file mode 100644 index 000000000..2279de212 --- /dev/null +++ b/gnode.c @@ -0,0 +1,887 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * GNode: N-way tree implementation. + * Copyright (C) 1998 Tim Janik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include "glib.h" + + +#define KEEP_NODES (1024) + + +/* --- variables --- */ +static GMemChunk *g_tree_node_chunk = NULL; +static GNode *free_nodes = NULL; +static guint n_free_nodes = 0; + + +/* --- functions --- */ +GNode* +g_node_new (gpointer data) +{ + register GNode *node; + + if (!g_tree_node_chunk) + g_tree_node_chunk = g_mem_chunk_create (GNode, 1024, G_ALLOC_AND_FREE); + + if (n_free_nodes) + { + node = free_nodes; + free_nodes = free_nodes->next; + n_free_nodes--; + } + else + node = g_chunk_new (GNode, g_tree_node_chunk); + + node->prev = NULL; + node->next = NULL; + node->parent = NULL; + node->children = NULL; + node->data = data; + + return node; +} + +static void +g_node_free (GNode *parent) +{ + GNode *node; + + node = parent->children; + + while (node) + { + register GNode *free_node; + + free_node = node; + node = free_node->next; + g_node_free (free_node); + } + + if (n_free_nodes < KEEP_NODES) + { + parent->next = free_nodes; + free_nodes = parent; + n_free_nodes++; + } + else + g_chunk_free (parent, g_tree_node_chunk); +} + +void +g_node_destroy (GNode *root) +{ + g_return_if_fail (root != NULL); + + if (!G_NODE_IS_ROOT (root)) + g_node_unlink (root); + + g_node_free (root); +} + +void +g_node_unlink (GNode *node) +{ + g_return_if_fail (node != NULL); + + if (node->prev) + node->prev->next = node->next; + else if (node->parent) + node->parent->children = node->next; + node->parent = NULL; + if (node->next) + { + node->next->prev = node->prev; + node->next = NULL; + } + node->prev = NULL; +} + +void +g_node_insert (GNode *parent, + gint position, + GNode *node) +{ + g_return_if_fail (parent != NULL); + g_return_if_fail (node != NULL); + g_return_if_fail (G_NODE_IS_ROOT (node)); + + if (position > 0) + g_node_insert_before (parent, + g_node_nth_child (parent, position), + node); + else if (position == 0) + g_node_prepend (parent, node); + else if (position < 0) + g_node_append (parent, node); +} + +void +g_node_insert_before (GNode *parent, + GNode *sibling, + GNode *node) +{ + g_return_if_fail (parent != NULL); + g_return_if_fail (node != NULL); + g_return_if_fail (G_NODE_IS_ROOT (node)); + if (sibling) + g_return_if_fail (sibling->parent == parent); + + node->parent = parent; + + if (sibling) + { + if (sibling->prev) + { + node->prev = sibling->prev; + node->prev->next = node; + node->next = sibling; + sibling->prev = node; + } + else + { + node->parent->children = node; + node->next = sibling; + sibling->prev = node; + } + } + else + { + if (parent->children) + { + sibling = parent->children; + while (sibling->next) + sibling = sibling->next; + node->prev = sibling; + sibling->next = node; + } + else + node->parent->children = node; + } +} + +void +g_node_prepend (GNode *parent, + GNode *node) +{ + g_return_if_fail (parent != NULL); + + g_node_insert_before (parent, parent->children, node); +} + +GNode* +g_node_get_root (GNode *node) +{ + g_return_val_if_fail (node != NULL, NULL); + + while (node->parent) + node = node->parent; + + return node; +} + +gboolean +g_node_is_ancestor (GNode *node, + GNode *descendant) +{ + g_return_val_if_fail (node != NULL, FALSE); + g_return_val_if_fail (descendant != NULL, FALSE); + + while (descendant) + { + if (descendant->parent == node) + return TRUE; + + descendant = descendant->parent; + } + + return FALSE; +} + +/* returns 1 for root, 2 for first level children, + * 3 for children's children... + */ +guint +g_node_depth (GNode *node) +{ + register guint depth = 0; + + while (node) + { + depth++; + node = node->parent; + } + + return depth; +} + +void +g_node_reverse_children (GNode *node) +{ + GNode *child; + GNode *last; + + g_return_if_fail (node != NULL); + + child = node->children; + last = NULL; + while (child) + { + last = child; + child = last->next; + last->next = last->prev; + last->prev = child; + } + node->children = last; +} + +guint +g_node_max_height (GNode *root) +{ + register GNode *child; + register guint max_height = 0; + + if (!root) + return 0; + + child = root->children; + while (child) + { + register guint tmp_height; + + tmp_height = g_node_max_height (child); + if (tmp_height > max_height) + max_height = tmp_height; + child = child->next; + } + + return max_height + 1; +} + +static gboolean +g_node_traverse_pre_order (GNode *node, + GTraverseFlags flags, + GNodeTraverseFunc func, + gpointer data) +{ + if (node->children) + { + GNode *child; + + if ((flags & G_TRAVERSE_NON_LEAFS) && + func (node, data)) + return TRUE; + + child = node->children; + while (child) + { + register GNode *current; + + current = child; + child = current->next; + if (g_node_traverse_pre_order (current, flags, func, data)) + return TRUE; + } + } + else if ((flags & G_TRAVERSE_LEAFS) && + func (node, data)) + return TRUE; + + return FALSE; +} + +static gboolean +g_node_depth_traverse_pre_order (GNode *node, + GTraverseFlags flags, + guint depth, + GNodeTraverseFunc func, + gpointer data) +{ + if (node->children) + { + GNode *child; + + if ((flags & G_TRAVERSE_NON_LEAFS) && + func (node, data)) + return TRUE; + + depth--; + if (!depth) + return FALSE; + + child = node->children; + while (child) + { + register GNode *current; + + current = child; + child = current->next; + if (g_node_depth_traverse_pre_order (current, flags, depth, func, data)) + return TRUE; + } + } + else if ((flags & G_TRAVERSE_LEAFS) && + func (node, data)) + return TRUE; + + return FALSE; +} + +static gboolean +g_node_traverse_post_order (GNode *node, + GTraverseFlags flags, + GNodeTraverseFunc func, + gpointer data) +{ + if (node->children) + { + GNode *child; + + child = node->children; + while (child) + { + register GNode *current; + + current = child; + child = current->next; + if (g_node_traverse_post_order (current, flags, func, data)) + return TRUE; + } + + if ((flags & G_TRAVERSE_NON_LEAFS) && + func (node, data)) + return TRUE; + + } + else if ((flags & G_TRAVERSE_LEAFS) && + func (node, data)) + return TRUE; + + return FALSE; +} + +static gboolean +g_node_depth_traverse_post_order (GNode *node, + GTraverseFlags flags, + guint depth, + GNodeTraverseFunc func, + gpointer data) +{ + if (node->children) + { + depth--; + if (depth) + { + GNode *child; + + child = node->children; + while (child) + { + register GNode *current; + + current = child; + child = current->next; + if (g_node_depth_traverse_post_order (current, flags, depth, func, data)) + return TRUE; + } + } + + if ((flags & G_TRAVERSE_NON_LEAFS) && + func (node, data)) + return TRUE; + + } + else if ((flags & G_TRAVERSE_LEAFS) && + func (node, data)) + return TRUE; + + return FALSE; +} + +static gboolean +g_node_traverse_in_order (GNode *node, + GTraverseFlags flags, + GNodeTraverseFunc func, + gpointer data) +{ + if (node->children) + { + GNode *child; + register GNode *current; + + child = node->children; + current = child; + child = current->next; + + if (g_node_traverse_in_order (current, flags, func, data)) + return TRUE; + + if ((flags & G_TRAVERSE_NON_LEAFS) && + func (node, data)) + return TRUE; + + while (child) + { + current = child; + child = current->next; + if (g_node_traverse_in_order (current, flags, func, data)) + return TRUE; + } + } + else if ((flags & G_TRAVERSE_LEAFS) && + func (node, data)) + return TRUE; + + return FALSE; +} + +static gboolean +g_node_depth_traverse_in_order (GNode *node, + GTraverseFlags flags, + guint depth, + GNodeTraverseFunc func, + gpointer data) +{ + if (node->children) + { + depth--; + if (depth) + { + GNode *child; + register GNode *current; + + child = node->children; + current = child; + child = current->next; + + if (g_node_depth_traverse_in_order (current, flags, depth, func, data)) + return TRUE; + + if ((flags & G_TRAVERSE_NON_LEAFS) && + func (node, data)) + return TRUE; + + while (child) + { + current = child; + child = current->next; + if (g_node_depth_traverse_in_order (current, flags, depth, func, data)) + return TRUE; + } + } + else if ((flags & G_TRAVERSE_NON_LEAFS) && + func (node, data)) + return TRUE; + } + else if ((flags & G_TRAVERSE_LEAFS) && + func (node, data)) + return TRUE; + + return FALSE; +} + +static gboolean +g_node_traverse_children (GNode *node, + GTraverseFlags flags, + GNodeTraverseFunc func, + gpointer data) +{ + GNode *child; + + child = node->children; + + while (child) + { + register GNode *current; + + current = child; + child = current->next; + + if (current->children) + { + if ((flags & G_TRAVERSE_NON_LEAFS) && + func (current, data)) + return TRUE; + } + else if ((flags & G_TRAVERSE_LEAFS) && + func (current, data)) + return TRUE; + } + + child = node->children; + + while (child) + { + register GNode *current; + + current = child; + child = current->next; + + if (current->children && + g_node_traverse_children (current, flags, func, data)) + return TRUE; + } + + return FALSE; +} + +static gboolean +g_node_depth_traverse_children (GNode *node, + GTraverseFlags flags, + guint depth, + GNodeTraverseFunc func, + gpointer data) +{ + GNode *child; + + child = node->children; + + while (child) + { + register GNode *current; + + current = child; + child = current->next; + + if (current->children) + { + if ((flags & G_TRAVERSE_NON_LEAFS) && + func (current, data)) + return TRUE; + } + else if ((flags & G_TRAVERSE_LEAFS) && + func (current, data)) + return TRUE; + } + + depth--; + if (!depth) + return FALSE; + + child = node->children; + + while (child) + { + register GNode *current; + + current = child; + child = current->next; + + if (current->children && + g_node_depth_traverse_children (current, flags, depth, func, data)) + return TRUE; + } + + return FALSE; +} + +void +g_node_traverse (GNode *root, + GTraverseType order, + GTraverseFlags flags, + gint depth, + GNodeTraverseFunc func, + gpointer data) +{ + g_return_if_fail (root != NULL); + g_return_if_fail (func != NULL); + g_return_if_fail (order <= G_LEVEL_ORDER); + g_return_if_fail (flags <= G_TRAVERSE_MASK); + g_return_if_fail (depth == -1 || depth > 0); + + switch (order) + { + case G_PRE_ORDER: + if (depth < 0) + g_node_traverse_pre_order (root, flags, func, data); + else + g_node_depth_traverse_pre_order (root, flags, depth, func, data); + break; + case G_POST_ORDER: + if (depth < 0) + g_node_traverse_post_order (root, flags, func, data); + else + g_node_depth_traverse_post_order (root, flags, depth, func, data); + break; + case G_IN_ORDER: + if (depth < 0) + g_node_traverse_in_order (root, flags, func, data); + else + g_node_depth_traverse_in_order (root, flags, depth, func, data); + break; + case G_LEVEL_ORDER: + if (root->children) + { + if (!((flags & G_TRAVERSE_NON_LEAFS) && + func (root, data))) + { + if (depth < 0) + g_node_traverse_children (root, flags, func, data); + else + { + depth--; + if (depth) + g_node_depth_traverse_children (root, flags, depth, func, data); + } + } + } + else if (flags & G_TRAVERSE_LEAFS) + func (root, data); + break; + } +} + +static gboolean +g_node_find_func (GNode *node, + gpointer data) +{ + register gpointer *d = data; + + if (*d != node->data) + return FALSE; + + *(++d) = node; + + return TRUE; +} + +GNode* +g_node_find (GNode *root, + GTraverseType order, + GTraverseFlags flags, + gpointer data) +{ + gpointer d[2]; + + g_return_val_if_fail (root != NULL, NULL); + g_return_val_if_fail (order <= G_LEVEL_ORDER, NULL); + g_return_val_if_fail (flags <= G_TRAVERSE_MASK, NULL); + + d[0] = data; + d[1] = NULL; + + g_node_traverse (root, order, flags, -1, g_node_find_func, d); + + return d[1]; +} + +static void +g_node_count_func (GNode *node, + GTraverseFlags flags, + guint *n) +{ + if (node->children) + { + GNode *child; + + if (flags & G_TRAVERSE_NON_LEAFS) + (*n)++; + + child = node->children; + while (child) + { + g_node_count_func (child, flags, n); + child = child->next; + } + } + else if (flags & G_TRAVERSE_LEAFS) + (*n)++; +} + +guint +g_node_n_nodes (GNode *root, + GTraverseFlags flags) +{ + guint n = 0; + + g_return_val_if_fail (root != NULL, 0); + g_return_val_if_fail (flags <= G_TRAVERSE_MASK, 0); + + g_node_count_func (root, flags, &n); + + return n; +} + +GNode* +g_node_last_child (GNode *node) +{ + g_return_val_if_fail (node != NULL, NULL); + + node = node->children; + if (node) + while (node->next) + node = node->next; + + return node; +} + +GNode* +g_node_nth_child (GNode *node, + guint n) +{ + g_return_val_if_fail (node != NULL, NULL); + + node = node->children; + if (node) + while ((n-- > 0) && node) + node = node->next; + + return node; +} + +guint +g_node_n_children (GNode *node) +{ + guint n = 0; + + g_return_val_if_fail (node != NULL, 0); + + node = node->children; + while (node) + { + n++; + node = node->next; + } + + return n; +} + +GNode* +g_node_find_child (GNode *node, + GTraverseFlags flags, + gpointer data) +{ + g_return_val_if_fail (node != NULL, NULL); + g_return_val_if_fail (flags <= G_TRAVERSE_MASK, NULL); + + node = node->children; + while (node) + { + if (node->data == data) + { + if (G_NODE_IS_LEAF (node)) + { + if (flags & G_TRAVERSE_LEAFS) + return node; + } + else + { + if (flags & G_TRAVERSE_NON_LEAFS) + return node; + } + } + node = node->next; + } + + return NULL; +} + +gint +g_node_child_position (GNode *node, + GNode *child) +{ + register guint n = 0; + + g_return_val_if_fail (node != NULL, -1); + g_return_val_if_fail (child != NULL, -1); + g_return_val_if_fail (child->parent == node, -1); + + node = node->children; + while (node) + { + if (node == child) + return n; + n++; + node = node->next; + } + + return -1; +} + +gint +g_node_child_index (GNode *node, + gpointer data) +{ + register guint n = 0; + + g_return_val_if_fail (node != NULL, -1); + + node = node->children; + while (node) + { + if (node->data == data) + return n; + n++; + node = node->next; + } + + return -1; +} + +GNode* +g_node_first_sibling (GNode *node) +{ + g_return_val_if_fail (node != NULL, NULL); + + while (node->prev) + node = node->prev; + + return node; +} + +GNode* +g_node_last_sibling (GNode *node) +{ + g_return_val_if_fail (node != NULL, NULL); + + while (node->next) + node = node->next; + + return node; +} + +void +g_node_children_foreach (GNode *node, + GTraverseFlags flags, + GNodeForeachFunc func, + gpointer data) +{ + g_return_if_fail (node != NULL); + g_return_if_fail (flags <= G_TRAVERSE_MASK); + g_return_if_fail (func != NULL); + + node = node->children; + while (node) + { + register GNode *current; + + current = node; + node = current->next; + if (G_NODE_IS_LEAF (node)) + { + if (flags & G_TRAVERSE_LEAFS) + func (current, data); + } + else + { + if (flags & G_TRAVERSE_NON_LEAFS) + func (current, data); + } + } +} @@ -177,6 +177,10 @@ g_tree_traverse (GTree *tree, case G_POST_ORDER: g_tree_node_post_order (rtree->root, traverse_func, data); break; + + case G_LEVEL_ORDER: + g_warning ("g_tree_traverse(): traverse type G_LEVEL_ORDER isn't implemented."); + break; } } diff --git a/testglib.c b/testglib.c index d89e76386..4fd5f7bf4 100644 --- a/testglib.c +++ b/testglib.c @@ -22,6 +22,159 @@ #include "glib.h" int array[10000]; +gboolean failed = FALSE; + +#define TEST(m,cond) G_STMT_START { failed = !(cond); \ +if (failed) \ + { if (!m) \ + g_print ("\n(%s:%d) failed for: %s\n", __FILE__, __LINE__, ( # cond )); \ + else \ + g_print ("\n(%s:%d) failed for: %s: (%s)\n", __FILE__, __LINE__, ( # cond ), (gchar*)m); \ + } \ +else \ + g_print ("."); fflush (stdout); \ +} G_STMT_END + +#define C2P(c) ((gpointer) ((long) (c))) +#define P2C(p) ((gchar) ((long) (p))) + +static gboolean +node_build_string (GNode *node, + gpointer data) +{ + gchar **p = data; + gchar *string; + gchar c[2] = "_"; + + c[0] = P2C (node->data); + + string = g_strconcat (*p ? *p : "", c, NULL); + g_free (*p); + *p = string; + + return FALSE; +} + +static void +g_node_test (void) +{ + GNode *root; + GNode *node; + GNode *node_B; + GNode *node_F; + GNode *node_G; + GNode *node_J; + guint i; + gchar *tstring; + + g_print ("checking n-way trees: "); + failed = FALSE; + + root = g_node_new (C2P ('A')); + TEST (NULL, g_node_depth (root) == 1 && g_node_max_height (root) == 1); + + node_B = g_node_new (C2P ('B')); + g_node_append (root, node_B); + TEST (NULL, root->children == node_B); + + g_node_append (node_B, g_node_new (C2P ('E'))); + g_node_prepend (node_B, g_node_new (C2P ('C'))); + g_node_insert (node_B, 1, g_node_new (C2P ('D'))); + + node_F = g_node_new (C2P ('F')); + g_node_append (root, node_F); + TEST (NULL, root->children->next == node_F); + + node_G = g_node_new (C2P ('G')); + g_node_append (node_F, node_G); + node_J = g_node_new (C2P ('J')); + g_node_insert (node_G, -1, node_J); + g_node_insert (node_G, 42, g_node_new (C2P ('K'))); + g_node_insert (node_G, 0, g_node_new (C2P ('H'))); + g_node_insert (node_G, 1, g_node_new (C2P ('I'))); + + TEST (NULL, g_node_depth (root) == 1); + TEST (NULL, g_node_max_height (root) == 4); + TEST (NULL, g_node_depth (node_G->children->next) == 4); + TEST (NULL, g_node_n_nodes (root, G_TRAVERSE_LEAFS) == 7); + TEST (NULL, g_node_n_nodes (root, G_TRAVERSE_NON_LEAFS) == 4); + TEST (NULL, g_node_n_nodes (root, G_TRAVERSE_ALL) == 11); + TEST (NULL, g_node_max_height (node_F) == 3); + TEST (NULL, g_node_n_children (node_G) == 4); + TEST (NULL, g_node_find_child (root, G_TRAVERSE_ALL, C2P ('F')) == node_F); + TEST (NULL, g_node_find (root, G_LEVEL_ORDER, G_TRAVERSE_NON_LEAFS, C2P ('I')) == NULL); + TEST (NULL, g_node_find (root, G_IN_ORDER, G_TRAVERSE_LEAFS, C2P ('J')) == node_J); + + for (i = 0; i < g_node_n_children (node_B); i++) + { + node = g_node_nth_child (node_B, i); + TEST (NULL, P2C (node->data) == ('C' + i)); + } + + for (i = 0; i < g_node_n_children (node_G); i++) + TEST (NULL, g_node_child_position (node_G, g_node_nth_child (node_G, i)) == i); + + /* we have built: A + * / \ + * B F + * / | \ \ + * C D E G + * / /\ \ + * H I J K + * + * for in-order traversal, 'G' is considered to be the "left" + * child of 'F', which will cause 'F' to be the last node visited. + */ + + tstring = NULL; + g_node_traverse (root, G_PRE_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring); + TEST (tstring, strcmp (tstring, "ABCDEFGHIJK") == 0); + g_free (tstring); tstring = NULL; + g_node_traverse (root, G_POST_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring); + TEST (tstring, strcmp (tstring, "CDEBHIJKGFA") == 0); + g_free (tstring); tstring = NULL; + g_node_traverse (root, G_IN_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring); + TEST (tstring, strcmp (tstring, "CBDEAHGIJKF") == 0); + g_free (tstring); tstring = NULL; + g_node_traverse (root, G_LEVEL_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring); + TEST (tstring, strcmp (tstring, "ABFCDEGHIJK") == 0); + g_free (tstring); tstring = NULL; + + g_node_traverse (root, G_LEVEL_ORDER, G_TRAVERSE_LEAFS, -1, node_build_string, &tstring); + TEST (tstring, strcmp (tstring, "CDEHIJK") == 0); + g_free (tstring); tstring = NULL; + g_node_traverse (root, G_PRE_ORDER, G_TRAVERSE_NON_LEAFS, -1, node_build_string, &tstring); + TEST (tstring, strcmp (tstring, "ABFG") == 0); + g_free (tstring); tstring = NULL; + + g_node_reverse_children (node_B); + g_node_reverse_children (node_G); + + g_node_traverse (root, G_LEVEL_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring); + TEST (tstring, strcmp (tstring, "ABFEDCGKJIH") == 0); + g_free (tstring); tstring = NULL; + + g_node_destroy (root); + + /* allocation tests */ + + root = g_node_new (NULL); + node = root; + + for (i = 0; i < 2048; i++) + { + g_node_append (node, g_node_new (NULL)); + if ((i%5) == 4) + node = node->children->next; + } + TEST (NULL, g_node_max_height (root) > 100); + TEST (NULL, g_node_n_nodes (root, G_TRAVERSE_ALL) == 1 + 2048); + + g_node_destroy (root); + + if (!failed) + g_print ("ok\n"); +} void my_hash_callback (gpointer key, @@ -268,7 +421,7 @@ main (int argc, g_print ("ok\n"); - g_print ("checking trees...\n"); + g_print ("checking binary trees...\n"); tree = g_tree_new (my_compare); i = 0; @@ -308,6 +461,9 @@ main (int argc, g_print ("ok\n"); + /* check n-way trees */ + g_node_test (); + g_print ("checking mem chunks..."); mem_chunk = g_mem_chunk_new ("test mem chunk", 50, 100, G_ALLOC_AND_FREE); diff --git a/tests/testglib.c b/tests/testglib.c index d89e76386..4fd5f7bf4 100644 --- a/tests/testglib.c +++ b/tests/testglib.c @@ -22,6 +22,159 @@ #include "glib.h" int array[10000]; +gboolean failed = FALSE; + +#define TEST(m,cond) G_STMT_START { failed = !(cond); \ +if (failed) \ + { if (!m) \ + g_print ("\n(%s:%d) failed for: %s\n", __FILE__, __LINE__, ( # cond )); \ + else \ + g_print ("\n(%s:%d) failed for: %s: (%s)\n", __FILE__, __LINE__, ( # cond ), (gchar*)m); \ + } \ +else \ + g_print ("."); fflush (stdout); \ +} G_STMT_END + +#define C2P(c) ((gpointer) ((long) (c))) +#define P2C(p) ((gchar) ((long) (p))) + +static gboolean +node_build_string (GNode *node, + gpointer data) +{ + gchar **p = data; + gchar *string; + gchar c[2] = "_"; + + c[0] = P2C (node->data); + + string = g_strconcat (*p ? *p : "", c, NULL); + g_free (*p); + *p = string; + + return FALSE; +} + +static void +g_node_test (void) +{ + GNode *root; + GNode *node; + GNode *node_B; + GNode *node_F; + GNode *node_G; + GNode *node_J; + guint i; + gchar *tstring; + + g_print ("checking n-way trees: "); + failed = FALSE; + + root = g_node_new (C2P ('A')); + TEST (NULL, g_node_depth (root) == 1 && g_node_max_height (root) == 1); + + node_B = g_node_new (C2P ('B')); + g_node_append (root, node_B); + TEST (NULL, root->children == node_B); + + g_node_append (node_B, g_node_new (C2P ('E'))); + g_node_prepend (node_B, g_node_new (C2P ('C'))); + g_node_insert (node_B, 1, g_node_new (C2P ('D'))); + + node_F = g_node_new (C2P ('F')); + g_node_append (root, node_F); + TEST (NULL, root->children->next == node_F); + + node_G = g_node_new (C2P ('G')); + g_node_append (node_F, node_G); + node_J = g_node_new (C2P ('J')); + g_node_insert (node_G, -1, node_J); + g_node_insert (node_G, 42, g_node_new (C2P ('K'))); + g_node_insert (node_G, 0, g_node_new (C2P ('H'))); + g_node_insert (node_G, 1, g_node_new (C2P ('I'))); + + TEST (NULL, g_node_depth (root) == 1); + TEST (NULL, g_node_max_height (root) == 4); + TEST (NULL, g_node_depth (node_G->children->next) == 4); + TEST (NULL, g_node_n_nodes (root, G_TRAVERSE_LEAFS) == 7); + TEST (NULL, g_node_n_nodes (root, G_TRAVERSE_NON_LEAFS) == 4); + TEST (NULL, g_node_n_nodes (root, G_TRAVERSE_ALL) == 11); + TEST (NULL, g_node_max_height (node_F) == 3); + TEST (NULL, g_node_n_children (node_G) == 4); + TEST (NULL, g_node_find_child (root, G_TRAVERSE_ALL, C2P ('F')) == node_F); + TEST (NULL, g_node_find (root, G_LEVEL_ORDER, G_TRAVERSE_NON_LEAFS, C2P ('I')) == NULL); + TEST (NULL, g_node_find (root, G_IN_ORDER, G_TRAVERSE_LEAFS, C2P ('J')) == node_J); + + for (i = 0; i < g_node_n_children (node_B); i++) + { + node = g_node_nth_child (node_B, i); + TEST (NULL, P2C (node->data) == ('C' + i)); + } + + for (i = 0; i < g_node_n_children (node_G); i++) + TEST (NULL, g_node_child_position (node_G, g_node_nth_child (node_G, i)) == i); + + /* we have built: A + * / \ + * B F + * / | \ \ + * C D E G + * / /\ \ + * H I J K + * + * for in-order traversal, 'G' is considered to be the "left" + * child of 'F', which will cause 'F' to be the last node visited. + */ + + tstring = NULL; + g_node_traverse (root, G_PRE_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring); + TEST (tstring, strcmp (tstring, "ABCDEFGHIJK") == 0); + g_free (tstring); tstring = NULL; + g_node_traverse (root, G_POST_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring); + TEST (tstring, strcmp (tstring, "CDEBHIJKGFA") == 0); + g_free (tstring); tstring = NULL; + g_node_traverse (root, G_IN_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring); + TEST (tstring, strcmp (tstring, "CBDEAHGIJKF") == 0); + g_free (tstring); tstring = NULL; + g_node_traverse (root, G_LEVEL_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring); + TEST (tstring, strcmp (tstring, "ABFCDEGHIJK") == 0); + g_free (tstring); tstring = NULL; + + g_node_traverse (root, G_LEVEL_ORDER, G_TRAVERSE_LEAFS, -1, node_build_string, &tstring); + TEST (tstring, strcmp (tstring, "CDEHIJK") == 0); + g_free (tstring); tstring = NULL; + g_node_traverse (root, G_PRE_ORDER, G_TRAVERSE_NON_LEAFS, -1, node_build_string, &tstring); + TEST (tstring, strcmp (tstring, "ABFG") == 0); + g_free (tstring); tstring = NULL; + + g_node_reverse_children (node_B); + g_node_reverse_children (node_G); + + g_node_traverse (root, G_LEVEL_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring); + TEST (tstring, strcmp (tstring, "ABFEDCGKJIH") == 0); + g_free (tstring); tstring = NULL; + + g_node_destroy (root); + + /* allocation tests */ + + root = g_node_new (NULL); + node = root; + + for (i = 0; i < 2048; i++) + { + g_node_append (node, g_node_new (NULL)); + if ((i%5) == 4) + node = node->children->next; + } + TEST (NULL, g_node_max_height (root) > 100); + TEST (NULL, g_node_n_nodes (root, G_TRAVERSE_ALL) == 1 + 2048); + + g_node_destroy (root); + + if (!failed) + g_print ("ok\n"); +} void my_hash_callback (gpointer key, @@ -268,7 +421,7 @@ main (int argc, g_print ("ok\n"); - g_print ("checking trees...\n"); + g_print ("checking binary trees...\n"); tree = g_tree_new (my_compare); i = 0; @@ -308,6 +461,9 @@ main (int argc, g_print ("ok\n"); + /* check n-way trees */ + g_node_test (); + g_print ("checking mem chunks..."); mem_chunk = g_mem_chunk_new ("test mem chunk", 50, 100, G_ALLOC_AND_FREE); |