You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by rh...@apache.org on 2013/07/04 22:39:29 UTC

svn commit: r1499863 - /subversion/trunk/subversion/libsvn_ra_serf/replay.c

Author: rhuijben
Date: Thu Jul  4 20:39:29 2013
New Revision: 1499863

URL: http://svn.apache.org/r1499863
Log:
Convert ra_serfs 'svn_ra_replay' support to the transition based xml parser.

* subversion/libsvn_ra_serf/replay.c
  (includes): Add svn_hash.h.
  (replay_state_e): Add new states. Use common prefix.

  (replay_ttable): New transition table.
  (replay_node_t): New struct, containing per file/dir state.

  (replay_context_t): Renamed to...
  (revision_report_t): ... this. Remove per item state and now unused vars.

  (push_state): Remove function.

  (start_replay,
   end_replay,
   cdata_replay): Remove functions fold functionality in these...

  (replay_opened,
   replay_closed,
   replay_cdata): New functions. Add more error checking based on the node
      stack.

  (replay_done): New function. Handles magic that was part of the old xml parser.
  (create_replay_body): Use passed pool for tempvars instead of long lived ctx.

  (svn_ra_serf__replay): Store context on the stack. Use handler->done.
    Use new parser.

  (svn_ra_serf__replay_range): Move variables referenced from contexts to the outer
    scope, to show they are used out of scope. Use new parser. Reduce scope of
    done handling variables.

Modified:
    subversion/trunk/subversion/libsvn_ra_serf/replay.c

Modified: subversion/trunk/subversion/libsvn_ra_serf/replay.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_serf/replay.c?rev=1499863&r1=1499862&r2=1499863&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_serf/replay.c (original)
+++ subversion/trunk/subversion/libsvn_ra_serf/replay.c Thu Jul  4 20:39:29 2013
@@ -29,6 +29,7 @@
 #include "svn_pools.h"
 #include "svn_ra.h"
 #include "svn_dav.h"
+#include "svn_hash.h"
 #include "svn_xml.h"
 #include "../libsvn_ra/ra_loader.h"
 #include "svn_config.h"
@@ -46,55 +47,92 @@
  * This enum represents the current state of our XML parsing.
  */
 typedef enum replay_state_e {
-  NONE = 0,
-  REPORT,
-  OPEN_DIR,
-  ADD_DIR,
-  OPEN_FILE,
-  ADD_FILE,
-  DELETE_ENTRY,
-  APPLY_TEXTDELTA,
-  CHANGE_PROP
+  INITIAL = XML_STATE_INITIAL,
+
+  REPLAY_REPORT,
+  REPLAY_TARGET_REVISION,
+  REPLAY_OPEN_ROOT,
+  REPLAY_OPEN_DIRECTORY,
+  REPLAY_OPEN_FILE,
+  REPLAY_ADD_DIRECTORY,
+  REPLAY_ADD_FILE,
+  REPLAY_DELETE_ENTRY,
+  REPLAY_CLOSE_FILE,
+  REPLAY_CLOSE_DIRECTORY,
+  REPLAY_CHANGE_DIRECTORY_PROP,
+  REPLAY_CHANGE_FILE_PROP,
+  REPLAY_APPLY_TEXTDELTA,
 } replay_state_e;
 
