You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by iv...@apache.org on 2015/09/11 17:51:34 UTC

svn commit: r1702504 [12/19] - in /subversion/branches/reuse-ra-session: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ contrib/hook-scripts/ notes/ subversion/bindings/ctypes-python/csvn/ext/ subversion/bindings/javahl/native/ ...

Modified: subversion/branches/reuse-ra-session/subversion/libsvn_ra_svn/marshal.c
URL: http://svn.apache.org/viewvc/subversion/branches/reuse-ra-session/subversion/libsvn_ra_svn/marshal.c?rev=1702504&r1=1702503&r2=1702504&view=diff
==============================================================================
--- subversion/branches/reuse-ra-session/subversion/libsvn_ra_svn/marshal.c (original)
+++ subversion/branches/reuse-ra-session/subversion/libsvn_ra_svn/marshal.c Fri Sep 11 15:51:30 2015
@@ -60,9 +60,10 @@
 
 /* We don't use "words" longer than this in our protocol.  The longest word
  * we are currently using is only about 16 chars long but we leave room for
- * longer future capability and command names.
+ * longer future capability and command names.  See read_item() to understand
+ * why MAX_WORD_LENGTH - 1 should be a multiple of 8.
  */
-#define MAX_WORD_LENGTH 31
+#define MAX_WORD_LENGTH 25
 
 /* The generic parsers will use the following value to limit the recursion
  * depth to some reasonable value.  The current protocol implementation
@@ -79,6 +80,98 @@ get_timeout(svn_ra_svn_conn_t *conn)
   return conn->block_handler ? 0 : -1;
 }
 
+/* --- Public / private API data conversion --- */
+
+void
+svn_ra_svn__to_public_item(svn_ra_svn_item_t *target,
+                           const svn_ra_svn__item_t *source,
+                           apr_pool_t *result_pool)
+{
+  target->kind = source->kind;
+  switch (source->kind)
+    {
+      case SVN_RA_SVN_STRING:
+        target->u.string = svn_string_dup(&source->u.string, result_pool);
+        break;
+      case SVN_RA_SVN_NUMBER:
+        target->u.number = source->u.number;
+        break;
+      case SVN_RA_SVN_WORD:
+        target->u.word = source->u.word;
+        break;
+      case SVN_RA_SVN_LIST:
+        target->u.list = svn_ra_svn__to_public_array(&source->u.list,
+                                                     result_pool);
+        break;
+    }
+}
+
+apr_array_header_t *
+svn_ra_svn__to_public_array(const svn_ra_svn__list_t *source,
+                            apr_pool_t *result_pool)
+{
+  apr_array_header_t *result = apr_array_make(result_pool, source->nelts,
+                                              sizeof(svn_ra_svn_item_t));
+
+  int i;
+  for (i = 0; i < source->nelts; ++i)
+    {
+      svn_ra_svn_item_t *sub_target = apr_array_push(result);
+      svn_ra_svn__item_t *sub_source = &SVN_RA_SVN__LIST_ITEM(source, i);
+
+      svn_ra_svn__to_public_item(sub_target, sub_source, result_pool);
+    }
+
+  return result;
+}
+
+void
+svn_ra_svn__to_private_item(svn_ra_svn__item_t *target,
+                            const svn_ra_svn_item_t *source,
+                            apr_pool_t *result_pool)
+{
+  target->kind = source->kind;
+  switch (source->kind)
+    {
+      case SVN_RA_SVN_STRING:
+        target->u.string = *source->u.string;
+        break;
+      case SVN_RA_SVN_NUMBER:
+        target->u.number = source->u.number;
+        break;
+      case SVN_RA_SVN_WORD:
+        target->u.word = source->u.word;
+        break;
+      case SVN_RA_SVN_LIST:
+        target->u.list = *svn_ra_svn__to_private_array(source->u.list,
+                                                       result_pool);
+        break;
+    }
+}
+
+svn_ra_svn__list_t *
+svn_ra_svn__to_private_array(const apr_array_header_t *source,
+                             apr_pool_t *result_pool)
+{
+  int i;
+
+  svn_ra_svn__list_t *result = apr_pcalloc(result_pool, sizeof(*result));
+  result->nelts = source->nelts;
+  result->items = apr_palloc(result_pool,
+                             source->nelts * sizeof(*result->items));
+
+  for (i = 0; i < source->nelts; ++i)
+    {
+      svn_ra_svn__item_t *sub_target = &result->items[i];
+      svn_ra_svn_item_t *sub_source = &APR_ARRAY_IDX(source, i,
+                                                     svn_ra_svn_item_t);
+
+      svn_ra_svn__to_private_item(sub_target, sub_source, result_pool);
+    }
+
+  return result;
+}
+
 /* --- CONNECTION INITIALIZATION --- */
 
 svn_ra_svn_conn_t *svn_ra_svn_create_conn4(apr_socket_t *sock,
@@ -132,16 +225,26 @@ svn_ra_svn_conn_t *svn_ra_svn_create_con
   return conn;
 }
 
-svn_error_t *svn_ra_svn_set_capabilities(svn_ra_svn_conn_t *conn,
-                                         const apr_array_header_t *list)
+svn_error_t *
+svn_ra_svn_set_capabilities(svn_ra_svn_conn_t *conn,
+                            const apr_array_header_t *list)
+{
+  svn_ra_svn__list_t *internal
+    = svn_ra_svn__to_private_array(list, list->pool);
+  return svn_error_trace(svn_ra_svn__set_capabilities(conn, internal));
+}
+
+svn_error_t *
+svn_ra_svn__set_capabilities(svn_ra_svn_conn_t *conn,
+                             const svn_ra_svn__list_t *list)
 {
   int i;
-  svn_ra_svn_item_t *item;
+  svn_ra_svn__item_t *item;
   const char *word;
 
   for (i = 0; i < list->nelts; i++)
     {
-      item = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t);
+      item = &SVN_RA_SVN__LIST_ITEM(list, i);
       if (item->kind != SVN_RA_SVN_WORD)
         return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
                                 _("Capability entry is not a word"));
@@ -512,22 +615,65 @@ svn_ra_svn__write_number(svn_ra_svn_conn
   return write_number(conn, pool, number, ' ');
 }
 
