You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by br...@apache.org on 2015/01/03 15:00:44 UTC

svn commit: r1649205 [14/30] - in /subversion/branches/authzperf: ./ build/ build/ac-macros/ notes/ subversion/bindings/ctypes-python/ subversion/bindings/cxxhl/ subversion/bindings/javahl/tests/org/apache/subversion/javahl/ subversion/bindings/swig/ s...

Modified: subversion/branches/authzperf/subversion/libsvn_fs_x/index.h
URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_fs_x/index.h?rev=1649205&r1=1649204&r2=1649205&view=diff
==============================================================================
--- subversion/branches/authzperf/subversion/libsvn_fs_x/index.h (original)
+++ subversion/branches/authzperf/subversion/libsvn_fs_x/index.h Sat Jan  3 14:00:41 2015
@@ -24,6 +24,7 @@
 #define SVN_LIBSVN_FS__INDEX_H
 
 #include "fs.h"
+#include "rev_file.h"
 
 /* Per-defined item index values.  They are used to identify empty or
  * mandatory items.
@@ -65,7 +66,7 @@ typedef struct svn_fs_x__p2l_entry_t
   apr_off_t size;
 
   /* type of the item (see SVN_FS_X__ITEM_TYPE_*) defines */
-  unsigned type;
+  apr_uint32_t type;
 
   /* modified FNV-1a checksum.  0 if unknown checksum */
   apr_uint32_t fnv1_checksum;
@@ -76,7 +77,7 @@ typedef struct svn_fs_x__p2l_entry_t
   apr_uint32_t item_count;
 
   /* List of items in that block / container */
-  svn_fs_x__id_part_t *items;
+  svn_fs_x__id_t *items;
 } svn_fs_x__p2l_entry_t;
 
 /* Return a (deep) copy of ENTRY, allocated in POOL.
@@ -86,20 +87,21 @@ svn_fs_x__p2l_entry_dup(const svn_fs_x__
                         apr_pool_t *pool);
 
 /* Open / create a log-to-phys index file with the full file path name
- * FILE_NAME.  Return the open file in *PROTO_INDEX and use POOL for
- * allocations.
+ * FILE_NAME.  Return the open file in *PROTO_INDEX allocated in
+ * RESULT_POOL.
  */
 svn_error_t *
 svn_fs_x__l2p_proto_index_open(apr_file_t **proto_index,
                                const char *file_name,
-                               apr_pool_t *pool);
+                               apr_pool_t *result_pool);
 
 /* Call this function before adding entries for the next revision to the
- * log-to-phys index file in PROTO_INDEX.  Use POOL for allocations.
+ * log-to-phys index file in PROTO_INDEX.  Use SCRATCH_POOL for temporary
+ * allocations.
  */
 svn_error_t *
 svn_fs_x__l2p_proto_index_add_revision(apr_file_t *proto_index,
-                                       apr_pool_t *pool);
+                                       apr_pool_t *scratch_pool);
 
 /* Add a new mapping, ITEM_INDEX to the (OFFSET, SUB_ITEM) pair, to log-to-
  * phys index file in PROTO_INDEX.  Please note that mappings may be added
@@ -109,72 +111,84 @@ svn_fs_x__l2p_proto_index_add_revision(a
  * that is already implied for all item indexes not explicitly given a
  * mapping.
  * 
- * Use POOL for allocations.
+ * Use SCRATCH_POOL for temporary allocations.
  */
 svn_error_t *
 svn_fs_x__l2p_proto_index_add_entry(apr_file_t *proto_index,
                                     apr_off_t offset,
                                     apr_uint32_t sub_item,
                                     apr_uint64_t item_index,
-                                    apr_pool_t *pool);
+                                    apr_pool_t *scratch_pool);
 
-/* Use the proto index file stored at PROTO_FILE_NAME and construct the
- * final log-to-phys index file at FILE_NAME.  The first revision will
+/* Use the proto index file stored at PROTO_FILE_NAME, construct the final
+ * log-to-phys index and append it to INDEX_FILE.  The first revision will
  * be REVISION, entries to the next revision will be assigned to REVISION+1
- * and so forth.  Use POOL for allocations.
+ * and so forth.  
+ *
+ * Return the MD5 checksum of the on-disk index data in *CHECKSUM, allocated
+ * in RESULT_POOL.  Use SCRATCH_POOL for temporary allocations.
  */
 svn_error_t *
-svn_fs_x__l2p_index_create(svn_fs_t *fs,
-                           const char *file_name,
+svn_fs_x__l2p_index_append(svn_checksum_t **checksum,
+                           svn_fs_t *fs,
+                           apr_file_t *index_file,
                            const char *proto_file_name,
                            svn_revnum_t revision,
-                           apr_pool_t *pool);
+                           apr_pool_t *result_pool,
+                           apr_pool_t *scratch_pool);
 
 /* Open / create a phys-to-log index file with the full file path name
- * FILE_NAME.  Return the open file in *PROTO_INDEX and use POOL for
- * allocations.
+ * FILE_NAME.  Return the open file in *PROTO_INDEX allocated in
+ * RESULT_POOL.
  */
 svn_error_t *
 svn_fs_x__p2l_proto_index_open(apr_file_t **proto_index,
                                const char *file_name,
-                               apr_pool_t *pool);
+                               apr_pool_t *result_pool);
 
 /* Add a new mapping ENTRY to the phys-to-log index file in PROTO_INDEX.
  * The entries must be added in ascending offset order and must not leave
  * intermittent ranges uncovered.  The revision value in ENTRY may be
- * SVN_INVALID_REVISION.  Use POOL for allocations.
+ * SVN_INVALID_REVISION.  Use SCRATCH_POOL for temporary allocations.
  */
 svn_error_t *
 svn_fs_x__p2l_proto_index_add_entry(apr_file_t *proto_index,
-                                    svn_fs_x__p2l_entry_t *entry,
-                                    apr_pool_t *pool);
+                                    const svn_fs_x__p2l_entry_t *entry,
+                                    apr_pool_t *scratch_pool);
 
 /* Set *NEXT_OFFSET to the first offset behind the last entry in the
  * phys-to-log proto index file PROTO_INDEX.  This will be 0 for empty
- * index files.  Use POOL for temporary allocations.
+ * index files.  Use SCRATCH_POOL for temporary allocations.
  */
 svn_error_t *
 svn_fs_x__p2l_proto_index_next_offset(apr_off_t *next_offset,
                                       apr_file_t *proto_index,
-                                      apr_pool_t *pool);
+                                      apr_pool_t *scratch_pool);
 
-/* Use the proto index file stored at PROTO_FILE_NAME and construct the
- * final phys-to-log index file at FILE_NAME.  Entries without a valid
+/* Use the proto index file stored at PROTO_FILE_NAME, construct the final
+ * phys-to-log index and append it to INDEX_FILE.  Entries without a valid
  * revision will be assigned to the REVISION given here.
- * Use POOL for allocations.
+ *
+ * Return the MD5 checksum of the on-disk index data in *CHECKSUM, allocated
+ * in RESULT_POOL.  Use SCRATCH_POOL for temporary allocations.
  */
 svn_error_t *
-svn_fs_x__p2l_index_create(svn_fs_t *fs,
-                           const char *file_name,
+svn_fs_x__p2l_index_append(svn_checksum_t **checksum,
+                           svn_fs_t *fs,
+                           apr_file_t *index_file,
                            const char *proto_file_name,
                            svn_revnum_t revision,
-                           apr_pool_t *pool);
+                           apr_pool_t *result_pool,
+                           apr_pool_t *scratch_pool);
 
 /* Use the phys-to-log mapping files in FS to build a list of entries
  * that (at least partly) overlap with the range given by BLOCK_START
  * offset and BLOCK_SIZE in the rep / pack file containing REVISION.
- * Return the array in *ENTRIES with svn_fs_fs__p2l_entry_t as elements.
- * Use POOL for allocations.
+ * Return the array in *ENTRIES with svn_fs_fs__p2l_entry_t as elements,
+ * allocated in RESULT_POOL.  REV_FILE determines whether to access single
+ * rev or pack file data.  If that is not available anymore (neither in
+ * cache nor on disk), return an error.  Use SCRATCH_POOL for temporary
+ * allocations.
  *
  * Note that (only) the first and the last mapping may cross a cluster
  * boundary.
@@ -182,69 +196,122 @@ svn_fs_x__p2l_index_create(svn_fs_t *fs,
 svn_error_t *
 svn_fs_x__p2l_index_lookup(apr_array_header_t **entries,
                            svn_fs_t *fs,
+                           svn_fs_x__revision_file_t *rev_file,
                            svn_revnum_t revision,
                            apr_off_t block_start,
                            apr_off_t block_size,
-                           apr_pool_t *pool);
+                           apr_pool_t *result_pool,
+                           apr_pool_t *scratch_pool);
 
 /* Use the phys-to-log mapping files in FS to return the entry for the
  * container or single item starting at global OFFSET in the rep file
- * containing REVISION in *ENTRY.  Sets *ENTRY to NULL if no item starts
- * at exactly that offset.  Use POOL for allocations.
+ * containing REVISION in*ENTRY, allocated in RESULT_POOL.  Sets *ENTRY
+ * to NULL if no item starts at exactly that offset.  REV_FILE determines
+ * whether to access single rev or pack file data.  If that is not available
+ * anymore (neither in cache nor on disk), return an error.
+ * Use SCRATCH_POOL for temporary allocations.
  */
 svn_error_t *
 svn_fs_x__p2l_entry_lookup(svn_fs_x__p2l_entry_t **entry,
                            svn_fs_t *fs,
+                           svn_fs_x__revision_file_t *rev_file,
                            svn_revnum_t revision,
                            apr_off_t offset,
-                           apr_pool_t *pool);
+                           apr_pool_t *result_pool,
+                           apr_pool_t *scratch_pool);
 
-/* Use the phys-to-log mapping files in FS to return the svn_fs_x__id_part_t
+/* Use the phys-to-log mapping files in FS to return the svn_fs_x__id_t
  * for the SUB_ITEM of the container starting at global OFFSET in the rep /
- * pack file containing REVISION in *ITEM.  Sets *ITEM to NULL if no element
- * starts at exactly that offset or if it contains no more than SUB_ITEM
- * sub-items.  Use POOL for allocations.
+ * pack file containing REVISION in *ITEM, allocated in RESULT_POOL.  Sets
+ * *ITEM to NULL if no element starts at exactly that offset or if it
+ * contains no more than SUB_ITEM sub-items.
+ *
+ * Use SCRATCH_POOL for temporary allocations.
  */
 svn_error_t *
-svn_fs_x__p2l_item_lookup(svn_fs_x__id_part_t **item,
+svn_fs_x__p2l_item_lookup(svn_fs_x__id_t **item,
                           svn_fs_t *fs,
+                          svn_fs_x__revision_file_t *rev_file,
                           svn_revnum_t revision,
                           apr_off_t offset,
                           apr_uint32_t sub_item,
-                          apr_pool_t *pool);
+                          apr_pool_t *result_pool,
+                          apr_pool_t *scratch_pool);
 
-/* Use the log-to-phys mapping files in FS to find the packed / non-packed /
- * proto-rev file offset and container sub-item of ITEM_ID.  *SUB_ITEM will
- * be 0 for non-container items.  Use POOL for allocations.
+/* For ITEM_ID in FS, return the position in the respective rev or pack file
+ * in *ABSOLUTE_POSITION and the *SUB_ITEM number within the object at that
+ * location. *SUB_ITEM will be 0 for non-container items.
+ *
+ * REV_FILE determines whether to access single rev or pack file data.
+ * If that is not available anymore (neither in cache nor on disk), re-open
+ * the rev / pack file and retry to open the index file.  For transaction
+ * content, REV_FILE may be NULL.
+ *
+ * Use SCRATCH_POOL for temporary allocations.
  */
 svn_error_t *
