You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2010/08/08 17:34:48 UTC

svn commit: r983430 [2/3] - in /subversion/branches/performance/subversion/libsvn_fs_fs: caching.c fs.h fs_fs.c temp_serializer.c temp_serializer.h

Copied: subversion/branches/performance/subversion/libsvn_fs_fs/temp_serializer.c (from r982417, subversion/branches/performance/subversion/libsvn_fs_fs/fs_fs.c)
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_fs_fs/temp_serializer.c?p2=subversion/branches/performance/subversion/libsvn_fs_fs/temp_serializer.c&p1=subversion/branches/performance/subversion/libsvn_fs_fs/fs_fs.c&r1=982417&r2=983430&rev=983430&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_fs_fs/fs_fs.c (original)
+++ subversion/branches/performance/subversion/libsvn_fs_fs/temp_serializer.c Sun Aug  8 15:34:48 2010
@@ -1,4 +1,4 @@
-/* fs_fs.c --- filesystem operations specific to fs_fs
+/* temp_serializer.c: serialization functions for caching of FSFS structures
  *
  * ====================================================================
  *    Licensed to the Apache Software Foundation (ASF) under one
@@ -20,7689 +20,239 @@
  * ====================================================================
  */
 
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-#include <assert.h>
-#include <errno.h>
-
-#include <apr_general.h>
 #include <apr_pools.h>
-#include <apr_file_io.h>
-#include <apr_uuid.h>
-#include <apr_lib.h>
-#include <apr_md5.h>
-#include <apr_sha1.h>
-#include <apr_strings.h>
-#include <apr_thread_mutex.h>
 
 #include "svn_pools.h"
-#include "svn_fs.h"
-#include "svn_dirent_uri.h"
-#include "svn_path.h"
 #include "svn_hash.h"
-#include "svn_props.h"
-#include "svn_sorts.h"
-#include "svn_time.h"
-#include "svn_mergeinfo.h"
-#include "svn_config.h"
-
-#include "fs.h"
-#include "err.h"
-#include "tree.h"
-#include "lock.h"
-#include "key-gen.h"
-#include "fs_fs.h"
-#include "id.h"
-#include "rep-cache.h"
 
-#include "revprops-db.h"
+#include "id.h"
+#include "svn_fs.h"
 
 #include "private/svn_fs_util.h"
