You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2012/11/16 21:04:32 UTC

svn commit: r1410554 - in /subversion/branches/tree-read-api/subversion: include/svn_tree.h libsvn_subr/tree.c

Author: julianfoad
Date: Fri Nov 16 20:04:31 2012
New Revision: 1410554

URL: http://svn.apache.org/viewvc?rev=1410554&view=rev
Log:
On the 'tree-read-api' branch: Change the tree traversal method to
per-directory visiting -- similar to Python's os.walk() function. This
inreases efficiency and flexibility. We provide a per-node walker built
on top of it.

* subversion/include/svn_tree.h
  (svn_tree_dir_visit_func_t, svn_tree_walk_dirs): New function type and
    function for per-directory walking.
  (svn_tree_walk_func_t): Remove the TODO comment relating to this change.

* subversion/libsvn_subr/tree.c
  (walk_dirs): Renamed from 'walk_tree' and adapted to walk only directories.
  (svn_tree_walk_dirs): New function.
  (per_dir_to_per_node_baton_t, per_dir_to_per_node_cb): New callback baton
    and function.
  (svn_tree_walk): Re-implement using walk_dirs() and the per_dir_to_per_node
    callback.

Modified:
    subversion/branches/tree-read-api/subversion/include/svn_tree.h
    subversion/branches/tree-read-api/subversion/libsvn_subr/tree.c