-svn_fs_x__item_offset(apr_off_t *offset,
+svn_fs_x__item_offset(apr_off_t *absolute_position,
                       apr_uint32_t *sub_item,
                       svn_fs_t *fs,
-                      const svn_fs_x__id_part_t *item_id,
-                      apr_pool_t *pool);
+                      svn_fs_x__revision_file_t *rev_file,
+                      const svn_fs_x__id_t *item_id,
+                      apr_pool_t *scratch_pool);
 
 /* Use the log-to-phys indexes in FS to determine the maximum item indexes
  * assigned to revision START_REV to START_REV + COUNT - 1.  That is a
  * close upper limit to the actual number of items in the respective revs.
- * Return the results in *MAX_IDS,  allocated in POOL.
+ * Return the results in *MAX_IDS,  allocated in RESULT_POOL.
+ * Use SCRATCH_POOL for temporary allocations.
  */
 svn_error_t *
 svn_fs_x__l2p_get_max_ids(apr_array_header_t **max_ids,
                           svn_fs_t *fs,
                           svn_revnum_t start_rev,
                           apr_size_t count,
-                          apr_pool_t *pool);
+                          apr_pool_t *result_pool,
+                          apr_pool_t *scratch_pool);
 
 /* In *OFFSET, return the first OFFSET in the pack / rev file containing
  * REVISION in FS not covered by the log-to-phys index.
- * Use POOL for allocations.
+ * Use SCRATCH_POOL for temporary allocations.
  */
 svn_error_t *
 svn_fs_x__p2l_get_max_offset(apr_off_t *offset,
                              svn_fs_t *fs,
+                             svn_fs_x__revision_file_t *rev_file,
                              svn_revnum_t revision,
-                             apr_pool_t *pool);
+                             apr_pool_t *scratch_pool);
+
+/* Index (re-)creation utilities.
+ */
+
+/* For FS, create a new L2P auto-deleting proto index file in POOL and return
+ * its name in *PROTONAME.  All entries to write are given in ENTRIES and
+ * entries are of type svn_fs_fs__p2l_entry_t* (sic!).  The ENTRIES array
+ * will be reordered.  Give the proto index file the lifetime of RESULT_POOL
+ * and use SCRATCH_POOL for temporary allocations.
+ */
+svn_error_t *
+svn_fs_x__l2p_index_from_p2l_entries(const char **protoname,
+                                     svn_fs_t *fs,
+                                     apr_array_header_t *entries,
+                                     apr_pool_t *result_pool,
+                                     apr_pool_t *scratch_pool);
+
+/* For FS, create a new P2L auto-deleting proto index file in POOL and return
+ * its name in *PROTONAME.  All entries to write are given in ENTRIES and
+ * of type svn_fs_fs__p2l_entry_t*.  The FVN1 checksums are not taken from
+ * ENTRIES but are begin calculated from the current contents of REV_FILE
+ * as we go.  Give the proto index file the lifetime of RESULT_POOL and use
+ * SCRATCH_POOL for temporary allocations.
+ */
+svn_error_t *
+svn_fs_x__p2l_index_from_p2l_entries(const char **protoname,
+                                     svn_fs_t *fs,
+                                     svn_fs_x__revision_file_t *rev_file,
+                                     apr_array_header_t *entries,
+                                     apr_pool_t *result_pool,
+                                     apr_pool_t *scratch_pool);
 
 /* Serialization and caching interface
  */

Modified: subversion/branches/authzperf/subversion/libsvn_fs_x/lock.c
URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_fs_x/lock.c?rev=1649205&r1=1649204&r2=1649205&view=diff
==============================================================================
--- subversion/branches/authzperf/subversion/libsvn_fs_x/lock.c (original)
+++ subversion/branches/authzperf/subversion/libsvn_fs_x/lock.c Sat Jan  3 14:00:41 2015
@@ -386,7 +386,8 @@ add_to_digest(const char *fs_path,
   const char *index_digest_path;
   apr_hash_t *children;
   svn_lock_t *lock;
-  int i, original_count;
+  int i;
+  unsigned int original_count;
 
   SVN_ERR(digest_path_from_path(&index_digest_path, fs_path, index_path, pool));
 

Modified: subversion/branches/authzperf/subversion/libsvn_fs_x/low_level.c
URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_fs_x/low_level.c?rev=1649205&r1=1649204&r2=1649205&view=diff
==============================================================================
--- subversion/branches/authzperf/subversion/libsvn_fs_x/low_level.c (original)
+++ subversion/branches/authzperf/subversion/libsvn_fs_x/low_level.c Sat Jan  3 14:00:41 2015
@@ -27,6 +27,7 @@
 #include "private/svn_sorts_private.h"
 #include "private/svn_string_private.h"
 #include "private/svn_subr_private.h"
+#include "private/svn_fspath.h"
 
 #include "../libsvn_fs/fs-loader.h"
 
@@ -37,6 +38,8 @@
 
 /* Headers used to describe node-revision in the revision file. */
 #define HEADER_ID          "id"
+#define HEADER_NODE        "node"
+#define HEADER_COPY        "copy"
 #define HEADER_TYPE        "type"
 #define HEADER_COUNT       "count"
 #define HEADER_PROPS       "props"
@@ -71,115 +74,126 @@
  * various flags. */
 #define MAX_CHANGE_LINE_LEN FSX_MAX_PATH_LEN + 256
 
-svn_error_t *
-svn_fs_x__parse_revision_trailer(apr_off_t *root_offset,
-                                 apr_off_t *changes_offset,
-                                 svn_stringbuf_t *trailer,
-                                 svn_revnum_t rev)
-{
-  int i, num_bytes;
-  const char *str;
-
-  /* This cast should be safe since the maximum amount read, 64, will
-     never be bigger than the size of an int. */
-  num_bytes = (int) trailer->len;
-
-  /* The last byte should be a newline. */
-  if (trailer->len == 0 || trailer->data[trailer->len - 1] != '\n')
+/* Convert the C string in *TEXT to a revision number and return it in *REV.
+ * Overflows, negative values other than -1 and terminating characters other
+ * than 0x20 or 0x0 will cause an error.  Set *TEXT to the first char after
+ * the initial separator or to EOS.
+ */
+static svn_error_t *
+parse_revnum(svn_revnum_t *rev,
+             const char **text)
+{
+  const char *string = *text;
+  if ((string[0] == '-') && (string[1] == '1'))
     {
-      return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
-                               _("Revision file (r%ld) lacks trailing newline"),
-                               rev);
+      *rev = SVN_INVALID_REVNUM;
+      string += 2;
     }
-
-  /* Look for the next previous newline. */
-  for (i = num_bytes - 2; i >= 0; i--)
+  else
     {
-      if (trailer->data[i] == '\n')
-        break;
+      SVN_ERR(svn_revnum_parse(rev, string, &string));
     }
 
-  if (i < 0)
-    {
-      return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
-                               _("Final line in revision file (r%ld) longer "
-                                 "than 64 characters"),
-                               rev);
-    }
+  if (*string == ' ')
+    ++string;
+  else if (*string != '\0')
+    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
+                            _("Invalid character in revision number"));
 
-  i++;
-  str = &trailer->data[i];
+  *text = string;
+  return SVN_NO_ERROR;
+}
 
-  /* find the next space */
-  for ( ; i < (num_bytes - 2) ; i++)
-    if (trailer->data[i] == ' ')
-      break;
+svn_error_t *
+svn_fs_x__parse_footer(apr_off_t *l2p_offset,
+                       svn_checksum_t **l2p_checksum,
+                       apr_off_t *p2l_offset,
+                       svn_checksum_t **p2l_checksum,
+                       svn_stringbuf_t *footer,
+                       svn_revnum_t rev,
+                       apr_pool_t *result_pool)
+{
+  apr_int64_t val;
+  char *last_str = footer->data;
 
-  if (i == (num_bytes - 2))
-    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
-                             _("Final line in revision file r%ld missing space"),
-                             rev);
+  /* Get the L2P offset. */
+  const char *str = svn_cstring_tokenize(" ", &last_str);
+  if (str == NULL)
+    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
+                            _("Invalid revision footer"));
 
-  if (root_offset)
-    {
-      apr_int64_t val;
+  SVN_ERR(svn_cstring_atoi64(&val, str));
+  *l2p_offset = (apr_off_t)val;
 
-      trailer->data[i] = '\0';
-      SVN_ERR(svn_cstring_atoi64(&val, str));
-      *root_offset = (apr_off_t)val;
-    }
+  /* Get the L2P checksum. */
+  str = svn_cstring_tokenize(" ", &last_str);
+  if (str == NULL)
+    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
+                            _("Invalid revision footer"));
 
-  i++;
-  str = &trailer->data[i];
+  SVN_ERR(svn_checksum_parse_hex(l2p_checksum, svn_checksum_md5, str,
+                                 result_pool));
 
-  /* find the next newline */
-  for ( ; i < num_bytes; i++)
-    if (trailer->data[i] == '\n')
-      break;
+  /* Get the P2L offset. */
+  str = svn_cstring_tokenize(" ", &last_str);
+  if (str == NULL)
+    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
+                            _("Invalid revision footer"));
 
-  if (changes_offset)
-    {
-      apr_int64_t val;
+  SVN_ERR(svn_cstring_atoi64(&val, str));
+  *p2l_offset = (apr_off_t)val;
 
-      trailer->data[i] = '\0';
-      SVN_ERR(svn_cstring_atoi64(&val, str));
-      *changes_offset = (apr_off_t)val;
-    }
+  /* Get the P2L checksum. */
+  str = svn_cstring_tokenize(" ", &last_str);
+  if (str == NULL)
+    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
+                            _("Invalid revision footer"));
+
+  SVN_ERR(svn_checksum_parse_hex(p2l_checksum, svn_checksum_md5, str,
+                                 result_pool));
 
   return SVN_NO_ERROR;
 }
 
 svn_stringbuf_t *
