Add versioned/persistent GTree interface. originalwork
authorDana Jansens <danakj@orodu.net>
Thu, 12 Nov 2009 23:23:09 +0000 (18:23 -0500)
committerDana Jansens <danakj@orodu.net>
Thu, 12 Nov 2009 23:23:09 +0000 (18:23 -0500)
Change g_tree_lookup_related() to not take a version, but search on the current/latest version of the GTree.
Add a func_v() version for each of the g_tree_lookup/foreach/search/height functions, which takes an extra version argument, which is the version of the GTree for which to query.

glib/gtree.c
glib/gtree.h
tests/tree-test.c

index 2b240a3573d69b7b018d3869dd8dea479429629b..8fe4f5a86581d3d46c33b531e2ae7fe96744d36d 100644 (file)
@@ -125,7 +125,6 @@ static gpointer   g_tree_node_search                (GTreeNode       *node,
                                                     gconstpointer    data,
                                                      GTreeSearchType  search_type,
                                                      guint            version);
-static gint       g_tree_node_height                (GTreeNode       *node);
 static GTreeNode* g_tree_node_rotate_left           (GTree           *tree,
                                                      GTreeNode       *node);
 static GTreeNode* g_tree_node_rotate_right          (GTree           *tree,
@@ -294,7 +293,7 @@ g_tree_root_find_version (GTree *tree,
   };
 
   if (v[0].version <= version)
-    return v;
+    return v; /* fast when looking for the current version */
 
   l = v+1;
   r = v+(nv-1);
@@ -1335,7 +1334,32 @@ gpointer
 g_tree_lookup (GTree         *tree,
               gconstpointer  key)
 {
-  return g_tree_lookup_related (tree, key, G_TREE_SEARCH_EXACT, tree->version);
+  return g_tree_lookup_related_v (tree, key, G_TREE_SEARCH_EXACT,
+                                  tree->version);
+}
+
+/**
+ * g_tree_lookup_v:
+ * @tree: a #GTree.
+ * @key: the key to look up.
+ * @version: the version of the tree within which to search.  If
+ *   g_tree_next_version() has not been used, then this is 0.  This value
+ *   must be at most the value returned by g_tree_current_version().
+ *
+ * Gets the value corresponding to the given key in a specified @version of
+ * the #GTree. Since a #GTree is
+ * automatically balanced as key/value pairs are added, key lookup is very
+ * fast.
+ *
+ * Return value: the value corresponding to the key, or %NULL if the key was
+ * not found.
+ **/
+gpointer
+g_tree_lookup_v (GTree         *tree,
+                 gconstpointer  key,
+                 guint          version)
+{
+  return g_tree_lookup_related_v (tree, key, G_TREE_SEARCH_EXACT, version);
 }
 
 /**
@@ -1345,8 +1369,6 @@ g_tree_lookup (GTree         *tree,
  * @search_type: the search behavior if the @key is not present in the #GTree,
  *   one of %G_TREE_SEARCH_EXACT, %G_TREE_SEARCH_SUCCESSOR, and
  *   %G_TREE_SEARCH_PREDECESSOR.
- * @version: the version of the tree within which to search.  If
- *   g_tree_next_version() has not been used, then this is 0.
  *
  * Gets a value corresponding to the given key.
  *
@@ -1370,8 +1392,47 @@ g_tree_lookup (GTree         *tree,
 gpointer
 g_tree_lookup_related  (GTree          *tree,
                         gconstpointer   key,
-                        GTreeSearchType search_type,
-                        guint           version)
+                        GTreeSearchType search_type)
+{
+  return g_tree_lookup_related_v (tree, key, search_type, tree->version);
+}
+
+/**
+ * g_tree_lookup_related_v:
+ * @tree: a #GTree.
+ * @key: the key to look up.
+ * @search_type: the search behavior if the @key is not present in the #GTree,
+ *   one of %G_TREE_SEARCH_EXACT, %G_TREE_SEARCH_SUCCESSOR, and
+ *   %G_TREE_SEARCH_PREDECESSOR.
+ * @version: the version of the tree within which to search.  If
+ *   g_tree_next_version() has not been used, then this is 0.  This value
+ *   must be at most the value returned by g_tree_current_version().
+ *
+ * Gets a value corresponding to the given key in a specified @version of the
+ * #GTree.
+ *
+ * If the given @key is present in the tree, then its corresponding value is
+ * always returned.  If it is not, then the @search_type will define the
+ * function's behavior.  If @search_type is %G_TREE_SEARCH_EXACT, the
+ * function will return %NULL if the @key is not found.  If @search_type is
+ * %G_TREE_SEARCH_SUCCESSOR, the function will find the next larger key that
+ * is present in the tree and return its value, or %NULL if there are no keys
+ * larger than @key in the tree.  If @search_type is
+ * %G_TREE_SEARCH_PREDECESSOR, the function will find the next smaller key that
+ * is present in the tree and return its value, or %NULL if there are no keys
+ * smaller than @key in the tree.
+ * Since a #GTree is
+ * automatically balanced as key/value pairs are added, key lookup is very
+ * fast.
+ *
+ * Return value: the value corresponding to the found key, or %NULL if no
+ * matching key was found.
+ **/
+gpointer
+g_tree_lookup_related_v (GTree          *tree,
+                         gconstpointer   key,
+                         GTreeSearchType search_type,
+                         guint           version)
 {
   GTreeNode *node;
 
@@ -1402,12 +1463,42 @@ g_tree_lookup_extended (GTree         *tree,
                         gconstpointer  lookup_key,
                         gpointer      *orig_key,
                         gpointer      *value)
+{
+  return g_tree_lookup_extended_v (tree, lookup_key, orig_key, value,
+                                   tree->version);
+}
+
+/**
+ * g_tree_lookup_extended_v:
+ * @tree: a #GTree.
+ * @lookup_key: the key to look up.
+ * @orig_key: returns the original key.
+ * @value: returns the value associated with the key.
+ * @version: the version of the tree within which to search.  If
+ *   g_tree_next_version() has not been used, then this is 0.  This value
+ *   must be at most the value returned by g_tree_current_version().
+ *
+ * Looks up a key in a specified @version of the #GTree, returning the
+ * original key and the
+ * associated value and a #gboolean which is %TRUE if the key was found. This 
+ * is useful if you need to free the memory allocated for the original key,
+ * for example before calling g_tree_remove().
+ *
+ * Return value: %TRUE if the key was found in the #GTree.
+ **/
+gboolean
+g_tree_lookup_extended_v (GTree         *tree,
+                          gconstpointer  lookup_key,
+                          gpointer      *orig_key,
+                          gpointer      *value,
+                          guint          version)
 {
   GTreeNode *node;
   
   g_return_val_if_fail (tree != NULL, FALSE);
+  g_return_val_if_fail (version <= tree->version, FALSE);
   
-  node = g_tree_find_node (tree, lookup_key, G_TREE_SEARCH_EXACT, tree->version);
+  node = g_tree_find_node (tree, lookup_key, G_TREE_SEARCH_EXACT, version);
   
   if (node)
     {
@@ -1441,22 +1532,52 @@ void
 g_tree_foreach (GTree         *tree,
                 GTraverseFunc  func,
                 gpointer       user_data)
+{
+  g_tree_foreach_v (tree, func, user_data, tree->version);
+}
+
+/**
+ * g_tree_foreach_v:
+ * @tree: a #GTree.
+ * @func: the function to call for each node visited. If this function
+ *   returns %TRUE, the traversal is stopped.
+ * @user_data: user data to pass to the function.
+ * @version: the version of the tree to traverse.  If
+ *   g_tree_next_version() has not been used, then this is 0.  This value
+ *   must be at most the value returned by g_tree_current_version().
+ *
+ * Calls the given function for each of the key/value pairs in a
+ * specified @version of the #GTree.
+ * The function is passed the key and value of each pair, and the given
+ * @data parameter. The tree is traversed in sorted order.
+ *
+ * The tree may not be modified while iterating over it (you can't
+ * add/remove items). To remove all items matching a predicate, you need
+ * to add each item to a list in your #GTraverseFunc as you walk over
+ * the tree, then walk the list and remove each item.
+ **/
+void
+g_tree_foreach_v (GTree         *tree,
+                  GTraverseFunc  func,
+                  gpointer       user_data,
+                  guint          version)
 {
   GTreeNode *node;
 
   g_return_if_fail (tree != NULL);
+  g_return_if_fail (version <= tree->version);
   
   if (!tree->r[0].root)
     return;
 
-  node = g_tree_first_node (tree, tree->version);
+  node = g_tree_first_node (tree, version);
   
   while (node)
     {
       if ((*func) (node->data->key, node->data->value, user_data))
        break;
       
-      node = g_tree_node_next (node, tree->version);
+      node = g_tree_node_next (node, version);
     }
 }
 
@@ -1531,8 +1652,43 @@ g_tree_search (GTree         *tree,
               GCompareFunc   search_func,
               gconstpointer  user_data)
 {
-  return g_tree_search_related (tree, search_func, user_data,
-                                G_TREE_SEARCH_EXACT);
+  return g_tree_search_v (tree, search_func, user_data, tree->version);
+}
+
+/**
+ * g_tree_search_v:
+ * @tree: a #GTree.
+ * @search_func: a function used to search the #GTree.
+ * @user_data: the data passed as the second argument to the @search_func
+ * function.
+ * @version: the version of the tree within which to search.  If
+ *   g_tree_next_version() has not been used, then this is 0.  This value
+ *   must be at most the value returned by g_tree_current_version().
+ *
+ * Searches a specified @version of the #GTree using @search_func.
+ *
+ * The @search_func is called with a pointer to the key of a key/value pair in 
+ * the tree, and the passed in @user_data. If @search_func returns 0 for a
+ * key/value pair, then g_tree_search_func() will return the value of that
+ * pair. If @search_func returns -1,  searching will proceed among the
+ * key/value pairs that have a smaller key; if @search_func returns 1,
+ * searching will proceed among the key/value pairs that have a larger key.
+ *
+ * Return value: the value corresponding to the found key, or %NULL if the key 
+ * was not found.
+ **/
+gpointer
+g_tree_search_v (GTree         *tree,
+                 GCompareFunc   search_func,
+                 gconstpointer  user_data,
+                 guint          version)
+{
+  g_return_val_if_fail (tree != NULL, NULL);
+  g_return_val_if_fail (version <= tree->version, NULL);
+
+  return g_tree_node_search (g_tree_root_find_version (tree, version)->root,
+                             search_func, user_data, G_TREE_SEARCH_EXACT,
+                             version);
 }
 
 /**
@@ -1571,21 +1727,70 @@ g_tree_search_related (GTree          *tree,
                        GCompareFunc    search_func,
                        gconstpointer   user_data,
                        GTreeSearchType search_type)
+{
+  return g_tree_search_related_v (tree, search_func, user_data, search_type,
+                                  tree->version);
+}
+
+/**
+ * g_tree_search_related_v:
+ * @tree: a #GTree.
+ * @search_func: a function used to search the #GTree.
+ * @user_data: the data passed as the second argument to the @search_func
+ * @search_type: the search behavior if the @key is not present in the #GTree,
+ *   one of %G_TREE_SEARCH_EXACT, %G_TREE_SEARCH_SUCCESSOR, and
+ *   %G_TREE_SEARCH_PREDECESSOR.
+ * @version: the version of the tree within which to search.  If
+ *   g_tree_next_version() has not been used, then this is 0.  This value
+ *   must be at most the value returned by g_tree_current_version().
+ *
+ * Searches a specified @version of the #GTree using @search_func.
+ *
+ * The @search_func is called with a pointer to the key of a key/value pair in 
+ * the tree, and the passed in @user_data. If @search_func returns 0 for a
+ * key/value pair, then g_tree_search_func() will return the value of that
+ * pair. If @search_func returns -1,  searching will proceed among the
+ * key/value pairs that have a smaller key; if @search_func returns 1,
+ * searching will proceed among the key/value pairs that have a larger key.
+ * If @search_func never returns 0 before searching the entire height of the
+ * tree, then the @search_type will define what the function returns.
+ * If @search_type is %G_TREE_SEARCH_EXACT, the
+ * function will return %NULL.  If @search_type is
+ * %G_TREE_SEARCH_SUCCESSOR, the function will return the value corresponding
+ * to the last key at which @search_func returned -1.  This key would be the
+ * successor to the element being searched for. If @search_type is
+ * %G_TREE_SEARCH_PREDECESSOR, the function return the value corresponding
+ * to the last key at which @search_func returned 1.  This key would be the
+ * predecessor to the element being searched for.
+ *
+ * Return value: the value corresponding to the found key, or %NULL if no
+ * matching key was found.
+ **/
+gpointer
+g_tree_search_related_v (GTree          *tree,
+                         GCompareFunc    search_func,
+                         gconstpointer   user_data,
+                         GTreeSearchType search_type,
+                         guint           version)
 {
   g_return_val_if_fail (tree != NULL, NULL);
+  g_return_val_if_fail (version <= tree->version, NULL);
 
-  return g_tree_node_search (tree->r[0].root,
+  return g_tree_node_search (g_tree_root_find_version (tree, version)->root,
                              search_func, user_data, search_type,
-                             tree->version);
+                             version);
 }
 
 static gint
-g_tree_node_height(GTreeNode *node)
+g_tree_node_height (GTreeNode *node,
+                    guint version)
 {
+  GTreeNodeVersion *nv;
   gint l = 0, r = 0;
   if (node == NULL) return 0;
-  if (node->v[0].left) l = g_tree_node_height (node->v[0].left);
-  if (node->v[0].right) r = g_tree_node_height (node->v[0].right);
+  nv = g_tree_node_find_version (node, version);
+  if (nv->left) l = g_tree_node_height (nv->left, version);
+  if (nv->right) r = g_tree_node_height (nv->right, version);
   return 1 + MAX(l, r);
 }
 
@@ -1603,10 +1808,34 @@ g_tree_node_height(GTreeNode *node)
  **/
 gint
 g_tree_height (GTree *tree)
+{
+  return g_tree_height_v (tree, tree->version);
+}
+
+/**
+ * g_tree_height_v:
+ * @tree: a #GTree.
+ * @version: the version of the tree which should be queried. If
+ *   g_tree_next_version() has not been used, then this is 0.  This value
+ *   must be at most the value returned by g_tree_current_version().
+ *
+ * Gets the height of a specified @version of the #GTree.
+ *
+ * If the #GTree contains no nodes, the height is 0.
+ * If the #GTree contains only one root node the height is 1.
+ * If the root node has children the height is 2, etc.
+ *
+ * Return value: the height of the #GTree.
+ **/
+gint
+g_tree_height_v (GTree *tree,
+                 guint  version)
 {
   g_return_val_if_fail (tree != NULL, 0);
+  g_return_val_if_fail (version <= tree->version, 0);
 
-  return g_tree_node_height (tree->r[0].root);
+  return g_tree_node_height (g_tree_root_find_version (tree, version)->root,
+                             version);
 }
 
 /**
index 5b1a0f30531f9292050d9768a002f710330ea745..9fa6bdeab78e1a181c0a8f3dc4ad409b4cafe9c4 100644 (file)
@@ -83,11 +83,26 @@ gboolean g_tree_lookup_extended (GTree            *tree,
                                  gpointer         *value);
 gpointer g_tree_lookup_related  (GTree            *tree,
                                  gconstpointer     key,
-                                 GTreeSearchType   search_type,
-                                 guint             version);
+                                 GTreeSearchType   search_type);
+gpointer g_tree_lookup_v          (GTree            *tree,
+                                   gconstpointer     key,
+                                   guint version);
+gboolean g_tree_lookup_extended_v (GTree            *tree,
+                                   gconstpointer     lookup_key,
+                                   gpointer         *orig_key,
+                                   gpointer         *value,
+                                   guint             version);
+gpointer g_tree_lookup_related_v (GTree            *tree,
+                                  gconstpointer     key,
+                                  GTreeSearchType   search_type,
+                                  guint version);
 void     g_tree_foreach         (GTree            *tree,
                                  GTraverseFunc    func,
                                  gpointer         user_data);
+void     g_tree_foreach_v       (GTree            *tree,
+                                 GTraverseFunc    func,
+                                 gpointer         user_data,
+                                 guint             version);
 
 #ifndef G_DISABLE_DEPRECATED
 void     g_tree_traverse        (GTree            *tree,
@@ -103,7 +118,18 @@ gpointer g_tree_search_related  (GTree            *tree,
                                  GCompareFunc      search_func,
                                  gconstpointer     user_data,
                                  GTreeSearchType   search_type);
+gpointer g_tree_search_v        (GTree            *tree,
+                                 GCompareFunc      search_func,
+                                 gconstpointer     user_data,
+                                 guint             version);
+gpointer g_tree_search_related_v(GTree            *tree,
+                                 GCompareFunc      search_func,
+                                 gconstpointer     user_data,
+                                 GTreeSearchType   search_type,
+                                 guint             version);
 gint     g_tree_height          (GTree            *tree);
+gint     g_tree_height_v        (GTree            *tree,
+                                 guint             version);
 gint     g_tree_nnodes          (GTree            *tree);
 
 G_END_DECLS
index 70041ac0f98f0e1b95c87ef5237b85af6cf663e0..aa9212d69cd64cb0e49a88629469d5097b243f28 100644 (file)
@@ -163,16 +163,16 @@ main (int   argc,
     gchar *r;
 
     r = g_tree_lookup_related (tree, &chars[i],
-                               G_TREE_SEARCH_SUCCESSOR, 0);
+                               G_TREE_SEARCH_SUCCESSOR);
     g_assert((i == n-1 && r == NULL) || (r && *r == chars[i+1]));
 
     g_tree_insert (tree, &chars[i], &chars[i]);
 
     r = g_tree_lookup_related (tree, &chars[i],
-                               G_TREE_SEARCH_PREDECESSOR, 0);
+                               G_TREE_SEARCH_PREDECESSOR);
     g_assert(r && *r == chars[i]);
     r = g_tree_lookup_related (tree, &chars[i],
-                               G_TREE_SEARCH_SUCCESSOR, 0);
+                               G_TREE_SEARCH_SUCCESSOR);
     g_assert(r && *r == chars[i]);
   }
   g_assert(n == g_tree_nnodes(tree));
@@ -266,33 +266,37 @@ main (int   argc,
     gchar *r;
     gint j;
 
-    r = g_tree_lookup_related (tree, &chars[i],
-                               G_TREE_SEARCH_PREDECESSOR, i);
+    r = g_tree_lookup_related_v (tree, &chars[i],
+                                 G_TREE_SEARCH_PREDECESSOR, i);
     g_assert((i == 0 && r == NULL) || (r && *r == chars[i-1]));
 
     if (i > 0)
       {
-        r = g_tree_lookup_related (tree, &chars[i],
-                                   G_TREE_SEARCH_PREDECESSOR, i-1);
+        r = g_tree_lookup_related_v (tree, &chars[i],
+                                     G_TREE_SEARCH_PREDECESSOR, i-1);
         g_assert((i == 1 && r == NULL) || (r && *r == chars[i-2]));
       }
 
     g_tree_next_version (tree);
     g_tree_insert (tree, &chars[i], &chars[i]);
 
-    r = g_tree_lookup_related (tree, &chars[i],
-                               G_TREE_SEARCH_PREDECESSOR, i+1);
+    r = g_tree_lookup_related_v (tree, &chars[i],
+                                 G_TREE_SEARCH_PREDECESSOR, i+1);
     g_assert(r && *r == chars[i]);
-    r = g_tree_lookup_related (tree, &chars[i],
-                               G_TREE_SEARCH_SUCCESSOR, i+1);
+    r = g_tree_lookup_related_v (tree, &chars[i],
+                                 G_TREE_SEARCH_SUCCESSOR, i+1);
     g_assert(r && *r == chars[i]);
 
     for (j = 0; j <= i+1; ++j)
       {
-        r = g_tree_lookup_related (tree, &chars[i],
-                                   G_TREE_SEARCH_PREDECESSOR, j);
+        r = g_tree_lookup_related_v (tree, &chars[i],
+                                     G_TREE_SEARCH_PREDECESSOR, j);
         g_assert((j == 0 && r == NULL) || (r && *r == chars[j-1]));
       }
+
+    traverse_num = 0;
+    g_tree_foreach_v (tree, my_traverse, NULL, i+1);
+    g_assert(traverse_num == i+1);
   }
   g_assert(i == g_tree_nnodes(tree));
 
@@ -337,8 +341,8 @@ main (int   argc,
 
       for (j = 0; chars[j]; ++j)
         {
-          r = g_tree_lookup_related (tree, &chars[j],
-                                     G_TREE_SEARCH_PREDECESSOR, i);
+          r = g_tree_lookup_related_v (tree, &chars[j],
+                                       G_TREE_SEARCH_PREDECESSOR, i);
 
           if (i == 0) /* the tree was empty */
             g_assert (r == NULL);
@@ -389,8 +393,8 @@ main (int   argc,
 
       for (j = 0; chars[j]; j++)
         {
-          r = g_tree_lookup_related (tree, &chars[j],
-                                     G_TREE_SEARCH_PREDECESSOR, i);
+          r = g_tree_lookup_related_v (tree, &chars[j],
+                                       G_TREE_SEARCH_PREDECESSOR, i);
           g_assert (r && *r == chars[MIN(j,i)]);
         }
     }
@@ -408,8 +412,8 @@ main (int   argc,
 
           for (j = 0; chars[j]; j++)
             {
-              r = g_tree_lookup_related (tree, &chars[j],
-                                         G_TREE_SEARCH_PREDECESSOR, k);
+              r = g_tree_lookup_related_v (tree, &chars[j],
+                                           G_TREE_SEARCH_PREDECESSOR, k);
               g_assert ((k <= i/2*2 && r == NULL) ||
                         (k > i/2*2 && r && *r == chars[MIN(j,k)]));
             }
@@ -425,8 +429,8 @@ main (int   argc,
 
       for (j = 0; chars[j]; j++)
         {
-          r = g_tree_lookup_related (tree, &chars[j],
-                                     G_TREE_SEARCH_PREDECESSOR, i);
+          r = g_tree_lookup_related_v (tree, &chars[j],
+                                       G_TREE_SEARCH_PREDECESSOR, i);
           g_assert (r == NULL);
         }
     }