-typedef struct replay_info_t replay_info_t;
+#define S_ SVN_XML_NAMESPACE
+static const svn_ra_serf__xml_transition_t replay_ttable[] = {
+  { INITIAL, S_, "editor-report", REPLAY_REPORT,
+    FALSE, { NULL }, TRUE },
 
-struct replay_info_t {
-  apr_pool_t *pool;
+  /* Replay just throws every operation as xml element directly
+     in the replay report, so we can't really use the nice exit
+     handling of the transition parser to handle clean callbacks */
 
-  void *baton;
-  svn_stream_t *stream;
+  { REPLAY_REPORT, S_, "target-revision", REPLAY_TARGET_REVISION,
+    FALSE, { "rev", NULL }, TRUE },
 
-  replay_info_t *parent;
-};
+  { REPLAY_REPORT, S_, "open-root", REPLAY_OPEN_ROOT,
+    FALSE, { "rev", NULL }, TRUE },
 
-typedef svn_error_t *
-(*change_prop_t)(void *baton,
-                 const char *name,
-                 const svn_string_t *value,
-                 apr_pool_t *pool);
+  { REPLAY_REPORT, S_, "open-directory", REPLAY_OPEN_DIRECTORY,
+    FALSE, { "name", "rev", NULL }, TRUE },
 
-typedef struct prop_info_t {
-  apr_pool_t *pool;
+  { REPLAY_REPORT, S_, "open-file", REPLAY_OPEN_FILE,
+    FALSE, { "name", "rev", NULL }, TRUE },
 
-  change_prop_t change;
+  { REPLAY_REPORT, S_, "add-directory", REPLAY_ADD_DIRECTORY,
+    FALSE, { "name", "?copyfrom-path", "?copyfrom-rev", NULL}, TRUE },
 
-  const char *name;
-  svn_boolean_t del_prop;
+  { REPLAY_REPORT, S_, "add-file", REPLAY_ADD_FILE,
+    FALSE, { "name", "?copyfrom-path", "?copyfrom-rev", NULL}, TRUE },
 
-  svn_stringbuf_t *prop_value;
+  { REPLAY_REPORT, S_, "delete-entry", REPLAY_DELETE_ENTRY,
+    FALSE, { "name", "rev", NULL }, TRUE },
 
-  replay_info_t *parent;
-} prop_info_t;
+  { REPLAY_REPORT, S_, "close-file", REPLAY_CLOSE_FILE,
+    FALSE, { "?checksum", NULL }, TRUE },
 
-typedef struct replay_context_t {
-  apr_pool_t *src_rev_pool;
-  apr_pool_t *dst_rev_pool;
-  /*file_pool is cleared after completion of each file. */
-  apr_pool_t *file_pool;
+  { REPLAY_REPORT, S_, "close-directory", REPLAY_CLOSE_DIRECTORY,
+    FALSE, { NULL }, TRUE },
 
-  /* Are we done fetching this file? */
-  svn_boolean_t done;
+  { REPLAY_REPORT, S_, "change-dir-prop", REPLAY_CHANGE_DIRECTORY_PROP,
+    TRUE, { "name", "?del", NULL }, TRUE },
+
+  { REPLAY_REPORT, S_, "change-file-prop", REPLAY_CHANGE_FILE_PROP,
+    TRUE, { "name", "?del", NULL }, TRUE },
+
+  { REPLAY_REPORT, S_, "apply-textdelta", REPLAY_APPLY_TEXTDELTA,
+    FALSE, { "?checksum", NULL }, TRUE },
+
+  { 0 }
+};
+
+/* Per directory/file state */
+typedef struct replay_node_t {
+  apr_pool_t *pool; /* pool allocating this node's data */
+  svn_boolean_t file; /* file or dir */
+
+  void *baton; /* node baton */
+  svn_stream_t *stream; /* stream while handling txdata */
+
+  struct replay_node_t *parent; /* parent node or NULL */
+} replay_node_t;
+
+/* Per revision replay report state */
+typedef struct revision_report_t {
+  apr_pool_t *pool; /* per revision pool */
+
+  struct replay_node_t *current_node;
+  struct replay_node_t *root_node;
+
+  /* Are we done fetching this file?
+     Handles book-keeping in multi-report case */
+  svn_boolean_t *done;
   svn_ra_serf__list_t **done_list;
   svn_ra_serf__list_t done_item;
 
@@ -126,81 +164,33 @@ typedef struct replay_context_t {
   apr_hash_t *revs_props;
   apr_hash_t *props;
 
-  /* Keep a reference to the XML parser ctx to report any errors. */
-  svn_ra_serf__xml_parser_t *parser_ctx;
-
   /* Handlers for the PROPFIND and REPORT for the current revision. */
   svn_ra_serf__handler_t *propfind_handler;
   svn_ra_serf__handler_t *report_handler;
 
-} replay_context_t;
-
-
-static void *
-push_state(svn_ra_serf__xml_parser_t *parser,
-           replay_context_t *replay_ctx,
-           replay_state_e state)
-{
-  svn_ra_serf__xml_push_state(parser, state);
-
-  if (state == OPEN_DIR || state == ADD_DIR ||
-      state == OPEN_FILE || state == ADD_FILE)
-    {
-      replay_info_t *info;
-
-      info = apr_palloc(replay_ctx->dst_rev_pool, sizeof(*info));
-
-      info->pool = replay_ctx->dst_rev_pool;
-      info->parent = parser->state->private;
-      info->baton = NULL;
-      info->stream = NULL;
-
-      parser->state->private = info;
-    }
-  else if (state == CHANGE_PROP)
-    {
-      prop_info_t *info;
-
-      info = apr_pcalloc(replay_ctx->dst_rev_pool, sizeof(*info));
-
-      info->pool = replay_ctx->dst_rev_pool;
-      info->parent = parser->state->private;
-      info->prop_value = svn_stringbuf_create_empty(info->pool);
-
-      parser->state->private = info;
-    }
-
-  return parser->state->private;
-}
+} revision_report_t;
 
+/* Conforms to svn_ra_serf__xml_opened_t */
 static svn_error_t *
-start_replay(svn_ra_serf__xml_parser_t *parser,
-             svn_ra_serf__dav_props_t name,
-             const char **attrs,
-             apr_pool_t *scratch_pool)
+replay_opened(svn_ra_serf__xml_estate_t *xes,
+              void *baton,
+              int entered_state,
+              const svn_ra_serf__dav_props_t *tag,
+              apr_pool_t *scratch_pool)
 {
-  replay_context_t *ctx = parser->user_data;
-  replay_state_e state;
-
-  state = parser->state->current_state;
+  struct revision_report_t *ctx = baton;
 
-  if (state == NONE &&
-      strcmp(name.name, "editor-report") == 0)
+  if (entered_state == REPLAY_REPORT)
     {
-      push_state(parser, ctx, REPORT);
-
       /* Before we can continue, we need the revision properties. */
       SVN_ERR_ASSERT(!ctx->propfind_handler || ctx->propfind_handler->done);
 
       /* Create a pool for the commit editor. */
-      ctx->dst_rev_pool = svn_pool_create(ctx->src_rev_pool);
-      ctx->file_pool = svn_pool_create(ctx->dst_rev_pool);
-
       SVN_ERR(svn_ra_serf__select_revprops(&ctx->props,
                                            ctx->revprop_target,
                                            ctx->revprop_rev,
                                            ctx->revs_props,
-                                           ctx->dst_rev_pool,
+                                           ctx->pool,
                                            scratch_pool));
 
       if (ctx->revstart_func)
@@ -208,382 +198,298 @@ start_replay(svn_ra_serf__xml_parser_t *
           SVN_ERR(ctx->revstart_func(ctx->revision, ctx->replay_baton,
                                      &ctx->editor, &ctx->editor_baton,
                                      ctx->props,
-                                     ctx->dst_rev_pool));
+                                     ctx->pool));
         }
     }