-svn_fs_x__unparse_revision_trailer(apr_off_t root_offset,
-                                   apr_off_t changes_offset,
-                                   apr_pool_t *pool)
-{
-  return svn_stringbuf_createf(pool,
-                               "%" APR_OFF_T_FMT " %" APR_OFF_T_FMT "\n",
-                               root_offset,
-                               changes_offset);
+svn_fs_x__unparse_footer(apr_off_t l2p_offset,
+                         svn_checksum_t *l2p_checksum,
+                         apr_off_t p2l_offset,
+                         svn_checksum_t *p2l_checksum,
+                         apr_pool_t *result_pool,
+                         apr_pool_t *scratch_pool)
+{
+  return svn_stringbuf_createf(result_pool,
+                               "%" APR_OFF_T_FMT " %s %" APR_OFF_T_FMT " %s",
+                               l2p_offset,
+                               svn_checksum_to_cstring(l2p_checksum,
+                                                       scratch_pool),
+                               p2l_offset,
+                               svn_checksum_to_cstring(p2l_checksum,
+                                                       scratch_pool));
 }
 
 /* Given a revision file FILE that has been pre-positioned at the
    beginning of a Node-Rev header block, read in that header block and
    store it in the apr_hash_t HEADERS.  All allocations will be from
-   POOL. */
+   RESULT_POOL. */
 static svn_error_t *
 read_header_block(apr_hash_t **headers,
                   svn_stream_t *stream,
-                  apr_pool_t *pool)
+                  apr_pool_t *result_pool)
 {
-  *headers = svn_hash__make(pool);
+  *headers = svn_hash__make(result_pool);
 
   while (1)
     {
       svn_stringbuf_t *header_str;
       const char *name, *value;
-      apr_ssize_t i = 0;
-      apr_ssize_t name_len;
+      apr_size_t i = 0;
+      apr_size_t name_len;
       svn_boolean_t eof;
 
-      SVN_ERR(svn_stream_readline(stream, &header_str, "\n", &eof, pool));
+      SVN_ERR(svn_stream_readline(stream, &header_str, "\n", &eof,
+                                  result_pool));
 
       if (eof || header_str->len == 0)
         break; /* end of header block */
@@ -199,13 +213,10 @@ read_header_block(apr_hash_t **headers,
       name = header_str->data;
       name_len = i;
 
-      /* Skip over the NULL byte and the space following it. */
-      i += 2;
-
-      if (i > header_str->len)
+      /* Check if we have enough data to parse. */
+      if (i + 2 > header_str->len)
         {
           /* Restore the original line for the error. */
-          i -= 2;
           header_str->data[i] = ':';
           return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
                                    _("Found malformed header '%s' in "
@@ -213,6 +224,9 @@ read_header_block(apr_hash_t **headers,
                                    header_str->data);
         }
 
+      /* Skip over the NULL byte and the space following it. */
+      i += 2;
+
       value = header_str->data + i;
 
       /* header_str is safely in our pool, so we can use bits of it as
@@ -224,17 +238,18 @@ read_header_block(apr_hash_t **headers,
 }
 
 svn_error_t *
-svn_fs_x__parse_representation(representation_t **rep_p,
+svn_fs_x__parse_representation(svn_fs_x__representation_t **rep_p,
                                svn_stringbuf_t *text,
-                               apr_pool_t *pool)
+                               apr_pool_t *result_pool,
+                               apr_pool_t *scratch_pool)
 {
-  representation_t *rep;
+  svn_fs_x__representation_t *rep;
   char *str;
   apr_int64_t val;
   char *string = text->data;
   svn_checksum_t *checksum;
 
-  rep = apr_pcalloc(pool, sizeof(*rep));
+  rep = apr_pcalloc(result_pool, sizeof(*rep));
   *rep_p = rep;
 
   str = svn_cstring_tokenize(" ", &string);
@@ -283,7 +298,8 @@ svn_fs_x__parse_representation(represent
     return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                             _("Malformed text representation offset line in node-rev"));
 
-  SVN_ERR(svn_checksum_parse_hex(&checksum, svn_checksum_md5, str, pool));
+  SVN_ERR(svn_checksum_parse_hex(&checksum, svn_checksum_md5, str,
+                                 scratch_pool));
   if (checksum)
     memcpy(rep->md5_digest, checksum->digest, sizeof(rep->md5_digest));
 
@@ -297,7 +313,8 @@ svn_fs_x__parse_representation(represent
     return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                             _("Malformed text representation offset line in node-rev"));
 
-  SVN_ERR(svn_checksum_parse_hex(&checksum, svn_checksum_sha1, str, pool));
+  SVN_ERR(svn_checksum_parse_hex(&checksum, svn_checksum_sha1, str,
+                                 scratch_pool));
   rep->has_sha1 = checksum != NULL;
   if (checksum)
     memcpy(rep->sha1_digest, checksum->digest, sizeof(rep->sha1_digest));
@@ -308,23 +325,28 @@ svn_fs_x__parse_representation(represent
 /* Wrap read_rep_offsets_body(), extracting its TXN_ID from our NODEREV_ID,
    and adding an error message. */
 static svn_error_t *
-read_rep_offsets(representation_t **rep_p,
+read_rep_offsets(svn_fs_x__representation_t **rep_p,
                  char *string,
-                 const svn_fs_id_t *noderev_id,
-                 apr_pool_t *pool)
+                 const svn_fs_x__id_t *noderev_id,
+                 apr_pool_t *result_pool,
+                 apr_pool_t *scratch_pool)
 {
   svn_error_t *err
     = svn_fs_x__parse_representation(rep_p,
-                                     svn_stringbuf_create_wrap(string, pool),
-                                     pool);
+                                     svn_stringbuf_create_wrap(string,
+                                                               scratch_pool),
+                                     result_pool,
+                                     scratch_pool);
   if (err)
     {
-      const svn_string_t *id_unparsed = svn_fs_x__id_unparse(noderev_id, pool);
+      const svn_string_t *id_unparsed;
       const char *where;
-      where = apr_psprintf(pool,
+
+      id_unparsed = svn_fs_x__id_unparse(noderev_id, scratch_pool);
+      where = apr_psprintf(scratch_pool,
                            _("While reading representation offsets "
                              "for node-revision '%s':"),
-                           noderev_id ? id_unparsed->data : "(null)");
+                           id_unparsed->data);
 
       return svn_error_quick_wrap(err, where);
     }
@@ -384,31 +406,45 @@ auto_unescape_path(const char *path,
    return path;
 }
 
+/* Find entry HEADER_NAME in HEADERS and parse its value into *ID. */
+static svn_error_t *
+read_id_part(svn_fs_x__id_t *id,
+             apr_hash_t *headers,
+             const char *header_name)
+{
+  const char *value = svn_hash_gets(headers, header_name);
+  if (value == NULL)
+    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+                             _("Missing %s field in node-rev"),
+                             header_name);
+
+  SVN_ERR(svn_fs_x__id_parse(id, value));
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
-svn_fs_x__read_noderev(node_revision_t **noderev_p,
+svn_fs_x__read_noderev(svn_fs_x__noderev_t **noderev_p,
                        svn_stream_t *stream,
-                       apr_pool_t *pool)
+                       apr_pool_t *result_pool,
+                       apr_pool_t *scratch_pool)
 {
   apr_hash_t *headers;
-  node_revision_t *noderev;
+  svn_fs_x__noderev_t *noderev;
   char *value;
   const char *noderev_id;
 
-  SVN_ERR(read_header_block(&headers, stream, pool));
+  SVN_ERR(read_header_block(&headers, stream, scratch_pool));
+  SVN_ERR(svn_stream_close(stream));
 
-  noderev = apr_pcalloc(pool, sizeof(*noderev));
+  noderev = apr_pcalloc(result_pool, sizeof(*noderev));
 
-  /* Read the node-rev id. */
-  value = svn_hash_gets(headers, HEADER_ID);
-  if (value == NULL)
-      /* ### More information: filename/offset coordinates */
-      return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                              _("Missing id field in node-rev"));
+  /* for error messages later */
+  noderev_id = svn_hash_gets(headers, HEADER_ID);
 
-  SVN_ERR(svn_stream_close(stream));
-
-  noderev->id = svn_fs_x__id_parse(value, strlen(value), pool);
-  noderev_id = value; /* for error messages later */
+  /* Read the node-rev id. */
+  SVN_ERR(read_id_part(&noderev->noderev_id, headers, HEADER_ID));
+  SVN_ERR(read_id_part(&noderev->node_id, headers, HEADER_NODE));
+  SVN_ERR(read_id_part(&noderev->copy_id, headers, HEADER_COPY));
 
   /* Read the type. */
   value = svn_hash_gets(headers, HEADER_TYPE);
@@ -437,7 +473,8 @@ svn_fs_x__read_noderev(node_revision_t *
   if (value)
     {
       SVN_ERR(read_rep_offsets(&noderev->prop_rep, value,
-                               noderev->id, pool));
+                               &noderev->noderev_id, result_pool,
+                               scratch_pool));
     }
 
   /* Get the data location. */
@@ -445,7 +482,8 @@ svn_fs_x__read_noderev(node_revision_t *
   if (value)
     {
       SVN_ERR(read_rep_offsets(&noderev->data_rep, value,
-                               noderev->id, pool));
+                               &noderev->noderev_id, result_pool,
+                               scratch_pool));
     }
 
   /* Get the created path. */
@@ -458,38 +496,42 @@ svn_fs_x__read_noderev(node_revision_t *
     }
   else
     {
-      noderev->created_path = auto_unescape_path(value, pool);
+      if (!svn_fspath__is_canonical(value))
+        return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+                            _("Non-canonical cpath field in node-rev '%s'"),
+                            noderev_id);
+
+      noderev->created_path = auto_unescape_path(apr_pstrdup(result_pool,
+                                                              value),
+                                                 result_pool);
     }
 
   /* Get the predecessor ID. */
   value = svn_hash_gets(headers, HEADER_PRED);
   if (value)
-    noderev->predecessor_id = svn_fs_x__id_parse(value, strlen(value), pool);
+    SVN_ERR(svn_fs_x__id_parse(&noderev->predecessor_id, value));
+  else
+    svn_fs_x__id_reset(&noderev->predecessor_id);
 
   /* Get the copyroot. */
   value = svn_hash_gets(headers, HEADER_COPYROOT);
   if (value == NULL)
     {
       noderev->copyroot_path = noderev->created_path;
-      noderev->copyroot_rev = svn_fs_x__id_rev(noderev->id);
+      noderev->copyroot_rev
+        = svn_fs_x__get_revnum(noderev->noderev_id.change_set);
     }
   else
     {
-      char *str;
-
-      str = svn_cstring_tokenize(" ", &value);
-      if (str == NULL)
-        return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
-                                 _("Malformed copyroot line in node-rev '%s'"),
-                                 noderev_id);
+      SVN_ERR(parse_revnum(&noderev->copyroot_rev, (const char **)&value));
 
-      noderev->copyroot_rev = SVN_STR_TO_REV(str);
-
-      if (*value == '\0')
+      if (!svn_fspath__is_canonical(value))
         return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
                                  _("Malformed copyroot line in node-rev '%s'"),
                                  noderev_id);
-      noderev->copyroot_path = auto_unescape_path(value, pool);
+      noderev->copyroot_path = auto_unescape_path(apr_pstrdup(result_pool,
+                                                              value),
+                                                  result_pool);
     }
 
   /* Get the copyfrom. */
@@ -501,19 +543,15 @@ svn_fs_x__read_noderev(node_revision_t *
     }
   else
     {
-      char *str = svn_cstring_tokenize(" ", &value);
-      if (str == NULL)
-        return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
-                                 _("Malformed copyfrom line in node-rev '%s'"),
-                                 noderev_id);
-
-      noderev->copyfrom_rev = SVN_STR_TO_REV(str);
+      SVN_ERR(parse_revnum(&noderev->copyfrom_rev, (const char **)&value));
 
       if (*value == 0)
         return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
                                  _("Malformed copyfrom line in node-rev '%s'"),
                                  noderev_id);
-      noderev->copyfrom_path = auto_unescape_path(value, pool);
+      noderev->copyfrom_path = auto_unescape_path(apr_pstrdup(result_pool,
+                                                              value),
+                                                  result_pool);
     }
 
   /* Get whether this is a fresh txn root. */