Modified: subversion/branches/tree-read-api/subversion/include/svn_tree.h
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/include/svn_tree.h?rev=1410554&r1=1410553&r2=1410554&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/include/svn_tree.h (original)
+++ subversion/branches/tree-read-api/subversion/include/svn_tree.h Fri Nov 16 20:04:31 2012
@@ -58,13 +58,51 @@ svn_tree_get_node_by_relpath(svn_tree_no
 
 /** A tree-walker callback.
  *
+ * The callback receives one directory node being visited, @a dir_node, and
+ * the closure @a dir_visit_baton.  It also receives two lists of nodes
+ * which together contain all the children to be visited.  The
+ * subdirectories are in @a subdirs, and the non-directory children in @a
+ * files.
+ *
+ * This is modeled on Python's 'os.walk' function.
+ *
+ * ### TODO? "The callback may modify the list of subdirs (in place) in
+ * order to influence the order and scope of traversal: the walker will
+ * recurse into the subdirs that are in the list when the callback returns."
+ *
+ * @a scratch_pool is available for use within the function until it returns.
+ */
+typedef svn_error_t *
+(*svn_tree_dir_visit_func_t)(svn_tree_node_t *dir_node,
+                             apr_array_header_t *subdirs,
+                             apr_array_header_t *files,
+                             void *dir_visit_baton,
+                             apr_pool_t *scratch_pool);
+
+/** Walk a subdirectory of a generic tree, starting at @a root_dir_node.
+ *
+ * ...
+ *
+ * Call @a dir_visit_func for each visited node, passing @a dir_visit_baton
+ * and the tree node object.
+ *
+ * If @a cancel_func is not null, call it with @a cancel_baton to check for
+ * cancellation.
+ */
+svn_error_t *
+svn_tree_walk_dirs(svn_tree_node_t *root_dir_node,
+                   svn_depth_t depth,
+                   svn_tree_dir_visit_func_t dir_visit_func,
+                   void *dir_visit_baton,
+                   svn_cancel_func_t cancel_func,
+                   void *cancel_baton,
+                   apr_pool_t *scratch_pool);
+
+/** A tree-walker callback.
+ *
  * This callback presents one tree node object being visited, @a node,
  * and the closure @a walk_baton.
  *
- * ### TODO: Consider re-modelling, more like Python's OS tree walker
- *     that passes a list of subdirs and a list of non-dir children,
- *     and lets the list of subdirs be modified before it recurses into them.
- *
  * @a scratch_pool is available for use within the function until it returns.
  */
 typedef svn_error_t *(*svn_tree_walk_func_t)(svn_tree_node_t *node,

Modified: subversion/branches/tree-read-api/subversion/libsvn_subr/tree.c
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/libsvn_subr/tree.c?rev=1410554&r1=1410553&r2=1410554&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/libsvn_subr/tree.c (original)
+++ subversion/branches/tree-read-api/subversion/libsvn_subr/tree.c Fri Nov 16 20:04:31 2012
@@ -87,40 +87,48 @@ tree_node_get_kind_or_unknown(svn_node_k
   return svn_error_trace(err);
 }
 
-/* The body of svn_tree_walk(), which see.
+/* The body of svn_tree_walk_dirs(), which see.
  */
 static svn_error_t *
-walk_tree(svn_tree_node_t *node,
+walk_dirs(svn_tree_node_t *dir_node,
           svn_depth_t depth,
-          svn_tree_walk_func_t walk_func,
+          svn_tree_dir_visit_func_t walk_func,
           void *walk_baton,
           svn_cancel_func_t cancel_func,
           void *cancel_baton,
           apr_pool_t *scratch_pool)
 {
-  svn_node_kind_t kind;
+  apr_array_header_t *dirs
+    = apr_array_make(scratch_pool, 1, sizeof(svn_tree_node_t *));
+  apr_array_header_t *files
+    = apr_array_make(scratch_pool, 1, sizeof(svn_tree_node_t *));
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+  int i;
+
+#ifdef SVN_DEBUG
+  {
+    svn_node_kind_t kind;
+
+    SVN_ERR(tree_node_get_kind_or_unknown(&kind, dir_node, scratch_pool));
+    assert(kind == svn_node_dir);
+  }
+#endif
 
   if (cancel_func)
     SVN_ERR(cancel_func(cancel_baton));
 
-  SVN_ERR(tree_node_get_kind_or_unknown(&kind, node, scratch_pool));
-
-  SVN_ERR(walk_func(node, walk_baton, scratch_pool));
-
-  /* Recurse */
-  if (kind == svn_node_dir && depth >= svn_depth_files)
+  if (depth >= svn_depth_files)
     {
       apr_hash_t *children;
       apr_array_header_t *children_sorted;
-      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
-      int i;
 
-      SVN_ERR(svn_tree_node_read_dir(node, &children, NULL /* props */,
+      SVN_ERR(svn_tree_node_read_dir(dir_node, &children, NULL /* props */,
                                      scratch_pool, scratch_pool));
       children_sorted = svn_sort__hash(children,
                                        svn_sort_compare_items_lexically,
                                        scratch_pool);
 
+      /* Categorize the children into dirs and non-dirs */
       for (i = 0; i < children_sorted->nelts; i++)
         {
           const svn_sort__item_t *item
@@ -130,17 +138,87 @@ walk_tree(svn_tree_node_t *node,
 
           svn_pool_clear(iterpool);
           SVN_ERR(tree_node_get_kind_or_unknown(&child_kind, child, iterpool));
-          if (depth >= svn_depth_immediates || child_kind == svn_node_file)
+          if (child_kind == svn_node_dir)
             {
-              SVN_ERR(walk_tree(child,
-                                depth == svn_depth_infinity ? depth
-                                       : svn_depth_empty,
-                                walk_func, walk_baton,
-                                cancel_func, cancel_baton, iterpool));
+              APR_ARRAY_PUSH(dirs, svn_tree_node_t *) = child;
+            }
+          else
+            {
+              if (depth >= svn_depth_immediates)
+                APR_ARRAY_PUSH(files, svn_tree_node_t *) = child;
             }
         }
-      svn_pool_destroy(iterpool);
     }
+
+  /* Call the visitor callback */
+  SVN_ERR(walk_func(dir_node, dirs, files, walk_baton, scratch_pool));
+
+  /* Recurse */
+  for (i = 0; i < dirs->nelts; i++)
+    {
+      const svn_sort__item_t *item
+        = &APR_ARRAY_IDX(dirs, i, svn_sort__item_t);
+      svn_tree_node_t *child = item->value;
+
+      svn_pool_clear(iterpool);
+      SVN_ERR(walk_dirs(child,
+                        depth == svn_depth_infinity ? depth
+                               : svn_depth_empty,
+                        walk_func, walk_baton,
+                        cancel_func, cancel_baton, iterpool));
+    }
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_tree_walk_dirs(svn_tree_node_t *root_dir_node,
+              svn_depth_t depth,
+              svn_tree_dir_visit_func_t dir_visit_func,
+              void *dir_visit_baton,
+              svn_cancel_func_t cancel_func,
+              void *cancel_baton,
+              apr_pool_t *scratch_pool)
+{
+  SVN_ERR(walk_dirs(root_dir_node, depth, dir_visit_func, dir_visit_baton,
+                    cancel_func, cancel_baton, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+/* Baton for per_dir_to_per_node_cb() */
+typedef struct per_dir_to_per_node_baton_t
+{
+  svn_tree_walk_func_t node_walk_func;
+  void *node_walk_baton;
+} per_dir_to_per_node_baton_t;
+
+/* A dir-walk callback that calls a per-node callback. */
+static svn_error_t *
+per_dir_to_per_node_cb(svn_tree_node_t *dir_node,
+                       apr_array_header_t *subdirs,
+                       apr_array_header_t *files,
+                       void *dir_visit_baton,
+                       apr_pool_t *scratch_pool)
+{
+  per_dir_to_per_node_baton_t *b = dir_visit_baton;
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+  int i;
+
+  /* Visit this dir */
+  SVN_ERR(b->node_walk_func(dir_node, b->node_walk_baton, scratch_pool));
+
+  /* Visit the non-directory children */
+  for (i = 0; i < files->nelts; i++)
+    {
+      svn_tree_node_t *child_node = APR_ARRAY_IDX(files, i, svn_tree_node_t *);
+
+      svn_pool_clear(iterpool);
+      SVN_ERR(b->node_walk_func(child_node, b->node_walk_baton, iterpool));
+    }
+  svn_pool_destroy(iterpool);
+
   return SVN_NO_ERROR;
 }
 
@@ -154,11 +232,25 @@ svn_tree_walk(svn_tree_t *tree,
               apr_pool_t *scratch_pool)
 {
   svn_tree_node_t *node;
+  svn_node_kind_t kind;
 
   SVN_ERR(svn_tree_get_root_node(&node, tree, scratch_pool, scratch_pool));
-  SVN_ERR(walk_tree(node, depth,
-                    walk_func, walk_baton,
-                    cancel_func, cancel_baton, scratch_pool));
+  SVN_ERR(svn_tree_node_get_kind(node, &kind, scratch_pool));
+
+  if (kind == svn_node_dir)
+    {
+      per_dir_to_per_node_baton_t b;
+
+      b.node_walk_func = walk_func;
+      b.node_walk_baton = walk_baton;
+      SVN_ERR(walk_dirs(node, depth, per_dir_to_per_node_cb, &b,
+                        cancel_func, cancel_baton, scratch_pool));
+    }
+  else
+    {
+      SVN_ERR(walk_func(node, walk_baton, scratch_pool));
+    }
+
   return SVN_NO_ERROR;
 }