-  else if (state == REPORT &&
-           strcmp(name.name, "target-revision") == 0)
+  else if (entered_state == REPLAY_APPLY_TEXTDELTA)
     {
-      const char *rev;
-
-      rev = svn_xml_get_attr_value("rev", attrs);
-      if (!rev)
-        {
-          return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-                    _("Missing revision attr in target-revision element"));
-        }
-
-      SVN_ERR(ctx->editor->set_target_revision(ctx->editor_baton,
-                                               SVN_STR_TO_REV(rev),
-                                               scratch_pool));
+       struct replay_node_t *node = ctx->current_node;
+       apr_hash_t *attrs;
+       const char *checksum;
+       svn_txdelta_window_handler_t handler;
+       void *handler_baton;
+
+       if (! node || ! node->file || node->stream)
+         return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
+
+       /* ### Is there a better way to access a specific attr here? */
+       attrs = svn_ra_serf__xml_gather_since(xes, REPLAY_APPLY_TEXTDELTA);
+       checksum = svn_hash_gets(attrs, "checksum");
+
+       SVN_ERR(ctx->editor->apply_textdelta(node->baton, checksum, node->pool,
+                                            &handler, &handler_baton));
+
+       if (handler != svn_delta_noop_window_handler)
+         {
+            node->stream = svn_base64_decode(
+                                    svn_txdelta_parse_svndiff(handler,
+                                                              handler_baton,
+                                                              TRUE,
+                                                              node->pool),
+                                    node->pool);
+         }
     }
-  else if (state == REPORT &&
-           strcmp(name.name, "open-root") == 0)
-    {
-      const char *rev;
-      replay_info_t *info;
 
-      rev = svn_xml_get_attr_value("rev", attrs);
-
-      if (!rev)
-        {
-          return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-                    _("Missing revision attr in open-root element"));
-        }
+  return SVN_NO_ERROR;
+}
 
-      info = push_state(parser, ctx, OPEN_DIR);
+/* Conforms to svn_ra_serf__xml_closed_t  */
+static svn_error_t *
+replay_closed(svn_ra_serf__xml_estate_t *xes,
+              void *baton,
+              int leaving_state,
+              const svn_string_t *cdata,
+              apr_hash_t *attrs,
+              apr_pool_t *scratch_pool)
+{
+  struct revision_report_t *ctx = baton;
 
-      SVN_ERR(ctx->editor->open_root(ctx->editor_baton,
-                                     SVN_STR_TO_REV(rev),
-                                     ctx->dst_rev_pool,
-                                     &info->baton));
-    }
-  else if ((state == OPEN_DIR || state == ADD_DIR) &&
-           strcmp(name.name, "delete-entry") == 0)
+  if (leaving_state == REPLAY_REPORT)
     {
-      const char *file_name, *rev;
-      replay_info_t *info;
+      if (ctx->current_node)
+        return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
 
-      file_name = svn_xml_get_attr_value("name", attrs);
-      if (!file_name)
-        {
-          return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-                    _("Missing name attr in delete-entry element"));
-        }
-      rev = svn_xml_get_attr_value("rev", attrs);
-      if (!rev)
+      if (ctx->revfinish_func)
         {
-          return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-                    _("Missing revision attr in delete-entry element"));
+          SVN_ERR(ctx->revfinish_func(ctx->revision, ctx->replay_baton,
+                                      ctx->editor, ctx->editor_baton,
+                                      ctx->props,
+                                      scratch_pool));
         }
-
-      info = push_state(parser, ctx, DELETE_ENTRY);
-
-      SVN_ERR(ctx->editor->delete_entry(file_name, SVN_STR_TO_REV(rev),
-                                        info->baton, scratch_pool));
-
-      svn_ra_serf__xml_pop_state(parser);
     }
-  else if ((state == OPEN_DIR || state == ADD_DIR) &&
-           strcmp(name.name, "open-directory") == 0)
+  else if (leaving_state == REPLAY_TARGET_REVISION)
     {
-      const char *rev, *dir_name;
-      replay_info_t *info;
-
-      dir_name = svn_xml_get_attr_value("name", attrs);
-      if (!dir_name)
-        {
-          return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-                    _("Missing name attr in open-directory element"));
-        }
-      rev = svn_xml_get_attr_value("rev", attrs);
-      if (!rev)
-        {
-          return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-                    _("Missing revision attr in open-directory element"));
-        }
-
-      info = push_state(parser, ctx, OPEN_DIR);
+      const char *revstr = svn_hash_gets(attrs, "rev");
+      apr_int64_t rev;
 
-      SVN_ERR(ctx->editor->open_directory(dir_name, info->parent->baton,
-                                          SVN_STR_TO_REV(rev),
-                                          ctx->dst_rev_pool, &info->baton));
+      SVN_ERR(svn_cstring_atoi64(&rev, revstr));
+      SVN_ERR(ctx->editor->set_target_revision(ctx->editor_baton,
+                                               (svn_revnum_t)rev,
+                                               scratch_pool));
     }
-  else if ((state == OPEN_DIR || state == ADD_DIR) &&
-           strcmp(name.name, "add-directory") == 0)
+  else if (leaving_state == REPLAY_OPEN_ROOT)
     {
-      const char *dir_name, *copyfrom, *copyrev;
-      svn_revnum_t rev;
-      replay_info_t *info;
+      const char *revstr = svn_hash_gets(attrs, "rev");
+      apr_int64_t rev;
+      apr_pool_t *root_pool = svn_pool_create(ctx->pool);
 
-      dir_name = svn_xml_get_attr_value("name", attrs);
-      if (!dir_name)
-        {
-          return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-                    _("Missing name attr in add-directory element"));
-        }
-      copyfrom = svn_xml_get_attr_value("copyfrom-path", attrs);
-      copyrev = svn_xml_get_attr_value("copyfrom-rev", attrs);
+      if (ctx->current_node || ctx->root_node)
+        return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
 