@@ -538,13 +576,13 @@ svn_fs_x__read_noderev(node_revision_t *
 
 /* Return a textual representation of the DIGEST of given KIND.
  * If IS_NULL is TRUE, no digest is available.
- * Use POOL for allocations.
+ * Allocate the result in RESULT_POOL.
  */
 static const char *
 format_digest(const unsigned char *digest,
               svn_checksum_kind_t kind,
               svn_boolean_t is_null,
-              apr_pool_t *pool)
+              apr_pool_t *result_pool)
 {
   svn_checksum_t checksum;
   checksum.digest = digest;
@@ -553,92 +591,105 @@ format_digest(const unsigned char *diges
   if (is_null)
     return "(null)";
 
-  return svn_checksum_to_cstring_display(&checksum, pool);
+  return svn_checksum_to_cstring_display(&checksum, result_pool);
 }
 
 svn_stringbuf_t *
-svn_fs_x__unparse_representation(representation_t *rep,
-                                 int format,
+svn_fs_x__unparse_representation(svn_fs_x__representation_t *rep,
                                  svn_boolean_t mutable_rep_truncated,
-                                 apr_pool_t *pool)
+                                 apr_pool_t *result_pool,
+                                 apr_pool_t *scratch_pool)
 {
   if (!rep->has_sha1)
     return svn_stringbuf_createf
-            (pool,
+            (result_pool,
              "%" APR_INT64_T_FMT " %" APR_UINT64_T_FMT " %" SVN_FILESIZE_T_FMT
              " %" SVN_FILESIZE_T_FMT " %s",
              rep->id.change_set, rep->id.number, rep->size,
              rep->expanded_size,
-             format_digest(rep->md5_digest, svn_checksum_md5, FALSE, pool));
+             format_digest(rep->md5_digest, svn_checksum_md5, FALSE,
+                           scratch_pool));
 
   return svn_stringbuf_createf
-          (pool,
+          (result_pool,
            "%" APR_INT64_T_FMT " %" APR_UINT64_T_FMT " %" SVN_FILESIZE_T_FMT
            " %" SVN_FILESIZE_T_FMT " %s %s",
            rep->id.change_set, rep->id.number, rep->size,
            rep->expanded_size,
-           format_digest(rep->md5_digest, svn_checksum_md5, FALSE, pool),
+           format_digest(rep->md5_digest, svn_checksum_md5,
+                         FALSE, scratch_pool),
            format_digest(rep->sha1_digest, svn_checksum_sha1,
-                         !rep->has_sha1, pool));
+                         !rep->has_sha1, scratch_pool));
 }
 
 
 svn_error_t *
 svn_fs_x__write_noderev(svn_stream_t *outfile,
-                        node_revision_t *noderev,
-                        int format,
-                        apr_pool_t *pool)
+                        svn_fs_x__noderev_t *noderev,
+                        apr_pool_t *scratch_pool)
 {
-  SVN_ERR(svn_stream_printf(outfile, pool, HEADER_ID ": %s\n",
-                            svn_fs_x__id_unparse(noderev->id, pool)->data));
+  svn_string_t *str_id;
+
+  str_id = svn_fs_x__id_unparse(&noderev->noderev_id, scratch_pool);
+  SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_ID ": %s\n",
+                            str_id->data));
+  str_id = svn_fs_x__id_unparse(&noderev->node_id, scratch_pool);
+  SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_NODE ": %s\n",
+                            str_id->data));
+  str_id = svn_fs_x__id_unparse(&noderev->copy_id, scratch_pool);
+  SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_COPY ": %s\n",
+                            str_id->data));
 
-  SVN_ERR(svn_stream_printf(outfile, pool, HEADER_TYPE ": %s\n",
+  SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_TYPE ": %s\n",
                             (noderev->kind == svn_node_file) ?
                             SVN_FS_X__KIND_FILE : SVN_FS_X__KIND_DIR));
 
-  if (noderev->predecessor_id)
-    SVN_ERR(svn_stream_printf(outfile, pool, HEADER_PRED ": %s\n",
-                              svn_fs_x__id_unparse(noderev->predecessor_id,
-                                                   pool)->data));
+  if (svn_fs_x__id_used(&noderev->predecessor_id))
+    SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_PRED ": %s\n",
+                              svn_fs_x__id_unparse(&noderev->predecessor_id,
+                                                   scratch_pool)->data));
 
-  SVN_ERR(svn_stream_printf(outfile, pool, HEADER_COUNT ": %d\n",
+  SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_COUNT ": %d\n",
                             noderev->predecessor_count));
 
   if (noderev->data_rep)
-    SVN_ERR(svn_stream_printf(outfile, pool, HEADER_TEXT ": %s\n",
+    SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_TEXT ": %s\n",
                               svn_fs_x__unparse_representation
                                 (noderev->data_rep,
-                                 format,
                                  noderev->kind == svn_node_dir,
-                                 pool)->data));
+                                 scratch_pool, scratch_pool)->data));
 
   if (noderev->prop_rep)
-    SVN_ERR(svn_stream_printf(outfile, pool, HEADER_PROPS ": %s\n",
+    SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_PROPS ": %s\n",
                               svn_fs_x__unparse_representation
-                                (noderev->prop_rep, format,
-                                 TRUE, pool)->data));
+                                (noderev->prop_rep,
+                                 TRUE, scratch_pool, scratch_pool)->data));
 
-  SVN_ERR(svn_stream_printf(outfile, pool, HEADER_CPATH ": %s\n",
-                            auto_escape_path(noderev->created_path, pool)));
+  SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_CPATH ": %s\n",
+                            auto_escape_path(noderev->created_path,
+                                             scratch_pool)));
 
   if (noderev->copyfrom_path)
-    SVN_ERR(svn_stream_printf(outfile, pool, HEADER_COPYFROM ": %ld"
+    SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_COPYFROM ": %ld"
                               " %s\n",
                               noderev->copyfrom_rev,
-                              auto_escape_path(noderev->copyfrom_path, pool)));
+                              auto_escape_path(noderev->copyfrom_path,
+                                               scratch_pool)));
 
-  if ((noderev->copyroot_rev != svn_fs_x__id_rev(noderev->id)) ||
-      (strcmp(noderev->copyroot_path, noderev->created_path) != 0))
-    SVN_ERR(svn_stream_printf(outfile, pool, HEADER_COPYROOT ": %ld"
+  if (   (   noderev->copyroot_rev
+           != svn_fs_x__get_revnum(noderev->noderev_id.change_set))
+      || (strcmp(noderev->copyroot_path, noderev->created_path) != 0))
+    SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_COPYROOT ": %ld"
                               " %s\n",
                               noderev->copyroot_rev,
-                              auto_escape_path(noderev->copyroot_path, pool)));
+                              auto_escape_path(noderev->copyroot_path,
+                                               scratch_pool)));
 
   if (noderev->is_fresh_txn_root)
     SVN_ERR(svn_stream_puts(outfile, HEADER_FRESHTXNRT ": y\n"));
 
   if (noderev->mergeinfo_count > 0)
-    SVN_ERR(svn_stream_printf(outfile, pool, HEADER_MINFO_CNT ": %"
+    SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_MINFO_CNT ": %"
                               APR_INT64_T_FMT "\n",
                               noderev->mergeinfo_count));
 
