Author: julianfoad
Date: Mon Jan 21 13:10:37 2019
New Revision: 1851738

URL: http://svn.apache.org/viewvc?rev=1851738&view=rev
Log:
Teach the delta editor path driver to work incrementally.

Instead of passing in the complete list of paths to be driven all at once,
this adds the option of passing in one path at a time.

* subversion/include/svn_delta.h,
  subversion/libsvn_delta/path_driver.c
  (svn_delta_path_driver2): Rewrite to use the incremental API.
  (svn_delta_path_driver_state_t,
   svn_delta_path_driver_start,
   svn_delta_path_driver_step,
   svn_delta_path_driver_finish): New.

Modified:
    subversion/trunk/subversion/include/svn_delta.h
    subversion/trunk/subversion/libsvn_delta/path_driver.c

Modified: subversion/trunk/subversion/include/svn_delta.h
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_delta.h?rev=1851738&r1=1851737&r2=1851738&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_delta.h (original)
+++ subversion/trunk/subversion/include/svn_delta.h Mon Jan 21 13:10:37 2019
@@ -1313,18 +1313,24 @@ typedef svn_error_t *(*svn_delta_path_dr
 
 
 /** Drive @a editor (with its @a edit_baton) to visit each path in @a paths.
+ *
  * As each path is hit as part of the editor drive, use
  * @a callback_func and @a callback_baton to allow the caller to handle
  * the portion of the editor drive related to that path.
  *
  * Each path in @a paths is a (const char *) relpath, relative
- * to the root path of the @a edit. The editor drive will be
+ * to the root path of the edit. The editor drive will be
  * performed in the same order as @a paths. The paths should be sorted
- * using something like svn_sort_compare_paths to ensure that a depth-first
- * pattern is observed for directory/file baton creation. If @a sort_paths
+ * using something like svn_sort_compare_paths() to ensure that each
+ * directory in the depth-first walk is visited only once. If @a sort_paths
  * is set, the function will sort the paths for you. Some callers may need
  * further customization of the order (ie. libsvn_delta/compat.c).
  *
+ * If the first target path (after any requested sorting) is @c "" (the
+ * root of the edit), the callback function will be responsible for
+ * calling the editor's @c open_root method; otherwise, this function
+ * will call @c open_root.
+ *
  * Use @a scratch_pool for all necessary allocations.
  *
  * @since New in 1.8.
@@ -1358,6 +1364,80 @@ svn_delta_path_driver(const svn_delta_ed
                       void *callback_baton,
                       apr_pool_t *scratch_pool);
 
+
+/** A state object for the path driver that is obtained from
+ * svn_delta_path_driver_start() and driven by
+ * svn_delta_path_driver_step() and svn_delta_path_driver_finish().
+ *
+ * @since New in 1.12.
+ */
+typedef struct svn_delta_path_driver_state_t svn_delta_path_driver_state_t;
+
+/** Return a path driver object that can drive @a editor (with its
+ * @a edit_baton) to visit a series of paths.
+ *
+ * As each path is hit as part of the editor drive, the path driver will
+ * call @a callback_func and @a callback_baton to allow the caller to handle
+ * the portion of the editor drive related to that path.
+ *
+ * This will not call the editor's open_root method; for that, see
+ * svn_delta_path_driver_step().
+ *
+ * @since New in 1.12.
+ */
+svn_error_t *
+svn_delta_path_driver_start(svn_delta_path_driver_state_t **state_p,
+                            const svn_delta_editor_t *editor,
+                            void *edit_baton,
+                            svn_delta_path_driver_cb_func_t callback_func,
+                            void *callback_baton,
+                            apr_pool_t *result_pool);
+
+/** Visit @a path.
+ *
+ * @a state is the object returned by svn_delta_path_driver_start().
+ *
+ * @a path is a relpath relative to the root path of the edit.
+ *
+ * This function uses the editor and the callback that were originally
+ * supplied to svn_delta_path_driver_start().
+ *
+ * This drives the editor in a depth-first order, closing and then opening
+ * directories if necessary to move from the last visited path to the new
+ * path, as required by the editor driving rules.
+ *
+ * This then calls the callback to allow the caller to handle
+ * the portion of the editor drive related to that path.
+ *
+ * If the first path to visit is @c "" (the root of the edit), the
+ * callback function will be responsible for calling the editor's
+ * @c open_root method; otherwise, this function will call @c open_root.
+ *
+ * The order of paths to visit should in general be sorted using something
+ * like svn_sort_compare_paths() to ensure that each directory in the
+ * depth-first walk is visited only once. Some editors may rely on such a
+ * restriction.
+ *
+ * @since New in 1.12.
+ */
+svn_error_t *
+svn_delta_path_driver_step(svn_delta_path_driver_state_t *state,
+                           const char *path,
+                           apr_pool_t *scratch_pool);
+
+/** Finish driving the editor.
+ *
+ * @a state is the object returned by svn_delta_path_driver_start().
+ *
+ * This drives the editor to close any open directories and then calls
+ * the editor's @c close_edit method.
+ *
+ * @since New in 1.12.
+ */
+svn_error_t *
+svn_delta_path_driver_finish(svn_delta_path_driver_state_t *state,
+                             apr_pool_t *scratch_pool);
+
 /** @} */
 
 

Modified: subversion/trunk/subversion/libsvn_delta/path_driver.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_delta/path_driver.c?rev=1851738&r1=1851737&r2=1851738&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_delta/path_driver.c (original)
+++ subversion/trunk/subversion/libsvn_delta/path_driver.c Mon Jan 21 13:10:37 
2019
@@ -139,13 +139,10 @@ svn_delta_path_driver2(const svn_delta_e
                        void *callback_baton,
                        apr_pool_t *pool)
 {
-  apr_array_header_t *db_stack = apr_array_make(pool, 4, sizeof(void *));
-  const char *last_path = NULL;
+  svn_delta_path_driver_state_t *state;
   int i = 0;
-  void *parent_db = NULL, *db = NULL;
   const char *path;
   apr_pool_t *subpool, *iterpool;
-  dir_stack_t *item;
 
   /* Do nothing if there are no paths. */
   if (! paths->nelts)
@@ -162,137 +159,189 @@ svn_delta_path_driver2(const svn_delta_e
       paths = sorted;
     }
 
-  item = apr_pcalloc(subpool, sizeof(*item));
-
-  /* If the root of the edit is also a target path, we want to call
-     the callback function to let the user open the root directory and
-     do what needs to be done.  Otherwise, we'll do the open_root()
-     ourselves. */
-  path = APR_ARRAY_IDX(paths, 0, const char *);
-  if (svn_path_is_empty(path))
-    {
-      SVN_ERR(callback_func(&db, NULL, callback_baton, path, subpool));
-      last_path = path;
-      i++;
-    }
-  else
-    {
-      SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM, subpool, &db));
-    }
-  item->pool = subpool;
-  item->dir_baton = db;
-  APR_ARRAY_PUSH(db_stack, void *) = item;
+  SVN_ERR(svn_delta_path_driver_start(&state,
+                                      editor, edit_baton,
+                                      callback_func, callback_baton,
+                                      pool));
 
   /* Now, loop over the commit items, traversing the URL tree and
      driving the editor. */
   for (; i < paths->nelts; i++)
     {
-      const char *pdir;
-      const char *common = "";
-      size_t common_len;
-
       /* Clear the iteration pool. */
       svn_pool_clear(iterpool);
 
       /* Get the next path. */
       path = APR_ARRAY_IDX(paths, i, const char *);
 
-      /*** Step A - Find the common ancestor of the last path and the
-           current one.  For the first iteration, this is just the
-           empty string. ***/
-      if (i > 0)
-        common = (last_path[0] == '/')
-          ? svn_fspath__get_longest_ancestor(last_path, path, iterpool)
-          : svn_relpath_get_longest_ancestor(last_path, path, iterpool);
-      common_len = strlen(common);
-
-      /*** Step B - Close any directories between the last path and
-           the new common ancestor, if any need to be closed.
-           Sometimes there is nothing to do here (like, for the first
-           iteration, or when the last path was an ancestor of the
-           current one). ***/
-      if ((i > 0) && (strlen(last_path) > common_len))
-        {
-          const char *rel = last_path + (common_len ? (common_len + 1) : 0);
-          int count = count_components(rel);
-          while (count--)
-            {
-              SVN_ERR(pop_stack(db_stack, editor));
-            }
-        }
+      SVN_ERR(svn_delta_path_driver_step(state, path, iterpool));
+    }
+
+  /* Destroy the iteration subpool. */
+  svn_pool_destroy(iterpool);
 