-      if (copyrev)
-        rev = SVN_STR_TO_REV(copyrev);
-      else
-        rev = SVN_INVALID_REVNUM;
+      ctx->root_node = apr_pcalloc(root_pool, sizeof(*ctx->root_node));
+      ctx->root_node->pool = root_pool;
 
-      info = push_state(parser, ctx, ADD_DIR);
+      ctx->current_node = ctx->root_node;
 
-      SVN_ERR(ctx->editor->add_directory(dir_name, info->parent->baton,
-                                         copyfrom, rev,
-                                         ctx->dst_rev_pool, &info->baton));
+      SVN_ERR(svn_cstring_atoi64(&rev, revstr));
+      SVN_ERR(ctx->editor->open_root(ctx->editor_baton, (svn_revnum_t)rev,
+                                     root_pool,
+                                     &ctx->current_node->baton));
     }
-  else if ((state == OPEN_DIR || state == ADD_DIR) &&
-           strcmp(name.name, "close-directory") == 0)
+  else if (leaving_state == REPLAY_OPEN_DIRECTORY
+           || leaving_state == REPLAY_OPEN_FILE
+           || leaving_state == REPLAY_ADD_DIRECTORY
+           || leaving_state == REPLAY_ADD_FILE)
     {
-      replay_info_t *info = parser->state->private;
+      struct replay_node_t *node;
+      apr_pool_t *node_pool;
+      const char *name = svn_hash_gets(attrs, "name");
+      const char *rev_str;
+      apr_int64_t rev;
 
-      SVN_ERR(ctx->editor->close_directory(info->baton, scratch_pool));
+      if (!ctx->current_node || ctx->current_node->file)
+        return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
 
-      svn_ra_serf__xml_pop_state(parser);
-    }
-  else if ((state == OPEN_DIR || state == ADD_DIR) &&
-           strcmp(name.name, "open-file") == 0)
-    {
-      const char *file_name, *rev;
-      replay_info_t *info;
+      node_pool = svn_pool_create(ctx->current_node->pool);
+      node = apr_pcalloc(node_pool, sizeof(*node));
+      node->pool = node_pool;
+      node->parent = ctx->current_node;
 
-      svn_pool_clear(ctx->file_pool);
-      file_name = svn_xml_get_attr_value("name", attrs);
-      if (!file_name)
+      if (leaving_state == REPLAY_OPEN_DIRECTORY
+          || leaving_state == REPLAY_OPEN_FILE)
         {
-          return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-                    _("Missing name attr in open-file element"));
+          rev_str = svn_hash_gets(attrs, "rev");
         }
-      rev = svn_xml_get_attr_value("rev", attrs);
-      if (!rev)
-        {
-          return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-                    _("Missing revision attr in open-file element"));
-        }
-
-      info = push_state(parser, ctx, OPEN_FILE);
-
-      SVN_ERR(ctx->editor->open_file(file_name, info->parent->baton,
-                                     SVN_STR_TO_REV(rev),
-                                     ctx->file_pool, &info->baton));
-    }
-  else if ((state == OPEN_DIR || state == ADD_DIR) &&
-           strcmp(name.name, "add-file") == 0)
-    {
-      const char *file_name, *copyfrom, *copyrev;
-      svn_revnum_t rev;
-      replay_info_t *info;
-
-      svn_pool_clear(ctx->file_pool);
-      file_name = svn_xml_get_attr_value("name", attrs);
-      if (!file_name)
-        {
-          return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-                    _("Missing name attr in add-file element"));
-        }
-      copyfrom = svn_xml_get_attr_value("copyfrom-path", attrs);
-      copyrev = svn_xml_get_attr_value("copyfrom-rev", attrs);
-
-      info = push_state(parser, ctx, ADD_FILE);
+      else
+        rev_str = svn_hash_gets(attrs, "copyfrom-rev");
 
-      if (copyrev)
-        rev = SVN_STR_TO_REV(copyrev);
+      if (rev_str)
+        SVN_ERR(svn_cstring_atoi64(&rev, rev_str));
       else
         rev = SVN_INVALID_REVNUM;
 