@@ -651,16 +702,17 @@ svn_fs_x__write_noderev(svn_stream_t *ou
 svn_error_t *
 svn_fs_x__read_rep_header(svn_fs_x__rep_header_t **header,
                           svn_stream_t *stream,
-                          apr_pool_t *pool)
+                          apr_pool_t *result_pool,
+                          apr_pool_t *scratch_pool)
 {
   svn_stringbuf_t *buffer;
   char *str, *last_str;
   apr_int64_t val;
   svn_boolean_t eol = FALSE;
 
-  SVN_ERR(svn_stream_readline(stream, &buffer, "\n", &eol, pool));
+  SVN_ERR(svn_stream_readline(stream, &buffer, "\n", &eol, scratch_pool));
 
-  *header = apr_pcalloc(pool, sizeof(**header));
+  *header = apr_pcalloc(result_pool, sizeof(**header));
   (*header)->header_size = buffer->len + 1;
   if (strcmp(buffer->data, REP_DELTA) == 0)
     {
@@ -677,10 +729,7 @@ svn_fs_x__read_rep_header(svn_fs_x__rep_
   if (! str || (strcmp(str, REP_DELTA) != 0))
     goto error;
 
-  str = svn_cstring_tokenize(" ", &last_str);
-  if (! str)
-    goto error;
-  (*header)->base_revision = SVN_STR_TO_REV(str);
+  SVN_ERR(parse_revnum(&(*header)->base_revision, (const char **)&last_str));
 
   str = svn_cstring_tokenize(" ", &last_str);
   if (! str)
@@ -704,7 +753,7 @@ svn_fs_x__read_rep_header(svn_fs_x__rep_
 svn_error_t *
 svn_fs_x__write_rep_header(svn_fs_x__rep_header_t *header,
                            svn_stream_t *stream,
-                           apr_pool_t *pool)
+                           apr_pool_t *scratch_pool)
 {
   const char *text;
   
@@ -715,8 +764,8 @@ svn_fs_x__write_rep_header(svn_fs_x__rep
         break;
 
       default:
-        text = apr_psprintf(pool, REP_DELTA " %ld %" APR_OFF_T_FMT " %"
-                            SVN_FILESIZE_T_FMT "\n",
+        text = apr_psprintf(scratch_pool, REP_DELTA " %ld %" APR_OFF_T_FMT
+                                          " %" SVN_FILESIZE_T_FMT "\n",
                             header->base_revision, header->base_item_index,
                             header->base_length);
     }
@@ -728,27 +777,26 @@ svn_fs_x__write_rep_header(svn_fs_x__rep
    the resulting change in *CHANGE_P.  If there is no next record,
    store NULL there.  Perform all allocations from POOL. */
 static svn_error_t *
-read_change(change_t **change_p,
+read_change(svn_fs_x__change_t **change_p,
             svn_stream_t *stream,
-            apr_pool_t *pool)
+            apr_pool_t *result_pool,
+            apr_pool_t *scratch_pool)
 {
   svn_stringbuf_t *line;
   svn_boolean_t eof = TRUE;
-  change_t *change;
+  svn_fs_x__change_t *change;
   char *str, *last_str, *kind_str;
-  svn_fs_path_change2_t *info;
 
   /* Default return value. */
   *change_p = NULL;
 
-  SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, pool));
+  SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, scratch_pool));
 
   /* Check for a blank line. */
   if (eof || (line->len == 0))
     return SVN_NO_ERROR;
 
-  change = apr_pcalloc(pool, sizeof(*change));
-  info = &change->info;
+  change = apr_pcalloc(result_pool, sizeof(*change));
   last_str = line->data;
 
   /* Get the node-id of the change. */
@@ -757,10 +805,7 @@ read_change(change_t **change_p,
     return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                             _("Invalid changes line in rev-file"));
 
-  info->node_rev_id = svn_fs_x__id_parse(str, strlen(str), pool);
-  if (info->node_rev_id == NULL)
-    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                            _("Invalid changes line in rev-file"));
+  SVN_ERR(svn_fs_x__id_parse(&change->noderev_id, str));
 
   /* Get the change type. */
   str = svn_cstring_tokenize(" ", &last_str);
@@ -770,7 +815,7 @@ read_change(change_t **change_p,
 
   /* Don't bother to check the format number before looking for
    * node-kinds: just read them if you find them. */
-  info->node_kind = svn_node_unknown;
+  change->node_kind = svn_node_unknown;
   kind_str = strchr(str, '-');
   if (kind_str)
     {
@@ -778,9 +823,9 @@ read_change(change_t **change_p,
       *kind_str = '\0';
       kind_str++;
       if (strcmp(kind_str, SVN_FS_X__KIND_FILE) == 0)
-        info->node_kind = svn_node_file;
+        change->node_kind = svn_node_file;
       else if (strcmp(kind_str, SVN_FS_X__KIND_DIR) == 0)
-        info->node_kind = svn_node_dir;
+        change->node_kind = svn_node_dir;
       else
         return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                                 _("Invalid changes line in rev-file"));
@@ -788,23 +833,23 @@ read_change(change_t **change_p,
 
   if (strcmp(str, ACTION_MODIFY) == 0)
     {
-      info->change_kind = svn_fs_path_change_modify;
+      change->change_kind = svn_fs_path_change_modify;
     }
   else if (strcmp(str, ACTION_ADD) == 0)
     {
-      info->change_kind = svn_fs_path_change_add;
+      change->change_kind = svn_fs_path_change_add;
     }
   else if (strcmp(str, ACTION_DELETE) == 0)
     {
-      info->change_kind = svn_fs_path_change_delete;
+      change->change_kind = svn_fs_path_change_delete;
     }
   else if (strcmp(str, ACTION_REPLACE) == 0)
     {
-      info->change_kind = svn_fs_path_change_replace;
+      change->change_kind = svn_fs_path_change_replace;
     }
   else if (strcmp(str, ACTION_RESET) == 0)
     {
-      info->change_kind = svn_fs_path_change_reset;
+      change->change_kind = svn_fs_path_change_reset;
     }
   else
     {
@@ -820,11 +865,11 @@ read_change(change_t **change_p,
 
   if (strcmp(str, FLAG_TRUE) == 0)
     {
-      info->text_mod = TRUE;
+      change->text_mod = TRUE;
     }
   else if (strcmp(str, FLAG_FALSE) == 0)
     {
-      info->text_mod = FALSE;
+      change->text_mod = FALSE;
     }
   else
     {
@@ -840,11 +885,11 @@ read_change(change_t **change_p,
 
   if (strcmp(str, FLAG_TRUE) == 0)
     {
-      info->prop_mod = TRUE;
+      change->prop_mod = TRUE;
     }
   else if (strcmp(str, FLAG_FALSE) == 0)
     {
-      info->prop_mod = FALSE;
+      change->prop_mod = FALSE;
     }
   else
     {
@@ -860,11 +905,11 @@ read_change(change_t **change_p,
 
   if (strcmp(str, FLAG_TRUE) == 0)
     {
-      info->mergeinfo_mod = svn_tristate_true;
+      change->mergeinfo_mod = svn_tristate_true;
     }
   else if (strcmp(str, FLAG_FALSE) == 0)
     {
-      info->mergeinfo_mod = svn_tristate_false;
+      change->mergeinfo_mod = svn_tristate_false;
     }
   else
     {
@@ -873,33 +918,34 @@ read_change(change_t **change_p,
     }
 
   /* Get the changed path. */
-  change->path.data = auto_unescape_path(apr_pstrmemdup(pool, last_str,
+  if (!svn_fspath__is_canonical(last_str))
+    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
+                            _("Invalid path in changes line"));
+
+  change->path.data = auto_unescape_path(apr_pstrmemdup(result_pool,
+                                                        last_str,
                                                         strlen(last_str)),
-                                         pool);
+                                         result_pool);
   change->path.len = strlen(change->path.data);
 
   /* Read the next line, the copyfrom line. */
-  SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, pool));
-  info->copyfrom_known = TRUE;
+  SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, result_pool));
+  change->copyfrom_known = TRUE;
   if (eof || line->len == 0)
     {
-      info->copyfrom_rev = SVN_INVALID_REVNUM;
-      info->copyfrom_path = NULL;
+      change->copyfrom_rev = SVN_INVALID_REVNUM;
+      change->copyfrom_path = NULL;
     }
   else
     {
       last_str = line->data;
-      str = svn_cstring_tokenize(" ", &last_str);
-      if (! str)
-        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                                _("Invalid changes line in rev-file"));
-      info->copyfrom_rev = SVN_STR_TO_REV(str);
+      SVN_ERR(parse_revnum(&change->copyfrom_rev, (const char **)&last_str));
 
-      if (! last_str)
+      if (!svn_fspath__is_canonical(last_str))
         return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                                _("Invalid changes line in rev-file"));
+                                _("Invalid copy-from path in changes line"));
 
-      info->copyfrom_path = auto_unescape_path(last_str, pool);
+      change->copyfrom_path = auto_unescape_path(last_str, result_pool);
     }
 
   *change_p = change;
@@ -910,33 +956,67 @@ read_change(change_t **change_p,
 svn_error_t *
 svn_fs_x__read_changes(apr_array_header_t **changes,
                        svn_stream_t *stream,
-                       apr_pool_t *pool)
+                       apr_pool_t *result_pool,
+                       apr_pool_t *scratch_pool)
 {
-  change_t *change;
+  svn_fs_x__change_t *change;
+  apr_pool_t *iterpool;
 
-  /* pre-allocate enough room for most change lists
-     (will be auto-expanded as necessary) */
-  *changes = apr_array_make(pool, 30, sizeof(change_t *));
-  
-  SVN_ERR(read_change(&change, stream, pool));
+  /* Pre-allocate enough room for most change lists.
+     (will be auto-expanded as necessary).
+
+     Chose the default to just below 2^N such that the doubling reallocs
+     will request roughly 2^M bytes from the OS without exceeding the
+     respective two-power by just a few bytes (leaves room array and APR
+     node overhead for large enough M).
+   */
+  *changes = apr_array_make(result_pool, 63, sizeof(svn_fs_x__change_t *));
+
+  SVN_ERR(read_change(&change, stream, result_pool, scratch_pool));
+  iterpool = svn_pool_create(scratch_pool);
   while (change)
     {
-      APR_ARRAY_PUSH(*changes, change_t*) = change;
-      SVN_ERR(read_change(&change, stream, pool));
+      APR_ARRAY_PUSH(*changes, svn_fs_x__change_t*) = change;
+      SVN_ERR(read_change(&change, stream, result_pool, iterpool));
+      svn_pool_clear(iterpool);
     }
+  svn_pool_destroy(iterpool);
 
   return SVN_NO_ERROR;
 }
 
-/* Write a single change entry, path PATH, change CHANGE, and copyfrom
-   string COPYFROM, into the file specified by FILE.  Only include the
-   node kind field if INCLUDE_NODE_KIND is true.  All temporary
-   allocations are in POOL. */
+svn_error_t *
+svn_fs_x__read_changes_incrementally(svn_stream_t *stream,
+                                     svn_fs_x__change_receiver_t
+                                       change_receiver,
+                                     void *change_receiver_baton,
+                                     apr_pool_t *scratch_pool)
+{
+  svn_fs_x__change_t *change;
+  apr_pool_t *iterpool;
+
+  iterpool = svn_pool_create(scratch_pool);
+  do
+    {
+      svn_pool_clear(iterpool);
+
+      SVN_ERR(read_change(&change, stream, iterpool, iterpool));
+      if (change)
+        SVN_ERR(change_receiver(change_receiver_baton, change, iterpool));
+    }
+  while (change);
+  svn_pool_destroy(iterpool);
+  
+  return SVN_NO_ERROR;
+}
+
+/* Write a single change entry, path PATH, change CHANGE, to STREAM.
+
+   All temporary allocations are in SCRATCH_POOL. */
 static svn_error_t *
 write_change_entry(svn_stream_t *stream,
-                   const char *path,
-                   svn_fs_path_change2_t *change,
-                   apr_pool_t *pool)
+                   svn_fs_x__change_t *change,
+                   apr_pool_t *scratch_pool)
 {
   const char *idstr;
   const char *change_string = NULL;
@@ -967,30 +1047,29 @@ write_change_entry(svn_stream_t *stream,
                                change->change_kind);
     }
 
-  if (change->node_rev_id)
-    idstr = svn_fs_x__id_unparse(change->node_rev_id, pool)->data;
-  else
-    idstr = ACTION_RESET;
+  idstr = svn_fs_x__id_unparse(&change->noderev_id, scratch_pool)->data;
 
   SVN_ERR_ASSERT(change->node_kind == svn_node_dir
-                  || change->node_kind == svn_node_file);
-  kind_string = apr_psprintf(pool, "-%s",
-                              change->node_kind == svn_node_dir
-                              ? SVN_FS_X__KIND_DIR
-                              : SVN_FS_X__KIND_FILE);
-  buf = svn_stringbuf_createf(pool, "%s %s%s %s %s %s %s\n",
+                 || change->node_kind == svn_node_file);
+  kind_string = apr_psprintf(scratch_pool, "-%s",
+                             change->node_kind == svn_node_dir
+                             ? SVN_FS_X__KIND_DIR
+                             : SVN_FS_X__KIND_FILE);
+
+  buf = svn_stringbuf_createf(scratch_pool, "%s %s%s %s %s %s %s\n",
                               idstr, change_string, kind_string,
                               change->text_mod ? FLAG_TRUE : FLAG_FALSE,
                               change->prop_mod ? FLAG_TRUE : FLAG_FALSE,
                               change->mergeinfo_mod == svn_tristate_true
                                                ? FLAG_TRUE : FLAG_FALSE,
-                              auto_escape_path(path, pool));
+                              auto_escape_path(change->path.data, scratch_pool));
 
   if (SVN_IS_VALID_REVNUM(change->copyfrom_rev))
     {
-      svn_stringbuf_appendcstr(buf, apr_psprintf(pool, "%ld %s",
-                             change->copyfrom_rev,
-                             auto_escape_path(change->copyfrom_path, pool)));
+      svn_stringbuf_appendcstr(buf, apr_psprintf(scratch_pool, "%ld %s",
+                               change->copyfrom_rev,
+                               auto_escape_path(change->copyfrom_path,
+                                                scratch_pool)));
     }
 
   svn_stringbuf_appendbyte(buf, '\n');
@@ -1005,36 +1084,40 @@ svn_fs_x__write_changes(svn_stream_t *st
                         svn_fs_t *fs,
                         apr_hash_t *changes,
                         svn_boolean_t terminate_list,
-                        apr_pool_t *pool)
+                        apr_pool_t *scratch_pool)
 {
-  apr_pool_t *iterpool = svn_pool_create(pool);
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
   apr_array_header_t *sorted_changed_paths;
   int i;
 
   /* For the sake of the repository administrator sort the changes so
      that the final file is deterministic and repeatable, however the
-     rest of the FSX code doesn't require any particular order here. */
+     rest of the FSX code doesn't require any particular order here.
+
+     Also, this sorting is only effective in writing all entries with
+     a single call as write_final_changed_path_info() does.  For the
+     list being written incrementally during transaction, we actually
+     *must not* change the order of entries from different calls.
+   */
   sorted_changed_paths = svn_sort__hash(changes,
-                                        svn_sort_compare_items_lexically, pool);
+                                        svn_sort_compare_items_lexically,
+                                        scratch_pool);
 
   /* Write all items to disk in the new order. */
   for (i = 0; i < sorted_changed_paths->nelts; ++i)
     {
-      svn_fs_path_change2_t *change;
-      const char *path;
+      svn_fs_x__change_t *change;
 
       svn_pool_clear(iterpool);
-
       change = APR_ARRAY_IDX(sorted_changed_paths, i, svn_sort__item_t).value;
-      path = APR_ARRAY_IDX(sorted_changed_paths, i, svn_sort__item_t).key;
 
       /* Write out the new entry into the final rev-file. */
-      SVN_ERR(write_change_entry(stream, path, change, iterpool));
+      SVN_ERR(write_change_entry(stream, change, iterpool));
     }
 
   if (terminate_list)
     svn_stream_puts(stream, "\n");
-  
+
   svn_pool_destroy(iterpool);
 
   return SVN_NO_ERROR;

Modified: subversion/branches/authzperf/subversion/libsvn_fs_x/low_level.h
URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_fs_x/low_level.h?rev=1649205&r1=1649204&r2=1649205&view=diff
==============================================================================
--- subversion/branches/authzperf/subversion/libsvn_fs_x/low_level.h (original)
+++ subversion/branches/authzperf/subversion/libsvn_fs_x/low_level.h Sat Jan  3 14:00:41 2015
@@ -36,62 +36,81 @@ extern "C" {
 #define SVN_FS_X__KIND_FILE          "file"
 #define SVN_FS_X__KIND_DIR           "dir"
 
-/* Given the last "few" bytes (should be at least 40) of revision REV in
- * TRAILER,  parse the last line and return the offset of the root noderev
- * in *ROOT_OFFSET and the offset of the changed paths list in
- * *CHANGES_OFFSET.  Offsets are relative to the revision's start offset.
- * ROOT_OFFSET and / or CHANGES_OFFSET may be NULL.
+/* The functions are grouped as follows:
+ *
+ * - revision footer
+ * - representation (as in "text:" and "props:" lines)
+ * - node revision
+ * - representation header ("DELTA" lines)
+ * - changed path list
+ */
+
+/* Given the FSX revision / pack FOOTER, parse it destructively
+ * and return the start offsets of the index data in *L2P_OFFSET and
+ * *P2L_OFFSET, respectively.  Also, return the expected checksums in
+ * in *L2P_CHECKSUM and *P2L_CHECKSUM.
  * 
- * Note that REV is only used to construct nicer error objects.
+ * Note that REV is only used to construct nicer error objects that
+ * mention this revision.  Allocate the checksums in RESULT_POOL.
  */
 svn_error_t *
-svn_fs_x__parse_revision_trailer(apr_off_t *root_offset,
-                                 apr_off_t *changes_offset,
-                                 svn_stringbuf_t *trailer,
-                                 svn_revnum_t rev);
-
-/* Given the offset of the root noderev in ROOT_OFFSET and the offset of
- * the changed paths list in CHANGES_OFFSET,  return the corresponding
- * revision's trailer.  Allocate it in POOL.
+svn_fs_x__parse_footer(apr_off_t *l2p_offset,
+                       svn_checksum_t **l2p_checksum,
+                       apr_off_t *p2l_offset,
+                       svn_checksum_t **p2l_checksum,
+                       svn_stringbuf_t *footer,
+                       svn_revnum_t rev,
+                       apr_pool_t *result_pool);
+
+/* Given the offset of the L2P index data in L2P_OFFSET, the content
+ * checksum in L2P_CHECKSUM and the offset plus checksum of the P2L
+ * index data in P2L_OFFSET and P2L_CHECKSUM.
+ *
+ * Return the corresponding format 7+ revision / pack file footer.
+ * Allocate it in RESULT_POOL and use SCRATCH_POOL for temporary.
  */
 svn_stringbuf_t *
-svn_fs_x__unparse_revision_trailer(apr_off_t root_offset,
-                                   apr_off_t changes_offset,
-                                   apr_pool_t *pool);
+svn_fs_x__unparse_footer(apr_off_t l2p_offset,
+                         svn_checksum_t *l2p_checksum,
+                         apr_off_t p2l_offset,
+                         svn_checksum_t *p2l_checksum,
+                         apr_pool_t *result_pool,
+                         apr_pool_t *scratch_pool);
 
 /* Parse the description of a representation from TEXT and store it
-   into *REP_P.  Allocate *REP_P in POOL. */
+   into *REP_P.  TEXT will be invalidated by this call.  Allocate *REP_P in
+   RESULT_POOL and use SCRATCH_POOL for temporaries. */
 svn_error_t *
-svn_fs_x__parse_representation(representation_t **rep_p,
+svn_fs_x__parse_representation(svn_fs_x__representation_t **rep_p,
                                svn_stringbuf_t *text,
-                               apr_pool_t *pool);
+                               apr_pool_t *result_pool,
+                               apr_pool_t *scratch_pool);
 
-/* Return a formatted string, compatible with filesystem format FORMAT,
-   that represents the location of representation REP.  If
-   MUTABLE_REP_TRUNCATED is given, the rep is for props or dir contents,
-   and only a "-1" revision number will be given for a mutable rep.
-   If MAY_BE_CORRUPT is true, guard for NULL when constructing the string.
-   Perform the allocation from POOL.  */
+/* Return a formatted string that represents the location of representation
+ * REP.  If MUTABLE_REP_TRUNCATED is given, the rep is for props or dir
+ * contents, and only a "-1" revision number will be given for a mutable rep.
+ * If MAY_BE_CORRUPT is true, guard for NULL when constructing the string.
+ * Allocate the result in RESULT_POOL and temporaries in SCRATCH_POOL. */
 svn_stringbuf_t *
-svn_fs_x__unparse_representation(representation_t *rep,
-                                 int format,
+svn_fs_x__unparse_representation(svn_fs_x__representation_t *rep,
                                  svn_boolean_t mutable_rep_truncated,
-                                 apr_pool_t *pool);
+                                 apr_pool_t *result_pool,
+                                 apr_pool_t *scratch_pool);
 
 /* Read a node-revision from STREAM. Set *NODEREV to the new structure,
-   allocated in POOL. */
+   allocated in RESULT_POOL. */
 svn_error_t *
-svn_fs_x__read_noderev(node_revision_t **noderev,
+svn_fs_x__read_noderev(svn_fs_x__noderev_t **noderev,
                        svn_stream_t *stream,
-                       apr_pool_t *pool);
+                       apr_pool_t *result_pool,
+                       apr_pool_t *scratch_pool);
 
-/* Write the node-revision NODEREV into the stream OUTFILE, compatible with
-   filesystem format FORMAT.  Temporary allocations are from POOL. */
+/* Write the node-revision NODEREV into the stream OUTFILE.
+   Temporary allocations are from SCRATCH_POOL. */
 svn_error_t *
 svn_fs_x__write_noderev(svn_stream_t *outfile,
-                        node_revision_t *noderev,
-                        int format,
-                        apr_pool_t *pool);
+                        svn_fs_x__noderev_t *noderev,
+                        apr_pool_t *scratch_pool);
 
 /* This type enumerates all forms of representations that we support. */
 typedef enum svn_fs_x__rep_type_t
@@ -132,39 +151,61 @@ typedef struct svn_fs_x__rep_header_t
   apr_size_t header_size;
 } svn_fs_x__rep_header_t;
 
-/* Read the next line from file FILE and parse it as a text
-   representation entry.  Return the parsed entry in *REP_ARGS_P.
-   Perform all allocations in POOL. */
+/* Read the next line from STREAM and parse it as a text
+   representation header.  Return the parsed entry in *HEADER, allocated
+   in RESULT_POOL. Perform temporary allocations in SCRATCH_POOL. */
 svn_error_t *
 svn_fs_x__read_rep_header(svn_fs_x__rep_header_t **header,
                           svn_stream_t *stream,
-                          apr_pool_t *pool);
+                          apr_pool_t *result_pool,
+                          apr_pool_t *scratch_pool);
 