-#include "../libsvn_fs/fs-loader.h"
-
-#include "svn_private_config.h"
-
-/* An arbitrary maximum path length, so clients can't run us out of memory
- * by giving us arbitrarily large paths. */
-#define FSFS_MAX_PATH_LEN 4096
-
-/* The default maximum number of files per directory to store in the
-   rev and revprops directory.  The number below is somewhat arbitrary,
-   and can be overriden by defining the macro while compiling; the
-   figure of 1000 is reasonable for VFAT filesystems, which are by far
-   the worst performers in this area. */
-#ifndef SVN_FS_FS_DEFAULT_MAX_FILES_PER_DIR
-#define SVN_FS_FS_DEFAULT_MAX_FILES_PER_DIR 1000
-#endif
-
-/* Following are defines that specify the textual elements of the
-   native filesystem directories and revision files. */
-
-/* Headers used to describe node-revision in the revision file. */
-#define HEADER_ID          "id"
-#define HEADER_TYPE        "type"
-#define HEADER_COUNT       "count"
-#define HEADER_PROPS       "props"
-#define HEADER_TEXT        "text"
-#define HEADER_CPATH       "cpath"
-#define HEADER_PRED        "pred"
-#define HEADER_COPYFROM    "copyfrom"
-#define HEADER_COPYROOT    "copyroot"
-#define HEADER_FRESHTXNRT  "is-fresh-txn-root"
-#define HEADER_MINFO_HERE  "minfo-here"
-#define HEADER_MINFO_CNT   "minfo-cnt"
-
-/* Kinds that a change can be. */
-#define ACTION_MODIFY      "modify"
-#define ACTION_ADD         "add"
-#define ACTION_DELETE      "delete"
-#define ACTION_REPLACE     "replace"
-#define ACTION_RESET       "reset"
-
-/* True and False flags. */
-#define FLAG_TRUE          "true"
-#define FLAG_FALSE         "false"
-
-/* Kinds that a node-rev can be. */
-#define KIND_FILE          "file"
-#define KIND_DIR           "dir"
-
-/* Kinds of representation. */
-#define REP_PLAIN          "PLAIN"
-#define REP_DELTA          "DELTA"
-
-/* Cookies used to classify cached file handle usage */
-/* Used whenever no other specific region of the rev file is being read. */
-#define DEFAULT_FILE_COOKIE 0
-
-/* Used when reading representation data.
- * Since this is often interleaved with other reads, use a separate 
- * cookie (hence a separate file handle) for the reps.  That way, rep
- * access can often be satisfied from the APR read buffer.  The same
- * applies to the meta data because it is not rep data. */
-#define REP_FILE_COOKIE     1
-
-/* Notes:
-
-To avoid opening and closing the rev-files all the time, it would
-probably be advantageous to keep each rev-file open for the
-lifetime of the transaction object.  I'll leave that as a later
-optimization for now.
-
-I didn't keep track of pool lifetimes at all in this code.  There
-are likely some errors because of that.
-
-*/
-
-/* The vtable associated with an open transaction object. */
-static txn_vtable_t txn_vtable = {
-  svn_fs_fs__commit_txn,
-  svn_fs_fs__commit_obliteration_txn,
-  svn_fs_fs__abort_txn,
-  svn_fs_fs__txn_prop,
-  svn_fs_fs__txn_proplist,
-  svn_fs_fs__change_txn_prop,
-  svn_fs_fs__txn_root,
-  svn_fs_fs__change_txn_props
-};
-
-/* SQL bits for revprops. */
-REVPROPS_DB_SQL_DECLARE_STATEMENTS(statements);
-
-/* Declarations. */
-
-static svn_error_t *
-read_min_unpacked_rev(svn_revnum_t *min_unpacked_rev,
-                      const char *path,
-                      apr_pool_t *pool);
-
-static svn_error_t *
-update_min_unpacked_rev(svn_fs_t *fs, apr_pool_t *pool);
-
-/* Pathname helper functions */
-
-/* Return TRUE is REV is packed in FS, FALSE otherwise. */
-static svn_boolean_t
-is_packed_rev(svn_fs_t *fs, svn_revnum_t rev)
-{
-  fs_fs_data_t *ffd = fs->fsap_data;
-
-  return (rev < ffd->min_unpacked_rev);
-}
-
-static const char *
-path_format(svn_fs_t *fs, apr_pool_t *pool)
-{
-  return svn_dirent_join(fs->path, PATH_FORMAT, pool);
-}
-
-static APR_INLINE const char *
-path_uuid(svn_fs_t *fs, apr_pool_t *pool)
-{
-  return svn_dirent_join(fs->path, PATH_UUID, pool);
-}
-
-const char *
-svn_fs_fs__path_current(svn_fs_t *fs, apr_pool_t *pool)
-{
-  return svn_dirent_join(fs->path, PATH_CURRENT, pool);
-}
-
-static APR_INLINE const char *
-path_txn_current(svn_fs_t *fs, apr_pool_t *pool)
-{
-  return svn_dirent_join(fs->path, PATH_TXN_CURRENT, pool);
-}
-
-static APR_INLINE const char *
-path_txn_current_lock(svn_fs_t *fs, apr_pool_t *pool)
-{
-  return svn_dirent_join(fs->path, PATH_TXN_CURRENT_LOCK, pool);
-}
-
-static APR_INLINE const char *
-path_lock(svn_fs_t *fs, apr_pool_t *pool)
-{
-  return svn_dirent_join(fs->path, PATH_LOCK_FILE, pool);
-}
-
-static const char *
-path_rev_packed(svn_fs_t *fs, svn_revnum_t rev, const char *kind,
-                apr_pool_t *pool)
-{
-  fs_fs_data_t *ffd = fs->fsap_data;
-
-  assert(ffd->max_files_per_dir);
-  assert(is_packed_rev(fs, rev));
-
-  return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
-                              apr_psprintf(pool, "%ld.pack",
-                                           rev / ffd->max_files_per_dir),
-                              kind, NULL);
-}
-
-static const char *
-path_rev_shard(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool)
-{
-  fs_fs_data_t *ffd = fs->fsap_data;
-
-  assert(ffd->max_files_per_dir);
-  return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
-                              apr_psprintf(pool, "%ld",
-                                                 rev / ffd->max_files_per_dir),
-                              NULL);
-}
-
-static const char *
-path_rev(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool)
-{
-  fs_fs_data_t *ffd = fs->fsap_data;
-
-  assert(! is_packed_rev(fs, rev));
-
-  if (ffd->max_files_per_dir)
-    {
-      return svn_dirent_join(path_rev_shard(fs, rev, pool),
-                             apr_psprintf(pool, "%ld", rev),
-                             pool);
-    }
-
-  return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR,
-                              apr_psprintf(pool, "%ld", rev), NULL);
-}
-
-/* Returns the path of REV in FS, whether in a pack file or not.
-   Allocate in POOL. */
-svn_error_t *
-svn_fs_fs__path_rev_absolute(const char **path,
-                             svn_fs_t *fs,
-                             svn_revnum_t rev,
-                             apr_pool_t *pool)
-{
-  if (! is_packed_rev(fs, rev))
-    {
-      fs_fs_data_t *ffd = fs->fsap_data;
-      svn_node_kind_t kind;
-
-      /* Initialize the return variable. */
-      *path = path_rev(fs, rev, pool);
-
-      /* quick check the path. For revs close to HEAD, this will often
-       * be effective (and, hence, efficient). */
-      if (svn_file_handle_cache__has_file(ffd->file_handle_cache, *path))
-        return SVN_NO_ERROR;
-
-      /* the expensive standard lookup check */
-      SVN_ERR(svn_io_check_path(*path, &kind, pool));
-      if (kind == svn_node_file)
-        {
-          /* *path is already set correctly. */
-          return SVN_NO_ERROR;
-        }
-      else
-        {
-          /* Someone must have run 'svnadmin pack' while this fs object
-           * was open. */
-
-          SVN_ERR(update_min_unpacked_rev(fs, pool));
-
-          /* The rev really should be present now. */
-          if (! is_packed_rev(fs, rev))
-            return svn_error_createf(APR_ENOENT, NULL,
-                                     _("Revision file '%s' does not exist, "
-                                       "and r%ld is not packed"),
-                                     svn_dirent_local_style(*path, pool),
-                                     rev);
-          /* Fall through. */
-        }
-    }
-
-  *path = path_rev_packed(fs, rev, "pack", pool);
-
-  return SVN_NO_ERROR;
-}
-
-static const char *
-path_revprops_shard(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool)
-{
-  fs_fs_data_t *ffd = fs->fsap_data;
-
-  assert(ffd->max_files_per_dir);
-  return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR,
-                              apr_psprintf(pool, "%ld",
-                                           rev / ffd->max_files_per_dir),
-                              NULL);
-}
-
-static const char *
-path_revprops(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool)
-{
-  fs_fs_data_t *ffd = fs->fsap_data;
-
-  if (ffd->max_files_per_dir)
-    {
-      return svn_dirent_join(path_revprops_shard(fs, rev, pool),
-                             apr_psprintf(pool, "%ld", rev),
-                             pool);
-    }
-
-  return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR,
-                              apr_psprintf(pool, "%ld", rev), NULL);
-}
-
-static APR_INLINE const char *
-path_txn_dir(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
-{
-  return svn_dirent_join_many(pool, fs->path, PATH_TXNS_DIR,
-                              apr_pstrcat(pool, txn_id, PATH_EXT_TXN, NULL),
-                              NULL);
-}
-
-static APR_INLINE const char *
-path_txn_changes(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
-{
-  return svn_dirent_join(path_txn_dir(fs, txn_id, pool), PATH_CHANGES, pool);
-}
-
-static APR_INLINE const char *
-path_txn_props(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
-{
-  return svn_dirent_join(path_txn_dir(fs, txn_id, pool), PATH_TXN_PROPS, pool);
-}
-
-static APR_INLINE const char *
-path_txn_next_ids(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
-{
-  return svn_dirent_join(path_txn_dir(fs, txn_id, pool), PATH_NEXT_IDS, pool);
-}
-
-static APR_INLINE const char *
-path_min_unpacked_rev(svn_fs_t *fs, apr_pool_t *pool)
-{
-  return svn_dirent_join(fs->path, PATH_MIN_UNPACKED_REV, pool);
-}
-
-static APR_INLINE const char *
-path_min_unpacked_revprop(svn_fs_t *fs, apr_pool_t *pool)
-{
-  return svn_dirent_join(fs->path, PATH_MIN_UNPACKED_REVPROP, pool);
-}
-
+#include "private/svn_temp_serializer.h"
 
-static APR_INLINE const char *
-path_txn_proto_rev(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
-{
-  fs_fs_data_t *ffd = fs->fsap_data;
-  if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
-    return svn_dirent_join_many(pool, fs->path, PATH_TXN_PROTOS_DIR,
-                                apr_pstrcat(pool, txn_id, PATH_EXT_REV, NULL),
-                                NULL);
-  else
-    return svn_dirent_join(path_txn_dir(fs, txn_id, pool), PATH_REV, pool);
-}
+#include "temp_serializer.h"
 
-static APR_INLINE const char *
-path_txn_proto_rev_lock(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
+/* Utility to encode a signed NUMBER into a variable-length sequence of 
+ * 8-bit chars in KEY_BUFFER and return the last writen position.
+ *
+ * Numbers will be stored in 7 bits / byte and using byte values above 
+ * 32 (' ') to make them combinable with other string by simply separating 
+ * individual parts with spaces.
+ */
+static char*
+encode_number(apr_int64_t number, char *key_buffer)
 {
-  fs_fs_data_t *ffd = fs->fsap_data;
-  if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
-    return svn_dirent_join_many(pool, fs->path, PATH_TXN_PROTOS_DIR,
-                                apr_pstrcat(pool, txn_id, PATH_EXT_REV_LOCK,
-                                            NULL),
-                                NULL);
+  /* encode the sign in the first byte */
+  if (number < 0)
+  {
+    number = -number;
+    *key_buffer = (number & 63) + ' ' + 65;
+  }
   else
-    return svn_dirent_join(path_txn_dir(fs, txn_id, pool), PATH_REV_LOCK,
-                           pool);
-}
-
-static const char *
-path_txn_node_rev(svn_fs_t *fs, const svn_fs_id_t *id, apr_pool_t *pool)
-{
-  const char *txn_id = svn_fs_fs__id_txn_id(id);
-  const char *node_id = svn_fs_fs__id_node_id(id);
-  const char *copy_id = svn_fs_fs__id_copy_id(id);
-  const char *name = apr_psprintf(pool, PATH_PREFIX_NODE "%s.%s",
-                                  node_id, copy_id);
+    *key_buffer = (number & 63) + ' ' + 1;
+  number /= 64;
 
-  return svn_dirent_join(path_txn_dir(fs, txn_id, pool), name, pool);
-}
-
-static APR_INLINE const char *
-path_txn_node_props(svn_fs_t *fs, const svn_fs_id_t *id, apr_pool_t *pool)
-{
-  return apr_pstrcat(pool, path_txn_node_rev(fs, id, pool), PATH_EXT_PROPS,
-                     NULL);
-}
-
-static APR_INLINE const char *
-path_txn_node_children(svn_fs_t *fs, const svn_fs_id_t *id, apr_pool_t *pool)
-{
-  return apr_pstrcat(pool, path_txn_node_rev(fs, id, pool),
-                     PATH_EXT_CHILDREN, NULL);
-}
+  /* write 7 bits / byte until no significant bits are left */
+  while (number)
+  {
+    *++key_buffer = (number & 127) + ' ' + 1;
+    number /= 128;
+  }
 
-static APR_INLINE const char *
-path_node_origin(svn_fs_t *fs, const char *node_id, apr_pool_t *pool)
-{
-  size_t len = strlen(node_id);
-  const char *node_id_minus_last_char =
-    (len == 1) ? "0" : apr_pstrmemdup(pool, node_id, len - 1);
-  return svn_dirent_join_many(pool, fs->path, PATH_NODE_ORIGINS_DIR,
-                              node_id_minus_last_char, NULL);
+  /* return the last written position */
+  return key_buffer;
 }
 
-
-/* Functions for working with shared transaction data. */
-
-/* Return the transaction object for transaction TXN_ID from the
-   transaction list of filesystem FS (which must already be locked via the
-   txn_list_lock mutex).  If the transaction does not exist in the list,
-   then create a new transaction object and return it (if CREATE_NEW is
-   true) or return NULL (otherwise). */
-static fs_fs_shared_txn_data_t *
-get_shared_txn(svn_fs_t *fs, const char *txn_id, svn_boolean_t create_new)
+/* Prepend the NUMBER to the STRING in a space efficient way that no other
+ * (number,string) combination can produce the same result. 
+ * Allocate temporaries as well as the result from POOL.
+ */
+const char*
+svn_fs_fs__combine_number_and_string(apr_int64_t number,
+                                     const char *string,
+                                     apr_pool_t *pool)
+{
+  apr_size_t len = strlen(string);
+
+  /* number part requires max. 10x7 bits + 1 space. 
+   * Add another 1 for the terminal 0 */
+  char *key_buffer = apr_palloc(pool, len + 12);
+  const char *key = key_buffer;
+
+  /* Prepend the number to the string and separate them by space. No other 
+   * number can result in the same prefix, no other string in the same 
+   * postfix nor can the boundary between them be ambiguous. */
+  key_buffer = encode_number(number, key_buffer);
+  *++key_buffer = ' ';
+  memcpy(++key_buffer, string, len+1);
+
+  /* return the start of the key */
+  return key;
+}
+
+/* Combine the numbers A and B a space efficient way that no other
+ * combination of numbers can produce the same result.
+ * Allocate temporaries as well as the result from POOL.
+ */
+const char*
+svn_fs_fs__combine_two_numbers(apr_int64_t a,
+                               apr_int64_t b,
+                               apr_pool_t *pool)
 {
-  fs_fs_data_t *ffd = fs->fsap_data;
-  fs_fs_shared_data_t *ffsd = ffd->shared;
-  fs_fs_shared_txn_data_t *txn;
-
-  for (txn = ffsd->txns; txn; txn = txn->next)
-    if (strcmp(txn->txn_id, txn_id) == 0)
-      break;
-
-  if (txn || !create_new)
-    return txn;
-
-  /* Use the transaction object from the (single-object) freelist,
-     if one is available, or otherwise create a new object. */
-  if (ffsd->free_txn)
-    {
-      txn = ffsd->free_txn;
-      ffsd->free_txn = NULL;
-    }
-  else
-    {
-      apr_pool_t *subpool = svn_pool_create(ffsd->common_pool);
-      txn = apr_palloc(subpool, sizeof(*txn));
-      txn->pool = subpool;
-    }
-
-  assert(strlen(txn_id) < sizeof(txn->txn_id));
-  apr_cpystrn(txn->txn_id, txn_id, sizeof(txn->txn_id));
-  txn->being_written = FALSE;
-
-  /* Link this transaction into the head of the list.  We will typically
-     be dealing with only one active transaction at a time, so it makes
-     sense for searches through the transaction list to look at the
-     newest transactions first.  */
-  txn->next = ffsd->txns;
-  ffsd->txns = txn;
+  /* encode numbers as 2x 10x7 bits + 1 space + 1 terminating \0*/
+  char *key_buffer = apr_palloc(pool, 22);
+  const char *key = key_buffer;
+
+  /* combine the numbers. Since the separator is disjoint from any part
+   * of the encoded numbers, there is no other combination that can yield
+   * the same result */
+  key_buffer = encode_number(a, key_buffer);
+  *++key_buffer = ' ';
+  key_buffer = encode_number(b, ++key_buffer);
+  *++key_buffer = '\0';
 
-  return txn;
+  /* return the start of the key */
+  return key;
 }
 
-/* Free the transaction object for transaction TXN_ID, and remove it
-   from the transaction list of filesystem FS (which must already be
-   locked via the txn_list_lock mutex).  Do nothing if the transaction
-   does not exist. */
+/* Utility function to serialize string S in the given serialization CONTEXT.
+ */
 static void
-free_shared_txn(svn_fs_t *fs, const char *txn_id)
+serialize_svn_string(svn_temp_serializer__context_t *context,
+                     const svn_string_t * const *s)
 {
-  fs_fs_data_t *ffd = fs->fsap_data;
-  fs_fs_shared_data_t *ffsd = ffd->shared;
-  fs_fs_shared_txn_data_t *txn, *prev = NULL;
-
-  for (txn = ffsd->txns; txn; prev = txn, txn = txn->next)
-    if (strcmp(txn->txn_id, txn_id) == 0)
-      break;
+  const svn_string_t *string = *s;
 
-  if (!txn)
+  /* Nothing to do for NULL string references. */
+  if (string == NULL)
     return;
 
-  if (prev)
-    prev->next = txn->next;
-  else
-    ffsd->txns = txn->next;
-
-  /* As we typically will be dealing with one transaction after another,
-     we will maintain a single-object free list so that we can hopefully
-     keep reusing the same transaction object. */
-  if (!ffsd->free_txn)
-    ffsd->free_txn = txn;
-  else
-    svn_pool_destroy(txn->pool);
-}
-
-
-/* Obtain a lock on the transaction list of filesystem FS, call BODY
-   with FS, BATON, and POOL, and then unlock the transaction list.
-   Return what BODY returned. */
-static svn_error_t *
-with_txnlist_lock(svn_fs_t *fs,
-                  svn_error_t *(*body)(svn_fs_t *fs,
-                                       const void *baton,
-                                       apr_pool_t *pool),
-                  const void *baton,
-                  apr_pool_t *pool)
-{
-  svn_error_t *err;
-#if APR_HAS_THREADS
-  fs_fs_data_t *ffd = fs->fsap_data;
-  fs_fs_shared_data_t *ffsd = ffd->shared;
-  apr_status_t apr_err;
-
-  apr_err = apr_thread_mutex_lock(ffsd->txn_list_lock);
-  if (apr_err)
-    return svn_error_wrap_apr(apr_err, _("Can't grab FSFS txn list mutex"));
-#endif
-
-  err = body(fs, baton, pool);
-
-#if APR_HAS_THREADS
-  apr_err = apr_thread_mutex_unlock(ffsd->txn_list_lock);
-  if (apr_err && !err)
-    return svn_error_wrap_apr(apr_err, _("Can't ungrab FSFS txn list mutex"));
-#endif
-
-  return svn_error_return(err);
-}
-
-
-/* Get a lock on empty file LOCK_FILENAME, creating it in POOL. */
-static svn_error_t *
-get_lock_on_filesystem(const char *lock_filename,
-                       apr_pool_t *pool)
-{
-  svn_error_t *err = svn_io_file_lock2(lock_filename, TRUE, FALSE, pool);
-
-  if (err && APR_STATUS_IS_ENOENT(err->apr_err))
-    {
-      /* No lock file?  No big deal; these are just empty files
-         anyway.  Create it and try again. */
-      svn_error_clear(err);
-      err = NULL;
-
-      SVN_ERR(svn_io_file_create(lock_filename, "", pool));
-      SVN_ERR(svn_io_file_lock2(lock_filename, TRUE, FALSE, pool));
-    }
-
-  return svn_error_return(err);
-}
-
-/* Obtain a write lock on the file LOCK_FILENAME (protecting with
-   LOCK_MUTEX if APR is threaded) in a subpool of POOL, call BODY with
-   BATON and that subpool, destroy the subpool (releasing the write
-   lock) and return what BODY returned. */
-static svn_error_t *
-with_some_lock(svn_error_t *(*body)(void *baton,
-                                    apr_pool_t *pool),
-               void *baton,
-               const char *lock_filename,
-#if SVN_FS_FS__USE_LOCK_MUTEX
-               apr_thread_mutex_t *lock_mutex,
-#endif
-               apr_pool_t *pool)
-{
-  apr_pool_t *subpool = svn_pool_create(pool);
-  svn_error_t *err;
-
-#if SVN_FS_FS__USE_LOCK_MUTEX
-  apr_status_t status;
-
-  /* POSIX fcntl locks are per-process, so we need to serialize locks
-     within the process. */
-  status = apr_thread_mutex_lock(lock_mutex);
-  if (status)
-    return svn_error_wrap_apr(status,
-                              _("Can't grab FSFS mutex for '%s'"),
-                              lock_filename);
-#endif
-
-  err = get_lock_on_filesystem(lock_filename, subpool);
-
-  if (!err)
-    err = body(baton, subpool);
-
-  svn_pool_destroy(subpool);
-
-#if SVN_FS_FS__USE_LOCK_MUTEX
-  status = apr_thread_mutex_unlock(lock_mutex);
-  if (status && !err)
-    return svn_error_wrap_apr(status,
-                              _("Can't ungrab FSFS mutex for '%s'"),
-                              lock_filename);
-#endif
-
-  return svn_error_return(err);
-}
-
-svn_error_t *
-svn_fs_fs__with_write_lock(svn_fs_t *fs,
-                           svn_error_t *(*body)(void *baton,
-                                                apr_pool_t *pool),
-                           void *baton,
-                           apr_pool_t *pool)
-{
-#if SVN_FS_FS__USE_LOCK_MUTEX
-  fs_fs_data_t *ffd = fs->fsap_data;
-  fs_fs_shared_data_t *ffsd = ffd->shared;
-  apr_thread_mutex_t *mutex = ffsd->fs_write_lock;
-#endif
-
-  return with_some_lock(body, baton,
-                        path_lock(fs, pool),
-#if SVN_FS_FS__USE_LOCK_MUTEX
-                        mutex,
-#endif
-                        pool);
-}
-
-/* Run BODY (with BATON and POOL) while the txn-current file
-   of FS is locked. */
-static svn_error_t *
-with_txn_current_lock(svn_fs_t *fs,
-                      svn_error_t *(*body)(void *baton,
-                                           apr_pool_t *pool),
-                      void *baton,
-                      apr_pool_t *pool)
-{
-#if SVN_FS_FS__USE_LOCK_MUTEX
-  fs_fs_data_t *ffd = fs->fsap_data;
-  fs_fs_shared_data_t *ffsd = ffd->shared;
-  apr_thread_mutex_t *mutex = ffsd->txn_current_lock;
-#endif
-
-  return with_some_lock(body, baton,
-                        path_txn_current_lock(fs, pool),
-#if SVN_FS_FS__USE_LOCK_MUTEX
-                        mutex,
-#endif
-                        pool);
+  svn_temp_serializer__push(context,
+                            (const void * const *)s,
+                            sizeof(*string));
+
+  /* the "string" content may actually be arbitrary binary data.
+   * Thus, we cannot use svn_temp_serializer__add_string. */
+  svn_temp_serializer__push(context,
+                            (const void * const *)&string->data,
+                            string->len);
+
+  /* back to the caller's nesting level */
+  svn_temp_serializer__pop(context);
+  svn_temp_serializer__pop(context);
 }
 
-/* A frequently used utility method: close all cached, idle file handles.
- * Call this at the end of write transactions to ensure that successive
- * reads will see the new file content.
+/* Utility function to deserialize the STRING inside the BUFFER.
  */
-static svn_error_t *
-sync_file_handle_cache(svn_fs_t *fs)
-{
-  fs_fs_data_t *ffd = fs->fsap_data;
-  return svn_file_handle_cache__flush(ffd->file_handle_cache);
-}
-
-/* A structure used by unlock_proto_rev() and unlock_proto_rev_body(),
-   which see. */
-struct unlock_proto_rev_baton
-{
-  const char *txn_id;
-  void *lockcookie;
-};
-
-/* Callback used in the implementation of unlock_proto_rev(). */
-static svn_error_t *
-unlock_proto_rev_body(svn_fs_t *fs, const void *baton, apr_pool_t *pool)
-{
-  const struct unlock_proto_rev_baton *b = baton;
-  const char *txn_id = b->txn_id;
-  apr_file_t *lockfile = b->lockcookie;
-  fs_fs_shared_txn_data_t *txn = get_shared_txn(fs, txn_id, FALSE);
-  apr_status_t apr_err;
-
-  if (!txn)
-    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
-                             _("Can't unlock unknown transaction '%s'"),
-                             txn_id);
-  if (!txn->being_written)
-    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
-                             _("Can't unlock nonlocked transaction '%s'"),
-                             txn_id);
-
-  apr_err = apr_file_unlock(lockfile);
-  if (apr_err)
-    return svn_error_wrap_apr
-      (apr_err,
-       _("Can't unlock prototype revision lockfile for transaction '%s'"),
-       txn_id);
-  apr_err = apr_file_close(lockfile);
-  if (apr_err)
-    return svn_error_wrap_apr
-      (apr_err,
-       _("Can't close prototype revision lockfile for transaction '%s'"),
-       txn_id);
-
-  txn->being_written = FALSE;
-
-  return SVN_NO_ERROR;
-}
-
-/* Unlock the prototype revision file for transaction TXN_ID in filesystem
-   FS using cookie LOCKCOOKIE.  The original prototype revision file must
-   have been closed _before_ calling this function.
-
-   Perform temporary allocations in POOL. */
-static svn_error_t *
-unlock_proto_rev(svn_fs_t *fs, const char *txn_id, void *lockcookie,
-                 apr_pool_t *pool)
-{
-  struct unlock_proto_rev_baton b;
-
-  b.txn_id = txn_id;
-  b.lockcookie = lockcookie;
-  return with_txnlist_lock(fs, unlock_proto_rev_body, &b, pool);
-}
-
-/* Same as unlock_proto_rev(), but requires that the transaction list
-   lock is already held. */
-static svn_error_t *
-unlock_proto_rev_list_locked(svn_fs_t *fs, const char *txn_id,
-                             void *lockcookie,
-                             apr_pool_t *pool)
-{
-  struct unlock_proto_rev_baton b;
-
-  b.txn_id = txn_id;
-  b.lockcookie = lockcookie;
-  return unlock_proto_rev_body(fs, &b, pool);
-}
-
-/* A structure used by get_writable_proto_rev() and
-   get_writable_proto_rev_body(), which see. */
-struct get_writable_proto_rev_baton
-{
-  apr_file_t **file;
-  void **lockcookie;
-  const char *txn_id;
-};
-
-/* Callback used in the implementation of get_writable_proto_rev(). */
-static svn_error_t *
-get_writable_proto_rev_body(svn_fs_t *fs, const void *baton, apr_pool_t *pool)
-{
-  const struct get_writable_proto_rev_baton *b = baton;
-  apr_file_t **file = b->file;
-  void **lockcookie = b->lockcookie;
-  const char *txn_id = b->txn_id;
-  svn_error_t *err;
-  fs_fs_shared_txn_data_t *txn = get_shared_txn(fs, txn_id, TRUE);
-
-  /* First, ensure that no thread in this process (including this one)
-     is currently writing to this transaction's proto-rev file. */
-  if (txn->being_written)
-    return svn_error_createf(SVN_ERR_FS_REP_BEING_WRITTEN, NULL,
-                             _("Cannot write to the prototype revision file "
-                               "of transaction '%s' because a previous "
-                               "representation is currently being written by "
-                               "this process"),
-                             txn_id);
-
-
-  /* We know that no thread in this process is writing to the proto-rev
-     file, and by extension, that no thread in this process is holding a
-     lock on the prototype revision lock file.  It is therefore safe
-     for us to attempt to lock this file, to see if any other process
-     is holding a lock. */
-
-  {
-    apr_file_t *lockfile;
-    apr_status_t apr_err;
-    const char *lockfile_path = path_txn_proto_rev_lock(fs, txn_id, pool);
-
-    /* Open the proto-rev lockfile, creating it if necessary, as it may
-       not exist if the transaction dates from before the lockfiles were
-       introduced.
-
-       ### We'd also like to use something like svn_io_file_lock2(), but
-           that forces us to create a subpool just to be able to unlock
-           the file, which seems a waste. */
-    SVN_ERR(svn_io_file_open(&lockfile, lockfile_path,
-                             APR_WRITE | APR_CREATE, APR_OS_DEFAULT, pool));
-
-    apr_err = apr_file_lock(lockfile,
-                            APR_FLOCK_EXCLUSIVE | APR_FLOCK_NONBLOCK);
-    if (apr_err)
-      {
-        svn_error_clear(svn_io_file_close(lockfile, pool));
-
-        if (APR_STATUS_IS_EAGAIN(apr_err))
-          return svn_error_createf(SVN_ERR_FS_REP_BEING_WRITTEN, NULL,
-                                   _("Cannot write to the prototype revision "
-                                     "file of transaction '%s' because a "
-                                     "previous representation is currently "
-                                     "being written by another process"),
-                                   txn_id);
-
-        return svn_error_wrap_apr(apr_err,
-                                  _("Can't get exclusive lock on file '%s'"),
-                                  svn_dirent_local_style(lockfile_path, pool));
-      }
-
-    *lockcookie = lockfile;
-  }
-
-  /* We've successfully locked the transaction; mark it as such. */
-  txn->being_written = TRUE;
-
-
-  /* Now open the prototype revision file and seek to the end. */
-  err = svn_io_file_open(file, path_txn_proto_rev(fs, txn_id, pool),
-                         APR_WRITE | APR_BUFFERED, APR_OS_DEFAULT, pool);
-
-  /* You might expect that we could dispense with the following seek
-     and achieve the same thing by opening the file using APR_APPEND.
-     Unfortunately, APR's buffered file implementation unconditionally
-     places its initial file pointer at the start of the file (even for
-     files opened with APR_APPEND), so we need this seek to reconcile
-     the APR file pointer to the OS file pointer (since we need to be
-     able to read the current file position later). */
-  if (!err)
-    {
-      apr_off_t offset = 0;
-      err = svn_io_file_seek(*file, APR_END, &offset, pool);
-    }
-
-  if (err)
-    {
-      svn_error_clear(unlock_proto_rev_list_locked(fs, txn_id, *lockcookie,
-                                                   pool));
-      *lockcookie = NULL;
-    }
-
-  return svn_error_return(err);
-}
-
-/* Get a handle to the prototype revision file for transaction TXN_ID in
-   filesystem FS, and lock it for writing.  Return FILE, a file handle
-   positioned at the end of the file, and LOCKCOOKIE, a cookie that
-   should be passed to unlock_proto_rev() to unlock the file once FILE
-   has been closed.
-
-   If the prototype revision file is already locked, return error
-   SVN_ERR_FS_REP_BEING_WRITTEN.
-
-   Perform all allocations in POOL. */
-static svn_error_t *
-get_writable_proto_rev(apr_file_t **file,
-                       void **lockcookie,
-                       svn_fs_t *fs, const char *txn_id,
-                       apr_pool_t *pool)
-{
-  struct get_writable_proto_rev_baton b;
-
-  b.file = file;
-  b.lockcookie = lockcookie;
-  b.txn_id = txn_id;
-
-  return with_txnlist_lock(fs, get_writable_proto_rev_body, &b, pool);
-}
-
-/* Callback used in the implementation of purge_shared_txn(). */
-static svn_error_t *
-purge_shared_txn_body(svn_fs_t *fs, const void *baton, apr_pool_t *pool)
-{
-  const char *txn_id = baton;
-
-  free_shared_txn(fs, txn_id);
-  return SVN_NO_ERROR;
-}
-
-/* Purge the shared data for transaction TXN_ID in filesystem FS.
-   Perform all allocations in POOL. */
-static svn_error_t *
-purge_shared_txn(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
-{
-  return with_txnlist_lock(fs, purge_shared_txn_body, txn_id, pool);
-}
-
-
-
-/* Fetch the current offset of FILE into *OFFSET_P. */
-static svn_error_t *
-get_file_offset(apr_off_t *offset_p, apr_file_t *file, apr_pool_t *pool)
-{
-  apr_off_t offset;
-
-  /* Note that, for buffered files, one (possibly surprising) side-effect
-     of this call is to flush any unwritten data to disk. */
-  offset = 0;
-  SVN_ERR(svn_io_file_seek(file, APR_CUR, &offset, pool));
-  *offset_p = offset;
-
-  return SVN_NO_ERROR;
-}
-
-
-/* Check that BUF, a buffer of text from format file PATH, contains
-   only digits, raising error SVN_ERR_BAD_VERSION_FILE_FORMAT if not.
-
-   Uses POOL for temporary allocation. */
-static svn_error_t *
-check_format_file_buffer_numeric(const char *buf, const char *path,
-                                 apr_pool_t *pool)
-{
-  const char *p;
-
-  for (p = buf; *p; p++)
-    if (!apr_isdigit(*p))
-      return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
-        _("Format file '%s' contains an unexpected non-digit"),
-        svn_dirent_local_style(path, pool));
-
-  return SVN_NO_ERROR;
-}
-
-/* Read the format number and maximum number of files per directory
-   from PATH and return them in *PFORMAT and *MAX_FILES_PER_DIR
-   respectively.
-
-   *MAX_FILES_PER_DIR is obtained from the 'layout' format option, and
-   will be set to zero if a linear scheme should be used.
-
-   Use POOL for temporary allocation. */
-static svn_error_t *
-read_format(int *pformat, int *max_files_per_dir,
-            const char *path, apr_pool_t *pool)
-{
-  svn_error_t *err;
-  apr_file_t *file;
-  char buf[80];
-  apr_size_t len;
-
-  err = svn_io_file_open(&file, path, APR_READ | APR_BUFFERED,
-                         APR_OS_DEFAULT, pool);
-  if (err && APR_STATUS_IS_ENOENT(err->apr_err))
-    {
-      /* Treat an absent format file as format 1.  Do not try to
-         create the format file on the fly, because the repository
-         might be read-only for us, or this might be a read-only
-         operation, and the spirit of FSFS is to make no changes
-         whatseover in read-only operations.  See thread starting at
-         http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=97600
-         for more. */
-      svn_error_clear(err);
-      *pformat = 1;
-      *max_files_per_dir = 0;
-
-      return SVN_NO_ERROR;
-    }
-  SVN_ERR(err);
-
-  len = sizeof(buf);
-  err = svn_io_read_length_line(file, buf, &len, pool);
-  if (err && APR_STATUS_IS_EOF(err->apr_err))
-    {
-      /* Return a more useful error message. */
-      svn_error_clear(err);
-      return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
-                               _("Can't read first line of format file '%s'"),
-                               svn_dirent_local_style(path, pool));
-    }
-  SVN_ERR(err);
-
-  /* Check that the first line contains only digits. */
-  SVN_ERR(check_format_file_buffer_numeric(buf, path, pool));
-  *pformat = atoi(buf);
-
-  /* Set the default values for anything that can be set via an option. */
-  *max_files_per_dir = 0;
-
-  /* Read any options. */
-  while (1)
-    {
-      len = sizeof(buf);
-      err = svn_io_read_length_line(file, buf, &len, pool);
-      if (err && APR_STATUS_IS_EOF(err->apr_err))
-        {
-          /* No more options; that's okay. */
-          svn_error_clear(err);
-          break;
-        }
-      SVN_ERR(err);
-
-      if (*pformat >= SVN_FS_FS__MIN_LAYOUT_FORMAT_OPTION_FORMAT &&
-          strncmp(buf, "layout ", 7) == 0)
-        {
-          if (strcmp(buf+7, "linear") == 0)
-            {
-              *max_files_per_dir = 0;
-              continue;
-            }
-
-          if (strncmp(buf+7, "sharded ", 8) == 0)
-            {
-              /* Check that the argument is numeric. */
-              SVN_ERR(check_format_file_buffer_numeric(buf+15, path, pool));
-              *max_files_per_dir = atoi(buf+15);
-              continue;
-            }
-        }
-
-      return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
-         _("'%s' contains invalid filesystem format option '%s'"),
-         svn_dirent_local_style(path, pool), buf);
-    }
-
-  return svn_io_file_close(file, pool);
-}
-
-/* Write the format number and maximum number of files per directory
-   to a new format file in PATH, possibly expecting to overwrite a
-   previously existing file.
-
-   Use POOL for temporary allocation. */
-static svn_error_t *
-write_format(const char *path, int format, int max_files_per_dir,
-             svn_boolean_t overwrite, apr_pool_t *pool)
-{
-  svn_stringbuf_t *sb;
-  svn_string_t *contents;
-
-  SVN_ERR_ASSERT(1 <= format && format <= SVN_FS_FS__FORMAT_NUMBER);
-
-  sb = svn_stringbuf_createf(pool, "%d\n", format);
-
-  if (format >= SVN_FS_FS__MIN_LAYOUT_FORMAT_OPTION_FORMAT)
-    {
-      if (max_files_per_dir)
-        svn_stringbuf_appendcstr(sb, apr_psprintf(pool, "layout sharded %d\n",
-                                                  max_files_per_dir));
-      else
-        svn_stringbuf_appendcstr(sb, "layout linear\n");
-    }
-
-  contents = svn_string_create_from_buf(sb, pool);
-
-  /* svn_io_write_version_file() does a load of magic to allow it to
-     replace version files that already exist.  We only need to do
-     that when we're allowed to overwrite an existing file. */
-  if (! overwrite)
-    {
-      /* Create the file */
-      SVN_ERR(svn_io_file_create(path, contents->data, pool));
-    }
-  else
-    {
-      const char *path_tmp;
-
-      SVN_ERR(svn_io_write_unique(&path_tmp,
-                                  svn_dirent_dirname(path, pool),
-                                  contents->data, contents->len,
-                                  svn_io_file_del_none, pool));
-
-      /* rename the temp file as the real destination */
-      SVN_ERR(svn_io_file_rename(path_tmp, path, pool));
-    }
-
-  /* And set the perms to make it read only */
-  return svn_io_set_file_read_only(path, FALSE, pool);
-}
-
-/* Return the error SVN_ERR_FS_UNSUPPORTED_FORMAT if FS's format
-   number is not the same as a format number supported by this
-   Subversion. */
-static svn_error_t *
-check_format(int format)
-{
-  /* We support all formats from 1-current simultaneously */
-  if (1 <= format && format <= SVN_FS_FS__FORMAT_NUMBER)
-    return SVN_NO_ERROR;
-
-  return svn_error_createf(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL,
-     _("Expected FS format between '1' and '%d'; found format '%d'"),
-     SVN_FS_FS__FORMAT_NUMBER, format);
-}
-
-svn_boolean_t
-svn_fs_fs__fs_supports_mergeinfo(svn_fs_t *fs)
-{
-  fs_fs_data_t *ffd = fs->fsap_data;
-  return ffd->format >= SVN_FS_FS__MIN_MERGEINFO_FORMAT;
-}
-
-static svn_error_t *
-read_config(svn_fs_t *fs,
-            apr_pool_t *pool)
+static void
+deserialize_svn_string(void *buffer, const svn_string_t **string)
 {
-  fs_fs_data_t *ffd = fs->fsap_data;
-
-  SVN_ERR(svn_config_read(&ffd->config,
-                          svn_dirent_join(fs->path, PATH_CONFIG, pool),
-                          FALSE, fs->pool));
-
-  /* Initialize ffd->rep_sharing_allowed. */
-  if (ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT)
-    SVN_ERR(svn_config_get_bool(ffd->config, &ffd->rep_sharing_allowed,
-                                CONFIG_SECTION_REP_SHARING,
-                                CONFIG_OPTION_ENABLE_REP_SHARING, TRUE));
-  else
-    ffd->rep_sharing_allowed = FALSE;
+  if (*string == NULL)
+    return;
 
-  return SVN_NO_ERROR;
+  svn_temp_deserializer__resolve(buffer, (void **)string);
+  svn_temp_deserializer__resolve(buffer, (void **)&(*string)->data);
 }
 
-static svn_error_t *
-write_config(svn_fs_t *fs,
-             apr_pool_t *pool)
-{
-#define NL APR_EOL_STR
-  static const char * const fsfs_conf_contents =
-"### This file controls the configuration of the FSFS filesystem."           NL
-""                                                                           NL
-"[" SVN_CACHE_CONFIG_CATEGORY_MEMCACHED_SERVERS "]"                          NL
-"### These options name memcached servers used to cache internal FSFS"       NL
-"### data.  See http://www.danga.com/memcached/ for more information on"     NL
-"### memcached.  To use memcached with FSFS, run one or more memcached"      NL
-"### servers, and specify each of them as an option like so:"                NL
-"# first-server = 127.0.0.1:11211"                                           NL
-"# remote-memcached = mymemcached.corp.example.com:11212"                    NL
-"### The option name is ignored; the value is of the form HOST:PORT."        NL
-"### memcached servers can be shared between multiple repositories;"         NL
-"### however, if you do this, you *must* ensure that repositories have"      NL
-"### distinct UUIDs and paths, or else cached data from one repository"      NL
-"### might be used by another accidentally.  Note also that memcached has"   NL
-"### no authentication for reads or writes, so you must ensure that your"    NL
-"### memcached servers are only accessible by trusted users."                NL
-""                                                                           NL
-"[" CONFIG_SECTION_CACHES "]"                                                NL
-"### When a cache-related error occurs, normally Subversion ignores it"      NL
-"### and continues, logging an error if the server is appropriately"         NL
-"### configured (and ignoring it with file:// access).  To make"             NL
-"### Subversion never ignore cache errors, uncomment this line."             NL
-"# " CONFIG_OPTION_FAIL_STOP " = true"                                       NL
-""                                                                           NL
-"[" CONFIG_SECTION_REP_SHARING "]"                                           NL
-"### To conserve space, the filesystem can optionally avoid storing"         NL
-"### duplicate representations.  This comes at a slight cost in"             NL
-"### performance, as maintaining a database of shared representations can"   NL
-"### increase commit times.  The space savings are dependent upon the size"  NL
-"### of the repository, the number of objects it contains and the amount of" NL
-"### duplication between them, usually a function of the branching and"      NL
-"### merging process."                                                       NL
-"###"                                                                        NL
-"### The following parameter enables rep-sharing in the repository.  It can" NL
-"### be switched on and off at will, but for best space-saving results"      NL
-"### should be enabled consistently over the life of the repository."        NL
-"# " CONFIG_OPTION_ENABLE_REP_SHARING " = true"                              NL
-
-;
-#undef NL
-  return svn_io_file_create(svn_dirent_join(fs->path, PATH_CONFIG, pool),
-                            fsfs_conf_contents, pool);
-}
 
-static svn_error_t *
-read_min_unpacked_rev(svn_revnum_t *min_unpacked_rev,
-                      const char *path,
-                      apr_pool_t *pool)
+/* Utility function to serialize COUNT svn_txdelta_op_t objects 
+ * at OPS in the given serialization CONTEXT.
+ */
+static void
+serialize_txdelta_ops(svn_temp_serializer__context_t *context,
+                      const svn_txdelta_op_t * const * ops,
+                      apr_size_t count)
 {
-  char buf[80];
-  apr_file_t *file;
-  apr_size_t len;
-
-  SVN_ERR(svn_io_file_open(&file, path, APR_READ | APR_BUFFERED,
-                           APR_OS_DEFAULT, pool));
-  len = sizeof(buf);
-  SVN_ERR(svn_io_read_length_line(file, buf, &len, pool));
-  SVN_ERR(svn_io_file_close(file, pool));
+  if (*ops == NULL)
+    return;
 
-  *min_unpacked_rev = SVN_STR_TO_REV(buf);
-  return SVN_NO_ERROR;
+  /* the ops form a simple chunk of memory with no further references */
+  svn_temp_serializer__push(context,
+                            (const void * const *)ops,
+                            sizeof(svn_txdelta_op_t[count]));
+  svn_temp_serializer__pop(context);
 }
 
-static svn_error_t *
-update_min_unpacked_rev(svn_fs_t *fs, apr_pool_t *pool)
+/* Utility function to serialize W in the given serialization CONTEXT.
+ */
+static void
+serialize_txdeltawindow(svn_temp_serializer__context_t *context,
+                        svn_txdelta_window_t * const * w)
 {
-  fs_fs_data_t *ffd = fs->fsap_data;
+  svn_txdelta_window_t *window = *w;
 
-  return read_min_unpacked_rev(&ffd->min_unpacked_rev,
-                               path_min_unpacked_rev(fs, pool),
-                               pool);
-}
+  /* serialize the window struct itself */
+  svn_temp_serializer__push(context,
+                            (const void * const *)w,
+                            sizeof(svn_txdelta_window_t));
 
-static svn_error_t *
-update_min_unpacked_revprop(svn_fs_t *fs, apr_pool_t *pool)
-{
-  fs_fs_data_t *ffd = fs->fsap_data;
+  /* serialize its sub-structures */
+  serialize_txdelta_ops(context, &window->ops, window->num_ops);
+  serialize_svn_string(context, &window->new_data);
 
-  return read_min_unpacked_rev(&ffd->min_unpacked_revprop,
-                               path_min_unpacked_revprop(fs, pool),
-                               pool);
+  svn_temp_serializer__pop(context);
 }
 
-static svn_error_t *
-get_youngest(svn_revnum_t *youngest_p, const char *fs_path, apr_pool_t *pool);
-
+/* Implements serialize_fn_t for svn_fs_fs__txdelta_cached_window_t 
+ */
 svn_error_t *
-svn_fs_fs__open(svn_fs_t *fs, const char *path, apr_pool_t *pool)
+svn_fs_fs__serialize_txdelta_window(char **buffer,
+                                    apr_size_t *buffer_size,
+                                    void *item,
+                                    apr_pool_t *pool)
 {
-  fs_fs_data_t *ffd = fs->fsap_data;
-  apr_file_t *uuid_file;
-  int format, max_files_per_dir;
-  char buf[APR_UUID_FORMATTED_LENGTH + 2];
-  apr_size_t limit;
-
-  fs->path = apr_pstrdup(fs->pool, path);
-
-  /* Read the FS format number. */
-  SVN_ERR(read_format(&format, &max_files_per_dir,
-                      path_format(fs, pool), pool));
-
-  /* Now we've got a format number no matter what. */
-  ffd->format = format;
-  ffd->max_files_per_dir = max_files_per_dir;
-  SVN_ERR(check_format(format));
-
-  /* Read in and cache the repository uuid. */
-  SVN_ERR(svn_io_file_open(&uuid_file, path_uuid(fs, pool),
-                           APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool));
-
-  limit = sizeof(buf);
-  SVN_ERR(svn_io_read_length_line(uuid_file, buf, &limit, pool));
-  ffd->uuid = apr_pstrdup(fs->pool, buf);
-
-  SVN_ERR(svn_io_file_close(uuid_file, pool));
-
-  /* Read the min unpacked revision. */
-  if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
-    SVN_ERR(update_min_unpacked_rev(fs, pool));
-
-  /* Read the configuration file. */
-  SVN_ERR(read_config(fs, pool));
-
-  /* Open the revprops db. */
-  if (ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT)
-    {
-      SVN_ERR(update_min_unpacked_revprop(fs, pool));
-
-      SVN_ERR(svn_sqlite__open(&ffd->revprop_db, svn_dirent_join_many(
-                                                    pool, path,
-                                                    PATH_REVPROPS_DIR,
-                                                    PATH_REVPROPS_DB,
-                                                    NULL),
-                               svn_sqlite__mode_readwrite, statements,
-                               0, NULL,
-                               fs->pool, pool));
-    }
+  svn_fs_fs__txdelta_cached_window_t *window_info = item;
+  svn_stringbuf_t *serialized;
 
-  return get_youngest(&(ffd->youngest_rev_cache), path, pool);
-}
+  /* initialize the serialization process and allocate a buffer large
+   * enough to do without the need of re-allocations in most cases. */
+  apr_size_t text_len = window_info->window->new_data
+                      ? window_info->window->new_data->len
+                      : 0;
+  svn_temp_serializer__context_t *context =
+      svn_temp_serializer__init(window_info,
+                                sizeof(*window_info),
+                                500 + text_len,
+                                pool);
 
-/* Wrapper around svn_io_file_create which ignores EEXIST. */
-static svn_error_t *
-create_file_ignore_eexist(const char *file,
-                          const char *contents,
-                          apr_pool_t *pool)
-{
-  svn_error_t *err = svn_io_file_create(file, contents, pool);
-  if (err && APR_STATUS_IS_EEXIST(err->apr_err))
-    {
-      svn_error_clear(err);
-      err = SVN_NO_ERROR;
-    }
-  return svn_error_return(err);
-}
+  /* serialize the sub-structure(s) */
+  serialize_txdeltawindow(context, &window_info->window);
 
-static svn_error_t *
-upgrade_body(void *baton, apr_pool_t *pool)
-{
-  svn_fs_t *fs = baton;
-  fs_fs_data_t *ffd = fs->fsap_data;
-  int format, max_files_per_dir;
-  const char *format_path = path_format(fs, pool);
-
-  /* Read the FS format number and max-files-per-dir setting. */
-  SVN_ERR(read_format(&format, &max_files_per_dir, format_path, pool));
-
-  /* If we're already up-to-date, there's nothing to be done here. */
-  if (format == SVN_FS_FS__FORMAT_NUMBER)
-    return SVN_NO_ERROR;
-
-  /* If our filesystem predates the existance of the 'txn-current
-     file', make that file and its corresponding lock file. */
-  if (format < SVN_FS_FS__MIN_TXN_CURRENT_FORMAT)
-    {
-      SVN_ERR(create_file_ignore_eexist(path_txn_current(fs, pool), "0\n",
-                                        pool));
-      SVN_ERR(create_file_ignore_eexist(path_txn_current_lock(fs, pool), "",
-                                        pool));
-    }
-
-  /* If our filesystem predates the existance of the 'txn-protorevs'
-     dir, make that directory.  */
-  if (format < SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
-    {
-      /* We don't use path_txn_proto_rev() here because it expects
-         we've already bumped our format. */
-      SVN_ERR(svn_io_make_dir_recursively(
-          svn_dirent_join(fs->path, PATH_TXN_PROTOS_DIR, pool), pool));
-    }
-
-  /* If our filesystem is new enough, write the min unpacked rev file. */
-  if (format < SVN_FS_FS__MIN_PACKED_FORMAT)
-    SVN_ERR(svn_io_file_create(path_min_unpacked_rev(fs, pool), "0\n", pool));
-
-  /* If our filesystem is new enough, write the min unpacked revprop file,
-     and create the database. */
-  if (format < SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT)
-    {
-      SVN_ERR(svn_io_file_create(path_min_unpacked_revprop(fs, pool), "0\n",
-                                 pool));
-
-      SVN_ERR(svn_sqlite__open(&ffd->revprop_db, svn_dirent_join_many(
-                                                          pool, fs->path,
-                                                          PATH_REVPROPS_DIR,
-                                                          PATH_REVPROPS_DB,
-                                                          NULL),
-                               svn_sqlite__mode_rwcreate, statements,
-                               0, NULL,
-                               fs->pool, pool));
-      SVN_ERR(svn_sqlite__exec_statements(ffd->revprop_db,
-                                          STMT_CREATE_SCHEMA));
-    }
-
-  /* Bump the format file. */
-  return write_format(format_path, SVN_FS_FS__FORMAT_NUMBER, max_files_per_dir,
-                      TRUE, pool);
-}
+  /* return the serialized result */
+  serialized = svn_temp_serializer__get(context);
 
+  *buffer = serialized->data;
+  *buffer_size = serialized->len;
 
-svn_error_t *
-svn_fs_fs__upgrade(svn_fs_t *fs, apr_pool_t *pool)
-{
-  return svn_fs_fs__with_write_lock(fs, upgrade_body, (void *)fs, pool);
+  return SVN_NO_ERROR;
 }
 
-
-/* SVN_ERR-like macros for dealing with recoverable errors on mutable files
- *
- * Revprops, current, and txn-current files are mutable; that is, they
- * change as part of normal fsfs operation, in constrat to revs files, or
- * the format file, which are written once at create (or upgrade) time.
- * When more than one host writes to the same repository, we will
- * sometimes see these recoverable errors when accesssing these files.
- *
- * These errors all relate to NFS, and thus we only use this retry code if
- * ESTALE is defined.
- *
- ** ESTALE
- *
- * In NFS v3 and under, the server doesn't track opened files.  If you
- * unlink(2) or rename(2) a file held open by another process *on the
- * same host*, that host's kernel typically renames the file to
- * .nfsXXXX and automatically deletes that when it's no longer open,
- * but this behavior is not required.
- *
- * For obvious reasons, this does not work *across hosts*.  No one
- * knows about the opened file; not the server, and not the deleting
- * client.  So the file vanishes, and the reader gets stale NFS file
- * handle.
- *
- ** EIO, ENOENT
- *
- * Some client implementations (at least the 2.6.18.5 kernel that ships
- * with Ubuntu Dapper) sometimes give spurious ENOENT (only on open) or
- * even EIO errors when trying to read these files that have been renamed
- * over on some other host.
- *
- ** Solution
- *
- * Wrap opens and reads of such files with RETRY_RECOVERABLE and
- * closes with IGNORE_RECOVERABLE.  Call these macros within a loop of
- * RECOVERABLE_RETRY_COUNT iterations (though, realistically, the
- * second try will succeed).  Make sure you put a break statement
- * after the close, at the end of your loop.  Immediately after your
- * loop, return err if err.
- *
- * You must initialize err to SVN_NO_ERROR and filehandle to NULL, as
- * these macros do not.
- */
-
-#define RECOVERABLE_RETRY_COUNT 10
-
-#ifdef ESTALE
-#define RETRY_RECOVERABLE(err, filehandle, expr)                \
-  {                                                             \
-    svn_error_clear(err);                                       \
-    err = (expr);                                               \
-    if (err)                                                    \
-      {                                                         \
-        apr_status_t _e = APR_TO_OS_ERROR(err->apr_err);        \
-        if ((_e == ESTALE) || (_e == EIO) || (_e == ENOENT)) {  \
-          if (NULL != filehandle)                               \
-            (void)apr_file_close(filehandle);                   \
-          continue;                                             \
-        }                                                       \
-        return svn_error_return(err);                           \
-      }                                                         \
-  }
-#define IGNORE_RECOVERABLE(err, expr)                           \
-  {                                                             \
-    svn_error_clear(err);                                       \
-    err = (expr);                                               \
-    if (err)                                                    \
-      {                                                         \
-        apr_status_t _e = APR_TO_OS_ERROR(err->apr_err);        \
-        if ((_e != ESTALE) && (_e != EIO))                      \
-          return svn_error_return(err);                         \
-      }                                                         \
-  }
-#else
-#define RETRY_RECOVERABLE(err, filehandle, expr)  SVN_ERR(expr)
-#define IGNORE_RECOVERABLE(err, expr) SVN_ERR(expr)
-#endif
-
-/* Long enough to hold: "<svn_revnum_t> <node id> <copy id>\0"
- * 19 bytes for svn_revnum_t (room for 32 or 64 bit values)
- * + 2 spaces
- * + 26 bytes for each id (these are actually unbounded, so we just
- *   have to pick something; 2^64 is 13 bytes in base-36)
- * + 1 terminating null
+/* Implements deserialize_fn_t for svn_fs_fs__txdelta_cached_window_t.
  */
-#define CURRENT_BUF_LEN 48
-
-/* Read the 'current' file FNAME and store the contents in *BUF.
-   Allocations are performed in POOL. */
-static svn_error_t *
-read_current(const char *fname, char **buf, apr_pool_t *pool)
-{
-  apr_file_t *revision_file = NULL;
-  apr_size_t len;
-  int i;
-  svn_error_t *err = SVN_NO_ERROR;
-  apr_pool_t *iterpool;
-
-  *buf = apr_palloc(pool, CURRENT_BUF_LEN);
-  iterpool = svn_pool_create(pool);
-  for (i = 0; i < RECOVERABLE_RETRY_COUNT; i++)
-    {
-      svn_pool_clear(iterpool);
-
-      RETRY_RECOVERABLE(err, revision_file,
-                        svn_io_file_open(&revision_file, fname,
-                                         APR_READ | APR_BUFFERED,
-                                         APR_OS_DEFAULT, iterpool));
-
-      len = CURRENT_BUF_LEN;
-      RETRY_RECOVERABLE(err, revision_file,
-                        svn_io_read_length_line(revision_file,
-                                                *buf, &len, iterpool));
-      IGNORE_RECOVERABLE(err, svn_io_file_close(revision_file, iterpool));
-
-      break;
-    }
-  svn_pool_destroy(iterpool);
-
-  return svn_error_return(err);
-}
-
-/* Find the youngest revision in a repository at path FS_PATH and
-   return it in *YOUNGEST_P.  Perform temporary allocations in
-   POOL. */
-static svn_error_t *
-get_youngest(svn_revnum_t *youngest_p,
-             const char *fs_path,
-             apr_pool_t *pool)
-{
-  char *buf;
+svn_error_t *
+svn_fs_fs__deserialize_txdelta_window(void **item,
+                                      const char *buffer,
+                                      apr_size_t buffer_size,
+                                      apr_pool_t *pool)
+{
+  /* Copy the _full_ buffer as it also contains the sub-structures. */
+  svn_fs_fs__txdelta_cached_window_t *window_info =
+      apr_palloc(pool, buffer_size);
+
+  memcpy(window_info, buffer, buffer_size);
+
+  /* pointer reference fixup */
+  svn_temp_deserializer__resolve(window_info,
+                                 (void **)&window_info->window);
+  svn_temp_deserializer__resolve(window_info,
+                                 (void **)&window_info->window->ops);
 
-  SVN_ERR(read_current(svn_dirent_join(fs_path, PATH_CURRENT, pool),
-                       &buf, pool));
+  deserialize_svn_string(window_info, &window_info->window->new_data);
 
-  *youngest_p = SVN_STR_TO_REV(buf);
+  /* done */
+  *item = window_info;
 
   return SVN_NO_ERROR;
 }
 
-svn_error_t *
-svn_fs_fs__hotcopy(const char *src_path,
-                   const char *dst_path,
-                   apr_pool_t *pool)
-{
-  const char *src_subdir, *dst_subdir;
-  svn_revnum_t youngest, rev, min_unpacked_rev, min_unpacked_revprop;
-  apr_pool_t *iterpool;
-  svn_node_kind_t kind;
-  int format, max_files_per_dir;
-
-  /* Check format to be sure we know how to hotcopy this FS. */
-  SVN_ERR(read_format(&format, &max_files_per_dir,
-                      svn_dirent_join(src_path, PATH_FORMAT, pool),
-                      pool));
-  SVN_ERR(check_format(format));
-
-  /* Copy the 'current' file. */
-  SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, PATH_CURRENT, pool));
-
-  /* Copy the uuid. */
-  SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, PATH_UUID, pool));
-
-  /* Copy the config. */
-  SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, PATH_CONFIG, pool));
-
-  /* Copy the rep cache before copying the rev files to make sure all
-     cached references will be present in the copy. */
-  src_subdir = svn_dirent_join(src_path, REP_CACHE_DB_NAME, pool);
-  dst_subdir = svn_dirent_join(dst_path, REP_CACHE_DB_NAME, pool);
-  SVN_ERR(svn_io_check_path(src_subdir, &kind, pool));
-  if (kind == svn_node_file)
-    SVN_ERR(svn_sqlite__hotcopy(src_subdir, dst_subdir, pool));
-
-  /* Copy the min unpacked rev, and read its value. */
-  if (format >= SVN_FS_FS__MIN_PACKED_FORMAT)
-    {
-      const char *min_unpacked_rev_path;
-      min_unpacked_rev_path = svn_dirent_join(src_path, PATH_MIN_UNPACKED_REV,
-                                              pool);
-
-      SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, PATH_MIN_UNPACKED_REV,
-                                   pool));
-      SVN_ERR(read_min_unpacked_rev(&min_unpacked_rev, min_unpacked_rev_path,
-                                    pool));
-    }
-  else
-    {
-      min_unpacked_rev = 0;
-    }
-
-  /* Find the youngest revision from this 'current' file. */
-  SVN_ERR(get_youngest(&youngest, dst_path, pool));
-
-  /* Copy the necessary rev files. */
-  src_subdir = svn_dirent_join(src_path, PATH_REVS_DIR, pool);
-  dst_subdir = svn_dirent_join(dst_path, PATH_REVS_DIR, pool);
-
-  SVN_ERR(svn_io_make_dir_recursively(dst_subdir, pool));
-
-  iterpool = svn_pool_create(pool);
-  /* First, copy packed shards. */
-  for (rev = 0; rev < min_unpacked_rev; rev += max_files_per_dir)
-    {
-      const char *packed_shard = apr_psprintf(iterpool, "%ld.pack",
-                                              rev / max_files_per_dir);
-      const char *src_subdir_packed_shard;
-      src_subdir_packed_shard = svn_dirent_join(src_subdir, packed_shard,
-                                                iterpool);
-
-      SVN_ERR(svn_io_copy_dir_recursively(src_subdir_packed_shard,
-                                          dst_subdir, packed_shard,
-                                          TRUE /* copy_perms */,
-                                          NULL /* cancel_func */, NULL,
-                                          iterpool));
-      svn_pool_clear(iterpool);
-    }
-
-  /* Then, copy non-packed shards. */
-  SVN_ERR_ASSERT(rev == min_unpacked_rev);
-  for (; rev <= youngest; rev++)
-    {
-      const char *src_subdir_shard = src_subdir,
-                 *dst_subdir_shard = dst_subdir;
-
-      if (max_files_per_dir)
-        {
-          const char *shard = apr_psprintf(iterpool, "%ld",
-                                           rev / max_files_per_dir);
-          src_subdir_shard = svn_dirent_join(src_subdir, shard, iterpool);
-          dst_subdir_shard = svn_dirent_join(dst_subdir, shard, iterpool);
-
-          if (rev % max_files_per_dir == 0)
-            {
-              SVN_ERR(svn_io_dir_make(dst_subdir_shard, APR_OS_DEFAULT,
-                                      iterpool));
-              SVN_ERR(svn_io_copy_perms(dst_subdir, dst_subdir_shard,
-                                        iterpool));
-            }
-        }
-
-      SVN_ERR(svn_io_dir_file_copy(src_subdir_shard, dst_subdir_shard,
-                                   apr_psprintf(iterpool, "%ld", rev),
-                                   iterpool));
-      svn_pool_clear(iterpool);
-    }
-
-  /* Copy the min unpacked revprop file, and read its value. */
-  if (format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT)
-    {
-      const char *min_unpacked_revprop_path;
-      min_unpacked_revprop_path = svn_dirent_join(src_path,
-                                                  PATH_MIN_UNPACKED_REVPROP,
-                                                  pool);
-      SVN_ERR(svn_io_dir_file_copy(src_path, dst_path,
-                                   PATH_MIN_UNPACKED_REVPROP, pool));
-      SVN_ERR(read_min_unpacked_rev(&min_unpacked_revprop,
-                                    min_unpacked_revprop_path, pool));
-    }
-  else
-    {
-      min_unpacked_revprop = 0;
-    }
-
-  /* Copy the necessary revprop files. */
-  src_subdir = svn_dirent_join(src_path, PATH_REVPROPS_DIR, pool);
-  dst_subdir = svn_dirent_join(dst_path, PATH_REVPROPS_DIR, pool);
-
-  SVN_ERR(svn_io_make_dir_recursively(dst_subdir, pool));
-
-  /* Copy the packed revprop db. */
-  if (format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT)
-    {
-      const char *src_file = svn_dirent_join(src_subdir, PATH_REVPROPS_DB,
-                                             pool);
-      const char *dst_file = svn_dirent_join(dst_subdir, PATH_REVPROPS_DB,
-                                             pool);
-      SVN_ERR(svn_sqlite__hotcopy(src_file, dst_file, pool));
-    }
-
-  for (rev = min_unpacked_revprop; rev <= youngest; rev++)
-    {
-      const char *src_subdir_shard = src_subdir,
-                 *dst_subdir_shard = dst_subdir;
-
-      svn_pool_clear(iterpool);
-
-      if (max_files_per_dir)
-        {
-          const char *shard = apr_psprintf(iterpool, "%ld",
-                                           rev / max_files_per_dir);
-          src_subdir_shard = svn_dirent_join(src_subdir, shard, iterpool);
-          dst_subdir_shard = svn_dirent_join(dst_subdir, shard, iterpool);
-
-          if (rev % max_files_per_dir == 0)
-            {
-              SVN_ERR(svn_io_dir_make(dst_subdir_shard, APR_OS_DEFAULT,
-                                      iterpool));
-              SVN_ERR(svn_io_copy_perms(dst_subdir, dst_subdir_shard,
-                                        iterpool));
-            }
-        }
-
-      SVN_ERR(svn_io_dir_file_copy(src_subdir_shard, dst_subdir_shard,
-                                   apr_psprintf(iterpool, "%ld", rev),
-                                   iterpool));
-    }
-
-  svn_pool_destroy(iterpool);
-
-  /* Make an empty transactions directory for now.  Eventually some
-     method of copying in progress transactions will need to be
-     developed.*/
-  dst_subdir = svn_dirent_join(dst_path, PATH_TXNS_DIR, pool);
-  SVN_ERR(svn_io_make_dir_recursively(dst_subdir, pool));
-  if (format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
-    {
-      dst_subdir = svn_dirent_join(dst_path, PATH_TXN_PROTOS_DIR, pool);
-      SVN_ERR(svn_io_make_dir_recursively(dst_subdir, pool));
-    }
-
-  /* Now copy the locks tree. */
-  src_subdir = svn_dirent_join(src_path, PATH_LOCKS_DIR, pool);
-  SVN_ERR(svn_io_check_path(src_subdir, &kind, pool));
-  if (kind == svn_node_dir)
-    SVN_ERR(svn_io_copy_dir_recursively(src_subdir, dst_path,
-                                        PATH_LOCKS_DIR, TRUE, NULL,
-                                        NULL, pool));
-
-  /* Now copy the node-origins cache tree. */
-  src_subdir = svn_dirent_join(src_path, PATH_NODE_ORIGINS_DIR, pool);
-  SVN_ERR(svn_io_check_path(src_subdir, &kind, pool));
-  if (kind == svn_node_dir)
-    SVN_ERR(svn_io_copy_dir_recursively(src_subdir, dst_path,
-                                        PATH_NODE_ORIGINS_DIR, TRUE, NULL,
-                                        NULL, pool));
-
-  /* Copy the txn-current file. */
-  if (format >= SVN_FS_FS__MIN_TXN_CURRENT_FORMAT)
-    SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, PATH_TXN_CURRENT, pool));
-
-  /* Hotcopied FS is complete. Stamp it with a format file. */
-  return write_format(svn_dirent_join(dst_path, PATH_FORMAT, pool),
-                      format, max_files_per_dir, FALSE, pool);
-}
-
-svn_error_t *
-svn_fs_fs__youngest_rev(svn_revnum_t *youngest_p,
-                        svn_fs_t *fs,
-                        apr_pool_t *pool)
-{
-  fs_fs_data_t *ffd = fs->fsap_data;
-
-  SVN_ERR(get_youngest(youngest_p, fs->path, pool));
-  ffd->youngest_rev_cache = *youngest_p;
-
-  return SVN_NO_ERROR;
-}
-
-/* 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. */
-static svn_error_t * read_header_block(apr_hash_t **headers,
-                                       svn_stream_t *stream,
-                                       apr_pool_t *pool)
-{
-  *headers = apr_hash_make(pool);
-
-  while (1)
-    {
-      svn_stringbuf_t *header_str;
-      const char *name, *value;
-      apr_size_t i = 0;
-      svn_boolean_t eof;
-
-      SVN_ERR(svn_stream_readline(stream, &header_str, "\n", &eof, pool));
-
-      if (eof || header_str->len == 0)
-        break; /* end of header block */
-
-      while (header_str->data[i] != ':')
-        {
-          if (header_str->data[i] == '\0')
-            return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                                    _("Found malformed header in "
-                                      "revision file"));
-          i++;
-        }
-
-      /* Create a 'name' string and point to it. */
-      header_str->data[i] = '\0';
-      name = header_str->data;
-
-      /* Skip over the NULL byte and the space following it. */
-      i += 2;
-
-      if (i > header_str->len)
-        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                                _("Found malformed header in "
-                                  "revision file"));
-
-      value = header_str->data + i;
-
-      /* header_str is safely in our pool, so we can use bits of it as
-         key and value. */
-      apr_hash_set(*headers, name, APR_HASH_KEY_STRING, value);
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Return SVN_ERR_FS_NO_SUCH_REVISION if the given revision is newer
-   than the current youngest revision or is simply not a valid
-   revision number, else return success.
-
-   FSFS is based around the concept that commits only take effect when
-   the number in "current" is bumped.  Thus if there happens to be a rev
-   or revprops file installed for a revision higher than the one recorded
-   in "current" (because a commit failed between installing the rev file
-   and bumping "current", or because an administrator rolled back the
-   repository by resetting "current" without deleting rev files, etc), it
-   ought to be completely ignored.  This function provides the check
-   by which callers can make that decision. */
-static svn_error_t *
-ensure_revision_exists(svn_fs_t *fs,
-                       svn_revnum_t rev,
-                       apr_pool_t *pool)
-{
-  fs_fs_data_t *ffd = fs->fsap_data;
-
-  if (! SVN_IS_VALID_REVNUM(rev))
-    return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
-                             _("Invalid revision number '%ld'"), rev);
-
-
-  /* Did the revision exist the last time we checked the current
-     file? */
-  if (rev <= ffd->youngest_rev_cache)
-    return SVN_NO_ERROR;
-
-  SVN_ERR(get_youngest(&(ffd->youngest_rev_cache), fs->path, pool));
-
-  /* Check again. */
-  if (rev <= ffd->youngest_rev_cache)
-    return SVN_NO_ERROR;
-
-  return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
-                           _("No such revision %ld"), rev);
-}
-
-/* Open the correct revision file for REV.  If the filesystem FS has
-   been packed, *FILE will be set to the packed file; otherwise, set *FILE
-   to the revision file for REV.  Return SVN_ERR_FS_NO_SUCH_REVISION if the
-   file doesn't exist.  Move the file pointer of OFFSET, if the latter is
-   not -1.  Prefer cached file handles that share the same COOKIE (again,
-   if not -1).  Use POOL for allocations. */
-static svn_error_t *
-open_pack_or_rev_file(svn_file_handle_cache__handle_t **file,
-                      svn_fs_t *fs,
-                      svn_revnum_t rev,
-                      apr_off_t offset,
-                      int cookie,
-                      apr_pool_t *pool)
-{
-  svn_error_t *err;
-  const char *path;
-
-  /* make sure file has a defined state */
-  *file = NULL;
-  err = svn_fs_fs__path_rev_absolute(&path, fs, rev, pool);
-
-  if (! err)
-    {
-      /* open the revision file in buffered r/o mode */
-      fs_fs_data_t *ffd = fs->fsap_data;
-      err = svn_file_handle_cache__open(file,
-                                        ffd->file_handle_cache,
-                                        path,
-                                        APR_READ | APR_BUFFERED,
-                                        APR_OS_DEFAULT,
-                                        offset,
-                                        cookie,
-                                        pool);
-
-      /* if that succeeded, there must be an underlying APR file */
-      assert(err || svn_file_handle_cache__get_apr_handle(*file));
-    }
-
-  if (err && APR_STATUS_IS_ENOENT(err->apr_err))
-    {
-      svn_error_clear(err);
-      return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
-                               _("No such revision %ld"), rev);
-    }
-
-  return svn_error_return(err);
-}
-
-/* Given REV in FS, set *REV_OFFSET to REV's offset in the packed file.
-   Use POOL for temporary allocations. */
-static svn_error_t *
-get_packed_offset(apr_off_t *rev_offset,
-                  svn_fs_t *fs,
-                  svn_revnum_t rev,
-                  apr_pool_t *pool)
-{
-  fs_fs_data_t *ffd = fs->fsap_data;
-  svn_stream_t *manifest_stream;
-  svn_boolean_t is_cached;
-  apr_int64_t shard;
-  apr_array_header_t *manifest;
-  apr_pool_t *iterpool;
-
-  shard = rev / ffd->max_files_per_dir;
-  SVN_ERR(svn_cache__get((void **) &manifest, &is_cached,
-                         ffd->packed_offset_cache, &shard, pool));
-
-  if (is_cached)
-    {
-      *rev_offset = APR_ARRAY_IDX(manifest, rev % ffd->max_files_per_dir,
-                                  apr_off_t);
-      return SVN_NO_ERROR;
-    }
-
-  /* Open the manifest file. */
-  SVN_ERR(svn_stream_open_readonly(&manifest_stream,
-                                   path_rev_packed(fs, rev, "manifest", pool),
-                                   pool, pool));
-
-  /* While we're here, let's just read the entire manifest file into an array,
-     so we can cache the entire thing. */
-  iterpool = svn_pool_create(pool);
-  manifest = apr_array_make(pool, ffd->max_files_per_dir, sizeof(apr_off_t));
-  while (1)
-    {
-      svn_stringbuf_t *sb;
-      svn_boolean_t eof;
-
-      svn_pool_clear(iterpool);
-      SVN_ERR(svn_stream_readline(manifest_stream, &sb, "\n", &eof, iterpool));
-      if (eof)
-        break;
-
-      errno = 0; /* apr_atoi64() in APR-0.9 does not always set errno */
-      APR_ARRAY_PUSH(manifest, apr_off_t) =
-                apr_atoi64(svn_string_create_from_buf(sb, iterpool)->data);
-      if (errno == ERANGE)
-        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                                "Manifest offset too large");
-    }
-  svn_pool_destroy(iterpool);
-
-  *rev_offset = APR_ARRAY_IDX(manifest, rev % ffd->max_files_per_dir,
-                              apr_off_t);
-
-  /* Close up shop and cache the array. */
-  SVN_ERR(svn_stream_close(manifest_stream));
-  return svn_cache__set(ffd->packed_offset_cache, &shard, manifest, pool);
-}
-
-/* Open the revision file for revision REV in filesystem FS and store
-   the newly opened file in FILE.  Seek to location OFFSET before
-   returning.  Prefer cached file handles with the specified COOKIE
-   (if not -1).  Perform temporary allocations in POOL. */
-static svn_error_t *
-open_and_seek_revision(svn_file_handle_cache__handle_t **file,
-                       svn_fs_t *fs,
-                       svn_revnum_t rev,
-                       apr_off_t offset,
-                       int cookie,
-                       apr_pool_t *pool)
-{
-  /* none of the following requires the file handle */
-  SVN_ERR(ensure_revision_exists(fs, rev, pool));
-  if (is_packed_rev(fs, rev))
-    {
-      apr_off_t rev_offset;
-
-      SVN_ERR(get_packed_offset(&rev_offset, fs, rev, pool));
-      offset += rev_offset;
-    }
-
-  /* So, open the revision file and position the pointer here in one go. */
-  return open_pack_or_rev_file(file, fs, rev, offset, cookie, pool);
-}
-
-/* Open the representation for a node-revision in transaction TXN_ID
-   in filesystem FS and store the newly opened file in FILE.  Seek to
-   location OFFSET before returning.  Prefer cached file handles witt
-   the specified COOKIE (if not -1).  Perform temporary allocations in
-   POOL.  Only appropriate for file contents, nor props or directory
-   contents. */
-static svn_error_t *
-open_and_seek_transaction(svn_file_handle_cache__handle_t **file,
-                          svn_fs_t *fs,
-                          const char *txn_id,
-                          representation_t *rep,
-                          int cookie,
-                          apr_pool_t *pool)
-{
-  fs_fs_data_t *ffd = fs->fsap_data;
-
-  /* open & seek in one call */
-  return svn_file_handle_cache__open(file,
-                                     ffd->file_handle_cache,
-                                     path_txn_proto_rev(fs, txn_id, pool),
-                                     APR_READ | APR_BUFFERED, 
-                                     APR_OS_DEFAULT, 
-                                     rep->offset,
-                                     cookie,
-                                     pool);
-}
-
-/* Given a node-id ID, and a representation REP in filesystem FS, open
-   the correct file and seek to the correction location.  Store this
-   file in *FILE_P.  Perform any allocations in POOL. */
-static svn_error_t *
-open_and_seek_representation(svn_file_handle_cache__handle_t **file_p,
-                             svn_fs_t *fs,
-                             representation_t *rep,
-                             apr_pool_t *pool)
-{
-  /* representation headers tend to cluster. Therefore, use separate
-   * file handles for them (controlled by the cookie) to maximize APR 
-   * buffer effectiveness. */
-  if (! rep->txn_id)
-    return open_and_seek_revision(file_p, fs, rep->revision, rep->offset,
-                                  REP_FILE_COOKIE, pool);
-  else
-    return open_and_seek_transaction(file_p, fs, rep->txn_id, rep, 
-                                     REP_FILE_COOKIE, pool);
-}
-
-/* Parse the description of a representation from STRING and store it
-   into *REP_P.  If the representation is mutable (the revision is
-   given as -1), then use TXN_ID for the representation's txn_id
-   field.  If MUTABLE_REP_TRUNCATED is true, then this representation
-   is for property or directory contents, and no information will be
-   expected except the "-1" revision number for a mutable
-   representation.  Allocate *REP_P in POOL. */
-static svn_error_t *
-read_rep_offsets(representation_t **rep_p,
-                 char *string,
-                 const char *txn_id,
-                 svn_boolean_t mutable_rep_truncated,
-                 apr_pool_t *pool)
-{
-  representation_t *rep;
-  char *str, *last_str;
-
-  rep = apr_pcalloc(pool, sizeof(*rep));
-  *rep_p = rep;
-
-  str = apr_strtok(string, " ", &last_str);
-  if (str == NULL)
-    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                            _("Malformed text representation offset line in node-rev"));
-
-
-  rep->revision = SVN_STR_TO_REV(str);
-  if (rep->revision == SVN_INVALID_REVNUM)
-    {
-      rep->txn_id = txn_id;
-      if (mutable_rep_truncated)
-        return SVN_NO_ERROR;
-    }
-
-  str = apr_strtok(NULL, " ", &last_str);
-  if (str == NULL)
-    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                            _("Malformed text representation offset line in node-rev"));
-
-  rep->offset = apr_atoi64(str);
-
-  str = apr_strtok(NULL, " ", &last_str);
-  if (str == NULL)
-    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                            _("Malformed text representation offset line in node-rev"));
-
-  rep->size = apr_atoi64(str);
-
-  str = apr_strtok(NULL, " ", &last_str);
-  if (str == NULL)
-    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                            _("Malformed text representation offset line in node-rev"));
-
-  rep->expanded_size = apr_atoi64(str);
-
-  /* Read in the MD5 hash. */
-  str = apr_strtok(NULL, " ", &last_str);
-  if ((str == NULL) || (strlen(str) != (APR_MD5_DIGESTSIZE * 2)))
-    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                            _("Malformed text representation offset line in node-rev"));
-
-  SVN_ERR(svn_checksum_parse_hex(&rep->md5_checksum, svn_checksum_md5, str,
-                                 pool));
-
-  /* The remaining fields are only used for formats >= 4, so check that. */
-  str = apr_strtok(NULL, " ", &last_str);
-  if (str == NULL)
-    return SVN_NO_ERROR;
-
-  /* Read the SHA1 hash. */
-  if (strlen(str) != (APR_SHA1_DIGESTSIZE * 2))
-    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                            _("Malformed text representation offset line in node-rev"));
-
-  SVN_ERR(svn_checksum_parse_hex(&rep->sha1_checksum, svn_checksum_sha1, str,
-                                 pool));
-
-  /* Read the uniquifier. */
-  str = apr_strtok(NULL, " ", &last_str);
-  if (str == NULL)
-    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                            _("Malformed text representation offset line in node-rev"));
-
-  rep->uniquifier = apr_pstrdup(pool, str);
-
-  return SVN_NO_ERROR;
-}
-
-/* Get the node-revision for the node ID in FS.
-   Set *NODEREV_P to the new node-revision structure, allocated in POOL.
-   See svn_fs_fs__get_node_revision, which wraps this and adds another
-   error. */
-static svn_error_t *
-get_node_revision_body(node_revision_t **noderev_p,
-                       svn_fs_t *fs,
-                       const svn_fs_id_t *id,
-                       apr_pool_t *pool)
-{
-  svn_file_handle_cache__handle_t *revision_file;
-  svn_error_t *err;
-
-  if (svn_fs_fs__id_txn_id(id))
-    {
-      /* This is a transaction node-rev. */
-      fs_fs_data_t *ffd = fs->fsap_data;
-      err = svn_file_handle_cache__open(&revision_file,
-                                        ffd->file_handle_cache,
-                                        path_txn_node_rev(fs, id, pool),
-                                        APR_READ | APR_BUFFERED,
-                                        APR_OS_DEFAULT,
-                                        0,
-                                        DEFAULT_FILE_COOKIE,
-                                        pool);
-    }
-  else
-    {
-      /* This is a revision node-rev. */
-      err = open_and_seek_revision(&revision_file, fs,
-                                   svn_fs_fs__id_rev(id),
-                                   svn_fs_fs__id_offset(id),
-                                   DEFAULT_FILE_COOKIE,
-                                   pool);
-    }
-
-  if (err)
-    {
-      if (APR_STATUS_IS_ENOENT(err->apr_err))
-        {
-          svn_error_clear(err);
-          return svn_fs_fs__err_dangling_id(fs, id);
-        }
-
-      return svn_error_return(err);
-    }
-
-  return svn_fs_fs__read_noderev(noderev_p,
-                                 svn_stream_from_cached_file_handle
-                                     (revision_file,
-                                      FALSE,
-                                      pool),
-                                 pool);
-}
-
-svn_error_t *
-svn_fs_fs__read_noderev(node_revision_t **noderev_p,
-                        svn_stream_t *stream,
-                        apr_pool_t *pool)
-{
-  apr_hash_t *headers;
-  node_revision_t *noderev;
-  char *value;
-
-  SVN_ERR(read_header_block(&headers, stream, pool));
-
-  noderev = apr_pcalloc(pool, sizeof(*noderev));
-
-  /* Read the node-rev id. */
-  value = apr_hash_get(headers, HEADER_ID, APR_HASH_KEY_STRING);
-  if (value == NULL)
-      return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                              _("Missing id field in node-rev"));
-
-  SVN_ERR(svn_stream_close(stream));
-
-  noderev->id = svn_fs_fs__id_parse(value, strlen(value), pool);
-
-  /* Read the type. */
-  value = apr_hash_get(headers, HEADER_TYPE, APR_HASH_KEY_STRING);
-
-  if ((value == NULL) ||
-      (strcmp(value, KIND_FILE) != 0 && strcmp(value, KIND_DIR)))
-    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                            _("Missing kind field in node-rev"));
-
-  noderev->kind = (strcmp(value, KIND_FILE) == 0) ? svn_node_file
-    : svn_node_dir;
-
-  /* Read the 'count' field. */
-  value = apr_hash_get(headers, HEADER_COUNT, APR_HASH_KEY_STRING);
-  noderev->predecessor_count = (value == NULL) ? 0 : atoi(value);
-
-  /* Get the properties location. */
-  value = apr_hash_get(headers, HEADER_PROPS, APR_HASH_KEY_STRING);
-  if (value)
-    {
-      SVN_ERR(read_rep_offsets(&noderev->prop_rep, value,
-                               svn_fs_fs__id_txn_id(noderev->id), TRUE, pool));
-    }
-
-  /* Get the data location. */
-  value = apr_hash_get(headers, HEADER_TEXT, APR_HASH_KEY_STRING);
-  if (value)
-    {
-      SVN_ERR(read_rep_offsets(&noderev->data_rep, value,
-                               svn_fs_fs__id_txn_id(noderev->id),
-                               (noderev->kind == svn_node_dir), pool));
-    }
-
-  /* Get the created path. */
-  value = apr_hash_get(headers, HEADER_CPATH, APR_HASH_KEY_STRING);
-  if (value == NULL)
-    {
-      return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                              _("Missing cpath in node-rev"));
-    }
-  else
-    {
-      noderev->created_path = apr_pstrdup(pool, value);
-    }
-
-  /* Get the predecessor ID. */
-  value = apr_hash_get(headers, HEADER_PRED, APR_HASH_KEY_STRING);
-  if (value)
-    noderev->predecessor_id = svn_fs_fs__id_parse(value, strlen(value),
-                                                  pool);
-
-  /* Get the copyroot. */
-  value = apr_hash_get(headers, HEADER_COPYROOT, APR_HASH_KEY_STRING);
-  if (value == NULL)
-    {
-      noderev->copyroot_path = apr_pstrdup(pool, noderev->created_path);
-      noderev->copyroot_rev = svn_fs_fs__id_rev(noderev->id);
-    }
-  else
-    {
-      char *str, *last_str;
-
-      str = apr_strtok(value, " ", &last_str);
-      if (str == NULL)
-        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                                _("Malformed copyroot line in node-rev"));
-
-      noderev->copyroot_rev = SVN_STR_TO_REV(str);
-
-      if (last_str == NULL)
-        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                                _("Malformed copyroot line in node-rev"));
-      noderev->copyroot_path = apr_pstrdup(pool, last_str);
-    }
-
-  /* Get the copyfrom. */
-  value = apr_hash_get(headers, HEADER_COPYFROM, APR_HASH_KEY_STRING);
-  if (value == NULL)
-    {
-      noderev->copyfrom_path = NULL;
-      noderev->copyfrom_rev = SVN_INVALID_REVNUM;
-    }
-  else
-    {
-      char *str, *last_str;
-
-      str = apr_strtok(value, " ", &last_str);
-      if (str == NULL)
-        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                                _("Malformed copyfrom line in node-rev"));
-
-      noderev->copyfrom_rev = SVN_STR_TO_REV(str);
-
-      if (last_str == NULL)
-        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                                _("Malformed copyfrom line in node-rev"));
-      noderev->copyfrom_path = apr_pstrdup(pool, last_str);
-    }
-
-  /* Get whether this is a fresh txn root. */
-  value = apr_hash_get(headers, HEADER_FRESHTXNRT, APR_HASH_KEY_STRING);
-  noderev->is_fresh_txn_root = (value != NULL);
-
-  /* Get the mergeinfo count. */
-  value = apr_hash_get(headers, HEADER_MINFO_CNT, APR_HASH_KEY_STRING);
-  noderev->mergeinfo_count = (value == NULL) ? 0 : apr_atoi64(value);
-
-  /* Get whether *this* node has mergeinfo. */
-  value = apr_hash_get(headers, HEADER_MINFO_HERE, APR_HASH_KEY_STRING);
-  noderev->has_mergeinfo = (value != NULL);
-
-  *noderev_p = noderev;
-
-  return SVN_NO_ERROR;
-}
-
-svn_error_t *
-svn_fs_fs__get_node_revision(node_revision_t **noderev_p,
-                             svn_fs_t *fs,
-                             const svn_fs_id_t *id,
-                             apr_pool_t *pool)
-{
-  svn_error_t *err = get_node_revision_body(noderev_p, fs, id, pool);
-  if (err && err->apr_err == SVN_ERR_FS_CORRUPT)
-    {
-      svn_string_t *id_string = svn_fs_fs__id_unparse(id, pool);
-      return svn_error_createf(SVN_ERR_FS_CORRUPT, err,
-                               "Corrupt node-revision '%s'",
-                               id_string->data);
-    }
-  return svn_error_return(err);
-}
-
-
-/* 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.
-   Perform the allocation from POOL.  */
-static const char *
-representation_string(representation_t *rep,
-                      int format,
-                      svn_boolean_t mutable_rep_truncated,
-                      apr_pool_t *pool)
-{
-  if (rep->txn_id && mutable_rep_truncated)
-    return "-1";
-
-  if (format < SVN_FS_FS__MIN_REP_SHARING_FORMAT || rep->sha1_checksum == NULL)
-    return apr_psprintf(pool, "%ld %" APR_OFF_T_FMT " %" SVN_FILESIZE_T_FMT
-                        " %" SVN_FILESIZE_T_FMT " %s",
-                        rep->revision, rep->offset, rep->size,
-                        rep->expanded_size,
-                        svn_checksum_to_cstring_display(rep->md5_checksum,
-                                                        pool));
-
-  return apr_psprintf(pool, "%ld %" APR_OFF_T_FMT " %" SVN_FILESIZE_T_FMT

[... 5396 lines stripped ...]