-      SVN_ERR(ctx->editor->add_file(file_name, info->parent->baton,
-                                    copyfrom, rev,
-                                    ctx->file_pool, &info->baton));
-    }
-  else if ((state == OPEN_FILE || state == ADD_FILE) &&
-           strcmp(name.name, "apply-textdelta") == 0)
-    {
-      const char *checksum;
-      replay_info_t *info;
-      svn_txdelta_window_handler_t textdelta;
-      void *textdelta_baton;
-      svn_stream_t *delta_stream;
-
-      info = push_state(parser, ctx, APPLY_TEXTDELTA);
-
-      checksum = svn_xml_get_attr_value("checksum", attrs);
-      if (checksum)
+      switch (leaving_state)
         {
-          checksum = apr_pstrdup(info->pool, checksum);
-        }
-
-      SVN_ERR(ctx->editor->apply_textdelta(info->baton, checksum,
-                                           ctx->file_pool,
-                                           &textdelta,
-                                           &textdelta_baton));
-
-      delta_stream = svn_txdelta_parse_svndiff(textdelta, textdelta_baton,
-                                               TRUE, info->pool);
-      info->stream = svn_base64_decode(delta_stream, info->pool);
-    }
-  else if ((state == OPEN_FILE || state == ADD_FILE) &&
-           strcmp(name.name, "close-file") == 0)
-    {
-      replay_info_t *info = parser->state->private;
-      const char *checksum;
+          case REPLAY_OPEN_DIRECTORY:
+            node->file = FALSE;
+            SVN_ERR(ctx->editor->open_directory(name,
+                                    ctx->current_node->baton,
+                                    (svn_revnum_t)rev,
+                                    node->pool,
+                                    &node->baton));
+            break;
+          case REPLAY_OPEN_FILE:
+            node->file = TRUE;
+            SVN_ERR(ctx->editor->open_file(name,
+                                    ctx->current_node->baton,
+                                    (svn_revnum_t)rev,
+                                    node->pool,
+                                    &node->baton));
+            break;
+          case REPLAY_ADD_DIRECTORY:
+            node->file = FALSE;
+            SVN_ERR(ctx->editor->add_directory(
+                                    name,
+                                    ctx->current_node->baton,
+                                    SVN_IS_VALID_REVNUM(rev)
+                                        ? svn_hash_gets(attrs, "copyfrom-path")
+                                        : NULL,
+                                    (svn_revnum_t)rev,
+                                    node->pool,
+                                    &node->baton));
+            break;
+          case REPLAY_ADD_FILE:
+            node->file = TRUE;
+            SVN_ERR(ctx->editor->add_file(
+                                    name,
+                                    ctx->current_node->baton,
+                                    SVN_IS_VALID_REVNUM(rev)
+                                        ? svn_hash_gets(attrs, "copyfrom-path")
+                                        : NULL,
+                                    (svn_revnum_t)rev,
+                                    node->pool,
+                                    &node->baton));
+            break;
+          /* default: unreachable */
+        }
+      ctx->current_node = node;
+    }
+  else if (leaving_state == REPLAY_CLOSE_FILE)
+    {
+      struct replay_node_t *node = ctx->current_node;
+
+      if (! node || ! node->file)
+        return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
+
+      SVN_ERR(ctx->editor->close_file(node->baton,
+                                      svn_hash_gets(attrs, "checksum"),
+                                      node->pool));
+      ctx->current_node = node->parent;
+      svn_pool_destroy(node->pool);
+    }
+  else if (leaving_state == REPLAY_CLOSE_DIRECTORY)
+    {
+      struct replay_node_t *node = ctx->current_node;
+
+      if (! node || node->file)
+        return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
+
+      SVN_ERR(ctx->editor->close_directory(node->baton, node->pool));
+      ctx->current_node = node->parent;
+      svn_pool_destroy(node->pool);
+    }
+  else if (leaving_state == REPLAY_DELETE_ENTRY)
+    {
+      struct replay_node_t *parent_node = ctx->current_node;
+      const char *name = svn_hash_gets(attrs, "name");
+      const char *revstr = svn_hash_gets(attrs, "rev");
+      apr_int64_t rev;
+
+      if (! parent_node || parent_node->file)
+        return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
+
+      SVN_ERR(svn_cstring_atoi64(&rev, revstr));
+      SVN_ERR(ctx->editor->delete_entry(name,
+                                        (svn_revnum_t)rev,
+                                        parent_node->baton,
+                                        scratch_pool));
+    }
+  else if (leaving_state == REPLAY_CHANGE_FILE_PROP
+           || leaving_state == REPLAY_CHANGE_DIRECTORY_PROP)
+    {
+      struct replay_node_t *node = ctx->current_node;
+      const char *name;
+      const svn_string_t *value;
 
-      checksum = svn_xml_get_attr_value("checksum", attrs);
+      if (! node || node->file != (leaving_state == REPLAY_CHANGE_FILE_PROP))
+        return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
 
-      SVN_ERR(ctx->editor->close_file(info->baton, checksum, scratch_pool));
+      name = svn_hash_gets(attrs, "name");
 
-      svn_ra_serf__xml_pop_state(parser);
-    }
-  else if (((state == OPEN_FILE || state == ADD_FILE) &&
-            strcmp(name.name, "change-file-prop") == 0) ||
-           ((state == OPEN_DIR || state == ADD_DIR) &&
-            strcmp(name.name, "change-dir-prop") == 0))
-    {
-      const char *prop_name;
-      prop_info_t *info;
-
-      prop_name = svn_xml_get_attr_value("name", attrs);
-      if (!prop_name)
-        {
-          return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-                                   _("Missing name attr in %s element"),
-                                   name.name);
-        }
-
-      info = push_state(parser, ctx, CHANGE_PROP);
-
-
-      if (svn_xml_get_attr_value("del", attrs))
-        info->del_prop = TRUE;
+      if (svn_hash_gets(attrs, "del"))
+        value = NULL;
       else
-        info->del_prop = FALSE;
+        value = svn_base64_decode_string(cdata, scratch_pool);
 
-      if (state == OPEN_FILE || state == ADD_FILE)
+      if (node->file)
         {
-          info->name = apr_pstrdup(ctx->file_pool, prop_name);
-          info->change = ctx->editor->change_file_prop;
+          SVN_ERR(ctx->editor->change_file_prop(node->baton, name, value,
+                                                scratch_pool));
         }
       else
         {
-          info->name = apr_pstrdup(ctx->dst_rev_pool, prop_name);
-          info->change = ctx->editor->change_dir_prop;
+          SVN_ERR(ctx->editor->change_dir_prop(node->baton, name, value,
+                                               scratch_pool));
         }
-
     }
+  else if (leaving_state == REPLAY_APPLY_TEXTDELTA)
+    {
+      struct replay_node_t *node = ctx->current_node;
+
+      if (! node || ! node->file || ! node->stream)
+        return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
+
+      SVN_ERR(svn_stream_close(node->stream));
 
+      node->stream = NULL;
+    }
   return SVN_NO_ERROR;
 }
 