-/* Write the representation HEADER to STREAM.  Use POOL for allocations. */
+/* Write the representation HEADER to STREAM. 
+ * Use SCRATCH_POOL for allocations. */
 svn_error_t *
 svn_fs_x__write_rep_header(svn_fs_x__rep_header_t *header,
                            svn_stream_t *stream,
-                           apr_pool_t *pool);
+                           apr_pool_t *scratch_pool);
 
-/* Read all the changes from STREAM and store them in *CHANGES.  Do all
-   allocations in POOL. */
+/* Read all the changes from STREAM and store them in *CHANGES,
+   allocated in RESULT_POOL. Do temporary allocations in SCRATCH_POOL. */
 svn_error_t *
 svn_fs_x__read_changes(apr_array_header_t **changes,
                        svn_stream_t *stream,
-                       apr_pool_t *pool);
+                       apr_pool_t *result_pool,
+                       apr_pool_t *scratch_pool);
+
+/* Callback function used by svn_fs_fs__read_changes_incrementally(),
+ * asking the receiver to process to process CHANGE using BATON.  CHANGE
+ * and SCRATCH_POOL will not be valid beyond the current callback invocation.
+ */
+typedef svn_error_t *(*svn_fs_x__change_receiver_t)(
+  void *baton,
+  svn_fs_x__change_t *change,
+  apr_pool_t *scratch_pool);
+
+/* Read all the changes from STREAM and invoke CHANGE_RECEIVER on each change.
+   Do all allocations in SCRATCH_POOL. */
+svn_error_t *
+svn_fs_x__read_changes_incrementally(svn_stream_t *stream,
+                                     svn_fs_x__change_receiver_t
+                                       change_receiver,
+                                     void *change_receiver_baton,
+                                     apr_pool_t *scratch_pool);
 
 /* Write the changed path info from CHANGES in filesystem FS to the
    output stream STREAM.  You may call this function multiple time on
-   the same stream but the last call should set TERMINATE_LIST to write
-   an extra empty line that marks the end of the changed paths list.
-   Perform temporary allocations in POOL.
+   the same stream.  If you are writing to a (proto-)revision file,
+   the last call must set TERMINATE_LIST to write an extra empty line
+   that marks the end of the changed paths list.
+   Perform temporary allocations in SCRATCH_POOL.
  */
 svn_error_t *
 svn_fs_x__write_changes(svn_stream_t *stream,
                         svn_fs_t *fs,
                         apr_hash_t *changes,
                         svn_boolean_t terminate_list,
-                        apr_pool_t *pool);
+                        apr_pool_t *scratch_pool);
 
 #ifdef __cplusplus
 }

Modified: subversion/branches/authzperf/subversion/libsvn_fs_x/noderevs.c
URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_fs_x/noderevs.c?rev=1649205&r1=1649204&r2=1649205&view=diff
==============================================================================
--- subversion/branches/authzperf/subversion/libsvn_fs_x/noderevs.c (original)
+++ subversion/branches/authzperf/subversion/libsvn_fs_x/noderevs.c Sat Jan  3 14:00:41 2015
@@ -49,19 +49,7 @@
 /* the noderev has copy-root path and revision */
 #define NODEREV_HAS_CPATH    0x00040
 
-/* Our internal representation of an id
- * (basically, strip off the txn_id and the fs-agnostic header)
- */
-typedef struct binary_id_t
-{
-  svn_fs_x__id_part_t node_id;
-  svn_fs_x__id_part_t copy_id;
-  svn_fs_x__id_part_t noderev_id;
-} binary_id_t;
-
 /* Our internal representation of an representation.
- * We simply omit the uniquifier, which allows us to share instances of
- * binary_representation_t and uniquify them in a shared_representation_t.
  */
 typedef struct binary_representation_t
 {
@@ -72,7 +60,7 @@ typedef struct binary_representation_t
   unsigned char md5_digest[APR_MD5_DIGESTSIZE];
 
   /* Location of this representation. */
-  svn_fs_x__id_part_t id;
+  svn_fs_x__id_t id;
 
   /* The size of the representation in bytes as seen in the revision
      file. */
@@ -83,7 +71,7 @@ typedef struct binary_representation_t
   svn_filesize_t expanded_size;
 } binary_representation_t;
 
-/* Our internal representation of a node_revision_t.
+/* Our internal representation of a svn_fs_x__noderev_t.
  * 
  * We will store path strings in a string container and reference them
  * from here.  Similarly, IDs and representations are being stored in
@@ -95,9 +83,15 @@ typedef struct binary_noderev_t
   /* node type and presence indicators */
   apr_uint32_t flags;
 
-  /* Index+1 of the node-id for this node-rev. */
+  /* Index+1 of the noderev-id for this node-rev. */
   int id;
 