-      /*** Step C - Open any directories between the common ancestor
-           and the parent of the current path. ***/
-      if (*path == '/')
-        pdir = svn_fspath__dirname(path, iterpool);
-      else
-        pdir = svn_relpath_dirname(path, iterpool);
+  SVN_ERR(svn_delta_path_driver_finish(state, pool));
 
-      if (strlen(pdir) > common_len)
-        {
-          const char *piece = pdir + common_len + 1;
+  return SVN_NO_ERROR;
+}
 
-          while (1)
-            {
-              const char *rel = pdir;
-
-              /* Find the first separator. */
-              piece = strchr(piece, '/');
-
-              /* Calculate REL as the portion of PDIR up to (but not
-                 including) the location to which PIECE is pointing. */
-              if (piece)
-                rel = apr_pstrmemdup(iterpool, pdir, piece - pdir);
-
-              /* Open the subdirectory. */
-              SVN_ERR(open_dir(db_stack, editor, rel, pool));
-
-              /* If we found a '/', advance our PIECE pointer to
-                 character just after that '/'.  Otherwise, we're
-                 done.  */
-              if (piece)
-                piece++;
-              else
-                break;
-            }
-        }
+struct svn_delta_path_driver_state_t
+{
+  const svn_delta_editor_t *editor;
+  void *edit_baton;
+  svn_delta_path_driver_cb_func_t callback_func;
+  void *callback_baton;
+  apr_array_header_t *db_stack;
+  const char *last_path;
+  apr_pool_t *pool;  /* at least the lifetime of the entire drive */
+};
+
+svn_error_t *
+svn_delta_path_driver_start(svn_delta_path_driver_state_t **state_p,
+                            const svn_delta_editor_t *editor,
+                            void *edit_baton,
+                            svn_delta_path_driver_cb_func_t callback_func,
+                            void *callback_baton,
+                            apr_pool_t *pool)
+{
+  svn_delta_path_driver_state_t *state = apr_pcalloc(pool, sizeof(*state));
 
-      /*** Step D - Tell our caller to handle the current path. ***/
-      item = APR_ARRAY_IDX(db_stack, db_stack->nelts - 1, void *);
-      parent_db = item->dir_baton;
-      subpool = svn_pool_create(pool);
-      SVN_ERR(callback_func(&db, parent_db, callback_baton, path, subpool));
-      if (db)
+  state->editor = editor;
+  state->edit_baton = edit_baton;
+  state->callback_func = callback_func;
+  state->callback_baton = callback_baton;
+  state->db_stack = apr_array_make(pool, 4, sizeof(void *));
+  state->last_path = NULL;
+  state->pool = pool;
+
+  *state_p = state;
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_delta_path_driver_step(svn_delta_path_driver_state_t *state,
+                           const char *path,
+                           apr_pool_t *scratch_pool)
+{
+  const char *pdir;
+  const char *common = "";
+  size_t common_len;
+  apr_pool_t *subpool;
+  dir_stack_t *item;
+  void *parent_db = NULL, *db = NULL;
+
+  /* If the first target path is not the root of the edit, we must first
+     call open_root() ourselves. (If the first target path is the root of
+     the edit, then we expect the user's callback to do so.) */
+  if (!state->last_path && !svn_path_is_empty(path))
+    {
+      subpool = svn_pool_create(state->pool);
+      SVN_ERR(state->editor->open_root(state->edit_baton, SVN_INVALID_REVNUM,
+                                       subpool, &db));
+      item = apr_pcalloc(subpool, sizeof (*item));
+      item->pool = subpool;
+      item->dir_baton = db;
+      APR_ARRAY_PUSH(state->db_stack, void *) = item;
+    }
+
+  /*** Step A - Find the common ancestor of the last path and the
+       current one.  For the first iteration, this is just the
+       empty string. ***/
+  if (state->last_path)
+    common = (state->last_path[0] == '/')
+    ? svn_fspath__get_longest_ancestor(state->last_path, path, scratch_pool)
+    : svn_relpath_get_longest_ancestor(state->last_path, path, scratch_pool);
+  common_len = strlen(common);
+
+  /*** Step B - Close any directories between the last path and
+       the new common ancestor, if any need to be closed.
+       Sometimes there is nothing to do here (like, for the first
+       iteration, or when the last path was an ancestor of the
+       current one). ***/
+  if ((state->last_path) && (strlen(state->last_path) > common_len))
+    {
+      const char *rel = state->last_path + (common_len ? (common_len + 1) : 0);
+      int count = count_components(rel);
+      while (count--)
         {
-          item = apr_pcalloc(subpool, sizeof(*item));
-          item->dir_baton = db;
-          item->pool = subpool;
-          APR_ARRAY_PUSH(db_stack, void *) = item;
+          SVN_ERR(pop_stack(state->db_stack, state->editor));
         }
-      else
+    }
+
+  /*** Step C - Open any directories between the common ancestor
+       and the parent of the current path. ***/
+  if (*path == '/')
+    pdir = svn_fspath__dirname(path, scratch_pool);
+  else
+    pdir = svn_relpath_dirname(path, scratch_pool);
+
+  if (strlen(pdir) > common_len)
+    {
+      const char *piece = pdir + common_len + 1;
+
+      while (1)
         {
-          svn_pool_destroy(subpool);
+          const char *rel = pdir;
+
+          /* Find the first separator. */
+          piece = strchr(piece, '/');
+
+          /* Calculate REL as the portion of PDIR up to (but not
+             including) the location to which PIECE is pointing. */
+          if (piece)
+            rel = apr_pstrmemdup(scratch_pool, pdir, piece - pdir);
+
+          /* Open the subdirectory. */
+          SVN_ERR(open_dir(state->db_stack, state->editor, rel, state->pool));
+
+          /* If we found a '/', advance our PIECE pointer to
+             character just after that '/'.  Otherwise, we're
+             done.  */
+          if (piece)
+            piece++;
+          else
+            break;
         }
+    }
 
-      /*** Step E - Save our state for the next iteration.  If our
-           caller opened or added PATH as a directory, that becomes
-           our LAST_PATH.  Otherwise, we use PATH's parent
-           directory. ***/
-
-      /* NOTE:  The variable LAST_PATH needs to outlive the loop. */
-      if (db)
-        last_path = path; /* lives in a pool outside our control. */
-      else
-        last_path = apr_pstrdup(pool, pdir); /* duping into POOL. */
+  /*** Step D - Tell our caller to handle the current path. ***/
+  if (state->db_stack->nelts)
+    {
+      item = APR_ARRAY_IDX(state->db_stack, state->db_stack->nelts - 1, void 
*);
+      parent_db = item->dir_baton;
+    }
+  subpool = svn_pool_create(state->pool);
+  SVN_ERR(state->callback_func(&db, parent_db, state->callback_baton,
+                               path, subpool));
+  if (db)
+    {
+      item = apr_pcalloc(subpool, sizeof (*item));
+      item->dir_baton = db;
+      item->pool = subpool;
+      APR_ARRAY_PUSH(state->db_stack, void *) = item;
+    }
+  else
+    {
+      svn_pool_destroy(subpool);
     }
 
-  /* Destroy the iteration subpool. */
-  svn_pool_destroy(iterpool);
+  /*** Step E - Save our state for the next iteration.  If our
+       caller opened or added PATH as a directory, that becomes
+       our LAST_PATH.  Otherwise, we use PATH's parent
+       directory. ***/
+  state->last_path = apr_pstrdup(state->pool, db ? path : pdir);
 
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_delta_path_driver_finish(svn_delta_path_driver_state_t *state,
+                             apr_pool_t *scratch_pool)
+{
   /* Close down any remaining open directory batons. */
-  while (db_stack->nelts)
+  while (state->db_stack->nelts)
     {
-      SVN_ERR(pop_stack(db_stack, editor));
+      SVN_ERR(pop_stack(state->db_stack, state->editor));
     }
 
   return SVN_NO_ERROR;


Reply via email to