+/* Conforms to svn_ra_serf__xml_cdata_t  */
 static svn_error_t *
-end_replay(svn_ra_serf__xml_parser_t *parser,
-           svn_ra_serf__dav_props_t name,
-           apr_pool_t *scratch_pool)
+replay_cdata(svn_ra_serf__xml_estate_t *xes,
+             void *baton,
+             int current_state,
+             const char *data,
+             apr_size_t len,
+             apr_pool_t *scratch_pool)
 {
-  replay_context_t *ctx = parser->user_data;
-  replay_state_e state;
+  struct revision_report_t *ctx = baton;
 
-  state = parser->state->current_state;
-
-  if (state == REPORT &&
-      strcmp(name.name, "editor-report") == 0)
-    {
-      svn_ra_serf__xml_pop_state(parser);
-      if (ctx->revfinish_func)
-        {
-          SVN_ERR(ctx->revfinish_func(ctx->revision, ctx->replay_baton,
-                                      ctx->editor, ctx->editor_baton,
-                                      ctx->props,
-                                      ctx->dst_rev_pool));
-        }
-      svn_pool_destroy(ctx->dst_rev_pool);
-    }
-  else if (state == OPEN_DIR && strcmp(name.name, "open-directory") == 0)
-    {
-      /* Don't do anything. */
-    }
-  else if (state == ADD_DIR && strcmp(name.name, "add-directory") == 0)
-    {
-      /* Don't do anything. */
-    }
-  else if (state == OPEN_FILE && strcmp(name.name, "open-file") == 0)
-    {
-      /* Don't do anything. */
-    }
-  else if (state == ADD_FILE && strcmp(name.name, "add-file") == 0)
-    {
-      /* Don't do anything. */
-    }
-  else if ((state == OPEN_FILE || state == ADD_FILE) &&
-           strcmp(name.name, "close-file") == 0)
-    {
-      /* Don't do anything. */
-    }
-  else if ((state == APPLY_TEXTDELTA) &&
-           strcmp(name.name, "apply-textdelta") == 0)
+  if (current_state == REPLAY_APPLY_TEXTDELTA)
     {
-      replay_info_t *info = parser->state->private;
-      SVN_ERR(svn_stream_close(info->stream));
-      svn_ra_serf__xml_pop_state(parser);
-    }
-  else if (state == CHANGE_PROP &&
-           (strcmp(name.name, "change-file-prop") == 0 ||
-            strcmp(name.name, "change-dir-prop") == 0))
-    {
-      prop_info_t *info = parser->state->private;
-      const svn_string_t *prop_val;
+      struct replay_node_t *node = ctx->current_node;
 
-      if (info->del_prop)
-        {
-          prop_val = NULL;
-        }
-      else
-        {
-          const svn_string_t *morph;
+      if (! node || ! node->file)
+        return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
 
-          morph = svn_stringbuf__morph_into_string(info->prop_value);
-#ifdef SVN_DEBUG
-          info->prop_value = NULL;  /* morph killed the stringbuf.  */
-#endif
+      if (node->stream)
+        {
+          apr_size_t written = len;
 
-          if (strcmp(name.name, "change-file-prop") == 0)
-            prop_val = svn_base64_decode_string(morph, ctx->file_pool);
-          else
-            prop_val = svn_base64_decode_string(morph, ctx->dst_rev_pool);
+          SVN_ERR(svn_stream_write(node->stream, data, &written));
+          if (written != len)
+            return svn_error_create(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
+                                    _("Error writing stream: unexpected EOF"));
         }
-
-      SVN_ERR(info->change(info->parent->baton, info->name, prop_val,
-                           info->parent->pool));
-      svn_ra_serf__xml_pop_state(parser);
     }
 
   return SVN_NO_ERROR;
 }
 
+/* Conforms to svn_ra_serf__xml_done_t  */
 static svn_error_t *
-cdata_replay(svn_ra_serf__xml_parser_t *parser,
-             const char *data,
-             apr_size_t len,
-             apr_pool_t *scratch_pool)
+replay_done(void *baton,
+           apr_pool_t *scratch_pool)
 {
-  replay_context_t *replay_ctx = parser->user_data;
-  replay_state_e state;
-
-  UNUSED_CTX(replay_ctx);
+  struct revision_report_t *ctx = baton;
 
-  state = parser->state->current_state;
+  *ctx->done = TRUE; /* Breaks out svn_ra_serf__context_run_wait */
 
-  if (state == APPLY_TEXTDELTA)
-    {
-      replay_info_t *info = parser->state->private;
-      apr_size_t written;
-
-      written = len;
-
-      SVN_ERR(svn_stream_write(info->stream, data, &written));
-
-      if (written != len)
-        return svn_error_create(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
-                                _("Error writing stream: unexpected EOF"));
-    }
-  else if (state == CHANGE_PROP)
-    {
-      prop_info_t *info = parser->state->private;
-
-      svn_stringbuf_appendbytes(info->prop_value, data, len);
-    }
+  ctx->done_item.data = ctx;
+  ctx->done_item.next = *ctx->done_list;
+  *ctx->done_list = &ctx->done_item;
 
   return SVN_NO_ERROR;
 }
@@ -594,7 +500,7 @@ create_replay_body(serf_bucket_t **bkt,
                    serf_bucket_alloc_t *alloc,
                    apr_pool_t *pool)
 {
-  replay_context_t *ctx = baton;
+  struct revision_report_t *ctx = baton;
   serf_bucket_t *body_bkt;
 
   body_bkt = serf_bucket_aggregate_create(alloc);
@@ -617,17 +523,17 @@ create_replay_body(serf_bucket_t **bkt,
     {
       svn_ra_serf__add_tag_buckets(body_bkt,
                                    "S:revision",
-                                   apr_ltoa(ctx->src_rev_pool, ctx->revision),
+                                   apr_ltoa(pool, ctx->revision),
                                    alloc);
     }
   svn_ra_serf__add_tag_buckets(body_bkt,
                                "S:low-water-mark",
-                               apr_ltoa(ctx->src_rev_pool, ctx->low_water_mark),
+                               apr_ltoa(pool, ctx->low_water_mark),
                                alloc);
 
   svn_ra_serf__add_tag_buckets(body_bkt,
                                "S:send-deltas",
-                               apr_ltoa(ctx->src_rev_pool, ctx->send_deltas),
+                               apr_ltoa(pool, ctx->send_deltas),
                                alloc);
 
   svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "S:replay-report");
@@ -643,57 +549,49 @@ svn_ra_serf__replay(svn_ra_session_t *ra
                     svn_boolean_t send_deltas,
                     const svn_delta_editor_t *editor,
                     void *edit_baton,
-                    apr_pool_t *pool)
+                    apr_pool_t *scratch_pool)
 {
-  replay_context_t *replay_ctx;
+  struct revision_report_t ctx = { NULL };
   svn_ra_serf__session_t *session = ra_session->priv;
   svn_ra_serf__handler_t *handler;
-  svn_ra_serf__xml_parser_t *parser_ctx;
+  svn_ra_serf__xml_context_t *xmlctx;
   svn_error_t *err;
   const char *report_target;
 
-  SVN_ERR(svn_ra_serf__report_resource(&report_target, session, NULL, pool));
+  SVN_ERR(svn_ra_serf__report_resource(&report_target, session, NULL,
+                                       scratch_pool));
 
-  replay_ctx = apr_pcalloc(pool, sizeof(*replay_ctx));
-  replay_ctx->src_rev_pool = pool;
-  replay_ctx->editor = editor;
-  replay_ctx->editor_baton = edit_baton;
-  replay_ctx->done = FALSE;
-  replay_ctx->revision = revision;
-  replay_ctx->low_water_mark = low_water_mark;
-  replay_ctx->send_deltas = send_deltas;
-  replay_ctx->revs_props = apr_hash_make(replay_ctx->src_rev_pool);
+  ctx.pool = svn_pool_create(scratch_pool);
+  ctx.editor = editor;
+  ctx.editor_baton = edit_baton;
+  ctx.done = FALSE;
+  ctx.revision = revision;
+  ctx.low_water_mark = low_water_mark;
+  ctx.send_deltas = send_deltas;
+  ctx.revs_props = apr_hash_make(scratch_pool);
+
+  xmlctx = svn_ra_serf__xml_context_create(replay_ttable,
+                                           replay_opened, replay_closed,
+                                           replay_cdata,
+                                           NULL,
+                                           &ctx,
+                                           scratch_pool);
 
-  handler = apr_pcalloc(pool, sizeof(*handler));
+  handler = svn_ra_serf__create_expat_handler(xmlctx, NULL, scratch_pool);
 
-  handler->handler_pool = pool;
   handler->method = "REPORT";
   handler->path = session->session_url.path;
   handler->body_delegate = create_replay_body;
-  handler->body_delegate_baton = replay_ctx;
+  handler->body_delegate_baton = &ctx;
   handler->body_type = "text/xml";
   handler->conn = session->conns[0];
   handler->session = session;
 
-  parser_ctx = apr_pcalloc(pool, sizeof(*parser_ctx));
-
-  parser_ctx->pool = pool;
-  parser_ctx->user_data = replay_ctx;
-  parser_ctx->start = start_replay;
-  parser_ctx->end = end_replay;
-  parser_ctx->cdata = cdata_replay;
-  parser_ctx->done = &replay_ctx->done;
-
-  handler->response_handler = svn_ra_serf__handle_xml_parser;
-  handler->response_baton = parser_ctx;
-
-  /* This is only needed to handle errors during XML parsing. */
-  replay_ctx->parser_ctx = parser_ctx;
-  replay_ctx->report_handler = handler; /* unused */
+  ctx.report_handler = handler; /* unused */
 
   svn_ra_serf__request_create(handler);
 
-  err = svn_ra_serf__context_run_wait(&replay_ctx->done, session, pool);
+  err = svn_ra_serf__context_run_wait(&handler->done, session, scratch_pool);
 
   SVN_ERR(svn_error_compose_create(
               svn_ra_serf__error_on_status(handler->sline,
@@ -751,6 +649,8 @@ svn_ra_serf__replay_range(svn_ra_session
   const char *report_target;
   int active_reports = 0;
   const char *include_path;
+  svn_boolean_t done;
+  svn_ra_serf__list_t *done_reports = NULL;
 
   SVN_ERR(svn_ra_serf__report_resource(&report_target, session, NULL, pool));
 
@@ -785,10 +685,6 @@ svn_ra_serf__replay_range(svn_ra_session
 
   while (active_reports || rev <= end_revision)
     {
-      svn_ra_serf__list_t *done_list;
-      svn_ra_serf__list_t *done_reports = NULL;
-      replay_context_t *replay_ctx;
-
       if (session->cancel_func)
         SVN_ERR(session->cancel_func(session->cancel_baton));
 
@@ -796,17 +692,19 @@ svn_ra_serf__replay_range(svn_ra_session
          requests to MAX_OUTSTANDING_REQUESTS. */
       if (rev <= end_revision  && active_reports < MAX_OUTSTANDING_REQUESTS)
         {
+          struct revision_report_t *replay_ctx;
           svn_ra_serf__handler_t *handler;
-          svn_ra_serf__xml_parser_t *parser_ctx;
           apr_pool_t *ctx_pool = svn_pool_create(pool);
+          svn_ra_serf__xml_context_t *xmlctx;
           const char *replay_target;
 
           replay_ctx = apr_pcalloc(ctx_pool, sizeof(*replay_ctx));
-          replay_ctx->src_rev_pool = ctx_pool;
+          replay_ctx->pool = ctx_pool;
           replay_ctx->revstart_func = revstart_func;
           replay_ctx->revfinish_func = revfinish_func;
           replay_ctx->replay_baton = replay_baton;
-          replay_ctx->done = FALSE;
+          replay_ctx->done = &done;
+          replay_ctx->done_list = &done_reports;
           replay_ctx->include_path = include_path;
           replay_ctx->revision = rev;
           replay_ctx->low_water_mark = low_water_mark;
@@ -814,7 +712,7 @@ svn_ra_serf__replay_range(svn_ra_session
           replay_ctx->done_item.data = replay_ctx;
 
           /* Request all properties of a certain revision. */
-          replay_ctx->revs_props = apr_hash_make(replay_ctx->src_rev_pool);
+          replay_ctx->revs_props = apr_hash_make(replay_ctx->pool);
 
           if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session))
             {
@@ -835,7 +733,7 @@ svn_ra_serf__replay_range(svn_ra_session
                                              replay_ctx->revprop_rev,
                                              "0", all_props,
                                              NULL,
-                                             replay_ctx->src_rev_pool));
+                                             replay_ctx->pool));
 
           /* Spin up the serf request for the PROPFIND.  */
           svn_ra_serf__request_create(replay_ctx->propfind_handler);
@@ -851,9 +749,14 @@ svn_ra_serf__replay_range(svn_ra_session
               replay_target = session->session_url.path;
             }
 
-          handler = apr_pcalloc(replay_ctx->src_rev_pool, sizeof(*handler));
+          xmlctx = svn_ra_serf__xml_context_create(replay_ttable,
+                                           replay_opened, replay_closed,
+                                           replay_cdata, replay_done,
+                                           replay_ctx,
+                                           ctx_pool);
+
+          handler = svn_ra_serf__create_expat_handler(xmlctx, NULL, ctx_pool);
 
-          handler->handler_pool = replay_ctx->src_rev_pool;
           handler->method = "REPORT";
           handler->path = replay_target;
           handler->body_delegate = create_replay_body;
@@ -861,31 +764,7 @@ svn_ra_serf__replay_range(svn_ra_session
           handler->conn = session->conns[0];
           handler->session = session;
 
-          parser_ctx = apr_pcalloc(replay_ctx->src_rev_pool,
-                                   sizeof(*parser_ctx));
-
-          /* Setup the XML parser context.
-             Because we have not one but a list of requests, the 'done' property
-             on the replay_ctx is not of much use. Instead, use 'done_list'.
-             On each handled response (succesfully or not), the parser will add
-             done_item to done_list, so by keeping track of the state of
-             done_list we know how many requests have been handled completely.
-          */
-          parser_ctx->pool = replay_ctx->src_rev_pool;
-          parser_ctx->user_data = replay_ctx;
-          parser_ctx->start = start_replay;
-          parser_ctx->end = end_replay;
-          parser_ctx->cdata = cdata_replay;
-          parser_ctx->done = &replay_ctx->done;
-          parser_ctx->done_list = &done_reports;
-          parser_ctx->done_item = &replay_ctx->done_item;
-          handler->response_handler = svn_ra_serf__handle_xml_parser;
-          handler->response_baton = parser_ctx;
           replay_ctx->report_handler = handler;
-
-          /* This is only needed to handle errors during XML parsing. */
-          replay_ctx->parser_ctx = parser_ctx;
-
           svn_ra_serf__request_create(handler);
 
           rev++;
@@ -893,26 +772,32 @@ svn_ra_serf__replay_range(svn_ra_session
         }
 
       /* Run the serf loop. */
-      SVN_ERR(svn_ra_serf__context_run_wait(&replay_ctx->done, session, pool));
+      SVN_ERR(svn_ra_serf__context_run_wait(&done, session, pool));
 
       /* Substract the number of completely handled responses from our
          total nr. of open requests', so we'll know when to stop this loop.
          Since the message is completely handled, we can destroy its pool. */
-      done_list = done_reports;
-      while (done_list)
-        {
-          replay_context_t *ctx = (replay_context_t *)done_list->data;
-          svn_ra_serf__handler_t *done_handler = ctx->report_handler;
+      {
+        svn_ra_serf__list_t *done_list;
 
-          done_list = done_list->next;
-          SVN_ERR(svn_ra_serf__error_on_status(done_handler->sline,
-                                               done_handler->path,
-                                               done_handler->location));
-          svn_pool_destroy(ctx->src_rev_pool);
-          active_reports--;
-        }
+        done_list = done_reports;
+
+        done = FALSE;
+        done_reports = NULL;
 
-      done_reports = NULL;
+        while (done_list)
+          {
+            revision_report_t *ctx = (revision_report_t *)done_list->data;
+            svn_ra_serf__handler_t *done_handler = ctx->report_handler;
+
+            done_list = done_list->next;
+            SVN_ERR(svn_ra_serf__error_on_status(done_handler->sline,
+                                                 done_handler->path,
+                                                 done_handler->location));
+            svn_pool_clear(ctx->pool);
+            active_reports--;
+          }
+      }
     }
 
   return SVN_NO_ERROR;