+  /* Index+1 of the node-id for this node-rev. */
+  int node_id;
+
+  /* Index+1 of the copy-id for this node-rev. */
+  int copy_id;
+
   /* Index+1 of the predecessor node revision id, or 0 if there is no
      predecessor for this node revision */
   int predecessor_id;
@@ -158,19 +152,14 @@ struct svn_fs_x__noderevs_t
   apr_hash_t *ids_dict;
 
   /* During construction, maps a full binary_representation_t to an index
-   * into DATA_REPS. */
-  apr_hash_t *data_reps_dict;
-
-  /* During construction, maps a full binary_representation_t to an index
-   * into PROP_REPS. */
-  apr_hash_t *prop_reps_dict;
+   * into REPS. */
+  apr_hash_t *reps_dict;
 
   /* array of binary_id_t */
   apr_array_header_t *ids;
 
-  /* arrays of binary_representation_t */
-  apr_array_header_t *data_reps;
-  apr_array_header_t *prop_reps;
+  /* array of binary_representation_t */
+  apr_array_header_t *reps;
 
   /* array of binary_noderev_t. */
   apr_array_header_t *noderevs;
@@ -184,16 +173,13 @@ svn_fs_x__noderevs_create(int initial_co
 
   noderevs->builder = svn_fs_x__string_table_builder_create(pool);
   noderevs->ids_dict = svn_hash__make(pool);
-  noderevs->data_reps_dict = svn_hash__make(pool);
-  noderevs->prop_reps_dict = svn_hash__make(pool);
+  noderevs->reps_dict = svn_hash__make(pool);
   noderevs->paths = NULL;
 
   noderevs->ids
-    = apr_array_make(pool, initial_count, sizeof(binary_id_t));
-  noderevs->data_reps
-    = apr_array_make(pool, initial_count, sizeof(binary_representation_t));
-  noderevs->prop_reps
-    = apr_array_make(pool, initial_count, sizeof(binary_representation_t));
+    = apr_array_make(pool, 2 * initial_count, sizeof(svn_fs_x__id_t));
+  noderevs->reps
+    = apr_array_make(pool, 2 * initial_count, sizeof(binary_representation_t));
   noderevs->noderevs
     = apr_array_make(pool, initial_count, sizeof(binary_noderev_t));
 
@@ -206,24 +192,19 @@ svn_fs_x__noderevs_create(int initial_co
 static int
 store_id(apr_array_header_t *ids,
          apr_hash_t *dict,
-         const svn_fs_id_t *id)
+         const svn_fs_x__id_t *id)
 {
-  binary_id_t bin_id = { { 0 } };
   int idx;
   void *idx_void;
 
-  if (id == NULL)
+  if (!svn_fs_x__id_used(id))
     return 0;
-  
-  bin_id.node_id = *svn_fs_x__id_node_id(id);
-  bin_id.copy_id = *svn_fs_x__id_copy_id(id);
-  bin_id.noderev_id = *svn_fs_x__id_noderev_id(id);
 
-  idx_void = apr_hash_get(dict, &bin_id, sizeof(bin_id));
+  idx_void = apr_hash_get(dict, &id, sizeof(id));
   idx = (int)(apr_uintptr_t)idx_void;
   if (idx == 0)
     {
-      APR_ARRAY_PUSH(ids, binary_id_t) = bin_id;
+      APR_ARRAY_PUSH(ids, svn_fs_x__id_t) = *id;
       idx = ids->nelts;
       apr_hash_set(dict, ids->elts + (idx-1) * ids->elt_size,
                    ids->elt_size, (void*)(apr_uintptr_t)idx);
@@ -238,7 +219,7 @@ store_id(apr_array_header_t *ids,
 static int
 store_representation(apr_array_header_t *reps,
                      apr_hash_t *dict,
-                     const representation_t *rep)
+                     const svn_fs_x__representation_t *rep)
 {
   binary_representation_t binary_rep = { 0 };
   int idx;
@@ -269,7 +250,7 @@ store_representation(apr_array_header_t
 
 apr_size_t
 svn_fs_x__noderevs_add(svn_fs_x__noderevs_t *container,
-                       node_revision_t *noderev)
+                       svn_fs_x__noderev_t *noderev)
 {
   binary_noderev_t binary_noderev = { 0 };
 
@@ -280,9 +261,13 @@ svn_fs_x__noderevs_add(svn_fs_x__noderev
                        | (int)noderev->kind;
 
   binary_noderev.id
-    = store_id(container->ids, container->ids_dict, noderev->id);
+    = store_id(container->ids, container->ids_dict, &noderev->noderev_id);
+  binary_noderev.node_id
+    = store_id(container->ids, container->ids_dict, &noderev->node_id);
+  binary_noderev.copy_id
+    = store_id(container->ids, container->ids_dict, &noderev->copy_id);
   binary_noderev.predecessor_id
-    = store_id(container->ids, container->ids_dict, noderev->predecessor_id);
+    = store_id(container->ids, container->ids_dict, &noderev->predecessor_id);
 
   if (noderev->copyfrom_path)
     {
@@ -303,19 +288,18 @@ svn_fs_x__noderevs_add(svn_fs_x__noderev
     }
 
   binary_noderev.predecessor_count = noderev->predecessor_count;
-  binary_noderev.prop_rep = store_representation(container->prop_reps,
-                                                 container->prop_reps_dict,
+  binary_noderev.prop_rep = store_representation(container->reps,
+                                                 container->reps_dict,
                                                  noderev->prop_rep);
-  if (noderev->data_rep)
-    binary_noderev.data_rep = store_representation(container->data_reps,
-                                                   container->data_reps_dict,
-                                                   noderev->data_rep);
+  binary_noderev.data_rep = store_representation(container->reps,
+                                                 container->reps_dict,
+                                                 noderev->data_rep);
 
   if (noderev->created_path)
     binary_noderev.created_path
       = svn_fs_x__string_table_builder_add(container->builder,
-                                            noderev->created_path,
-                                            0);
+                                           noderev->created_path,
+                                           0);
 
   binary_noderev.mergeinfo_count = noderev->mergeinfo_count;
   
@@ -333,58 +317,48 @@ svn_fs_x__noderevs_estimate_size(const s
 
   /* string table code makes its own prediction,
    * noderevs should be < 16 bytes each,
-   * ids < 10 bytes each,
+   * id parts < 4 bytes each,
    * data representations < 40 bytes each,
    * property representations < 30 bytes each,
    * some static overhead should be assumed */
   return svn_fs_x__string_table_builder_estimate_size(container->builder)
        + container->noderevs->nelts * 16
-       + container->ids->nelts * 10
-       + container->data_reps->nelts * 40
-       + container->prop_reps->nelts * 30
+       + container->ids->nelts * 4
+       + container->reps->nelts * 40
        + 100;
 }
 
-/* Create an svn_fs_id_t in *ID, allocated in POOL based on the id stored
- * at index IDX in IDS.
+/* Set *ID to the ID part stored at index IDX in IDS.
  */
 static svn_error_t *
-get_id(const svn_fs_id_t **id,
+get_id(svn_fs_x__id_t *id,
        const apr_array_header_t *ids,
-       int idx,
-       apr_pool_t *pool)
+       int idx)
 {
-  binary_id_t *binary_id;
-
   /* handle NULL IDs  */
   if (idx == 0)
     {
-      *id = NULL;
+      svn_fs_x__id_reset(id);
       return SVN_NO_ERROR;
     }
 
   /* check for corrupted data */
   if (idx < 0 || idx > ids->nelts)
     return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL,
-                             _("Node revision ID index %d" 
-                               " exceeds container size %d"),
+                             _("ID part index %d exceeds container size %d"),
                              idx, ids->nelts);
 
-  /* create a svn_fs_id_t from stored info */
-  binary_id = &APR_ARRAY_IDX(ids, idx - 1, binary_id_t);
-  *id = svn_fs_x__id_create(&binary_id->node_id,
-                            &binary_id->copy_id,
-                            &binary_id->noderev_id,
-                            pool);
+  /* Return the requested ID. */
+  *id = APR_ARRAY_IDX(ids, idx - 1, svn_fs_x__id_t);
 
   return SVN_NO_ERROR;
 }
 
-/* Create a representation_t in *REP, allocated in POOL based on the
+/* Create a svn_fs_x__representation_t in *REP, allocated in POOL based on the
  * representation stored at index IDX in REPS.
  */
 static svn_error_t *
-get_representation(representation_t **rep,
+get_representation(svn_fs_x__representation_t **rep,
                    const apr_array_header_t *reps,
                    int idx,
                    apr_pool_t *pool)
@@ -422,14 +396,14 @@ get_representation(representation_t **re
 }
 
 svn_error_t *
-svn_fs_x__noderevs_get(node_revision_t **noderev_p,
+svn_fs_x__noderevs_get(svn_fs_x__noderev_t **noderev_p,
                        const svn_fs_x__noderevs_t *container,
                        apr_size_t idx,
                        apr_pool_t *pool)
 {
-  node_revision_t *noderev;
+  svn_fs_x__noderev_t *noderev;
   binary_noderev_t *binary_noderev;
-  
+
   /* CONTAINER must be in 'finalized' mode */
   SVN_ERR_ASSERT(container->builder == NULL);
   SVN_ERR_ASSERT(container->paths);
@@ -446,11 +420,15 @@ svn_fs_x__noderevs_get(node_revision_t *
   /* allocate result struct and fill it field by field */
   noderev = apr_pcalloc(pool, sizeof(*noderev));
   binary_noderev = &APR_ARRAY_IDX(container->noderevs, idx, binary_noderev_t);
-  
+
   noderev->kind = (svn_node_kind_t)(binary_noderev->flags & NODEREV_KIND_MASK);
-  SVN_ERR(get_id(&noderev->id, container->ids, binary_noderev->id, pool));
+  SVN_ERR(get_id(&noderev->noderev_id, container->ids, binary_noderev->id));
+  SVN_ERR(get_id(&noderev->node_id, container->ids,
+                 binary_noderev->node_id));
+  SVN_ERR(get_id(&noderev->copy_id, container->ids,
+                 binary_noderev->copy_id));
   SVN_ERR(get_id(&noderev->predecessor_id, container->ids,
-                 binary_noderev->predecessor_id, pool));
+                 binary_noderev->predecessor_id));
 
   if (binary_noderev->flags & NODEREV_HAS_COPYFROM)
     {
@@ -484,9 +462,9 @@ svn_fs_x__noderevs_get(node_revision_t *
 
   noderev->predecessor_count = binary_noderev->predecessor_count;
 
-  SVN_ERR(get_representation(&noderev->prop_rep, container->prop_reps,
+  SVN_ERR(get_representation(&noderev->prop_rep, container->reps,
                              binary_noderev->prop_rep, pool));
-  SVN_ERR(get_representation(&noderev->data_rep, container->data_reps,
+  SVN_ERR(get_representation(&noderev->data_rep, container->reps,
                              binary_noderev->data_rep, pool));
 
   if (binary_noderev->flags & NODEREV_HAS_CPATH)
@@ -576,40 +554,34 @@ svn_fs_x__write_noderevs_container(svn_s
     = svn_packed__create_int_stream(root, FALSE, FALSE);
   svn_packed__int_stream_t *ids_stream
     = svn_packed__create_int_substream(structs_stream, FALSE, FALSE);
-  svn_packed__int_stream_t *data_reps_stream
-    = create_rep_stream(structs_stream);
-  svn_packed__int_stream_t *prop_reps_stream
+  svn_packed__int_stream_t *reps_stream
     = create_rep_stream(structs_stream);
   svn_packed__int_stream_t *noderevs_stream
     = svn_packed__create_int_substream(structs_stream, FALSE, FALSE);
   svn_packed__byte_stream_t *digests_stream
     = svn_packed__create_bytes_stream(root);
 
-  /* structure the CHANGES_STREAM such we can extract much of the redundancy
-   * from the binary_change_t structs */
-  for (i = 0; i < 3 * 2; ++i)
+  /* structure the IDS_STREAM such we can extract much of the redundancy
+   * from the svn_fs_x__ip_part_t structs */
+  for (i = 0; i < 2; ++i)
     svn_packed__create_int_substream(ids_stream, TRUE, FALSE);
 
+  /* Same storing binary_noderev_t in the NODEREVS_STREAM */
   svn_packed__create_int_substream(noderevs_stream, FALSE, FALSE);
-  for (i = 0; i < 11; ++i)
+  for (i = 0; i < 13; ++i)
     svn_packed__create_int_substream(noderevs_stream, TRUE, FALSE);
 
   /* serialize ids array */
   for (i = 0; i < container->ids->nelts; ++i)
     {
-      binary_id_t *id = &APR_ARRAY_IDX(container->ids, i, binary_id_t);
+      svn_fs_x__id_t *id = &APR_ARRAY_IDX(container->ids, i, svn_fs_x__id_t);
 
-      svn_packed__add_int(ids_stream, id->node_id.change_set);
-      svn_packed__add_uint(ids_stream, id->node_id.number);
-      svn_packed__add_int(ids_stream, id->copy_id.change_set);
-      svn_packed__add_uint(ids_stream, id->copy_id.number);
-      svn_packed__add_int(ids_stream, id->noderev_id.change_set);
-      svn_packed__add_uint(ids_stream, id->noderev_id.number);
+      svn_packed__add_int(ids_stream, id->change_set);
+      svn_packed__add_uint(ids_stream, id->number);
     }
 
   /* serialize rep arrays */
-  write_reps(data_reps_stream, digests_stream, container->data_reps);
-  write_reps(prop_reps_stream, digests_stream, container->prop_reps);
+  write_reps(reps_stream, digests_stream, container->reps);
 
   /* serialize noderevs array */
   for (i = 0; i < container->noderevs->nelts; ++i)
@@ -620,6 +592,8 @@ svn_fs_x__write_noderevs_container(svn_s
       svn_packed__add_uint(noderevs_stream, noderev->flags);
 
       svn_packed__add_uint(noderevs_stream, noderev->id);
+      svn_packed__add_uint(noderevs_stream, noderev->node_id);
+      svn_packed__add_uint(noderevs_stream, noderev->copy_id);
       svn_packed__add_uint(noderevs_stream, noderev->predecessor_id);
       svn_packed__add_uint(noderevs_stream, noderev->predecessor_count);
 
@@ -642,9 +616,9 @@ svn_fs_x__write_noderevs_container(svn_s
   return SVN_NO_ERROR;
 }
 
-/* Allocate a representation_t array in POOL and return it in *REPS_P.
- * Deserialize the data in REP_STREAM and DIGEST_STREAM and store the
- * resulting representations into the *REPS_P.
+/* Allocate a svn_fs_x__representation_t array in POOL and return it in
+ * REPS_P.  Deserialize the data in REP_STREAM and DIGEST_STREAM and store
+ * the resulting representations into the *REPS_P.
  */
 static svn_error_t *
 read_reps(apr_array_header_t **reps_p,
@@ -721,8 +695,7 @@ svn_fs_x__read_noderevs_container(svn_fs
   svn_packed__data_root_t *root;
   svn_packed__int_stream_t *structs_stream;
   svn_packed__int_stream_t *ids_stream;
-  svn_packed__int_stream_t *data_reps_stream;
-  svn_packed__int_stream_t *prop_reps_stream;
+  svn_packed__int_stream_t *reps_stream;
   svn_packed__int_stream_t *noderevs_stream;
   svn_packed__byte_stream_t *digests_stream;
 
@@ -734,34 +707,27 @@ svn_fs_x__read_noderevs_container(svn_fs
   /* get streams */
   structs_stream = svn_packed__first_int_stream(root);
   ids_stream = svn_packed__first_int_substream(structs_stream);
-  data_reps_stream = svn_packed__next_int_stream(ids_stream);
-  prop_reps_stream = svn_packed__next_int_stream(data_reps_stream);
-  noderevs_stream = svn_packed__next_int_stream(prop_reps_stream);
+  reps_stream = svn_packed__next_int_stream(ids_stream);
+  noderevs_stream = svn_packed__next_int_stream(reps_stream);
   digests_stream = svn_packed__first_byte_stream(root);
 
   /* read ids array */
   count
     = svn_packed__int_count(svn_packed__first_int_substream(ids_stream));
   noderevs->ids
-    = apr_array_make(result_pool, (int)count, sizeof(binary_id_t));
+    = apr_array_make(result_pool, (int)count, sizeof(svn_fs_x__id_t));
   for (i = 0; i < count; ++i)
     {
-      binary_id_t id;
+      svn_fs_x__id_t id;
 
-      id.node_id.change_set = (svn_revnum_t)svn_packed__get_int(ids_stream);
-      id.node_id.number = svn_packed__get_uint(ids_stream);
-      id.copy_id.change_set = (svn_revnum_t)svn_packed__get_int(ids_stream);
-      id.copy_id.number = svn_packed__get_uint(ids_stream);
-      id.noderev_id.change_set = (svn_revnum_t)svn_packed__get_int(ids_stream);
-      id.noderev_id.number = svn_packed__get_uint(ids_stream);
+      id.change_set = (svn_revnum_t)svn_packed__get_int(ids_stream);
+      id.number = svn_packed__get_uint(ids_stream);
 
-      APR_ARRAY_PUSH(noderevs->ids, binary_id_t) = id;
+      APR_ARRAY_PUSH(noderevs->ids, svn_fs_x__id_t) = id;
     }
 
   /* read rep arrays */
-  SVN_ERR(read_reps(&noderevs->data_reps, data_reps_stream, digests_stream,
-                    result_pool));
-  SVN_ERR(read_reps(&noderevs->prop_reps, prop_reps_stream, digests_stream,
+  SVN_ERR(read_reps(&noderevs->reps, reps_stream, digests_stream,
                     result_pool));
 
   /* read noderevs array */
@@ -776,6 +742,8 @@ svn_fs_x__read_noderevs_container(svn_fs
       noderev.flags = (apr_uint32_t)svn_packed__get_uint(noderevs_stream);
 
       noderev.id = (int)svn_packed__get_uint(noderevs_stream);
+      noderev.node_id = (int)svn_packed__get_uint(noderevs_stream);
+      noderev.copy_id = (int)svn_packed__get_uint(noderevs_stream);
       noderev.predecessor_id = (int)svn_packed__get_uint(noderevs_stream);
       noderev.predecessor_count = (int)svn_packed__get_uint(noderevs_stream);
 
@@ -808,8 +776,7 @@ svn_fs_x__serialize_noderevs_container(v
   svn_stringbuf_t *serialized;
   apr_size_t size
     = noderevs->ids->elt_size * noderevs->ids->nelts
-    + noderevs->data_reps->elt_size * noderevs->data_reps->nelts
-    + noderevs->prop_reps->elt_size * noderevs->prop_reps->nelts
+    + noderevs->reps->elt_size * noderevs->reps->nelts
     + noderevs->noderevs->elt_size * noderevs->noderevs->nelts
     + 10 * noderevs->noderevs->elt_size
     + 100;
@@ -821,8 +788,7 @@ svn_fs_x__serialize_noderevs_container(v
   /* serialize sub-structures */
   svn_fs_x__serialize_string_table(context, &noderevs->paths);
   svn_fs_x__serialize_apr_array(context, &noderevs->ids);
-  svn_fs_x__serialize_apr_array(context, &noderevs->data_reps);
-  svn_fs_x__serialize_apr_array(context, &noderevs->prop_reps);
+  svn_fs_x__serialize_apr_array(context, &noderevs->reps);
   svn_fs_x__serialize_apr_array(context, &noderevs->noderevs);
 
   /* return the serialized result */
@@ -845,8 +811,7 @@ svn_fs_x__deserialize_noderevs_container
   /* de-serialize sub-structures */
   svn_fs_x__deserialize_string_table(noderevs, &noderevs->paths);
   svn_fs_x__deserialize_apr_array(noderevs, &noderevs->ids, pool);
-  svn_fs_x__deserialize_apr_array(noderevs, &noderevs->data_reps, pool);
-  svn_fs_x__deserialize_apr_array(noderevs, &noderevs->prop_reps, pool);
+  svn_fs_x__deserialize_apr_array(noderevs, &noderevs->reps, pool);
   svn_fs_x__deserialize_apr_array(noderevs, &noderevs->noderevs, pool);
 
   /* done */
@@ -880,12 +845,11 @@ svn_fs_x__noderevs_get_func(void **out,
                             void *baton,
                             apr_pool_t *pool)
 {
-  node_revision_t *noderev;
+  svn_fs_x__noderev_t *noderev;
   binary_noderev_t *binary_noderev;
-  
+
   apr_array_header_t ids;
-  apr_array_header_t data_reps;
-  apr_array_header_t prop_reps;
+  apr_array_header_t reps;
   apr_array_header_t noderevs;
 
   apr_uint32_t idx = *(apr_uint32_t *)baton;
@@ -897,18 +861,19 @@ svn_fs_x__noderevs_get_func(void **out,
                          (const void *const *)&container->paths);
 
   resolve_apr_array_header(&ids, container, &container->ids);
-  resolve_apr_array_header(&data_reps, container, &container->data_reps);
-  resolve_apr_array_header(&prop_reps, container, &container->prop_reps);
+  resolve_apr_array_header(&reps, container, &container->reps);
   resolve_apr_array_header(&noderevs, container, &container->noderevs);
-  
+
   /* allocate result struct and fill it field by field */
   noderev = apr_pcalloc(pool, sizeof(*noderev));
   binary_noderev = &APR_ARRAY_IDX(&noderevs, idx, binary_noderev_t);
-  
+
   noderev->kind = (svn_node_kind_t)(binary_noderev->flags & NODEREV_KIND_MASK);
-  SVN_ERR(get_id(&noderev->id, &ids, binary_noderev->id, pool));
+  SVN_ERR(get_id(&noderev->noderev_id, &ids, binary_noderev->id));
+  SVN_ERR(get_id(&noderev->node_id, &ids, binary_noderev->node_id));
+  SVN_ERR(get_id(&noderev->copy_id, &ids, binary_noderev->copy_id));
   SVN_ERR(get_id(&noderev->predecessor_id, &ids,
-                 binary_noderev->predecessor_id, pool));
+                 binary_noderev->predecessor_id));
 
   if (binary_noderev->flags & NODEREV_HAS_COPYFROM)
     {
@@ -942,9 +907,9 @@ svn_fs_x__noderevs_get_func(void **out,
 
   noderev->predecessor_count = binary_noderev->predecessor_count;
 
-  SVN_ERR(get_representation(&noderev->prop_rep, &prop_reps,
+  SVN_ERR(get_representation(&noderev->prop_rep, &reps,
                              binary_noderev->prop_rep, pool));
-  SVN_ERR(get_representation(&noderev->data_rep, &data_reps,
+  SVN_ERR(get_representation(&noderev->data_rep, &reps,
                              binary_noderev->data_rep, pool));
 
   if (binary_noderev->flags & NODEREV_HAS_CPATH)