+/* Write string S of length LEN to TARGET and return the first position
+   after the written data.
+
+   NOTE: This function assumes that TARGET has enough room for S, the LEN
+         prefix and the required separators.  The available buffer size
+         should be SVN_INT64_BUFFER_SIZE + LEN + 1 to avoid any chance of
+         overflow.
+ */
+static char *
+write_ncstring_quick(char *target,
+                     const char *s,
+                     apr_size_t len)
+{
+  /* Write string length. */
+  if (len < 10)
+    {
+      *target = (char)(len + '0');
+      target++;
+    }
+  else
+    {
+      target += svn__ui64toa(target, len);
+    }
+
+  /* Separator & contents. */
+  target[0] = ':';
+  memcpy(target + 1, s, len);
+  target[len + 1] = ' ';
+
+  /* First location after the string. */
+  return target + len + 2;
+}
+
+
 static svn_error_t *
 svn_ra_svn__write_ncstring(svn_ra_svn_conn_t *conn,
                            apr_pool_t *pool,
                            const char *s,
                            apr_size_t len)
 {
-  if (len < 10)
-    {
-      SVN_ERR(writebuf_writechar(conn, pool, (char)(len + '0')));
-      SVN_ERR(writebuf_writechar(conn, pool, ':'));
+  apr_size_t needed = SVN_INT64_BUFFER_SIZE + len + 1;
+
+  /* In most cases, there is enough left room in the WRITE_BUF
+     the we can serialize directly into it. */
+  if (conn->write_pos + needed < sizeof(conn->write_buf))
+    {
+      /* Quick path. */
+      conn->write_pos = write_ncstring_quick(conn->write_buf
+                                               + conn->write_pos, s, len)
+                      - conn->write_buf;
     }
   else
-    SVN_ERR(write_number(conn, pool, len, ':'));
+    {
+      /* Slower fallback code. */
+      SVN_ERR(write_number(conn, pool, len, ':'));
 
-  SVN_ERR(writebuf_write(conn, pool, s, len));
-  SVN_ERR(writebuf_writechar(conn, pool, ' '));
+      SVN_ERR(writebuf_write(conn, pool, s, len));
+      SVN_ERR(writebuf_writechar(conn, pool, ' '));
+    }
 
   return SVN_NO_ERROR;
 }
@@ -755,6 +901,52 @@ write_tuple_string_opt(svn_ra_svn_conn_t
   return str ? svn_ra_svn__write_string(conn, pool, str) : SVN_NO_ERROR;
 }
 
+/* Optimized sending code for the "(s?)" pattern. */
+static svn_error_t *
+write_tuple_string_opt_list(svn_ra_svn_conn_t *conn,
+                            apr_pool_t *pool,
+                            const svn_string_t *str)
+{
+  apr_size_t needed;
+
+  /* Special case. */
+  if (!str)
+    return writebuf_write(conn, pool, "( ) ", 4);
+
+  /* If there is at least this much the room left in the WRITE_BUF,
+     we can serialize directly into it. */
+  needed = 2                       /* open list */
+         + SVN_INT64_BUFFER_SIZE   /* string length + separator */
+         + str->len                /* string contents */
+         + 2;                      /* close list */
+
+  if (conn->write_pos + needed <= sizeof(conn->write_buf))
+    {
+      /* Quick path. */
+      /* Open list. */
+      char *p = conn->write_buf + conn->write_pos;
+      p[0] = '(';
+      p[1] = ' ';
+
+      /* Write string. */
+      p = write_ncstring_quick(p + 2, str->data, str->len);
+
+      /* Close list. */
+      p[0] = ')';
+      p[1] = ' ';
+      conn->write_pos = p + 2 - conn->write_buf;
+    }
+  else
+    {
+      /* Standard code path (fallback). */
+      SVN_ERR(svn_ra_svn__start_list(conn, pool));
+      SVN_ERR(svn_ra_svn__write_string(conn, pool, str));
+      SVN_ERR(svn_ra_svn__end_list(conn, pool));
+    }
+
+  return SVN_NO_ERROR;
+}
+
 static svn_error_t *
 write_tuple_start_list(svn_ra_svn_conn_t *conn,
                        apr_pool_t *pool)
@@ -809,14 +1001,14 @@ static svn_error_t *
 write_cmd_add_node(svn_ra_svn_conn_t *conn,
                    apr_pool_t *pool,
                    const char *path,
-                   const char *parent_token,
-                   const char *token,
+                   const svn_string_t *parent_token,
+                   const svn_string_t *token,
                    const char *copy_path,
                    svn_revnum_t copy_rev)
 {
   SVN_ERR(write_tuple_cstring(conn, pool, path));
-  SVN_ERR(write_tuple_cstring(conn, pool, parent_token));
-  SVN_ERR(write_tuple_cstring(conn, pool, token));
+  SVN_ERR(write_tuple_string(conn, pool, parent_token));
+  SVN_ERR(write_tuple_string(conn, pool, token));
   SVN_ERR(write_tuple_start_list(conn, pool));
   SVN_ERR(write_tuple_cstring_opt(conn, pool, copy_path));
   SVN_ERR(write_tuple_revision_opt(conn, pool, copy_rev));
@@ -829,13 +1021,13 @@ static svn_error_t *
 write_cmd_open_node(svn_ra_svn_conn_t *conn,
                     apr_pool_t *pool,
                     const char *path,
-                    const char *parent_token,
-                    const char *token,
+                    const svn_string_t *parent_token,
+                    const svn_string_t *token,
                     svn_revnum_t rev)
 {
   SVN_ERR(write_tuple_cstring(conn, pool, path));
-  SVN_ERR(write_tuple_cstring(conn, pool, parent_token));
-  SVN_ERR(write_tuple_cstring(conn, pool, token));
+  SVN_ERR(write_tuple_string(conn, pool, parent_token));
+  SVN_ERR(write_tuple_string(conn, pool, token));
   SVN_ERR(write_tuple_start_list(conn, pool));
   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
   SVN_ERR(write_tuple_end_list(conn, pool));
@@ -846,15 +1038,13 @@ write_cmd_open_node(svn_ra_svn_conn_t *c
 static svn_error_t *
 write_cmd_change_node_prop(svn_ra_svn_conn_t *conn,
                            apr_pool_t *pool,
-                           const char *token,
+                           const svn_string_t *token,
                            const char *name,
                            const svn_string_t *value)
 {
-  SVN_ERR(write_tuple_cstring(conn, pool, token));
+  SVN_ERR(write_tuple_string(conn, pool, token));
   SVN_ERR(write_tuple_cstring(conn, pool, name));
-  SVN_ERR(write_tuple_start_list(conn, pool));
-  SVN_ERR(write_tuple_string_opt(conn, pool, value));
-  SVN_ERR(write_tuple_end_list(conn, pool));
+  SVN_ERR(write_tuple_string_opt_list(conn, pool, value));
 
   return SVN_NO_ERROR;
 }
@@ -863,10 +1053,10 @@ static svn_error_t *
 write_cmd_absent_node(svn_ra_svn_conn_t *conn,
                       apr_pool_t *pool,
                       const char *path,
-                      const char *token)
+                      const svn_string_t *token)
 {
   SVN_ERR(write_tuple_cstring(conn, pool, path));
-  SVN_ERR(write_tuple_cstring(conn, pool, token));
+  SVN_ERR(write_tuple_string(conn, pool, token));
 
   return SVN_NO_ERROR;
 }
@@ -939,7 +1129,7 @@ svn_ra_svn__write_tuple(svn_ra_svn_conn_
  * Afterwards, *ITEM is of type 'SVN_RA_SVN_STRING', and its string
  * data is allocated in POOL. */
 static svn_error_t *read_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
-                                svn_ra_svn_item_t *item, apr_uint64_t len64)
+                                svn_ra_svn__item_t *item, apr_uint64_t len64)
 {
   apr_size_t len = (apr_size_t)len64;
   apr_size_t readbuf_len;
@@ -955,7 +1145,8 @@ static svn_error_t *read_string(svn_ra_s
   if (conn->read_ptr + len <= conn->read_end)
     {
       item->kind = SVN_RA_SVN_STRING;
-      item->u.string = svn_string_ncreate(conn->read_ptr, len, pool);
+      item->u.string.data = apr_pstrmemdup(pool, conn->read_ptr, len);
+      item->u.string.len = len;
       conn->read_ptr += len;
     }
   else
@@ -996,7 +1187,8 @@ static svn_error_t *read_string(svn_ra_s
 
       /* Return the string properly wrapped into an RA_SVN item. */
       item->kind = SVN_RA_SVN_STRING;
-      item->u.string = svn_stringbuf__morph_into_string(stringbuf);
+      item->u.string.data = stringbuf->data;
+      item->u.string.len = stringbuf->len;
     }
 
   return SVN_NO_ERROR;
@@ -1007,12 +1199,12 @@ static svn_error_t *read_string(svn_ra_s
  * to 0 for the first call and is used to enforce a recursion limit
  * on the parser. */
 static svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
-                              svn_ra_svn_item_t *item, char first_char,
+                              svn_ra_svn__item_t *item, char first_char,
                               int level)
 {
   char c = first_char;
   apr_uint64_t val;
-  svn_ra_svn_item_t *listitem;
+  svn_ra_svn__item_t *listitem;
 
   if (++level >= ITEM_NESTING_LIMIT)
     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
@@ -1060,17 +1252,40 @@ static svn_error_t *read_item(svn_ra_svn
       char *p = buffer + 1;
 
       buffer[0] = c;
-      while (1)
+      if (conn->read_ptr + MAX_WORD_LENGTH <= conn->read_end)
         {
-          SVN_ERR(readbuf_getchar(conn, pool, p));
-          if (!svn_ctype_isalnum(*p) && *p != '-')
-            break;
+          /* Fast path: we can simply take a chunk from the read
+           * buffer and inspect it with no overflow checks etc.
+           *
+           * Copying these 24 bytes unconditionally is also faster
+           * than a variable-sized memcpy.  Note that P is at BUFFER[1].
+           */
+          memcpy(p, conn->read_ptr, MAX_WORD_LENGTH - 1);
+          *end = 0;
+
+          /* This will terminate at P == END because of *END == NUL. */
+          while (svn_ctype_isalnum(*p) || *p == '-')
+            ++p;
 
-          if (++p == end)
-            return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
-                                    _("Word is too long"));
+          /* Only now do we mark data as actually read. */
+          conn->read_ptr += p - buffer;
+        }
+      else
+        {
+          /* Slow path. Byte-by-byte copying and checking for
+           * input and output buffer boundaries. */
+          for (p = buffer + 1; p != end; ++p)
+            {
+              SVN_ERR(readbuf_getchar(conn, pool, p));
+              if (!svn_ctype_isalnum(*p) && *p != '-')
+                break;
+            }
         }
 
+      if (p == end)
+        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
+                                _("Word is too long"));
+
       c = *p;
       *p = '\0';
 
@@ -1079,48 +1294,64 @@ static svn_error_t *read_item(svn_ra_svn
     }
   else if (c == '(')
     {
-      /* Allocate an APR array with room for (initially) 4 items.
-       * We do this manually because lists are the most frequent protocol
-       * element, often used to frame a single, optional value.  We save
-       * about 20% of total protocol handling time. */
-      char *buffer = apr_palloc(pool, sizeof(apr_array_header_t)
-                                      + 4 * sizeof(svn_ra_svn_item_t));
-      svn_ra_svn_item_t *data
-        = (svn_ra_svn_item_t *)(buffer + sizeof(apr_array_header_t));
-
-      item->kind = SVN_RA_SVN_LIST;
-      item->u.list = (apr_array_header_t *)buffer;
-      item->u.list->elts = (char *)data;
-      item->u.list->pool = pool;
-      item->u.list->elt_size = sizeof(*data);
-      item->u.list->nelts = 0;
-      item->u.list->nalloc = 4;
-
-      listitem = data;
+      /* The largest struct that the protocol currently defines has 10
+       * elements (log-entry) and add some headroom for future extensions.
+       * At a maximum nesting level of 64 this use <= 18kB of stack.
+       *
+       * All system-defined data structures will fit into this and will be
+       * copied into ITEM after a single apr_palloc with no over-provision.
+       * Unbounded lists with more than 12 but less than 25 entries will
+       * also see only a single allocation from POOL.  However, there will
+       * be some over-provision.  Longer lists will see log N resizes and
+       * O(N) total cost.
+       */
+      svn_ra_svn__item_t stack_items[12];
+      svn_ra_svn__item_t *items = stack_items;
+      int capacity = sizeof(stack_items) / sizeof(stack_items[0]);
+      int count = 0;
 
       /* Read in the list items. */
+      item->kind = SVN_RA_SVN_LIST;
       while (1)
         {
           SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
           if (c == ')')
             break;
 
-          /* increase array capacity if necessary */
-          if (item->u.list->nelts == item->u.list->nalloc)
+          /* Auto-expand the list. */
+          if (count == capacity)
             {
-              data = apr_palloc(pool, 2 * item->u.list->nelts * sizeof(*data));
-              memcpy(data, item->u.list->elts, item->u.list->nelts * sizeof(*data));
-              item->u.list->elts = (char *)data;
-              item->u.list->nalloc *= 2;
-              listitem = data + item->u.list->nelts;
+              svn_ra_svn__item_t *new_items
+                = apr_palloc(pool, 2 * capacity * sizeof(*new_items));
+              memcpy(new_items, items, capacity * sizeof(*new_items));
+              items = new_items;
+              capacity = 2 * capacity;
             }
 
-          /* read next protocol item */
+          listitem = &items[count];
+          ++count;
+
           SVN_ERR(read_item(conn, pool, listitem, c, level));
+        }
 
-          listitem++;
-          item->u.list->nelts++;
+      /* Store the list in ITEM - if not empty (= default). */
+      if (count)
+        {
+          item->u.list.nelts = count;
+
+          /* If we haven't allocated from POOL, yet, do it now. */
+          if (items == stack_items)
+            item->u.list.items = apr_pmemdup(pool, items,
+                                             count * sizeof(*items));
+          else
+            item->u.list.items = items;
+        }
+      else
+        {
+          item->u.list.items = NULL;
+          item->u.list.nelts = 0;
         }
+
       SVN_ERR(readbuf_getchar(conn, pool, &c));
     }
 
@@ -1222,7 +1453,7 @@ read_command_only(svn_ra_svn_conn_t *con
 svn_error_t *
 svn_ra_svn__read_item(svn_ra_svn_conn_t *conn,
                       apr_pool_t *pool,
-                      svn_ra_svn_item_t **item)
+                      svn_ra_svn__item_t **item)
 {
   char c;
 
@@ -1272,29 +1503,32 @@ svn_ra_svn__skip_leading_garbage(svn_ra_
 
 /* --- READING AND PARSING TUPLES --- */
 
-/* Parse a tuple of svn_ra_svn_item_t *'s.  Advance *FMT to the end of the
+/* Parse a tuple of svn_ra_svn__item_t *'s.  Advance *FMT to the end of the
  * tuple specification and advance AP by the corresponding arguments. */
-static svn_error_t *vparse_tuple(const apr_array_header_t *items, apr_pool_t *pool,
-                                 const char **fmt, va_list *ap)
+static svn_error_t *
+vparse_tuple(const svn_ra_svn__list_t *items,
+             apr_pool_t *pool,
+             const char **fmt,
+             va_list *ap)
 {
   int count, nesting_level;
-  svn_ra_svn_item_t *elt;
+  svn_ra_svn__item_t *elt;
 
   for (count = 0; **fmt && count < items->nelts; (*fmt)++, count++)
     {
       /* '?' just means the tuple may stop; skip past it. */
       if (**fmt == '?')
         (*fmt)++;
-      elt = &APR_ARRAY_IDX(items, count, svn_ra_svn_item_t);
+      elt = &SVN_RA_SVN__LIST_ITEM(items, count);
       if (**fmt == '(' && elt->kind == SVN_RA_SVN_LIST)
         {
           (*fmt)++;
-          SVN_ERR(vparse_tuple(elt->u.list, pool, fmt, ap));
+          SVN_ERR(vparse_tuple(&elt->u.list, pool, fmt, ap));
         }
       else if (**fmt == 'c' && elt->kind == SVN_RA_SVN_STRING)
-        *va_arg(*ap, const char **) = elt->u.string->data;
+        *va_arg(*ap, const char **) = elt->u.string.data;
       else if (**fmt == 's' && elt->kind == SVN_RA_SVN_STRING)
-        *va_arg(*ap, svn_string_t **) = elt->u.string;
+        *va_arg(*ap, svn_string_t **) = &elt->u.string;
       else if (**fmt == 'w' && elt->kind == SVN_RA_SVN_WORD)
         *va_arg(*ap, const char **) = elt->u.word;
       else if (**fmt == 'b' && elt->kind == SVN_RA_SVN_WORD)
@@ -1329,7 +1563,7 @@ static svn_error_t *vparse_tuple(const a
             break;
         }
       else if (**fmt == 'l' && elt->kind == SVN_RA_SVN_LIST)
-        *va_arg(*ap, apr_array_header_t **) = elt->u.list;
+        *va_arg(*ap, svn_ra_svn__list_t **) = &elt->u.list;
       else if (**fmt == ')')
         return SVN_NO_ERROR;
       else
@@ -1355,7 +1589,7 @@ static svn_error_t *vparse_tuple(const a
               *va_arg(*ap, const char **) = NULL;
               break;
             case 'l':
-              *va_arg(*ap, apr_array_header_t **) = NULL;
+              *va_arg(*ap, svn_ra_svn__list_t **) = NULL;
               break;
             case 'B':
             case 'n':
@@ -1383,7 +1617,7 @@ static svn_error_t *vparse_tuple(const a
 }
 
 svn_error_t *
-svn_ra_svn__parse_tuple(const apr_array_header_t *list,
+svn_ra_svn__parse_tuple(const svn_ra_svn__list_t *list,
                         apr_pool_t *pool,
                         const char *fmt, ...)
 {
@@ -1402,7 +1636,7 @@ svn_ra_svn__read_tuple(svn_ra_svn_conn_t
                        const char *fmt, ...)
 {
   va_list ap;
-  svn_ra_svn_item_t *item;
+  svn_ra_svn__item_t *item;
   svn_error_t *err;
 
   SVN_ERR(svn_ra_svn__read_item(conn, pool, &item));
@@ -1410,7 +1644,7 @@ svn_ra_svn__read_tuple(svn_ra_svn_conn_t
     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
                             _("Malformed network data"));
   va_start(ap, fmt);
-  err = vparse_tuple(item->u.list, pool, &fmt, &ap);
+  err = vparse_tuple(&item->u.list, pool, &fmt, &ap);
   va_end(ap);
   return err;
 }
@@ -1429,23 +1663,23 @@ svn_ra_svn__read_command_only(svn_ra_svn
 
 
 svn_error_t *
-svn_ra_svn__parse_proplist(const apr_array_header_t *list,
+svn_ra_svn__parse_proplist(const svn_ra_svn__list_t *list,
                            apr_pool_t *pool,
                            apr_hash_t **props)
 {
   svn_string_t *name;
   svn_string_t *value;
-  svn_ra_svn_item_t *elt;
+  svn_ra_svn__item_t *elt;
   int i;
 
   *props = svn_hash__make(pool);
   for (i = 0; i < list->nelts; i++)
     {
-      elt = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t);
+      elt = &SVN_RA_SVN__LIST_ITEM(list, i);
       if (elt->kind != SVN_RA_SVN_LIST)
         return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
                                 _("Proplist element not a list"));
-      SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, pool, "ss",
+      SVN_ERR(svn_ra_svn__parse_tuple(&elt->u.list, pool, "ss",
                                       &name, &value));
       apr_hash_set(*props, name->data, name->len, value);
     }
@@ -1471,12 +1705,13 @@ svn_error_t *svn_ra_svn__locate_real_err
   return this_link;
 }
 
-svn_error_t *svn_ra_svn__handle_failure_status(const apr_array_header_t *params,
-                                               apr_pool_t *pool)
+svn_error_t *
+svn_ra_svn__handle_failure_status(const svn_ra_svn__list_t *params,
+                                  apr_pool_t *pool)
 {
   const char *message, *file;
   svn_error_t *err = NULL;
-  svn_ra_svn_item_t *elt;
+  svn_ra_svn__item_t *elt;
   int i;
   apr_uint64_t apr_err, line;
   apr_pool_t *subpool = svn_pool_create(pool);
@@ -1489,11 +1724,11 @@ svn_error_t *svn_ra_svn__handle_failure_
   for (i = params->nelts - 1; i >= 0; i--)
     {
       svn_pool_clear(subpool);
-      elt = &APR_ARRAY_IDX(params, i, svn_ra_svn_item_t);
+      elt = &SVN_RA_SVN__LIST_ITEM(params, i);
       if (elt->kind != SVN_RA_SVN_LIST)
         return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
                                 _("Malformed error list"));
-      SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, subpool, "nccn",
+      SVN_ERR(svn_ra_svn__parse_tuple(&elt->u.list, subpool, "nccn",
                                       &apr_err, &message, &file, &line));
       /* The message field should have been optional, but we can't
          easily change that, so "" means a nonexistent message. */
@@ -1530,7 +1765,7 @@ svn_ra_svn__read_cmd_response(svn_ra_svn
 {
   va_list ap;
   const char *status;
-  apr_array_header_t *params;
+  svn_ra_svn__list_t *params;
   svn_error_t *err;
 
   SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "wl", &status, &params));
@@ -1579,8 +1814,8 @@ svn_ra_svn__handle_command(svn_boolean_t
 {
   const char *cmdname;
   svn_error_t *err, *write_err;
-  apr_array_header_t *params;
-  const svn_ra_svn_cmd_entry_t *command;
+  svn_ra_svn__list_t *params;
+  const svn_ra_svn__cmd_entry_t *command;
 
   *terminate = FALSE;
   err = svn_ra_svn__read_tuple(conn, pool, "wl", &cmdname, &params);
@@ -1599,7 +1834,21 @@ svn_ra_svn__handle_command(svn_boolean_t
   command = svn_hash_gets(cmd_hash, cmdname);
   if (command)
     {
-      err = (*command->handler)(conn, pool, params, baton);
+      /* Call the standard command handler.
+       * If that is not set, then this is a lecagy API call and we invoke
+       * the legacy command handler. */
+      if (command->handler)
+        {
+          err = (*command->handler)(conn, pool, params, baton);
+        }
+      else
+        {
+          apr_array_header_t *deprecated_params
+            = svn_ra_svn__to_public_array(params, pool);
+          err = (*command->deprecated_handler)(conn, pool, deprecated_params,
+                                               baton);
+        }
+
       *terminate = command->terminate;
     }
   else
@@ -1624,13 +1873,13 @@ svn_ra_svn__handle_command(svn_boolean_t
 svn_error_t *
 svn_ra_svn__handle_commands2(svn_ra_svn_conn_t *conn,
                              apr_pool_t *pool,
-                             const svn_ra_svn_cmd_entry_t *commands,
+                             const svn_ra_svn__cmd_entry_t *commands,
                              void *baton,
                              svn_boolean_t error_on_disconnect)
 {
   apr_pool_t *subpool = svn_pool_create(pool);
   apr_pool_t *iterpool = svn_pool_create(subpool);
-  const svn_ra_svn_cmd_entry_t *command;
+  const svn_ra_svn__cmd_entry_t *command;
   apr_hash_t *cmd_hash = apr_hash_make(subpool);
 
   for (command = commands; command->cmdname; command++)
@@ -1673,13 +1922,13 @@ svn_error_t *
 svn_ra_svn__write_cmd_open_root(svn_ra_svn_conn_t *conn,
                                 apr_pool_t *pool,
                                 svn_revnum_t rev,
-                                const char *token)
+                                const svn_string_t *token)
 {
   SVN_ERR(writebuf_write_literal(conn, pool, "( open-root ( "));
   SVN_ERR(write_tuple_start_list(conn, pool));
   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
   SVN_ERR(write_tuple_end_list(conn, pool));
-  SVN_ERR(write_tuple_cstring(conn, pool, token));
+  SVN_ERR(write_tuple_string(conn, pool, token));
   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
 
   return SVN_NO_ERROR;
@@ -1690,14 +1939,14 @@ svn_ra_svn__write_cmd_delete_entry(svn_r
                                    apr_pool_t *pool,
                                    const char *path,
                                    svn_revnum_t rev,
-                                   const char *token)
+                                   const svn_string_t *token)
 {
   SVN_ERR(writebuf_write_literal(conn, pool, "( delete-entry ( "));
   SVN_ERR(write_tuple_cstring(conn, pool, path));
   SVN_ERR(write_tuple_start_list(conn, pool));
   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
   SVN_ERR(write_tuple_end_list(conn, pool));
-  SVN_ERR(write_tuple_cstring(conn, pool, token));
+  SVN_ERR(write_tuple_string(conn, pool, token));
   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
 
   return SVN_NO_ERROR;
@@ -1707,8 +1956,8 @@ svn_error_t *
 svn_ra_svn__write_cmd_add_dir(svn_ra_svn_conn_t *conn,
                               apr_pool_t *pool,
                               const char *path,
-                              const char *parent_token,
-                              const char *token,
+                              const svn_string_t *parent_token,
+                              const svn_string_t *token,
                               const char *copy_path,
                               svn_revnum_t copy_rev)
 {
@@ -1724,8 +1973,8 @@ svn_error_t *
 svn_ra_svn__write_cmd_open_dir(svn_ra_svn_conn_t *conn,
                                apr_pool_t *pool,
                                const char *path,
-                               const char *parent_token,
-                               const char *token,
+                               const svn_string_t *parent_token,
+                               const svn_string_t *token,
                                svn_revnum_t rev)
 {
   SVN_ERR(writebuf_write_literal(conn, pool, "( open-dir ( "));
@@ -1738,7 +1987,7 @@ svn_ra_svn__write_cmd_open_dir(svn_ra_sv
 svn_error_t *
 svn_ra_svn__write_cmd_change_dir_prop(svn_ra_svn_conn_t *conn,
                                       apr_pool_t *pool,
-                                      const char *token,
+                                      const svn_string_t *token,
                                       const char *name,
                                       const svn_string_t *value)
 {
@@ -1752,10 +2001,10 @@ svn_ra_svn__write_cmd_change_dir_prop(sv
 svn_error_t *
 svn_ra_svn__write_cmd_close_dir(svn_ra_svn_conn_t *conn,
                                 apr_pool_t *pool,
-                                const char *token)
+                                const svn_string_t *token)
 {
   SVN_ERR(writebuf_write_literal(conn, pool, "( close-dir ( "));
-  SVN_ERR(write_tuple_cstring(conn, pool, token));
+  SVN_ERR(write_tuple_string(conn, pool, token));
   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
 
   return SVN_NO_ERROR;
@@ -1765,7 +2014,7 @@ svn_error_t *
 svn_ra_svn__write_cmd_absent_dir(svn_ra_svn_conn_t *conn,
                                  apr_pool_t *pool,
                                  const char *path,
-                                 const char *parent_token)
+                                 const svn_string_t *parent_token)
 {
   SVN_ERR(writebuf_write_literal(conn, pool, "( absent-dir ( "));
   SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token));
@@ -1778,8 +2027,8 @@ svn_error_t *
 svn_ra_svn__write_cmd_add_file(svn_ra_svn_conn_t *conn,
                                apr_pool_t *pool,
                                const char *path,
-                               const char *parent_token,
-                               const char *token,
+                               const svn_string_t *parent_token,
+                               const svn_string_t *token,
                                const char *copy_path,
                                svn_revnum_t copy_rev)
 {
@@ -1795,8 +2044,8 @@ svn_error_t *
 svn_ra_svn__write_cmd_open_file(svn_ra_svn_conn_t *conn,
                                 apr_pool_t *pool,
                                 const char *path,
-                                const char *parent_token,
-                                const char *token,
+                                const svn_string_t *parent_token,
+                                const svn_string_t *token,
                                 svn_revnum_t rev)
 {
   SVN_ERR(writebuf_write_literal(conn, pool, "( open-file ( "));
@@ -1809,7 +2058,7 @@ svn_ra_svn__write_cmd_open_file(svn_ra_s
 svn_error_t *
 svn_ra_svn__write_cmd_change_file_prop(svn_ra_svn_conn_t *conn,
                                        apr_pool_t *pool,
-                                       const char *token,
+                                       const svn_string_t *token,
                                        const char *name,
                                        const svn_string_t *value)
 {
@@ -1823,11 +2072,11 @@ svn_ra_svn__write_cmd_change_file_prop(s
 svn_error_t *
 svn_ra_svn__write_cmd_close_file(svn_ra_svn_conn_t *conn,
                                  apr_pool_t *pool,
-                                 const char *token,
+                                 const svn_string_t *token,
                                  const char *text_checksum)
 {
   SVN_ERR(writebuf_write_literal(conn, pool, "( close-file ( "));
-  SVN_ERR(write_tuple_cstring(conn, pool, token));
+  SVN_ERR(write_tuple_string(conn, pool, token));
   SVN_ERR(write_tuple_start_list(conn, pool));
   SVN_ERR(write_tuple_cstring_opt(conn, pool, text_checksum));
   SVN_ERR(write_tuple_end_list(conn, pool));
@@ -1840,7 +2089,7 @@ svn_error_t *
 svn_ra_svn__write_cmd_absent_file(svn_ra_svn_conn_t *conn,
                                   apr_pool_t *pool,
                                   const char *path,
-                                  const char *parent_token)
+                                  const svn_string_t *parent_token)
 {
   SVN_ERR(writebuf_write_literal(conn, pool, "( absent-file ( "));
   SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token));
@@ -1852,11 +2101,11 @@ svn_ra_svn__write_cmd_absent_file(svn_ra
 svn_error_t *
 svn_ra_svn__write_cmd_textdelta_chunk(svn_ra_svn_conn_t *conn,
                                       apr_pool_t *pool,
-                                      const char *token,
+                                      const svn_string_t *token,
                                       const svn_string_t *chunk)
 {
   SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-chunk ( "));
-  SVN_ERR(write_tuple_cstring(conn, pool, token));
+  SVN_ERR(write_tuple_string(conn, pool, token));
   SVN_ERR(write_tuple_string(conn, pool, chunk));
   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
 
@@ -1866,10 +2115,10 @@ svn_ra_svn__write_cmd_textdelta_chunk(sv
 svn_error_t *
 svn_ra_svn__write_cmd_textdelta_end(svn_ra_svn_conn_t *conn,
                                     apr_pool_t *pool,
-                                    const char *token)
+                                    const svn_string_t *token)
 {
   SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-end ( "));
-  SVN_ERR(write_tuple_cstring(conn, pool, token));
+  SVN_ERR(write_tuple_string(conn, pool, token));
   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
 
   return SVN_NO_ERROR;
@@ -1878,11 +2127,11 @@ svn_ra_svn__write_cmd_textdelta_end(svn_
 svn_error_t *
 svn_ra_svn__write_cmd_apply_textdelta(svn_ra_svn_conn_t *conn,
                                       apr_pool_t *pool,
-                                      const char *token,
+                                      const svn_string_t *token,
                                       const char *base_checksum)
 {
   SVN_ERR(writebuf_write_literal(conn, pool, "( apply-textdelta ( "));
-  SVN_ERR(write_tuple_cstring(conn, pool, token));
+  SVN_ERR(write_tuple_string(conn, pool, token));
   SVN_ERR(write_tuple_start_list(conn, pool));
   SVN_ERR(write_tuple_cstring_opt(conn, pool, base_checksum));
   SVN_ERR(write_tuple_end_list(conn, pool));
@@ -2020,9 +2269,7 @@ svn_ra_svn__write_cmd_change_rev_prop2(s
   SVN_ERR(writebuf_write_literal(conn, pool, "( change-rev-prop2 ( "));
   SVN_ERR(write_tuple_revision(conn, pool, rev));
   SVN_ERR(write_tuple_cstring(conn, pool, name));
-  SVN_ERR(write_tuple_start_list(conn, pool));
-  SVN_ERR(write_tuple_string_opt(conn, pool, value));
-  SVN_ERR(write_tuple_end_list(conn, pool));
+  SVN_ERR(write_tuple_string_opt_list(conn, pool, value));
   SVN_ERR(write_tuple_start_list(conn, pool));
   SVN_ERR(write_tuple_boolean(conn, pool, dont_care));
   SVN_ERR(write_tuple_string_opt(conn, pool, old_value));
@@ -2274,14 +2521,12 @@ svn_error_t *
 svn_ra_svn__write_cmd_unlock(svn_ra_svn_conn_t *conn,
                              apr_pool_t *pool,
                              const char *path,
-                             const char *token,
+                             const svn_string_t *token,
                              svn_boolean_t break_lock)
 {
   SVN_ERR(writebuf_write_literal(conn, pool, "( unlock ( "));
   SVN_ERR(write_tuple_cstring(conn, pool, path));
-  SVN_ERR(write_tuple_start_list(conn, pool));
-  SVN_ERR(write_tuple_cstring_opt(conn, pool, token));
-  SVN_ERR(write_tuple_end_list(conn, pool));
+  SVN_ERR(write_tuple_string_opt_list(conn, pool, token));
   SVN_ERR(write_tuple_boolean(conn, pool, break_lock));
   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
 
@@ -2431,6 +2676,49 @@ svn_error_t *svn_ra_svn__write_cmd_failu
   return writebuf_write_literal(conn, pool, ") ) ");
 }
 
+/* Initializer for static svn_string_t . */
+#define STATIC_SVN_STRING(x) { x, sizeof(x) - 1 }
+
+/* Return a pre-cooked serialized representation for the changed path
+   flags NODE_KIND, TEXT_MODIFIED and PROPS_MODIFIED.  If we don't
+   have a suitable pre-cooked string, return an empty string. */
+static const svn_string_t *
+changed_path_flags(svn_node_kind_t node_kind,
+                   svn_boolean_t text_modified,
+                   svn_boolean_t props_modified)
+{
+  const static svn_string_t file_flags[4]
+    = { STATIC_SVN_STRING(" ) ( 4:file false false ) ) "),
+        STATIC_SVN_STRING(" ) ( 4:file false true ) ) "),
+        STATIC_SVN_STRING(" ) ( 4:file true false ) ) "),
+        STATIC_SVN_STRING(" ) ( 4:file true true ) ) ") };
+
+  const static svn_string_t dir_flags[4]
+    = { STATIC_SVN_STRING(" ) ( 3:dir false false ) ) "),
+        STATIC_SVN_STRING(" ) ( 3:dir false true ) ) "),
+        STATIC_SVN_STRING(" ) ( 3:dir true false ) ) "),
+        STATIC_SVN_STRING(" ) ( 3:dir true true ) ) ") };
+
+  const static svn_string_t no_flags = STATIC_SVN_STRING("");
+
+  /* Select the array based on the NODE_KIND. */
+  const svn_string_t *flags;
+  if (node_kind == svn_node_file)
+    flags = file_flags;
+  else if (node_kind == svn_node_dir)
+    flags = dir_flags;
+  else
+    return &no_flags;
+
+  /* Select the correct array entry. */
+  if (text_modified)
+    flags += 2;
+  if (props_modified)
+    flags++;
+
+  return flags;
+}
+
 svn_error_t *
 svn_ra_svn__write_data_log_changed_path(svn_ra_svn_conn_t *conn,
                                         apr_pool_t *pool,
@@ -2442,21 +2730,77 @@ svn_ra_svn__write_data_log_changed_path(
                                         svn_boolean_t text_modified,
                                         svn_boolean_t props_modified)
 {
-  SVN_ERR(write_tuple_start_list(conn, pool));
+  apr_size_t path_len = strlen(path);
+  apr_size_t copyfrom_len = copyfrom_path ? strlen(copyfrom_path) : 0;
+  const svn_string_t *flags_str = changed_path_flags(node_kind,
+                                                     text_modified,
+                                                     props_modified);
+
+  /* How much buffer space do we need (worst case)? */
+  apr_size_t needed = 2                 /* list start */
+                    + path_len + SVN_INT64_BUFFER_SIZE
+                                        /* path */
+                    + 2                 /* action */
+                    + 2 + copyfrom_len + 2 * SVN_INT64_BUFFER_SIZE
+                                        /* list start + copy-from info */
+                    + flags_str->len;   /* flags and closing lists */
+
+  /* If the remaining buffer is big enough and we've got all parts,
+     directly copy into the buffer. */
+  if (   (conn->write_pos + needed <= sizeof(conn->write_buf))
+      && (flags_str->len > 0))
+    {
+      /* Quick path. */
+      /* Open list. */
+      char *p = conn->write_buf + conn->write_pos;
+      p[0] = '(';
+      p[1] = ' ';
+
+      /* Write path. */
+      p = write_ncstring_quick(p + 2, path, path_len);
+
+      /* Action */
+      p[0] = action;
+      p[1] = ' ';
+      p[2] = '(';
+
+      /* Copy-from info (if given) */
+      if (copyfrom_path)
+        {
+          p[3] = ' ';
+          p = write_ncstring_quick(p + 4, copyfrom_path, copyfrom_len);
+          p += svn__ui64toa(p, copyfrom_rev);
+        }
+      else
+        {
+          p += 3;
+        }
 
-  SVN_ERR(write_tuple_cstring(conn, pool, path));
-  SVN_ERR(writebuf_writechar(conn, pool, action));
-  SVN_ERR(writebuf_writechar(conn, pool, ' '));
-  SVN_ERR(write_tuple_start_list(conn, pool));
-  SVN_ERR(write_tuple_cstring_opt(conn, pool, copyfrom_path));
-  SVN_ERR(write_tuple_revision_opt(conn, pool, copyfrom_rev));
-  SVN_ERR(write_tuple_end_list(conn, pool));
-  SVN_ERR(write_tuple_start_list(conn, pool));
-  SVN_ERR(write_tuple_cstring(conn, pool, svn_node_kind_to_word(node_kind)));
-  SVN_ERR(write_tuple_boolean(conn, pool, text_modified));
-  SVN_ERR(write_tuple_boolean(conn, pool, props_modified));
+      /* Close with flags. */
+      memcpy(p, flags_str->data, flags_str->len);
+      conn->write_pos = p + flags_str->len - conn->write_buf;
+    }
+  else
+    {
+      /* Standard code path (fallback). */
+      SVN_ERR(write_tuple_start_list(conn, pool));
 
-  return writebuf_write_literal(conn, pool, ") ) ");
+      SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, path, path_len));
+      SVN_ERR(writebuf_writechar(conn, pool, action));
+      SVN_ERR(writebuf_writechar(conn, pool, ' '));
+      SVN_ERR(write_tuple_start_list(conn, pool));
+      SVN_ERR(write_tuple_cstring_opt(conn, pool, copyfrom_path));
+      SVN_ERR(write_tuple_revision_opt(conn, pool, copyfrom_rev));
+      SVN_ERR(write_tuple_end_list(conn, pool));
+      SVN_ERR(write_tuple_start_list(conn, pool));
+      SVN_ERR(write_tuple_cstring(conn, pool, svn_node_kind_to_word(node_kind)));
+      SVN_ERR(write_tuple_boolean(conn, pool, text_modified));
+      SVN_ERR(write_tuple_boolean(conn, pool, props_modified));
+
+      SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
+    }
+
+  return SVN_NO_ERROR;
 }
 
 svn_error_t *
@@ -2471,15 +2815,9 @@ svn_ra_svn__write_data_log_entry(svn_ra_
                                  unsigned revprop_count)
 {
   SVN_ERR(write_tuple_revision(conn, pool, revision));
-  SVN_ERR(write_tuple_start_list(conn, pool));
-  SVN_ERR(write_tuple_string_opt(conn, pool, author));
-  SVN_ERR(write_tuple_end_list(conn, pool));
-  SVN_ERR(write_tuple_start_list(conn, pool));
-  SVN_ERR(write_tuple_string_opt(conn, pool, date));
-  SVN_ERR(write_tuple_end_list(conn, pool));
-  SVN_ERR(write_tuple_start_list(conn, pool));
-  SVN_ERR(write_tuple_string_opt(conn, pool, message));
-  SVN_ERR(write_tuple_end_list(conn, pool));
+  SVN_ERR(write_tuple_string_opt_list(conn, pool, author));
+  SVN_ERR(write_tuple_string_opt_list(conn, pool, date));
+  SVN_ERR(write_tuple_string_opt_list(conn, pool, message));
   SVN_ERR(write_tuple_boolean(conn, pool, has_children));
   SVN_ERR(write_tuple_boolean(conn, pool, invalid_revnum));
   SVN_ERR(svn_ra_svn__write_number(conn, pool, revprop_count));
@@ -2497,13 +2835,13 @@ svn_ra_svn__write_data_log_entry(svn_ra_
 /* In *RESULT, return the SVN-style string at index IDX in tuple ITEMS.
  */
 static svn_error_t *
-svn_ra_svn__read_string(const apr_array_header_t *items,
+svn_ra_svn__read_string(const svn_ra_svn__list_t *items,
                         int idx,
                         svn_string_t **result)
 {
-  svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
+  svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING);
-  *result = elt->u.string;
+  *result = &elt->u.string;
 
   return SVN_NO_ERROR;
 }
@@ -2511,13 +2849,13 @@ svn_ra_svn__read_string(const apr_array_
 /* In *RESULT, return the C-style string at index IDX in tuple ITEMS.
  */
 static svn_error_t *
-svn_ra_svn__read_cstring(const apr_array_header_t *items,
+svn_ra_svn__read_cstring(const svn_ra_svn__list_t *items,
                          int idx,
                          const char **result)
 {
-  svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
+  svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING);
-  *result = elt->u.string->data;
+  *result = elt->u.string.data;
 
   return SVN_NO_ERROR;
 }
@@ -2525,11 +2863,11 @@ svn_ra_svn__read_cstring(const apr_array
 /* In *RESULT, return the word at index IDX in tuple ITEMS.
  */
 static svn_error_t *
-svn_ra_svn__read_word(const apr_array_header_t *items,
+svn_ra_svn__read_word(const svn_ra_svn__list_t *items,
                       int idx,
                       const char **result)
 {
-  svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
+  svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD);
   *result = elt->u.word;
 
@@ -2539,11 +2877,11 @@ svn_ra_svn__read_word(const apr_array_he
 /* In *RESULT, return the revision at index IDX in tuple ITEMS.
  */
 static svn_error_t *
-svn_ra_svn__read_revision(const apr_array_header_t *items,
+svn_ra_svn__read_revision(const svn_ra_svn__list_t *items,
                           int idx,
                           svn_revnum_t *result)
 {
-  svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
+  svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_NUMBER);
   *result = (svn_revnum_t)elt->u.number;
 
@@ -2553,11 +2891,11 @@ svn_ra_svn__read_revision(const apr_arra
 /* In *RESULT, return the boolean at index IDX in tuple ITEMS.
  */
 static svn_error_t *
-svn_ra_svn__read_boolean(const apr_array_header_t *items,
+svn_ra_svn__read_boolean(const svn_ra_svn__list_t *items,
                          int idx,
                          apr_uint64_t *result)
 {
-  svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
+  svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD);
   if (elt->u.word[0] == 't' && strcmp(elt->u.word, "true") == 0)
     *result = TRUE;
@@ -2572,21 +2910,21 @@ svn_ra_svn__read_boolean(const apr_array
 /* In *RESULT, return the tuple at index IDX in tuple ITEMS.
  */
 static svn_error_t *
-svn_ra_svn__read_list(const apr_array_header_t *items,
+svn_ra_svn__read_list(const svn_ra_svn__list_t *items,
                       int idx,
-                      const apr_array_header_t **result)
+                      const svn_ra_svn__list_t **result)
 {
-  svn_ra_svn_item_t *elt  = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
+  svn_ra_svn__item_t *elt  = &SVN_RA_SVN__LIST_ITEM(items, idx);
   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_LIST);
 
-  *result = elt->u.list;
+  *result = &elt->u.list;
   return SVN_NO_ERROR;
 }
 
 /* Verify the tuple ITEMS contains at least MIN and at most MAX elements.
  */
 static svn_error_t *
-svn_ra_svn__read_check_array_size(const apr_array_header_t *items,
+svn_ra_svn__read_check_array_size(const svn_ra_svn__list_t *items,
                                   int min,
                                   int max)
 {
@@ -2595,7 +2933,7 @@ svn_ra_svn__read_check_array_size(const
 }
 
 svn_error_t *
-svn_ra_svn__read_data_log_changed_entry(const apr_array_header_t *items,
+svn_ra_svn__read_data_log_changed_entry(const svn_ra_svn__list_t *items,
                                         svn_string_t **cpath,
                                         const char **action,
                                         const char **copy_path,
@@ -2604,7 +2942,7 @@ svn_ra_svn__read_data_log_changed_entry(
                                         apr_uint64_t *text_mods,
                                         apr_uint64_t *prop_mods)
 {
-  const apr_array_header_t *sub_items;
+  const svn_ra_svn__list_t *sub_items;
 
   /* initialize optional values */
   *copy_path = NULL;

Modified: subversion/branches/reuse-ra-session/subversion/libsvn_ra_svn/ra_svn.h
URL: http://svn.apache.org/viewvc/subversion/branches/reuse-ra-session/subversion/libsvn_ra_svn/ra_svn.h?rev=1702504&r1=1702503&r2=1702504&view=diff
==============================================================================
--- subversion/branches/reuse-ra-session/subversion/libsvn_ra_svn/ra_svn.h (original)
+++ subversion/branches/reuse-ra-session/subversion/libsvn_ra_svn/ra_svn.h Fri Sep 11 15:51:30 2015
@@ -165,8 +165,9 @@ svn_error_t *svn_ra_svn__locate_real_err
  * command response indicating failure).  The error chain will be
  * in the same order as the errors indicated in @a params.  Use
  * @a pool for temporary allocations. */
-svn_error_t *svn_ra_svn__handle_failure_status(const apr_array_header_t *params,
-                                               apr_pool_t *pool);
+svn_error_t *
+svn_ra_svn__handle_failure_status(const svn_ra_svn__list_t *params,
+                                  apr_pool_t *pool);
 
 /* Returns a stream that reads/writes from/to SOCK. */
 svn_ra_svn__stream_t *svn_ra_svn__stream_from_sock(apr_socket_t *sock,
@@ -220,7 +221,7 @@ svn_ra_svn__stream_data_available(svn_ra
  * tokens. */
 svn_error_t *
 svn_ra_svn__do_cyrus_auth(svn_ra_svn__session_baton_t *sess,
-                          const apr_array_header_t *mechlist,
+                          const svn_ra_svn__list_t *mechlist,
                           const char *realm, apr_pool_t *pool);
 
 /* Same as svn_ra_svn__do_cyrus_auth, but uses the built-in implementation of
@@ -229,7 +230,7 @@ svn_ra_svn__do_cyrus_auth(svn_ra_svn__se
  * mechanism with the server. */
 svn_error_t *
 svn_ra_svn__do_internal_auth(svn_ra_svn__session_baton_t *sess,
-                             const apr_array_header_t *mechlist,
+                             const svn_ra_svn__list_t *mechlist,
                              const char *realm, apr_pool_t *pool);
 
 /* Having picked a mechanism, start authentication by writing out an
@@ -239,8 +240,8 @@ svn_error_t *svn_ra_svn__auth_response(s
                                        apr_pool_t *pool,
                                        const char *mech, const char *mech_arg);
 
-/* Looks for MECH as a word in MECHLIST (an array of svn_ra_svn_item_t). */
-svn_boolean_t svn_ra_svn__find_mech(const apr_array_header_t *mechlist,
+/* Looks for MECH as a word in MECHLIST. */
+svn_boolean_t svn_ra_svn__find_mech(const svn_ra_svn__list_t *mechlist,
                                     const char *mech);
 
 /* Initialize the SASL library. */

Modified: subversion/branches/reuse-ra-session/subversion/libsvn_repos/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/branches/reuse-ra-session/subversion/libsvn_repos/deprecated.c?rev=1702504&r1=1702503&r2=1702504&view=diff
==============================================================================
--- subversion/branches/reuse-ra-session/subversion/libsvn_repos/deprecated.c (original)
+++ subversion/branches/reuse-ra-session/subversion/libsvn_repos/deprecated.c Fri Sep 11 15:51:30 2015
@@ -731,6 +731,33 @@ repos_notify_handler(void *baton,
   }
 }
 
+svn_error_t *
+svn_repos_dump_fs3(svn_repos_t *repos,
+                   svn_stream_t *stream,
+                   svn_revnum_t start_rev,
+                   svn_revnum_t end_rev,
+                   svn_boolean_t incremental,
+                   svn_boolean_t use_deltas,
+                   svn_repos_notify_func_t notify_func,
+                   void *notify_baton,
+                   svn_cancel_func_t cancel_func,
+                   void *cancel_baton,
+                   apr_pool_t *pool)
+{
+  return svn_error_trace(svn_repos_dump_fs4(repos,
+                                            stream,
+                                            start_rev,
+                                            end_rev,
+                                            incremental,
+                                            use_deltas,
+                                            TRUE,
+                                            TRUE,
+                                            notify_func,
+                                            notify_baton,
+                                            cancel_func,
+                                            cancel_baton,
+                                            pool));
+}
 
 svn_error_t *
 svn_repos_dump_fs2(svn_repos_t *repos,
@@ -774,9 +801,9 @@ svn_repos_verify_fs2(svn_repos_t *repos,
                                               end_rev,
                                               FALSE,
                                               FALSE,
-                                              FALSE,
                                               notify_func,
                                               notify_baton,
+                                              NULL, NULL,
                                               cancel_func,
                                               cancel_baton,
                                               pool));

Modified: subversion/branches/reuse-ra-session/subversion/libsvn_repos/dump.c
URL: http://svn.apache.org/viewvc/subversion/branches/reuse-ra-session/subversion/libsvn_repos/dump.c?rev=1702504&r1=1702503&r2=1702504&view=diff
==============================================================================
--- subversion/branches/reuse-ra-session/subversion/libsvn_repos/dump.c (original)
+++ subversion/branches/reuse-ra-session/subversion/libsvn_repos/dump.c Fri Sep 11 15:51:30 2015
@@ -1919,33 +1919,44 @@ get_dump_editor(const svn_delta_editor_t
 /* Helper for svn_repos_dump_fs.
 
    Write a revision record of REV in FS to writable STREAM, using POOL.
+   Dump revision properties as well if INCLUDE_REVPROPS has been set.
  */
 static svn_error_t *
 write_revision_record(svn_stream_t *stream,
                       svn_fs_t *fs,
                       svn_revnum_t rev,
+                      svn_boolean_t include_revprops,
                       apr_pool_t *pool)
 {
   apr_hash_t *props;
   apr_time_t timetemp;
   svn_string_t *datevalue;
 
-  SVN_ERR(svn_fs_revision_proplist(&props, fs, rev, pool));
+  if (include_revprops)
+    {
+      SVN_ERR(svn_fs_revision_proplist(&props, fs, rev, pool));
 
-  /* Run revision date properties through the time conversion to
-     canonicalize them. */
-  /* ### Remove this when it is no longer needed for sure. */
-  datevalue = svn_hash_gets(props, SVN_PROP_REVISION_DATE);
-  if (datevalue)
-    {
-      SVN_ERR(svn_time_from_cstring(&timetemp, datevalue->data, pool));
-      datevalue = svn_string_create(svn_time_to_cstring(timetemp, pool),
-                                    pool);
-      svn_hash_sets(props, SVN_PROP_REVISION_DATE, datevalue);
+      /* Run revision date properties through the time conversion to
+        canonicalize them. */
+      /* ### Remove this when it is no longer needed for sure. */
+      datevalue = svn_hash_gets(props, SVN_PROP_REVISION_DATE);
+      if (datevalue)
+        {
+          SVN_ERR(svn_time_from_cstring(&timetemp, datevalue->data, pool));
+          datevalue = svn_string_create(svn_time_to_cstring(timetemp, pool),
+                                        pool);
+          svn_hash_sets(props, SVN_PROP_REVISION_DATE, datevalue);
+        }
+    }
+   else
+    {
+      /* Although we won't use it, we still need this container for the
+         call below. */
+      props = apr_hash_make(pool);
     }
 
   SVN_ERR(svn_repos__dump_revision_record(stream, rev, NULL, props,
-                                          TRUE /*props_section_always*/,
+                                          include_revprops,
                                           pool));
   return SVN_NO_ERROR;
 }
@@ -1954,12 +1965,14 @@ write_revision_record(svn_stream_t *stre
 
 /* The main dumper. */
 svn_error_t *
-svn_repos_dump_fs3(svn_repos_t *repos,
+svn_repos_dump_fs4(svn_repos_t *repos,
                    svn_stream_t *stream,
                    svn_revnum_t start_rev,
                    svn_revnum_t end_rev,
                    svn_boolean_t incremental,
                    svn_boolean_t use_deltas,
+                   svn_boolean_t include_revprops,
+                   svn_boolean_t include_changes,
                    svn_repos_notify_func_t notify_func,
                    void *notify_baton,
                    svn_cancel_func_t cancel_func,
@@ -1970,7 +1983,7 @@ svn_repos_dump_fs3(svn_repos_t *repos,
   void *dump_edit_baton = NULL;
   svn_revnum_t rev;
   svn_fs_t *fs = svn_repos_fs(repos);
-  apr_pool_t *subpool = svn_pool_create(pool);
+  apr_pool_t *iterpool = svn_pool_create(pool);
   svn_revnum_t youngest;
   const char *uuid;
   int version;
@@ -2029,18 +2042,20 @@ svn_repos_dump_fs3(svn_repos_t *repos,
       svn_fs_root_t *to_root;
       svn_boolean_t use_deltas_for_rev;
 
-      svn_pool_clear(subpool);
+      svn_pool_clear(iterpool);
 
       /* Check for cancellation. */
       if (cancel_func)
         SVN_ERR(cancel_func(cancel_baton));
 
       /* Write the revision record. */
-      SVN_ERR(write_revision_record(stream, fs, rev, subpool));
+      SVN_ERR(write_revision_record(stream, fs, rev, include_revprops,
+                                    iterpool));
 
       /* When dumping revision 0, we just write out the revision record.
-         The parser might want to use its properties. */
-      if (rev == 0)
+         The parser might want to use its properties.
+         If we don't want revision changes at all, skip in any case. */
+      if (rev == 0 || !include_changes)
         goto loop_end;
 
       /* Fetch the editor which dumps nodes to a file.  Regardless of
@@ -2052,10 +2067,10 @@ svn_repos_dump_fs3(svn_repos_t *repos,
                               &found_old_mergeinfo, NULL,
                               notify_func, notify_baton,
                               start_rev, use_deltas_for_rev, FALSE, FALSE,
-                              subpool));
+                              iterpool));
 
       /* Drive the editor in one way or another. */
-      SVN_ERR(svn_fs_revision_root(&to_root, fs, rev, subpool));
+      SVN_ERR(svn_fs_revision_root(&to_root, fs, rev, iterpool));
 
       /* If this is the first revision of a non-incremental dump,
          we're in for a full tree dump.  Otherwise, we want to simply
@@ -2064,7 +2079,7 @@ svn_repos_dump_fs3(svn_repos_t *repos,
         {
           /* Compare against revision 0, so everything appears to be added. */
           svn_fs_root_t *from_root;
-          SVN_ERR(svn_fs_revision_root(&from_root, fs, 0, subpool));
+          SVN_ERR(svn_fs_revision_root(&from_root, fs, 0, iterpool));
           SVN_ERR(svn_repos_dir_delta2(from_root, "", "",
                                        to_root, "",
                                        dump_editor, dump_edit_baton,
@@ -2074,25 +2089,25 @@ svn_repos_dump_fs3(svn_repos_t *repos,
                                        svn_depth_infinity,
                                        FALSE, /* don't send entry props */
                                        FALSE, /* don't ignore ancestry */
-                                       subpool));
+                                       iterpool));
         }
       else
         {
           /* The normal case: compare consecutive revs. */
           SVN_ERR(svn_repos_replay2(to_root, "", SVN_INVALID_REVNUM, FALSE,
                                     dump_editor, dump_edit_baton,
-                                    NULL, NULL, subpool));
+                                    NULL, NULL, iterpool));
 
           /* While our editor close_edit implementation is a no-op, we still
              do this for completeness. */
-          SVN_ERR(dump_editor->close_edit(dump_edit_baton, subpool));
+          SVN_ERR(dump_editor->close_edit(dump_edit_baton, iterpool));
         }
 
     loop_end:
       if (notify_func)
         {
           notify->revision = rev;
-          notify_func(notify_baton, notify, subpool);
+          notify_func(notify_baton, notify, iterpool);
         }
     }
 
@@ -2103,12 +2118,12 @@ svn_repos_dump_fs3(svn_repos_t *repos,
          warning, since the inline warnings already issued might easily be
          missed. */
 
-      notify = svn_repos_notify_create(svn_repos_notify_dump_end, subpool);
-      notify_func(notify_baton, notify, subpool);
+      notify = svn_repos_notify_create(svn_repos_notify_dump_end, iterpool);
+      notify_func(notify_baton, notify, iterpool);
 
       if (found_old_reference)
         {
-          notify_warning(subpool, notify_func, notify_baton,
+          notify_warning(iterpool, notify_func, notify_baton,
                          svn_repos_notify_warning_found_old_reference,
                          _("The range of revisions dumped "
                            "contained references to "
@@ -2120,7 +2135,7 @@ svn_repos_dump_fs3(svn_repos_t *repos,
          in dumped mergeinfo. */
       if (found_old_mergeinfo)
         {
-          notify_warning(subpool, notify_func, notify_baton,
+          notify_warning(iterpool, notify_func, notify_baton,
                          svn_repos_notify_warning_found_old_mergeinfo,
                          _("The range of revisions dumped "
                            "contained mergeinfo "
@@ -2129,7 +2144,7 @@ svn_repos_dump_fs3(svn_repos_t *repos,
         }
     }
 
-  svn_pool_destroy(subpool);
+  svn_pool_destroy(iterpool);
 
   return SVN_NO_ERROR;
 }
@@ -2265,24 +2280,6 @@ verify_close_directory(void *dir_baton,
   return close_directory(dir_baton, pool);
 }
 
-static void
-notify_verification_error(svn_revnum_t rev,
-                          svn_error_t *err,
-                          svn_repos_notify_func_t notify_func,
-                          void *notify_baton,
-                          apr_pool_t *pool)
-{
-  svn_repos_notify_t *notify_failure;
-
-  if (notify_func == NULL)
-    return;
-
-  notify_failure = svn_repos_notify_create(svn_repos_notify_failure, pool);
-  notify_failure->err = err;
-  notify_failure->revision = rev;
-  notify_func(notify_baton, notify_failure, pool);
-}
-
 /* Verify revision REV in file system FS. */
 static svn_error_t *
 verify_one_revision(svn_fs_t *fs,
@@ -2359,15 +2356,42 @@ verify_fs_notify_func(svn_revnum_t revis
                             notify_baton->notify, pool);
 }
 
+static svn_error_t *
+report_error(svn_revnum_t revision,
+             svn_error_t *verify_err,
+             svn_repos_verify_callback_t verify_callback,
+             void *verify_baton,
+             apr_pool_t *pool)
+{
+  if (verify_callback)
+    {
+      svn_error_t *cb_err;
+
+      /* The caller provided us with a callback, so make him responsible
+         for what's going to happen with the error. */
+      cb_err = verify_callback(verify_baton, revision, verify_err, pool);
+      svn_error_clear(verify_err);
+      SVN_ERR(cb_err);
+
+      return SVN_NO_ERROR;
+    }
+  else
+    {
+      /* No callback -- no second guessing.  Just return the error. */
+      return svn_error_trace(verify_err);
+    }
+}
+
 svn_error_t *
 svn_repos_verify_fs3(svn_repos_t *repos,
                      svn_revnum_t start_rev,
                      svn_revnum_t end_rev,
-                     svn_boolean_t keep_going,
                      svn_boolean_t check_normalization,
                      svn_boolean_t metadata_only,
                      svn_repos_notify_func_t notify_func,
                      void *notify_baton,
+                     svn_repos_verify_callback_t verify_callback,
+                     void *verify_baton,
                      svn_cancel_func_t cancel_func,
                      void *cancel_baton,
                      apr_pool_t *pool)
@@ -2380,8 +2404,6 @@ svn_repos_verify_fs3(svn_repos_t *repos,
   svn_fs_progress_notify_func_t verify_notify = NULL;
   struct verify_fs_notify_func_baton_t *verify_notify_baton = NULL;
   svn_error_t *err;
-  svn_boolean_t failed_metadata = FALSE;
-  svn_revnum_t failed_revisions = 0;
 
   /* Determine the current youngest revision of the filesystem. */
   SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool));
@@ -2430,20 +2452,8 @@ svn_repos_verify_fs3(svn_repos_t *repos,
     }
   else if (err)
     {
-      notify_verification_error(SVN_INVALID_REVNUM, err, notify_func,
-                                notify_baton, iterpool);
-
-      if (!keep_going)
-        {
-          /* Return the error, the caller doesn't want us to continue. */
-          return svn_error_trace(err);
-        }
-      else
-        {
-          /* Clear the error and keep going. */
-          failed_metadata = TRUE;
-          svn_error_clear(err);
-        }
+      SVN_ERR(report_error(SVN_INVALID_REVNUM, err, verify_callback,
+                           verify_baton, iterpool));
     }
 
   if (!metadata_only)
@@ -2463,20 +2473,8 @@ svn_repos_verify_fs3(svn_repos_t *repos,
           }
         else if (err)
           {
-            notify_verification_error(rev, err, notify_func, notify_baton,
-                                      iterpool);
-
-            if (!keep_going)
-              {
-                /* Return the error, the caller doesn't want us to continue. */
-                return svn_error_trace(err);
-              }
-            else
-              {
-                /* Clear the error and keep going. */
-                ++failed_revisions;
-                svn_error_clear(err);
-              }
+            SVN_ERR(report_error(rev, err, verify_callback, verify_baton,
+                                 iterpool));
           }
         else if (notify_func)
           {
@@ -2495,40 +2493,5 @@ svn_repos_verify_fs3(svn_repos_t *repos,
 
   svn_pool_destroy(iterpool);
 
-  /* Summarize the results. */
-  if (failed_metadata || 0 != failed_revisions)
-    {
-      const char *const repos_path =
-        svn_dirent_local_style(svn_repos_path(repos, pool), pool);
-
-      if (0 == failed_revisions)
-        {
-          return svn_error_createf(
-              SVN_ERR_REPOS_VERIFY_FAILED, NULL,
-              _("Metadata verification failed on repository '%s'"),
-              repos_path);
-        }
-      else
-        {
-          const char* format_string;
-
-          if (failed_metadata)
-            format_string = apr_psprintf(
-                pool, _("Verification of metadata and"
-                        " %%%s out of %%%s revisions"
-                        " failed on repository '%%s'"),
-                SVN_REVNUM_T_FMT, SVN_REVNUM_T_FMT);
-          else
-            format_string = apr_psprintf(
-                pool, _("Verification of %%%s out of %%%s revisions"
-                        " failed on repository '%%s'"),
-                SVN_REVNUM_T_FMT, SVN_REVNUM_T_FMT);
-
-          return svn_error_createf(
-              SVN_ERR_REPOS_VERIFY_FAILED, NULL, format_string,
-              failed_revisions, end_rev - start_rev + 1, repos_path);
-        }
-    }
-
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/reuse-ra-session/subversion/libsvn_repos/fs-wrap.c
URL: http://svn.apache.org/viewvc/subversion/branches/reuse-ra-session/subversion/libsvn_repos/fs-wrap.c?rev=1702504&r1=1702503&r2=1702504&view=diff
==============================================================================
--- subversion/branches/reuse-ra-session/subversion/libsvn_repos/fs-wrap.c (original)
+++ subversion/branches/reuse-ra-session/subversion/libsvn_repos/fs-wrap.c Fri Sep 11 15:51:30 2015
@@ -975,15 +975,18 @@ pack_notify_func(void *baton,
 {
   struct pack_notify_baton *pnb = baton;
   svn_repos_notify_t *notify;
+  svn_repos_notify_action_t repos_action;
 
   /* Simple conversion works for these values. */
   SVN_ERR_ASSERT(pack_action >= svn_fs_pack_notify_start
-                 && pack_action <= svn_fs_pack_notify_end_revprop);
+                 && pack_action <= svn_fs_pack_notify_noop);
 
-  notify = svn_repos_notify_create(pack_action
-                                   + svn_repos_notify_pack_shard_start
-                                   - svn_fs_pack_notify_start,
-                                   pool);
+  repos_action = pack_action == svn_fs_pack_notify_noop
+               ? svn_repos_notify_pack_noop
+               : pack_action + svn_repos_notify_pack_shard_start
+                             - svn_fs_pack_notify_start;
+
+  notify = svn_repos_notify_create(repos_action, pool);
   notify->shard = shard;
   pnb->notify_func(pnb->notify_baton, notify, pool);
 

Modified: subversion/branches/reuse-ra-session/subversion/libsvn_repos/load-fs-vtable.c
URL: http://svn.apache.org/viewvc/subversion/branches/reuse-ra-session/subversion/libsvn_repos/load-fs-vtable.c?rev=1702504&r1=1702503&r2=1702504&view=diff
==============================================================================
--- subversion/branches/reuse-ra-session/subversion/libsvn_repos/load-fs-vtable.c (original)
+++ subversion/branches/reuse-ra-session/subversion/libsvn_repos/load-fs-vtable.c Fri Sep 11 15:51:30 2015
@@ -624,14 +624,6 @@ maybe_add_with_history(struct node_baton
 }
 
 static svn_error_t *
-magic_header_record(int version,
-                    void *parse_baton,
-                    apr_pool_t *pool)
-{
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
 uuid_record(const char *uuid,
             void *parse_baton,
             apr_pool_t *pool)
@@ -1194,7 +1186,7 @@ svn_repos_get_fs_build_parser5(const svn
   if (SVN_IS_VALID_REVNUM(start_rev))
     SVN_ERR_ASSERT(start_rev <= end_rev);
 
-  parser->magic_header_record = magic_header_record;
+  parser->magic_header_record = NULL;
   parser->uuid_record = uuid_record;
   parser->new_revision_record = new_revision_record;
   parser->new_node_record = new_node_record;
@@ -1271,3 +1263,215 @@ svn_repos_load_fs5(svn_repos_t *repos,
   return svn_repos_parse_dumpstream3(dumpstream, parser, parse_baton, FALSE,
                                      cancel_func, cancel_baton, pool);
 }
+
+/*----------------------------------------------------------------------*/
+
+/** The same functionality for revprops only **/
+
+/* Implement svn_repos_parse_fns3_t.new_revision_record.
+ *
+ * Because the revision is supposed to already exist, we don't need to
+ * start transactions etc. */
+static svn_error_t *
+revprops_new_revision_record(void **revision_baton,
+                             apr_hash_t *headers,
+                             void *parse_baton,
+                             apr_pool_t *pool)
+{
+  struct parse_baton *pb = parse_baton;
+  struct revision_baton *rb;
+
+  rb = make_revision_baton(headers, pb, pool);
+
+  /* If we're skipping this revision, try to notify someone. */
+  if (rb->skipped && pb->notify_func)
+    {
+      /* ### TODO: Use proper scratch pool instead of pb->notify_pool */
+      svn_repos_notify_t *notify = svn_repos_notify_create(
+                                        svn_repos_notify_load_skipped_rev,
+                                        pb->notify_pool);
+
+      notify->old_revision = rb->rev;
+      pb->notify_func(pb->notify_baton, notify, pb->notify_pool);
+      svn_pool_clear(pb->notify_pool);
+    }
+
+  /* If we're parsing revision 0, only the revision props are (possibly)
+     interesting to us: when loading the stream into an empty
+     filesystem, then we want new filesystem's revision 0 to have the
+     same props.  Otherwise, we just ignore revision 0 in the stream. */
+
+  *revision_baton = rb;
+  return SVN_NO_ERROR;
+}
+
+/* Implement svn_repos_parse_fns3_t.close_revision.
+ *
+ * Simply set the revprops we previously parsed and send notifications.
+ * This is the place where we will detect missing revisions. */
+static svn_error_t *
+revprops_close_revision(void *baton)
+{
+  struct revision_baton *rb = baton;
+  struct parse_baton *pb = rb->pb;
+  apr_hash_t *orig_props;
+  apr_hash_t *new_props;
+  apr_array_header_t *diff;
+  int i;
+
+  /* If we're skipping this revision we're done here. */
+  if (rb->skipped)
+    return SVN_NO_ERROR;
+
+  /* If the dumpstream doesn't have an 'svn:date' property and we
+     aren't ignoring the dates in the dumpstream altogether, remove
+     any 'svn:date' revision property that was set by FS layer when
+     the TXN was created.  */
+  if (! (pb->ignore_dates || rb->datestamp))
+    {
+      svn_prop_t *prop = &APR_ARRAY_PUSH(rb->revprops, svn_prop_t);
+      prop->name = SVN_PROP_REVISION_DATE;
+      prop->value = NULL;
+    }
+
+  SVN_ERR(svn_fs_revision_proplist(&orig_props, pb->fs, rb->rev, rb->pool));
+  new_props = svn_prop_array_to_hash(rb->revprops, rb->pool);
+  SVN_ERR(svn_prop_diffs(&diff, new_props, orig_props, rb->pool));
+
+  for (i = 0; i < diff->nelts; i++)
+    {
+      const svn_prop_t *prop = &APR_ARRAY_IDX(diff, i, svn_prop_t);
+
+      SVN_ERR(change_rev_prop(pb->repos, rb->rev, prop->name, prop->value,
+                              pb->validate_props, rb->pool));
+    }
+
+  if (pb->notify_func)
+    {
+      /* ### TODO: Use proper scratch pool instead of pb->notify_pool */
+      svn_repos_notify_t *notify = svn_repos_notify_create(
+                                        svn_repos_notify_load_revprop_set,
+                                        pb->notify_pool);
+
+      notify->new_revision = rb->rev;
+      notify->old_revision = SVN_INVALID_REVNUM;
+      pb->notify_func(pb->notify_baton, notify, pb->notify_pool);
+      svn_pool_clear(pb->notify_pool);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* Set *CALLBACKS and *PARSE_BATON to a vtable parser which commits new
+ * revisions to the fs in REPOS.  Allocate the objects in RESULT_POOL.
+ *
+ * START_REV and END_REV act as filters, the lower and upper (inclusive)
+ * range values of revisions in DUMPSTREAM which will be loaded.  Either
+ * both of these values are #SVN_INVALID_REVNUM (in  which case no
+ * revision-based filtering occurs at all), or both are valid revisions
+ * (where START_REV is older than or equivalent to END_REV).
+ * 
+ * START_REV and END_REV act as filters, the lower and upper (inclusive)
+ * range values of revisions which will
+ * be loaded.  Either both of these values are #SVN_INVALID_REVNUM (in
+ * which case no revision-based filtering occurs at all), or both are
+ * valid revisions (where START_REV is older than or equivalent to
+ * END_REV).  They refer to dump stream revision numbers rather than
+ * committed revision numbers.
+ *
+ * If VALIDATE_PROPS is set, then validate Subversion revision properties
+ * (those in the svn: namespace) against established rules for those things.
+ *
+ * If IGNORE_DATES is set, ignore any revision datestamps found in
+ * DUMPSTREAM, keeping whatever timestamps the revisions currently have.
+ */
+static svn_error_t *
+build_revprop_parser(const svn_repos_parse_fns3_t **callbacks,
+                     void **parse_baton,
+                     svn_repos_t *repos,
+                     svn_revnum_t start_rev,
+                     svn_revnum_t end_rev,
+                     svn_boolean_t validate_props,
+                     svn_boolean_t ignore_dates,
+                     svn_repos_notify_func_t notify_func,
+                     void *notify_baton,
+                     apr_pool_t *result_pool)
+{
+  svn_repos_parse_fns3_t *parser = apr_pcalloc(result_pool, sizeof(*parser));
+  struct parse_baton *pb = apr_pcalloc(result_pool, sizeof(*pb));
+
+  SVN_ERR_ASSERT((SVN_IS_VALID_REVNUM(start_rev) &&
+                  SVN_IS_VALID_REVNUM(end_rev))
+                 || ((! SVN_IS_VALID_REVNUM(start_rev)) &&
+                     (! SVN_IS_VALID_REVNUM(end_rev))));
+  if (SVN_IS_VALID_REVNUM(start_rev))
+    SVN_ERR_ASSERT(start_rev <= end_rev);
+
+  parser->magic_header_record = NULL;
+  parser->uuid_record = uuid_record;
+  parser->new_revision_record = revprops_new_revision_record;
+  parser->new_node_record = NULL;
+  parser->set_revision_property = set_revision_property;
+  parser->set_node_property = NULL;
+  parser->remove_node_props = NULL;
+  parser->set_fulltext = NULL;
+  parser->close_node = NULL;
+  parser->close_revision = revprops_close_revision;
+  parser->delete_node_property = NULL;
+  parser->apply_textdelta = NULL;
+
+  pb->repos = repos;
+  pb->fs = svn_repos_fs(repos);
+  pb->use_history = FALSE;
+  pb->validate_props = validate_props;
+  pb->notify_func = notify_func;
+  pb->notify_baton = notify_baton;
+  pb->uuid_action = svn_repos_load_uuid_ignore; /* Never touch the UUID. */
+  pb->parent_dir = NULL;
+  pb->pool = result_pool;
+  pb->notify_pool = svn_pool_create(result_pool);
+  pb->rev_map = NULL;
+  pb->oldest_dumpstream_rev = SVN_INVALID_REVNUM;
+  pb->last_rev_mapped = SVN_INVALID_REVNUM;
+  pb->start_rev = start_rev;
+  pb->end_rev = end_rev;
+  pb->use_pre_commit_hook = FALSE;
+  pb->use_post_commit_hook = FALSE;
+  pb->ignore_dates = ignore_dates;
+
+  *callbacks = parser;
+  *parse_baton = pb;
+  return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_repos_load_fs_revprops(svn_repos_t *repos,
+                           svn_stream_t *dumpstream,
+                           svn_revnum_t start_rev,
+                           svn_revnum_t end_rev,
+                           svn_boolean_t validate_props,
+                           svn_boolean_t ignore_dates,
+                           svn_repos_notify_func_t notify_func,
+                           void *notify_baton,
+                           svn_cancel_func_t cancel_func,
+                           void *cancel_baton,
+                           apr_pool_t *scratch_pool)
+{
+  const svn_repos_parse_fns3_t *parser;
+  void *parse_baton;
+
+  /* This is really simple. */
+
+  SVN_ERR(build_revprop_parser(&parser, &parse_baton,
+                               repos,
+                               start_rev, end_rev,
+                               validate_props,
+                               ignore_dates,
+                               notify_func,
+                               notify_baton,
+                               scratch_pool));
+
+  return svn_repos_parse_dumpstream3(dumpstream, parser, parse_baton, FALSE,
+                                     cancel_func, cancel_baton, scratch_pool);
+}

Modified: subversion/branches/reuse-ra-session/subversion/libsvn_repos/load.c
URL: http://svn.apache.org/viewvc/subversion/branches/reuse-ra-session/subversion/libsvn_repos/load.c?rev=1702504&r1=1702503&r2=1702504&view=diff
==============================================================================
--- subversion/branches/reuse-ra-session/subversion/libsvn_repos/load.c (original)
+++ subversion/branches/reuse-ra-session/subversion/libsvn_repos/load.c Fri Sep 11 15:51:30 2015
@@ -385,7 +385,135 @@ parse_format_version(int *version,
   return SVN_NO_ERROR;
 }
 
+/*----------------------------------------------------------------------*/
+
+/** Dummy callback implementations for functions not provided by the user **/
+
+static svn_error_t *
+dummy_handler_magic_header_record(int version,
+                                  void *parse_baton,
+                                  apr_pool_t *pool)
+{
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+dummy_handler_uuid_record(const char *uuid,
+                          void *parse_baton,
+                          apr_pool_t *pool)
+{
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+dummy_handler_new_revision_record(void **revision_baton,
+                                  apr_hash_t *headers,
+                                  void *parse_baton,
+                                  apr_pool_t *pool)
+{
+  *revision_baton = NULL;
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+dummy_handler_new_node_record(void **node_baton,
+                              apr_hash_t *headers,
+                              void *revision_baton,
+                              apr_pool_t *pool)
+{
+  *node_baton = NULL;
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+dummy_handler_set_revision_property(void *revision_baton,
+                                    const char *name,
+                                    const svn_string_t *value)
+{
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+dummy_handler_set_node_property(void *node_baton,
+                                const char *name,
+                                const svn_string_t *value)
+{
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+dummy_handler_delete_node_property(void *node_baton,
+                                   const char *name)
+{
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+dummy_handler_remove_node_props(void *node_baton)
+{
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+dummy_handler_set_fulltext(svn_stream_t **stream,
+                               void *node_baton)
+{
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+dummy_handler_apply_textdelta(svn_txdelta_window_handler_t *handler,
+                              void **handler_baton,
+                              void *node_baton)
+{
+  /* Only called by parse_text_block() and that tests for NULL handlers. */
+  *handler = NULL;
+  *handler_baton = NULL;
+  return SVN_NO_ERROR;
+}
 
+static svn_error_t *
+dummy_handler_close_node(void *node_baton)
+{
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+dummy_handler_close_revision(void *revision_baton)
+{
+  return SVN_NO_ERROR;
+}
+
+/* Helper macro to copy the function pointer SOURCE->NAME to DEST->NAME.
+ * If the source pointer is NULL, pick the corresponding dummy handler
+ * instead. */
+#define SET_VTABLE_ENTRY(dest, source, name) \
+  dest->name = provided->name ? provided->name : dummy_handler_##name
+
+/* Return a copy of PROVIDED with all NULL callbacks replaced by a dummy
+ * handler.  Allocate the result in RESULT_POOL. */
+static const svn_repos_parse_fns3_t *
+complete_vtable(const svn_repos_parse_fns3_t *provided,
+                apr_pool_t *result_pool)
+{
+  svn_repos_parse_fns3_t *completed = apr_pcalloc(result_pool,
+                                                  sizeof(*completed));
+
+  SET_VTABLE_ENTRY(completed, provided, magic_header_record);
+  SET_VTABLE_ENTRY(completed, provided, uuid_record);
+  SET_VTABLE_ENTRY(completed, provided, new_revision_record);
+  SET_VTABLE_ENTRY(completed, provided, new_node_record);
+  SET_VTABLE_ENTRY(completed, provided, set_revision_property);
+  SET_VTABLE_ENTRY(completed, provided, set_node_property);
+  SET_VTABLE_ENTRY(completed, provided, delete_node_property);
+  SET_VTABLE_ENTRY(completed, provided, remove_node_props);
+  SET_VTABLE_ENTRY(completed, provided, set_fulltext);
+  SET_VTABLE_ENTRY(completed, provided, apply_textdelta);
+  SET_VTABLE_ENTRY(completed, provided, close_node);
+  SET_VTABLE_ENTRY(completed, provided, close_revision);
+
+  return completed;
+}
 
 /*----------------------------------------------------------------------*/
 
@@ -410,6 +538,10 @@ svn_repos_parse_dumpstream3(svn_stream_t
   apr_pool_t *nodepool = svn_pool_create(pool);
   int version;
 
+  /* Make sure we can blindly invoke callbacks. */
+  parse_fns = complete_vtable(parse_fns, pool);
+
+  /* Start parsing process. */
   SVN_ERR(svn_stream_readline(stream, &linebuf, "\n", &eof, linepool));
   if (eof)
     return stream_ran_dry();

Modified: subversion/branches/reuse-ra-session/subversion/libsvn_repos/log.c
URL: http://svn.apache.org/viewvc/subversion/branches/reuse-ra-session/subversion/libsvn_repos/log.c?rev=1702504&r1=1702503&r2=1702504&view=diff
==============================================================================
--- subversion/branches/reuse-ra-session/subversion/libsvn_repos/log.c (original)
+++ subversion/branches/reuse-ra-session/subversion/libsvn_repos/log.c Fri Sep 11 15:51:30 2015
@@ -1907,8 +1907,7 @@ store_search(svn_mergeinfo_t processed,
       const char *path = APR_ARRAY_IDX(paths, i, const char *);
       svn_rangelist_t *ranges = apr_array_make(processed_pool, 1,
                                                sizeof(svn_merge_range_t*));
-      svn_merge_range_t *range = apr_palloc(processed_pool,
-                                            sizeof(svn_merge_range_t));
+      svn_merge_range_t *range = apr_palloc(processed_pool, sizeof(*range));
 
       range->start = start;
       range->end = end;
@@ -2176,7 +2175,7 @@ do_logs(svn_fs_t *fs,
           if (rev_mergeinfo)
             {
               struct added_deleted_mergeinfo *add_and_del_mergeinfo =
-                apr_hash_get(rev_mergeinfo, &current, sizeof(svn_revnum_t));
+                apr_hash_get(rev_mergeinfo, &current, sizeof(current));
               added_mergeinfo = add_and_del_mergeinfo->added_mergeinfo;
               deleted_mergeinfo = add_and_del_mergeinfo->deleted_mergeinfo;
               has_children = (apr_hash_count(added_mergeinfo) > 0