subversion-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From stef...@apache.org
Subject svn commit: r1410995 [2/2] - in /subversion/trunk: build.conf tools/server-side/fsfs-stats.c
Date Sun, 18 Nov 2012 21:45:55 GMT
Copied: subversion/trunk/tools/server-side/fsfs-stats.c (from r1410722, subversion/trunk/tools/server-side/fsfs-reorg.c)
URL: http://svn.apache.org/viewvc/subversion/trunk/tools/server-side/fsfs-stats.c?p2=subversion/trunk/tools/server-side/fsfs-stats.c&p1=subversion/trunk/tools/server-side/fsfs-reorg.c&r1=1410722&r2=1410995&rev=1410995&view=diff
==============================================================================
--- subversion/trunk/tools/server-side/fsfs-reorg.c (original)
+++ subversion/trunk/tools/server-side/fsfs-stats.c Sun Nov 18 21:45:53 2012
@@ -22,6 +22,7 @@
 
 
 #include <assert.h>
+#include <sys/stat.h>
 
 #include <apr.h>
 #include <apr_general.h>
@@ -40,6 +41,7 @@
 #include "private/svn_string_private.h"
 #include "private/svn_subr_private.h"
 #include "private/svn_dep_compat.h"
+#include <../test/testutil.h>
 
 #ifndef _
 #define _(x) x
@@ -47,293 +49,88 @@
 
 #define ERROR_TAG "diff: "
 
-/* forward declarations */
-typedef struct noderev_t noderev_t;
-typedef struct revision_info_t revision_info_t;
-
-/* A FSFS rev file is sequence of fragments and unused space (the latter
- * only being inserted by this tool and not during ordinary SVN operation).
- * 
- * This type defines the type of any fragment.
- *
- * Please note that the classification as "property", "dir" or "file"
- * fragments is only to be used while determining the future placement
- * of a representation.  If the rep is shared, the same rep may be used
- * as *any* of the 3 kinds.
- */
-enum fragment_kind_t
+typedef enum rep_kind_t
 {
-  /* the 2 number line containing changes and root node offsets */
-  header_fragment,
+  unused_rep,
 
-  /* list of all changes in a revision */
-  changes_fragment,
+  dir_property_rep,
 
-  /* (the textual representation of) a noderev */
-  noderev_fragment,
-
-  /* a property rep (including PLAIN / DELTA header) */
-  property_fragment,
+  file_property_rep,
 
   /* a directory rep (including PLAIN / DELTA header) */
-  dir_fragment,
+  dir_rep,
 
   /* a file rep (including PLAIN / DELTA header) */
-  file_fragment
-};
-
-/* A fragment.  This is used to represent the final ordering, i.e. there
- * will be an array containing elements of this type that basically put
- * a fragment at some location in the target file.
- */
-typedef struct fragment_t
-{
-  /* position in the target file */
-  apr_size_t position;
-
-  /* kind of fragment */
-  enum fragment_kind_t kind;
-
-  /* pointer to the  fragment struct; type depends on KIND */
-  void *data;
-} fragment_t;
-
-/* Location info for a single revision.
- */
-typedef struct revision_location_t
-{
-  /* pack file offset (manifest value), 0 for non-packed files */
-  apr_size_t offset;
-  
-  /* offset of the changes list relative to OFFSET */
-  apr_size_t changes;
+  file_rep
+} rep_kind_t;
 
-  /* length of the changes list on bytes */
-  apr_size_t changes_len;
-
-  /* first offset behind the revision data in the pack file (file length
-   * for non-packed revs) */
-  apr_size_t end;  
-} revision_location_t;
-
-/* Absolute position and size of some item.
+/* A representation fragment.
  */
-typedef struct location_t
+typedef struct representation_t
 {
   /* absolute offset in the file */
   apr_size_t offset;
 
   /* item length in bytes */
   apr_size_t size;
-} location_t;
-
-/* A parsed directory entry. Note that instances of this struct may be
- * shared between different DIRECTORY_T containers.
- */
-typedef struct direntry_t
-{
-  /* (local) entry / path name */
-  const char *name;
-
-  /* strlen (name) */
-  apr_size_t name_len;
-
-  /* node rev providing ID and representation(s) */
-  noderev_t *node;
-} direntry_t;
-
-/* Representation of a parsed directory content.
- */
-typedef struct directory_t
-{
-  /* array of pointers to DIRENTRY_T */
-  apr_array_header_t *entries;
-
-  /* MD5 of the textual representation. Will be set lazily as a side-effect
-   * of determining the length of this dir's textual representation. */
-  unsigned char target_md5[16];
-
-  /* (expanded) length of the textual representation.
-   * Determined lazily during the write process. */
-  apr_size_t size;
-} directory_t;
-
-/* A representation fragment.
- */
-typedef struct representation_t
-{
-  /* location in the source file */
-  location_t original;
 
-  /* location in the reordered target file */
-  location_t target;
-
-  /* length of the PLAIN / DELTA line in the source file in bytes */
-  apr_size_t header_size;
+  apr_size_t expanded_size;
 
   /* deltification base, or NULL if there is none */
   struct representation_t *delta_base;
 
   /* revision that contains this representation
    * (may be referenced by other revisions, though) */
-  revision_info_t *revision;
-
-  /* representation content parsed as a directory. This will be NULL, if
-   * *no* directory noderev uses this representation. */
-  directory_t *dir;
-
-  /* the source content has a PLAIN header, so we may simply copy the
-   * source content into the target */
-  svn_boolean_t is_plain;
-
-  /* coloring flag used in the reordering algorithm to keep track of
-   * representations that still need to be placed. */
-  svn_boolean_t covered;
-} representation_t;
-
-/* A node rev.
- */
-struct noderev_t
-{
-  /* location within the source file */
-  location_t original;
-
-  /* location within the reorganized target file. */
-  location_t target;
-
-  /* predecessor node, or NULL if there is none */
-  noderev_t *predecessor;
-
-  /* content representation; may be NULL if there is none */
-  representation_t *text;
+  
+  apr_uint32_t revision;
+  apr_uint32_t ref_count;
 
-  /* properties representation; may be NULL if there is none */
-  representation_t *props;
+  struct
+    {
+      /* length of the PLAIN / DELTA line in the source file in bytes */
+      apr_size_t header_size : 12;
 
-  /* revision that this noderev belongs to */
-  revision_info_t *revision;
+      rep_kind_t kind : 3;
 
-  /* coloring flag used in the reordering algorithm to keep track of
-   * representations that still need to be placed. */
-  svn_boolean_t covered;
-};
+      /* the source content has a PLAIN header, so we may simply copy the
+      * source content into the target */
+      svn_boolean_t is_plain : 1;
+    };
+  
+} representation_t;
 
 /* Represents a single revision.
  * There will be only one instance per revision. */
-struct revision_info_t
+typedef struct revision_info_t
 {
   /* number of this revision */
   svn_revnum_t revision;
 
-  /* position in the source file */
-  revision_location_t original;
+  /* pack file offset (manifest value), 0 for non-packed files */
+  apr_size_t offset;
+
+  /* offset of the changes list relative to OFFSET */
+  apr_size_t changes;
 
-  /* position in the reorganized target file */
-  revision_location_t target;
+  /* length of the changes list on bytes */
+  apr_size_t changes_len;
 
-  /* noderev of the root directory */
-  noderev_t *root_noderev;
+  /* offset of the changes list relative to OFFSET */
+  apr_size_t change_count;
 
-  /* all noderevs_t of this revision (in no particular order),
-   * i.e. those that point back to this struct */
-  apr_array_header_t *node_revs;
+  /* first offset behind the revision data in the pack file (file length
+   * for non-packed revs) */
+  apr_size_t end;
+
+  apr_size_t dir_noderev_count;
+  apr_size_t file_noderev_count;
+  apr_size_t dir_noderev_size;
+  apr_size_t file_noderev_size;
   
   /* all representation_t of this revision (in no particular order),
    * i.e. those that point back to this struct */
   apr_array_header_t *representations;
-};
-
-/* Represents a packed revision file.
- */
-typedef struct revision_pack_t
-{
-  /* first revision in the pack file */
-  svn_revnum_t base;
-
-  /* revision_info_t* of all revisions in the pack file; in revision order. */
-  apr_array_header_t *info;
-
-  /* list of fragments to place in the target pack file; in target order. */
-  apr_array_header_t *fragments;
-
-  /* source pack file length */
-  apr_size_t filesize;
-
-  /* temporary value. Equal to the number of bytes in the target pack file
-   * already allocated to fragments. */
-  apr_size_t target_offset;
-} revision_pack_t;
-
-/* Cache for revision source content.  All content is stored in DATA and
- * the HASH maps revision number to an svn_string_t instance whose data
- * member points into DATA.
- * 
- * Once TOTAL_SIZE exceeds LIMIT, all content will be discarded.  Similarly,
- * the hash gets cleared every 10000 insertions to keep the HASH_POOL
- * memory usage in check.
- */
-typedef struct content_cache_t
-{
-  /* pool used for HASH */
-  apr_pool_t *hash_pool;
-
-  /* svn_revnum_t -> svn_string_t.
-   * The strings become (potentially) invalid when adding new cache entries. */
-  apr_hash_t *hash;
-
-  /* data buffer. the first TOTAL_SIZE bytes are actually being used. */
-  char *data;
-
-  /* DATA capacity */
-  apr_size_t limit;
-
-  /* number of bytes used in DATA */
-  apr_size_t total_size;
-
-  /* number of insertions since the last hash cleanup */
-  apr_size_t insert_count;
-} content_cache_t;
-
-/* A cached directory. In contrast to directory_t, this stored the data as
- * the plain hash that the normal FSFS will use to serialize & diff dirs.
- */
-typedef struct dir_cache_entry_t
-{
-  /* revision containing the representation */
-  svn_revnum_t revision;
-
-  /* offset of the representation within that revision */
-  apr_size_t offset;
-
-  /* key-value representation of the directory entries */
-  apr_hash_t *hash;
-} dir_cache_entry_t;
-
-/* Directory cache. (revision, offset) will be mapped directly into the
- * ENTRIES array of ENTRY_COUNT buckets (many entries will be NULL).
- * Two alternating pools will be used to allocate dir content.
- * 
- * If the INSERT_COUNT exceeds a given limit, the pools get exchanged and
- * the older of the two will be cleared. This is to keep dir objects valid
- * for at least one insertion.
- */
-typedef struct dir_cache_t
-{
-  /* fixed-size array of ENTRY_COUNT elements */
-  dir_cache_entry_t *entries;
-
-  /* currently used for entry allocations */
-  apr_pool_t *pool1;
-  
-  /* previously used for entry allocations */
-  apr_pool_t *pool2;
-
-  /* size of ENTRIES in elements */
-  apr_size_t entry_count;
-
-  /* number of directory elements added. I.e. usually >> #cached dirs */
-  apr_size_t insert_count;
-} dir_cache_t;
+} revision_info_t;
 
 /* A cached, undeltified txdelta window.
  */
@@ -398,19 +195,10 @@ typedef struct fs_fs_t
   /* all revisions */
   apr_array_header_t *revisions;
 
-  /* all packed files */
-  apr_array_header_t *packs;
-
   /* empty representation.
    * Used as a dummy base for DELTA reps without base. */
   representation_t *null_base;
 
-  /* revision content cache */
-  content_cache_t *cache;
-
-  /* directory hash cache */
-  dir_cache_t *dir_cache;
-
   /* undeltified txdelta window cache */
   window_cache_t *window_cache;
 } fs_fs_t;
@@ -458,238 +246,46 @@ open_rev_or_pack_file(apr_file_t **file,
  * in *CONTENT.
  */
 static svn_error_t *
-read_rev_or_pack_file(svn_stringbuf_t **content,
+rev_or_pack_file_size(apr_off_t *file_size,
                       fs_fs_t *fs,
                       svn_revnum_t rev,
                       apr_pool_t *pool)
 {
-  return svn_stringbuf_from_file2(content,
-                                  rev_or_pack_file_name(fs, rev, pool),
-                                  pool);
-}
-
-/* Return a new content cache with the given size LIMIT.  Use POOL for
- * all cache-related allocations.
- */
-static content_cache_t *
-create_content_cache(apr_pool_t *pool,
-                     apr_size_t limit)
-{
-  content_cache_t *result = apr_pcalloc(pool, sizeof(*result));
-
-  result->hash_pool = svn_pool_create(pool);
-  result->hash = svn_hash__make(result->hash_pool);
-  result->limit = limit;
-  result->total_size = 0;
-  result->insert_count = 0;
-  result->data = apr_palloc(pool, limit);
-  
-  return result;
-}
-
-/* Return the content of revision REVISION from CACHE. Return NULL upon a
- * cache miss. This is a cache-internal function.
- */
-static svn_string_t *
-get_cached_content(content_cache_t *cache,
-                   svn_revnum_t revision)
-{
-  return apr_hash_get(cache->hash, &revision, sizeof(revision));
-}
-
-/* Take the content in DATA and store it under REVISION in CACHE.
- * This is a cache-internal function.
- */
-static void
-set_cached_content(content_cache_t *cache,
-                   svn_revnum_t revision,
-                   svn_string_t *data)
-{
-  svn_string_t *content;
-  svn_revnum_t *key;
-
-  /* double insertion? -> broken cache logic */
-  assert(get_cached_content(cache, revision) == NULL);
-
-  /* purge the cache upon overflow */
-  if (cache->total_size + data->len > cache->limit)
-    {
-      /* the hash pool grows slowly over time; clear it once in a while */
-      if (cache->insert_count > 10000)
-        {
-          svn_pool_clear(cache->hash_pool);
-          cache->hash = svn_hash__make(cache->hash_pool);
-          cache->insert_count = 0;
-        }
-      else
-        cache->hash = svn_hash__make(cache->hash_pool);
-
-      cache->total_size = 0;
-
-      /* buffer overflow / revision too large */
-      if (data->len > cache->limit)
-        SVN_ERR_MALFUNCTION_NO_RETURN();
-    }
-
-  /* copy data to cache and update he index (hash) */
-  content = apr_palloc(cache->hash_pool, sizeof(*content));
-  content->data = cache->data + cache->total_size;
-  content->len = data->len;
+  apr_file_t *file;
+  apr_finfo_t finfo;
 
-  memcpy(cache->data + cache->total_size, data->data, data->len);
-  cache->total_size += data->len;
-    
-  key = apr_palloc(cache->hash_pool, sizeof(*key));
-  *key = revision;
+  SVN_ERR(open_rev_or_pack_file(&file, fs, rev, pool));
+  SVN_ERR(svn_io_file_info_get(&finfo, APR_FINFO_SIZE, file, pool));
+  SVN_ERR(svn_io_file_close(file, pool));
 
-  apr_hash_set(cache->hash, key, sizeof(*key), content);
-  ++cache->insert_count;
+  *file_size = finfo.size;
+  return SVN_NO_ERROR;
 }
 
 /* Get the file content of revision REVISION in FS and return it in *DATA.
  * Use SCRATCH_POOL for temporary allocations.
  */
 static svn_error_t *
-get_content(svn_string_t **data,
+get_content(svn_stringbuf_t **content,
             fs_fs_t *fs,
             svn_revnum_t revision,
-            apr_pool_t *scratch_pool)
+            apr_off_t offset,
+            apr_size_t len,
+            apr_pool_t *pool)
 {
   apr_file_t *file;
-  revision_info_t *revision_info;
-  svn_stringbuf_t *temp;
-  apr_off_t temp_offset;
-
-  /* try getting the data from our cache */
-  svn_string_t *result = get_cached_content(fs->cache, revision);
-  if (result)
-    {
-      *data = result;
-      return SVN_NO_ERROR;
-    }
-
-  /* not in cache. Is the revision valid at all? */
-  if (revision - fs->start_revision > fs->revisions->nelts)
-    return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
-                             _("Unknown revision %ld"), revision);
-  revision_info = APR_ARRAY_IDX(fs->revisions,
-                                revision - fs->start_revision,
-                                revision_info_t*);
-
-  /* read the revision content. Assume that the file has *not* been
-   * reorg'ed, yet, i.e. all data is in one place. */
-  temp = svn_stringbuf_create_ensure(  revision_info->original.end
-                                     - revision_info->original.offset,
-                                     scratch_pool);
-  temp->len = revision_info->original.end - revision_info->original.offset;
-  SVN_ERR(open_rev_or_pack_file(&file, fs, revision, scratch_pool));
-
-  temp_offset = revision_info->original.offset;
-  SVN_ERR(svn_io_file_seek(file, APR_SET, &temp_offset,
-                           scratch_pool));
-  SVN_ERR_ASSERT(temp_offset < APR_SIZE_MAX);
-  revision_info->original.offset = (apr_size_t)temp_offset;
-  SVN_ERR(svn_io_file_read(file, temp->data, &temp->len, scratch_pool));
-
-  /* cache the result and return it */
-  set_cached_content(fs->cache, revision,
-                     svn_stringbuf__morph_into_string(temp));
-  *data = get_cached_content(fs->cache, revision);
-
-  return SVN_NO_ERROR;
-}
-
-/* Return a new directory cache with ENTRY_COUNT buckets in its index.
- * Use POOL for all cache-related allocations.
- */
-static dir_cache_t *
-create_dir_cache(apr_pool_t *pool,
-                 apr_size_t entry_count)
-{
-  dir_cache_t *result = apr_pcalloc(pool, sizeof(*result));
-
-  result->pool1 = svn_pool_create(pool);
-  result->pool2 = svn_pool_create(pool);
-  result->entry_count = entry_count;
-  result->insert_count = 0;
-  result->entries = apr_pcalloc(pool, sizeof(*result->entries) * entry_count);
-
-  return result;
-}
-
-/* Return the position within FS' dir cache ENTRIES index for the given
- * (REVISION, OFFSET) pair. This is a cache-internal function.
- */
-static apr_size_t
-get_dir_cache_index(fs_fs_t *fs,
-                    svn_revnum_t revision,
-                    apr_size_t offset)
-{
-  return (revision + offset * 0xd1f3da69) % fs->dir_cache->entry_count;
-}
-
-/* Return the currently active pool of FS' dir cache. Note that it may be
- * cleared after *2* insertions.
- */
-static apr_pool_t *
-get_cached_dir_pool(fs_fs_t *fs)
-{
-  return fs->dir_cache->pool1;
-}
-
-/* Return the cached directory content stored in REPRESENTAION within FS.
- * If that has not been found in cache, return NULL.
- */
-static apr_hash_t *
-get_cached_dir(fs_fs_t *fs,
-               representation_t *representation)
-{
-  svn_revnum_t revision = representation->revision->revision;
-  apr_size_t offset = representation->original.offset;
-
-  apr_size_t i = get_dir_cache_index(fs, revision, offset);
-  dir_cache_entry_t *entry = &fs->dir_cache->entries[i];
-  
-  return entry->offset == offset && entry->revision == revision
-    ? entry->hash
-    : NULL;
-}
-
-/* Cache the directory HASH for  REPRESENTAION within FS.
- */
-static void
-set_cached_dir(fs_fs_t *fs,
-               representation_t *representation,
-               apr_hash_t *hash)
-{
-  /* select the entry to use */
-  svn_revnum_t revision = representation->revision->revision;
-  apr_size_t offset = representation->original.offset;
+  apr_pool_t * file_pool = svn_pool_create(pool);
 
-  apr_size_t i = get_dir_cache_index(fs, revision, offset);
-  dir_cache_entry_t *entry = &fs->dir_cache->entries[i];
+  SVN_ERR(open_rev_or_pack_file(&file, fs, revision, file_pool));
+  SVN_ERR(svn_io_file_seek(file, APR_SET, &offset, pool));
 
-  /* clean the cache and rotate pools at regular intervals */
-  fs->dir_cache->insert_count += apr_hash_count(hash);
-  if (fs->dir_cache->insert_count >= fs->dir_cache->entry_count * 100)
-    {
-      apr_pool_t *pool;
-
-      svn_pool_clear(fs->dir_cache->pool2);
-      memset(fs->dir_cache->entries,
-             0,
-             sizeof(*fs->dir_cache->entries) * fs->dir_cache->entry_count);
-      fs->dir_cache->insert_count = 0;
-
-      pool = fs->dir_cache->pool2;
-      fs->dir_cache->pool2 = fs->dir_cache->pool1;
-      fs->dir_cache->pool1 = pool;
-    }
+  *content = svn_stringbuf_create_ensure(len, pool);
+  (*content)->len = len;
+  SVN_ERR(svn_io_file_read_full2(file, (*content)->data, len,
+                                 NULL, NULL, pool));
+  svn_pool_destroy(file_pool);
 
-  /* write data to cache */
-  entry->hash = hash;
-  entry->offset = offset;
-  entry->revision = revision;
+  return SVN_NO_ERROR;
 }
 
 /* Return a new txdelta window cache with ENTRY_COUNT buckets in its index
@@ -731,8 +327,8 @@ get_cached_window(fs_fs_t *fs,
                   representation_t *representation,
                   apr_pool_t *pool)
 {
-  svn_revnum_t revision = representation->revision->revision;
-  apr_size_t offset = representation->original.offset;
+  svn_revnum_t revision = representation->revision;
+  apr_size_t offset = representation->offset;
 
   apr_size_t i = get_window_cache_index(fs, revision, offset);
   window_cache_entry_t *entry = &fs->window_cache->entries[i];
@@ -750,8 +346,8 @@ set_cached_window(fs_fs_t *fs,
                   svn_stringbuf_t *window)
 {
   /* select entry */
-  svn_revnum_t revision = representation->revision->revision;
-  apr_size_t offset = representation->original.offset;
+  svn_revnum_t revision = representation->revision;
+  apr_size_t offset = representation->offset;
 
   apr_size_t i = get_window_cache_index(fs, revision, offset);
   window_cache_entry_t *entry = &fs->window_cache->entries[i];
@@ -822,8 +418,6 @@ read_revision_header(apr_size_t *changes
                      apr_size_t *changes_len,
                      apr_size_t *root_noderev,
                      svn_stringbuf_t *file_content,
-                     apr_size_t start,
-                     apr_size_t end,
                      apr_pool_t *pool)
 {
   char buf[64];
@@ -834,10 +428,10 @@ read_revision_header(apr_size_t *changes
   
   /* Read in this last block, from which we will identify the last line. */
   len = sizeof(buf);
-  if (start + len > end)
-    len = end - start;
+  if (len > file_content->len)
+    len = file_content->len;
   
-  memcpy(buf, file_content->data + end - len, len);
+  memcpy(buf, file_content->data + file_content->len - len, len);
 
   /* The last byte should be a newline. */
   if (buf[(apr_ssize_t)len - 1] != '\n')
@@ -863,7 +457,7 @@ read_revision_header(apr_size_t *changes
   *root_noderev = (apr_size_t)val;
   SVN_ERR(svn_cstring_strtoui64(&val, space+1, 0, APR_SIZE_MAX, 10));
   *changes = (apr_size_t)val;
-  *changes_len = end - *changes - start - (buf + len - line) + 1;
+  *changes_len = file_content->len - *changes - (buf + len - line) + 1;
 
   return SVN_NO_ERROR;
 }
@@ -996,90 +590,9 @@ key_matches(svn_string_t *string, const 
 }
 
 static int
-compare_noderev_offsets(const void *data, const void *key)
-{
-  apr_ssize_t diff = (*(const noderev_t **)data)->original.offset
-                   - *(const apr_size_t *)key;
-
-  /* sizeof(int) may be < sizeof(ssize_t) */
-  if (diff < 0)
-    return -1;
-  return diff > 0 ? 1 : 0;
-}
-
-static svn_error_t *
-parse_revnode_pos(revision_info_t **revision_info,
-                  apr_size_t *offset,
-                  fs_fs_t *fs,
-                  svn_string_t *id)
-{
-  int revision;
-  apr_uint64_t temp;
-
-  const char *revision_pos = strrchr(id->data, 'r');
-  char *offset_pos = (char *)strchr(id->data, '/');
-
-  if (revision_pos == NULL || offset_pos == NULL)
-    return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
-                             _("Invalid node id '%s'"), id->data);
-
-  *offset_pos = 0;
-  SVN_ERR(svn_cstring_atoi(&revision, revision_pos + 1));
-  SVN_ERR(svn_cstring_strtoui64(&temp, offset_pos + 1, 0, APR_SIZE_MAX, 10));
-  *offset = (apr_size_t)temp;
-  *offset_pos = '/';
-
-  if (revision - fs->start_revision > fs->revisions->nelts)
-    return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
-                             _("Unknown revision %d"), revision);
-
-  *revision_info = APR_ARRAY_IDX(fs->revisions,
-                                 revision - fs->start_revision,
-                                 revision_info_t*);
-
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-find_noderev(noderev_t **result,
-            revision_info_t *revision_info,
-            apr_size_t offset)
-{
-  int idx = svn_sort__bsearch_lower_bound(&offset,
-                                          revision_info->node_revs,
-                                          compare_noderev_offsets);
-  if ((idx < 0) || (idx >= revision_info->node_revs->nelts))
-    return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
-                             _("No noderev found at offset %" APR_OFF_T_FMT),
-                             offset);
-
-  *result = APR_ARRAY_IDX(revision_info->node_revs, idx, noderev_t *);
-  if ((*result)->original.offset != offset)
-    return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
-                             _("No noderev found at offset %" APR_OFF_T_FMT),
-                             offset);
-
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-parse_pred(noderev_t **result,
-           fs_fs_t *fs,
-           svn_string_t *id)
-{
-  apr_size_t offset;
-  revision_info_t *revision_info;
-
-  SVN_ERR(parse_revnode_pos(&revision_info, &offset, fs, id));
-  SVN_ERR(find_noderev(result, revision_info, offset));
-
-  return SVN_NO_ERROR;
-}
-
-static int
 compare_representation_offsets(const void *data, const void *key)
 {
-  apr_ssize_t diff = (*(const representation_t **)data)->original.offset
+  apr_ssize_t diff = (*(const representation_t **)data)->offset
                    - *(const apr_size_t *)key;
 
   /* sizeof(int) may be < sizeof(ssize_t) */
@@ -1118,7 +631,7 @@ find_representation(int *idx,
     {
       representation_t *result
         = APR_ARRAY_IDX(info->representations, *idx, representation_t *);
-      if (result->original.offset == offset)
+      if (result->offset == offset)
         return result;
     }
 
@@ -1187,25 +700,33 @@ parse_representation(representation_t **
 
   apr_uint64_t offset;
   apr_uint64_t size;
+  apr_uint64_t expanded_size;
   int idx;
 
   char *c = (char *)value->data;
   SVN_ERR(svn_cstring_atoi(&revision, svn_cstring_tokenize(" ", &c)));
   SVN_ERR(svn_cstring_strtoui64(&offset, svn_cstring_tokenize(" ", &c), 0, APR_SIZE_MAX, 10));
   SVN_ERR(svn_cstring_strtoui64(&size, svn_cstring_tokenize(" ", &c), 0, APR_SIZE_MAX, 10));
+  SVN_ERR(svn_cstring_strtoui64(&expanded_size, svn_cstring_tokenize(" ", &c), 0, APR_SIZE_MAX, 10));
 
   result = find_representation(&idx, fs, &revision_info, revision, (apr_size_t)offset);
   if (!result)
     {
+      apr_size_t header_size;
+      svn_boolean_t is_plain;
+      
       result = apr_pcalloc(pool, sizeof(*result));
-      result->revision = revision_info;
-      result->original.offset = (apr_size_t)offset;
-      result->original.size = (apr_size_t)size;
-      SVN_ERR(read_rep_base(&result->delta_base, &result->header_size,
-                            &result->is_plain, fs, file_content,
-                            (apr_size_t)offset + revision_info->original.offset,
+      result->revision = revision;
+      result->expanded_size = (apr_size_t)(expanded_size ? expanded_size : size);
+      result->offset = (apr_size_t)offset;
+      result->size = (apr_size_t)size;
+      SVN_ERR(read_rep_base(&result->delta_base, &header_size,
+                            &is_plain, fs, file_content,
+                            (apr_size_t)offset,
                             pool, scratch_pool));
 
+      result->header_size = header_size;
+      result->is_plain = is_plain;
       svn_sort__array_insert(&result, revision_info->representations, idx);
     }
     
@@ -1214,29 +735,68 @@ parse_representation(representation_t **
   return SVN_NO_ERROR;
 }
 
+/* Get the file content of revision REVISION in FS and return it in *DATA.
+ * Use SCRATCH_POOL for temporary allocations.
+ */
+static svn_error_t *
+get_rep_content(svn_stringbuf_t **content,
+                fs_fs_t *fs,
+                representation_t *representation,
+                svn_stringbuf_t *file_content,
+                apr_pool_t *pool)
+{
+  apr_off_t offset;
+  svn_revnum_t revision = representation->revision;
+  revision_info_t *revision_info = APR_ARRAY_IDX(fs->revisions,
+                                            revision - fs->start_revision,
+                                            revision_info_t*);
+
+  /* not in cache. Is the revision valid at all? */
+  if (revision - fs->start_revision > fs->revisions->nelts)
+    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+                             _("Unknown revision %ld"), revision);
+
+  if (file_content)
+    {
+      offset = representation->offset
+            +  representation->header_size;
+      *content = svn_stringbuf_ncreate(file_content->data + offset,
+                                       representation->size, pool);
+    }
+  else
+    {
+      offset = revision_info->offset
+             + representation->offset
+             + representation->header_size;
+      SVN_ERR(get_content(content, fs, revision, offset,
+                          representation->size, pool));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+
 /* Skip forwards to THIS_CHUNK in REP_STATE and then read the next delta
    window into *NWIN. */
 static svn_error_t *
 read_windows(apr_array_header_t **windows,
              fs_fs_t *fs,
              representation_t *representation,
+             svn_stringbuf_t *file_content,
              apr_pool_t *pool)
 {
-  svn_string_t *content;
-  svn_string_t data;
+  svn_stringbuf_t *content;
   svn_stream_t *stream;
-  apr_size_t offset = representation->original.offset
-                    + representation->header_size;
   char version;
   apr_size_t len = sizeof(version);
 
   *windows = apr_array_make(pool, 0, sizeof(svn_txdelta_window_t *));
 
-  SVN_ERR(get_content(&content, fs, representation->revision->revision, pool));
+  SVN_ERR(get_rep_content(&content, fs, representation, file_content, pool));
 
-  data.data = content->data + offset + 3;
-  data.len = representation->original.size - 3;
-  stream = svn_stream_from_string(&data, pool);
+  content->data += 3;
+  content->len -= 3;
+  stream = svn_stream_from_stringbuf(content, pool);
   SVN_ERR(svn_stream_read(stream, &version, &len));
 
   while (TRUE)
@@ -1259,25 +819,6 @@ read_windows(apr_array_header_t **window
   return SVN_NO_ERROR;
 }
 
-static svn_error_t *
-read_plain(svn_stringbuf_t **content,
-           fs_fs_t *fs,
-           representation_t *representation,
-           apr_pool_t *pool)
-{
-  svn_string_t *data;
-  apr_size_t offset = representation->original.offset
-                    + representation->header_size;
-
-  SVN_ERR(get_content(&data, fs, representation->revision->revision, pool));
-
-  *content = svn_stringbuf_ncreate(data->data + offset,
-                                   representation->original.size,
-                                   pool);
-
-  return SVN_NO_ERROR;
-}
-
 /* Get the undeltified window that is a result of combining all deltas
    from the current desired representation identified in *RB with its
    base representation.  Store the window in *RESULT. */
@@ -1285,6 +826,7 @@ static svn_error_t *
 get_combined_window(svn_stringbuf_t **content,
                     fs_fs_t *fs,
                     representation_t *representation,
+                    svn_stringbuf_t *file_content,
                     apr_pool_t *pool)
 {
   int i;
@@ -1295,16 +837,16 @@ get_combined_window(svn_stringbuf_t **co
   apr_pool_t *iter_pool = svn_pool_create(pool);
 
   if (representation->is_plain)
-    return read_plain(content, fs, representation, pool);
+    return get_rep_content(content, fs, representation, file_content, pool);
 
   *content = get_cached_window(fs, representation, pool);
   if (*content)
     return SVN_NO_ERROR;
   
-  SVN_ERR(read_windows(&windows, fs, representation, sub_pool));
+  SVN_ERR(read_windows(&windows, fs, representation, file_content, sub_pool));
   if (representation->delta_base && representation->delta_base->revision)
     SVN_ERR(get_combined_window(&base_content, fs,
-                                representation->delta_base, sub_pool));
+                                representation->delta_base, NULL, sub_pool));
   else
     base_content = svn_stringbuf_create_empty(sub_pool);
 
@@ -1337,8 +879,7 @@ get_combined_window(svn_stringbuf_t **co
 }
 
 static svn_error_t *
-read_noderev(noderev_t **noderev,
-             fs_fs_t *fs,
+read_noderev(fs_fs_t *fs,
              svn_stringbuf_t *file_content,
              apr_size_t offset,
              revision_info_t *revision_info,
@@ -1346,157 +887,93 @@ read_noderev(noderev_t **noderev,
              apr_pool_t *scratch_pool);
 
 static svn_error_t *
-get_noderev(noderev_t **noderev,
-            fs_fs_t *fs,
-            svn_stringbuf_t *file_content,
-            apr_size_t offset,
-            revision_info_t *revision_info,
-            apr_pool_t *pool,
-            apr_pool_t *scratch_pool)
-{
-  int idx = svn_sort__bsearch_lower_bound(&offset,
-                                          revision_info->node_revs,
-                                          compare_noderev_offsets);
-  if ((idx < 0) || (idx >= revision_info->node_revs->nelts))
-    SVN_ERR(read_noderev(noderev, fs, file_content, offset, revision_info,
-                         pool, scratch_pool));
-  else
-    {
-      *noderev = APR_ARRAY_IDX(revision_info->node_revs, idx, noderev_t *);
-      if ((*noderev)->original.offset != offset)
-        SVN_ERR(read_noderev(noderev, fs, file_content, offset, revision_info,
-                             pool, scratch_pool));
-    }
+parse_dir(fs_fs_t *fs,
+          svn_stringbuf_t *file_content,
+          representation_t *representation,
+          revision_info_t *revision_info,
+          apr_pool_t *pool,
+          apr_pool_t *scratch_pool)
+{
+  svn_stringbuf_t *text;
+  apr_pool_t *iter_pool;
+  apr_pool_t *text_pool;
+  const char *current;
+  const char *revision_key;
+  apr_size_t key_len;
 
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-read_dir(apr_hash_t **hash,
-         fs_fs_t *fs,
-         representation_t *representation,
-         apr_pool_t *scratch_pool)
-{
-  svn_stringbuf_t *text;
-  apr_pool_t *text_pool;
-  svn_stream_t *stream;
-  apr_pool_t *pool;
-
-  *hash = get_cached_dir(fs, representation);
-  if (*hash)
+  if (representation == NULL)
     return SVN_NO_ERROR;
 
-  pool = get_cached_dir_pool(fs);
-  *hash = svn_hash__make(pool);
-  if (representation != NULL)
-    {
-      text_pool = svn_pool_create(scratch_pool);
-      SVN_ERR(get_combined_window(&text, fs, representation, text_pool));
-      stream = svn_stream_from_stringbuf(text, text_pool);
-      SVN_ERR(svn_hash_read2(*hash, stream, SVN_HASH_TERMINATOR, pool));
-      svn_pool_destroy(text_pool);
-    }
+  iter_pool = svn_pool_create(scratch_pool);
+  text_pool = svn_pool_create(scratch_pool);
 
-  set_cached_dir(fs, representation, *hash);
-  
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-parse_dir(fs_fs_t *fs,
-          svn_stringbuf_t *file_content,
-          representation_t *representation,
-          apr_pool_t *pool,
-          apr_pool_t *scratch_pool)
-{
-  apr_hash_t *hash;
-  apr_hash_index_t *hi;
-  apr_pool_t *iter_pool = svn_pool_create(scratch_pool);
-  apr_hash_t *base_dir = svn_hash__make(scratch_pool);
-
-  if (representation == NULL)
-    return SVN_NO_ERROR;
+  SVN_ERR(get_combined_window(&text, fs, representation, file_content,
+                              text_pool));
+  current = text->data;
 
-  if (representation->delta_base && representation->delta_base->dir)
+  revision_key = apr_psprintf(text_pool, "r%d/", representation->revision);
+  key_len = strlen(revision_key);
+  
+  /* Translate the string dir entries into real entries. */
+  while (*current != 'E')
     {
-      apr_array_header_t *dir = representation->delta_base->dir->entries;
-      int i;
+      char *next;
 
-      for (i = 0; i < dir->nelts; ++i)
+      current = strchr(current, '\n');
+      if (current)
+        current = strchr(current+1, '\n');
+      if (current)
+        current = strchr(current+1, '\n');
+      next = current ? strchr(++current, '\n') : NULL;
+      if (next == NULL)
+        return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+           _("Corrupt directory representation in rev %d at offset %ld"),
+                                 representation->revision,
+                                 representation->offset);
+      
+      *next = 0;
+      current = strstr(current, revision_key);
+      if (current)
         {
-          direntry_t *entry = APR_ARRAY_IDX(dir, i, direntry_t *);
-          apr_hash_set(base_dir, entry->name, entry->name_len, entry);
-        }
-    }
+          apr_uint64_t offset;
 
-  SVN_ERR(read_dir(&hash, fs, representation, scratch_pool));
+          SVN_ERR(svn_cstring_strtoui64(&offset, current + key_len, 0,
+                                        APR_SIZE_MAX, 10));
+          SVN_ERR(read_noderev(fs, file_content, (apr_size_t)offset,
+                               revision_info, pool, iter_pool));
 
-  representation->dir = apr_pcalloc(pool, sizeof(*representation->dir));
-  representation->dir->entries
-    = apr_array_make(pool, apr_hash_count(hash), sizeof(direntry_t *));
-
-  /* Translate the string dir entries into real entries. */
-  for (hi = apr_hash_first(pool, hash); hi; hi = apr_hash_next(hi))
-    {
-      const char *name = svn__apr_hash_index_key(hi);
-      svn_string_t *str_val = svn__apr_hash_index_val(hi);
-      apr_size_t offset;
-      revision_info_t *revision_info;
-      apr_size_t name_len = strlen(name);
-      direntry_t *entry = base_dir
-                        ? apr_hash_get(base_dir, name, name_len)
-                        : NULL;
-
-      SVN_ERR(parse_revnode_pos(&revision_info, &offset, fs, str_val));
-
-      if (   !entry
-          || !entry->node->text
-          || entry->node->text->revision != revision_info
-          || entry->node->original.offset != offset)
-        {
-          direntry_t *new_entry = apr_pcalloc(pool, sizeof(*entry));
-          new_entry->name_len = name_len;
-          if (entry)
-            new_entry->name = entry->name;
-          else
-            new_entry->name = apr_pstrdup(pool, name);
-
-          entry = new_entry;
-          SVN_ERR(get_noderev(&entry->node, fs, file_content, offset,
-                              revision_info, pool, iter_pool));
+          svn_pool_clear(iter_pool);
         }
-
-      APR_ARRAY_PUSH(representation->dir->entries, direntry_t *) = entry;
-      svn_pool_clear(iter_pool);
+      current = next+1;
     }
 
   svn_pool_destroy(iter_pool);
+  svn_pool_destroy(text_pool);
   return SVN_NO_ERROR;
 }
 
 static svn_error_t *
-read_noderev(noderev_t **noderev,
-             fs_fs_t *fs,
+read_noderev(fs_fs_t *fs,
              svn_stringbuf_t *file_content,
              apr_size_t offset,
              revision_info_t *revision_info,
              apr_pool_t *pool,
              apr_pool_t *scratch_pool)
 {
-  noderev_t *result = apr_pcalloc(pool, sizeof(*result));
   svn_string_t *line;
+  representation_t *text = NULL;
+  representation_t *props = NULL;
+  apr_size_t start_offset = offset;
   svn_boolean_t is_dir = FALSE;
 
   scratch_pool = svn_pool_create(scratch_pool);
   
-  result->original.offset = offset;
   while (1)
     {
       svn_string_t key;
       svn_string_t value;
       char *sep;
-      const char *start = file_content->data + offset
-                        + revision_info->original.offset;
+      const char *start = file_content->data + offset;
       const char *end = strchr(start, '\n');
 
       line = svn_string_ncreate(start, end - start, scratch_pool);
@@ -1520,37 +997,57 @@ read_noderev(noderev_t **noderev,
 
       if (key_matches(&key, "type"))
         is_dir = strcmp(value.data, "dir") == 0;
-      else if (key_matches(&key, "pred"))
-        SVN_ERR(parse_pred(&result->predecessor, fs, &value));
       else if (key_matches(&key, "text"))
-        SVN_ERR(parse_representation(&result->text, fs, file_content,
-                                     &value, revision_info,
-                                     pool, scratch_pool));
+        {
+          SVN_ERR(parse_representation(&text, fs, file_content,
+                                       &value, revision_info,
+                                       pool, scratch_pool));
+          if (++text->ref_count == 1)
+            text->kind = is_dir ? dir_rep : file_rep;
+        }
       else if (key_matches(&key, "props"))
-        SVN_ERR(parse_representation(&result->props, fs, file_content,
-                                     &value, revision_info,
-                                     pool, scratch_pool));
+        {
+          SVN_ERR(parse_representation(&props, fs, file_content,
+                                       &value, revision_info,
+                                       pool, scratch_pool));
+          if (++props->ref_count == 1)
+            props->kind = is_dir ? dir_property_rep : file_property_rep;
+        }
     }
 
-  result->revision = revision_info;
-  result->original.size = offset - result->original.offset;
-
-  svn_sort__array_insert(&result,
-                         revision_info->node_revs,
-                         svn_sort__bsearch_lower_bound(&offset,
-                                                       revision_info->node_revs,
-                                                       compare_noderev_offsets));
-
-  if (is_dir)
-    SVN_ERR(parse_dir(fs, file_content, result->text,
+  if (is_dir && text && text->ref_count == 1)
+    SVN_ERR(parse_dir(fs, file_content, text, revision_info,
                       pool, scratch_pool));
 
+  if (is_dir)
+    {
+      revision_info->dir_noderev_size += offset - start_offset;
+      revision_info->dir_noderev_count++;
+    }
+  else
+    {
+      revision_info->file_noderev_size += offset - start_offset;
+      revision_info->file_noderev_count++;
+    }
   svn_pool_destroy(scratch_pool);
-  *noderev = result;
 
   return SVN_NO_ERROR;
 }
 
+static apr_size_t
+get_change_count(const char *changes,
+                 apr_size_t len)
+{
+  apr_size_t lines = 0;
+  const char *end = changes + len;
+
+  for (; changes < end; ++changes)
+    if (*changes == '\n')
+      ++lines;
+
+  return lines / 2;
+}
+
 static void print_progress(svn_revnum_t revision)
 {
   printf("%8ld", revision);
@@ -1566,58 +1063,48 @@ read_pack_file(fs_fs_t *fs,
   apr_pool_t *local_pool = svn_pool_create(pool);
   apr_pool_t *iter_pool = svn_pool_create(local_pool);
   int i;
-  svn_stringbuf_t *file_content;
-  revision_pack_t *revisions;
+  apr_off_t file_size = 0;
   const char *pack_folder = get_pack_folder(fs, base, local_pool);
-  SVN_ERR(read_rev_or_pack_file(&file_content, fs, base, local_pool));
-
-  revisions = apr_pcalloc(pool, sizeof(*revisions));
-  revisions->base = base;
-  revisions->fragments = NULL;
-  revisions->info = apr_array_make(pool,
-                                   fs->max_files_per_dir,
-                                   sizeof(revision_info_t*));
-  revisions->filesize = file_content->len;
-  APR_ARRAY_PUSH(fs->packs, revision_pack_t*) = revisions;
 
   SVN_ERR(read_manifest(&manifest, fs, pack_folder, local_pool));
   if (manifest->nelts != fs->max_files_per_dir)
     return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, NULL);
 
+  SVN_ERR(rev_or_pack_file_size(&file_size, fs, base, pool));
+
   for (i = 0; i < manifest->nelts; ++i)
     {
       apr_size_t root_node_offset;
-      svn_string_t rev_content;
+      svn_stringbuf_t *rev_content;
   
       revision_info_t *info = apr_pcalloc(pool, sizeof(*info));
-      info->node_revs = apr_array_make(iter_pool, 4, sizeof(noderev_t*));
       info->representations = apr_array_make(iter_pool, 4, sizeof(representation_t*));
 
       info->revision = base + i;
-      info->original.offset = APR_ARRAY_IDX(manifest, i, apr_size_t);
-      info->original.end = i+1 < manifest->nelts
+      info->offset = APR_ARRAY_IDX(manifest, i, apr_size_t);
+      info->end = i+1 < manifest->nelts
                          ? APR_ARRAY_IDX(manifest, i+1 , apr_size_t)
-                         : file_content->len;
-      SVN_ERR(read_revision_header(&info->original.changes,
-                                   &info->original.changes_len,
+                         : file_size;
+
+      SVN_ERR(get_content(&rev_content, fs, info->revision,
+                          info->offset,
+                          info->end - info->offset,
+                          iter_pool));
+      
+      SVN_ERR(read_revision_header(&info->changes,
+                                   &info->changes_len,
                                    &root_node_offset,
-                                   file_content,
-                                   APR_ARRAY_IDX(manifest, i , apr_size_t),
-                                   info->original.end,
+                                   rev_content,
                                    iter_pool));
-
-      APR_ARRAY_PUSH(revisions->info, revision_info_t*) = info;
-      APR_ARRAY_PUSH(fs->revisions, revision_info_t*) = info;
       
-      rev_content.data = file_content->data + info->original.offset;
-      rev_content.len = info->original.end - info->original.offset;
-      set_cached_content(fs->cache, info->revision, &rev_content);
-
-      SVN_ERR(read_noderev(&info->root_noderev, fs, file_content,
+      info->change_count
+        = get_change_count(rev_content->data + info->changes,
+                           info->changes_len);
+      SVN_ERR(read_noderev(fs, rev_content,
                            root_node_offset, info, pool, iter_pool));
 
-      info->node_revs = apr_array_copy(pool, info->node_revs);
       info->representations = apr_array_copy(pool, info->representations);
+      APR_ARRAY_PUSH(fs->revisions, revision_info_t*) = info;
       
       svn_pool_clear(iter_pool);
     }
@@ -1635,44 +1122,35 @@ read_revision_file(fs_fs_t *fs,
 {
   apr_size_t root_node_offset;
   apr_pool_t *local_pool = svn_pool_create(pool);
-  svn_stringbuf_t *file_content;
-  svn_string_t rev_content;
-  revision_pack_t *revisions = apr_pcalloc(pool, sizeof(*revisions));
+  svn_stringbuf_t *rev_content;
   revision_info_t *info = apr_pcalloc(pool, sizeof(*info));
+  apr_off_t file_size = 0;
 
-  SVN_ERR(read_rev_or_pack_file(&file_content, fs, revision, local_pool));
+  SVN_ERR(rev_or_pack_file_size(&file_size, fs, revision, pool));
 
-  info->node_revs = apr_array_make(pool, 4, sizeof(noderev_t*));
   info->representations = apr_array_make(pool, 4, sizeof(representation_t*));
 
   info->revision = revision;
-  info->original.offset = 0;
-  info->original.end = file_content->len;
-  SVN_ERR(read_revision_header(&info->original.changes,
-                               &info->original.changes_len,
+  info->offset = 0;
+  info->end = file_size;
+
+  SVN_ERR(get_content(&rev_content, fs, revision, 0, file_size, local_pool));
+
+  SVN_ERR(read_revision_header(&info->changes,
+                               &info->changes_len,
                                &root_node_offset,
-                               file_content,
-                               0,
-                               info->original.end,
+                               rev_content,
                                local_pool));
 
   APR_ARRAY_PUSH(fs->revisions, revision_info_t*) = info;
 
-  revisions->base = revision;
-  revisions->fragments = NULL;
-  revisions->info = apr_array_make(pool, 1, sizeof(revision_info_t*));
-  revisions->filesize = file_content->len;
-  APR_ARRAY_PUSH(revisions->info, revision_info_t*) = info;
-  APR_ARRAY_PUSH(fs->packs, revision_pack_t*) = revisions;
-
-  rev_content.data = file_content->data + info->original.offset;
-  rev_content.len = info->original.end - info->original.offset;
-  set_cached_content(fs->cache, info->revision, &rev_content);
+  info->change_count
+    = get_change_count(rev_content->data + info->changes,
+                       info->changes_len);
 
-  SVN_ERR(read_noderev(&info->root_noderev, fs, file_content,
+  SVN_ERR(read_noderev(fs, rev_content,
                        root_node_offset, info,
                        pool, local_pool));
-  APR_ARRAY_PUSH(info->node_revs, noderev_t*) = info->root_noderev;
 
   if (revision % fs->max_files_per_dir == 0)
     print_progress(revision);
@@ -1690,18 +1168,14 @@ read_revisions(fs_fs_t **fs,
                apr_pool_t *pool)
 {
   svn_revnum_t revision;
-  apr_size_t content_cache_size;
   apr_size_t window_cache_size;
-  apr_size_t dir_cache_size;
 
   /* determine cache sizes */
 
   if (memsize < 100)
     memsize = 100;
   
-  content_cache_size = memsize * 7 / 10 > 4000 ? 4000 : memsize * 7 / 10;
-  window_cache_size = memsize * 2 / 10 * 1024 * 1024;
-  dir_cache_size = (memsize / 10) * 16000;
+  window_cache_size = memsize * 1024 * 1024;
   
   SVN_ERR(fs_open(fs, path, pool));
 
@@ -1710,19 +1184,7 @@ read_revisions(fs_fs_t **fs,
   (*fs)->revisions = apr_array_make(pool,
                                     (*fs)->max_revision + 1 - (*fs)->start_revision,
                                     sizeof(revision_info_t *));
-  (*fs)->packs = apr_array_make(pool,
-                                ((*fs)->min_unpacked_rev - (*fs)->start_revision)
-                                   / (*fs)->max_files_per_dir,
-                                sizeof(revision_pack_t *));
   (*fs)->null_base = apr_pcalloc(pool, sizeof(*(*fs)->null_base));
-  (*fs)->cache = create_content_cache
-                    (apr_allocator_owner_get
-                         (svn_pool_create_allocator(FALSE)),
-                          content_cache_size * 1024 * 1024);
-  (*fs)->dir_cache = create_dir_cache
-                    (apr_allocator_owner_get
-                         (svn_pool_create_allocator(FALSE)),
-                          dir_cache_size);
   (*fs)->window_cache = create_window_cache
                     (apr_allocator_owner_get
                          (svn_pool_create_allocator(FALSE)),
@@ -1739,966 +1201,194 @@ read_revisions(fs_fs_t **fs,
   return SVN_NO_ERROR;
 }
 
-static apr_size_t
-get_max_offset_len(const revision_pack_t *pack)
+typedef struct rep_pack_stats_t
 {
-  apr_size_t max_future_size = pack->filesize * 2 + 10000;
-  apr_size_t result = 0;
-
-  while (max_future_size > 0)
-    {
-      ++result;
-      max_future_size /= 10;
-    }
+  apr_int64_t count;
+  apr_int64_t packed_size;
+  apr_int64_t expanded_size;
+  apr_int64_t overhead_size;
+} rep_pack_stats_t;
 
-  return result;
-}
-
-static svn_error_t *
-add_revisions_pack_heads(revision_pack_t *pack,
-                         apr_pool_t *pool)
+typedef struct representation_stats_t
 {
-  int i;
-  revision_info_t *info;
-  apr_size_t offset_len = get_max_offset_len(pack);
-  fragment_t fragment;
-
-  /* allocate fragment arrays */
-
-  int fragment_count = 1;
-  for (i = 0; i < pack->info->nelts; ++i)
-    {
-      info = APR_ARRAY_IDX(pack->info, i, revision_info_t*);
-      fragment_count += info->node_revs->nelts
-                      + info->representations->nelts
-                      + 2;
-    }
-
-  pack->target_offset = pack->info->nelts > 1 ? 64 : 0;
-  pack->fragments = apr_array_make(pool,
-                                   fragment_count,
-                                   sizeof(fragment_t));
-
-  /* put revision headers first */
-
-  for (i = 0; i < pack->info->nelts - 1; ++i)
-    {
-      info = APR_ARRAY_IDX(pack->info, i, revision_info_t*);
-      info->target.offset = pack->target_offset;
-      
-      fragment.data = info;
-      fragment.kind = header_fragment;
-      fragment.position = pack->target_offset;
-      APR_ARRAY_PUSH(pack->fragments, fragment_t) = fragment;
-
-      pack->target_offset += 2 * offset_len + 3;
-    }
-
-  info = APR_ARRAY_IDX(pack->info, pack->info->nelts - 1, revision_info_t*);
-  info->target.offset = pack->target_offset;
+  rep_pack_stats_t total;
+  rep_pack_stats_t uniques;
+  rep_pack_stats_t shared;
   
-  /* followed by the changes list */
-
-  for (i = 0; i < pack->info->nelts; ++i)
-    {
-      info = APR_ARRAY_IDX(pack->info, i, revision_info_t*);
-
-      info->target.changes = pack->target_offset - info->target.offset;
-      info->target.changes_len = info->original.changes_len;
-
-      fragment.data = info;
-      fragment.kind = changes_fragment;
-      fragment.position = pack->target_offset;
-      APR_ARRAY_PUSH(pack->fragments, fragment_t) = fragment;
-
-      pack->target_offset += info->original.changes_len;
-    }
-
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-get_target_offset(apr_size_t **current_pos,
-                  apr_array_header_t **fragments,
-                  fs_fs_t *fs,
-                  revision_info_t *info)
-{
-  int i;
-  revision_pack_t *pack;
-  svn_revnum_t revision = info->revision;
-
-  if (fs->min_unpacked_rev > revision)
-    {
-      i = (revision - fs->start_revision) / fs->max_files_per_dir;
-    }
-  else
-    {
-      i = (fs->min_unpacked_rev - fs->start_revision) / fs->max_files_per_dir;
-      i += revision - fs->min_unpacked_rev;
-    }
-
-  pack = APR_ARRAY_IDX(fs->packs, i, revision_pack_t*);
-  *current_pos = &pack->target_offset;
-  *fragments = pack->fragments;
-
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-add_noderev_recursively(fs_fs_t *fs,
-                        noderev_t *node,
-                        apr_pool_t *pool);
-
-static svn_error_t *
-add_representation_recursively(fs_fs_t *fs,
-                               representation_t *representation,
-                               enum fragment_kind_t kind,
-                               apr_pool_t *pool)
-{
-  apr_size_t *current_pos;
-  apr_array_header_t *fragments;
-  fragment_t fragment;
-  
-  if (   representation == NULL
-      || representation->covered
-      || (representation->dir && kind != dir_fragment)
-      || representation == fs->null_base)
-    return SVN_NO_ERROR;
-
-  SVN_ERR(get_target_offset(&current_pos, &fragments,
-                            fs, representation->revision));
-  representation->target.offset = *current_pos;
-  representation->covered = TRUE;
-  
-  fragment.data = representation;
-  fragment.kind = kind;
-  fragment.position = *current_pos;
-  APR_ARRAY_PUSH(fragments, fragment_t) = fragment;
-
-  if (   kind != dir_fragment
-      && representation->delta_base && representation->delta_base->dir)
-    {
-      apr_pool_t *text_pool = svn_pool_create(pool);
-      svn_stringbuf_t *content;
-
-      get_combined_window(&content, fs, representation, text_pool);
-      representation->target.size = content->len;
-      *current_pos += representation->target.size + 13;
-
-      svn_pool_destroy(text_pool);
-    }
-  else
-    if (   kind == dir_fragment
-        || (representation->delta_base && representation->delta_base->dir))
-      {
-        if (representation->original.size < 50)
-          *current_pos += 300;
-        else
-          *current_pos += representation->original.size * 3 + 150;
-      }
-    else
-      {
-        representation->target.size = representation->original.size;
-
-        if (representation->delta_base &&
-            (representation->delta_base != fs->null_base))
-          *current_pos += representation->original.size + 50;
-        else
-          *current_pos += representation->original.size + 13;
-      }
-
-  if (representation->delta_base)
-    SVN_ERR(add_representation_recursively(fs,
-                                           representation->delta_base,
-                                           kind,
-                                           pool));
-
-  if (representation->dir)
-    {
-      int i;
-      apr_array_header_t *entries = representation->dir->entries;
-
-      for (i = 0; i < entries->nelts; ++i)
-        {
-          direntry_t *entry = APR_ARRAY_IDX(entries, i, direntry_t *);
-          if (entry->node)
-            SVN_ERR(add_noderev_recursively(fs, entry->node, pool));
-        }
-    }
-
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-add_noderev_recursively(fs_fs_t *fs,
-                        noderev_t *node,
-                        apr_pool_t *pool)
-{
-  apr_size_t *current_pos;
-  apr_array_header_t *fragments;
-  fragment_t fragment;
-
-  if (node->covered)
-    return SVN_NO_ERROR;
-
-  SVN_ERR(get_target_offset(&current_pos, &fragments, fs, node->revision));
-  node->covered = TRUE;
-  node->target.offset = *current_pos;
-
-  fragment.data = node;
-  fragment.kind = noderev_fragment;
-  fragment.position = *current_pos;
-  APR_ARRAY_PUSH(fragments, fragment_t) = fragment;
-
-  *current_pos += node->original.size + 40;
-  
-  if (node->text && node->text->dir)
-    SVN_ERR(add_representation_recursively(fs, node->text, dir_fragment, pool));
-  else
-    SVN_ERR(add_representation_recursively(fs, node->text, file_fragment, pool));
-
-  SVN_ERR(add_representation_recursively(fs, node->props, property_fragment, pool));
-
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-add_revisions_pack_tail(revision_pack_t *pack,
-                        apr_pool_t *pool)
-{
-  int i;
-  revision_info_t *info;
-  apr_size_t offset_len = get_max_offset_len(pack);
-  fragment_t fragment;
-
-  /* put final revision header last and fix up revision lengths */
-
-  info = APR_ARRAY_IDX(pack->info, pack->info->nelts-1, revision_info_t*);
-
-  fragment.data = info;
-  fragment.kind = header_fragment;
-  fragment.position = pack->target_offset;
-  APR_ARRAY_PUSH(pack->fragments, fragment_t) = fragment;
-
-  pack->target_offset += 2 * offset_len + 3;
-
-  for (i = 0; i < pack->info->nelts; ++i)
-    {
-      info = APR_ARRAY_IDX(pack->info, i, revision_info_t*);
-      info->target.end = pack->target_offset;
-    }
-
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-reorder_revisions(fs_fs_t *fs,
-                  apr_pool_t *pool)
-{
-  int i, k;
-
-  /* headers and changes */
-
-  for (i = 0; i < fs->packs->nelts; ++i)
-    {
-      revision_pack_t *pack = APR_ARRAY_IDX(fs->packs, i, revision_pack_t*);
-      SVN_ERR(add_revisions_pack_heads(pack, pool));
-    }
-
-  /* representations & nodes */
-
-  for (i = fs->revisions->nelts-1; i >= 0; --i)
-    {
-      revision_info_t *info = APR_ARRAY_IDX(fs->revisions, i, revision_info_t*);
-      for (k = info->node_revs->nelts - 1; k >= 0; --k)
-        {
-          noderev_t *node = APR_ARRAY_IDX(info->node_revs, k, noderev_t*);
-          SVN_ERR(add_noderev_recursively(fs, node, pool));
-        }
-
-      if (info->revision % fs->max_files_per_dir == 0)
-        print_progress(info->revision);
-    }
-
-  /* pack file tails */
-
-  for (i = 0; i < fs->packs->nelts; ++i)
-    {
-      revision_pack_t *pack = APR_ARRAY_IDX(fs->packs, i, revision_pack_t*);
-      SVN_ERR(add_revisions_pack_tail(pack, pool));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-get_fragment_content(svn_string_t **content,
-                     fs_fs_t *fs,
-                     fragment_t *fragment,
-                     apr_pool_t *pool);
-
-static svn_error_t *
-update_noderevs(fs_fs_t *fs,
-                revision_pack_t *pack,
-                apr_pool_t *pool)
-{
-  int i;
-  apr_pool_t *itempool = svn_pool_create(pool);
-
-  for (i = 0; i < pack->fragments->nelts; ++i)
-    {
-      fragment_t *fragment = &APR_ARRAY_IDX(pack->fragments, i, fragment_t);
-      if (fragment->kind == dir_fragment)
-        {
-          svn_string_t *content;
-
-          SVN_ERR(get_fragment_content(&content, fs, fragment, itempool));
-          svn_pool_clear(itempool);
-        }
-    }
-
-  svn_pool_destroy(itempool);
+  apr_int64_t references;
+  apr_int64_t expanded_size;
+} representation_stats_t;
 
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-get_content_length(apr_size_t *length,
-                   fs_fs_t *fs,
-                   fragment_t *fragment,
-                   svn_boolean_t add_padding,
-                   apr_pool_t *pool)
+typedef struct node_stats_t
 {
-  svn_string_t *content;
-
-  SVN_ERR(get_fragment_content(&content, fs, fragment, pool));
-  if (add_padding)
-    switch (fragment->kind)
-      {
-        case dir_fragment:
-          *length = content->len + 16;
-          break;
-        case noderev_fragment:
-          *length = content->len + 3;
-          break;
-        default:
-          *length = content->len;
-          break;
-      }
-  else
-    *length = content->len;
-    
-  return SVN_NO_ERROR;
-}
+  apr_int64_t count;
+  apr_int64_t size;
+} node_stats_t;
 
 static void
-move_fragment(fragment_t *fragment,
-              apr_size_t new_position)
+add_rep_pack_stats(rep_pack_stats_t *stats,
+                   representation_t *rep)
 {
-  revision_info_t *info;
-  representation_t *representation;
-  noderev_t *node;
+  stats->count++;
   
-  fragment->position = new_position; 
-
-  switch (fragment->kind)
-    {
-      case header_fragment:
-        info = fragment->data;
-        info->target.offset = new_position;
-        break;
-
-      case changes_fragment:
-        info = fragment->data;
-        info->target.changes = new_position - info->target.offset;
-        break;
-
-      case property_fragment:
-      case file_fragment:
-      case dir_fragment:
-        representation = fragment->data;
-        representation->target.offset = new_position;
-        break;
-
-      case noderev_fragment:
-        node = fragment->data;
-        node->target.offset = new_position;
-        break;
-    }
+  stats->packed_size += rep->size;
+  stats->expanded_size += rep->expanded_size;
+  stats->overhead_size += rep->header_size + 7;
 }
 
-static svn_error_t *
-pack_revisions(fs_fs_t *fs,
-               revision_pack_t *pack,
-               apr_pool_t *pool)
+static void
+add_rep_stats(representation_stats_t *stats,
+              representation_t *rep)
 {
-  int i;
-  fragment_t *fragment, *next;
-  svn_boolean_t needed_to_expand;
-  revision_info_t *info;
-  apr_size_t current_pos, len, old_len;
-
-  apr_pool_t *itempool = svn_pool_create(pool);
-
-  SVN_ERR(update_noderevs(fs, pack, pool));
-
-  current_pos = pack->info->nelts > 1 ? 64 : 0;
-  for (i = 0; i + 1 < pack->fragments->nelts; ++i)
-    {
-      fragment = &APR_ARRAY_IDX(pack->fragments, i, fragment_t);
-      SVN_ERR(get_content_length(&len, fs, fragment, TRUE, itempool));
-      move_fragment(fragment, current_pos);
-      current_pos += len;
-
-      svn_pool_clear(itempool);
-    }
-
-  fragment = &APR_ARRAY_IDX(pack->fragments, pack->fragments->nelts-1, fragment_t);
-  fragment->position = current_pos;
-
-  do
-    {
-      needed_to_expand = FALSE;
-      current_pos = pack->info->nelts > 1 ? 64 : 0;
-
-      for (i = 0; i + 1 < pack->fragments->nelts; ++i)
-        {
-          fragment = &APR_ARRAY_IDX(pack->fragments, i, fragment_t);
-          next = &APR_ARRAY_IDX(pack->fragments, i + 1, fragment_t);
-          old_len = next->position - fragment->position;
-
-          SVN_ERR(get_content_length(&len, fs, fragment, FALSE, itempool));
-
-          if (len > old_len)
-            {
-              len = (apr_size_t)(len * 1.1) + 10;
-              needed_to_expand = TRUE;
-            }
-          else
-            len = old_len;
-
-          if (i == pack->info->nelts - 1)
-            {
-              info = APR_ARRAY_IDX(pack->info, pack->info->nelts - 1, revision_info_t*);
-              info->target.offset = current_pos;
-            }
-
-          move_fragment(fragment, current_pos);
-          current_pos += len;
-
-          svn_pool_clear(itempool);
-        }
-
-      fragment = &APR_ARRAY_IDX(pack->fragments, pack->fragments->nelts-1, fragment_t);
-      fragment->position = current_pos;
-
-      SVN_ERR(get_content_length(&len, fs, fragment, FALSE, itempool));
-      current_pos += len;
-
-      for (i = 0; i < pack->info->nelts; ++i)
-        {
-          info = APR_ARRAY_IDX(pack->info, i, revision_info_t*);
-          info->target.end = current_pos;
-        }
-    }
-  while (needed_to_expand);
-  
-  svn_pool_destroy(itempool);
+  add_rep_pack_stats(&stats->total, rep);
+  if (rep->ref_count == 1)
+    add_rep_pack_stats(&stats->uniques, rep);
+  else
+    add_rep_pack_stats(&stats->shared, rep);
 
-  return SVN_NO_ERROR;
+  stats->references += rep->ref_count;
+  stats->expanded_size += rep->ref_count * rep->expanded_size;
 }
 
-static svn_error_t *
-write_revisions(fs_fs_t *fs,
-                revision_pack_t *pack,
+static void
+print_rep_stats(representation_stats_t *stats,
                 apr_pool_t *pool)
 {
-  int i;
-  fragment_t *fragment = NULL;
-  svn_string_t *content;
-
-  apr_pool_t *itempool = svn_pool_create(pool);
-  apr_pool_t *iterpool = svn_pool_create(pool);
-
-  apr_file_t *file;
-  apr_size_t current_pos = 0;
-  svn_stringbuf_t *null_buffer = svn_stringbuf_create_empty(iterpool);
-
-  const char *dir = apr_psprintf(iterpool, "%s/new/%ld%s",
-                                  fs->path, pack->base / fs->max_files_per_dir,
-                                  pack->info->nelts > 1 ? ".pack" : "");
-  SVN_ERR(svn_io_make_dir_recursively(dir, pool));
-  SVN_ERR(svn_io_file_open(&file,
-                            pack->info->nelts > 1
-                              ? apr_psprintf(iterpool, "%s/pack", dir)
-                              : apr_psprintf(iterpool, "%s/%ld", dir, pack->base),
-                            APR_WRITE | APR_CREATE | APR_BUFFERED,
-                            APR_OS_DEFAULT,
-                            iterpool));
-
-  for (i = 0; i < pack->fragments->nelts; ++i)
-    {
-      apr_size_t padding;
-      fragment = &APR_ARRAY_IDX(pack->fragments, i, fragment_t);
-      SVN_ERR(get_fragment_content(&content, fs, fragment, itempool));
-
-      SVN_ERR_ASSERT(fragment->position >= current_pos);
-      if (   fragment->kind == header_fragment
-          && i+1 < pack->fragments->nelts)
-        padding = APR_ARRAY_IDX(pack->fragments, i+1, fragment_t).position -
-                  content->len - current_pos;
-      else
-        padding = fragment->position - current_pos;
-
-      if (padding)
-        {
-          while (null_buffer->len < padding)
-            svn_stringbuf_appendbyte(null_buffer, 0);
-
-          SVN_ERR(svn_io_file_write_full(file,
-                                          null_buffer->data,
-                                          padding,
-                                          NULL,
-                                          itempool));
-          current_pos += padding;
-        }
-
-      SVN_ERR(svn_io_file_write_full(file,
-                                      content->data,
-                                      content->len,
-                                      NULL,
-                                      itempool));
-      current_pos += content->len;
-
-      svn_pool_clear(itempool);
-    }
-
-  apr_file_close(file);
-
-  if (pack->info->nelts > 1)
-    {
-      svn_stream_t *stream;
-      SVN_ERR(svn_io_file_open(&file,
-                                apr_psprintf(iterpool, "%s/manifest", dir),
-                                APR_WRITE | APR_CREATE | APR_BUFFERED,
-                                APR_OS_DEFAULT,
-                                iterpool));
-      stream = svn_stream_from_aprfile2(file, FALSE, iterpool);
-
-      for (i = 0; i < pack->info->nelts; ++i)
-        {
-          revision_info_t *info = APR_ARRAY_IDX(pack->info, i,
-                                                revision_info_t *);
-          SVN_ERR(svn_stream_printf(stream, itempool,
-                                    "%" APR_UINT64_T_FMT "\n",
-                                    info->target.offset));
-          svn_pool_clear(itempool);
-        }
-    }
-
-  svn_pool_destroy(itempool);
-  svn_pool_destroy(iterpool);
-
-  return SVN_NO_ERROR;
+  printf(_("%20s bytes in %12s reps\n"
+           "%20s bytes in %12s shared reps\n"
+           "%20s bytes expanded size\n"
+           "%20s bytes expanded shared size\n"
+           "%20s bytes with rep-sharing off\n"
+           "%20s shared references\n"),
+         svn__i64toa_sep(stats->total.packed_size, ',', pool),
+         svn__i64toa_sep(stats->total.count, ',', pool),
+         svn__i64toa_sep(stats->shared.packed_size, ',', pool),
+         svn__i64toa_sep(stats->shared.count, ',', pool),
+         svn__i64toa_sep(stats->total.expanded_size, ',', pool),
+         svn__i64toa_sep(stats->shared.expanded_size, ',', pool),
+         svn__i64toa_sep(stats->expanded_size, ',', pool),
+         svn__i64toa_sep(stats->references - stats->total.count, ',', pool));
 }
 
-static svn_error_t *
-pack_and_write_revisions(fs_fs_t *fs,
-                         apr_pool_t *pool)
-{
-  int i;
-
-  SVN_ERR(svn_io_make_dir_recursively(apr_psprintf(pool, "%s/new",
-                                                   fs->path),
-                                      pool));
-
-  for (i = 0; i < fs->packs->nelts; ++i)
-    {
-      revision_pack_t *pack = APR_ARRAY_IDX(fs->packs, i, revision_pack_t*);
-      if (pack->base % fs->max_files_per_dir == 0)
-        print_progress(pack->base);
-
-      SVN_ERR(pack_revisions(fs, pack, pool));
-      SVN_ERR(write_revisions(fs, pack, pool));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-get_updated_dir(svn_string_t **content,
-                fs_fs_t *fs,
-                representation_t *representation,
-                apr_pool_t *pool,
-                apr_pool_t *scratch_pool)
+static void
+print_stats(fs_fs_t *fs,
+            apr_pool_t *pool)
 {
-  apr_hash_t *hash;
-  apr_pool_t *hash_pool = svn_pool_create(scratch_pool);
-  apr_array_header_t *dir = representation->dir->entries;
-  int i;
-  svn_stream_t *stream;
-  svn_stringbuf_t *result;
+  int i, k;
   
-  SVN_ERR(read_dir(&hash, fs, representation, scratch_pool));
-  hash = apr_hash_copy(hash_pool, hash);
-  for (i = 0; i < dir->nelts; ++i)
-    {
-      char buffer[256];
-      svn_string_t *new_val;
-      apr_size_t pos;
-      direntry_t *entry = APR_ARRAY_IDX(dir, i, direntry_t *);
-      svn_string_t *str_val = apr_hash_get(hash, entry->name, entry->name_len);
-      if (str_val == NULL)
-        return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
-                                 _("Dir entry '%s' not found"), entry->name);
-
-      SVN_ERR_ASSERT(str_val->len < sizeof(buffer));
-      
-      memcpy(buffer, str_val->data, str_val->len+1);
-      pos = strchr(buffer, '/') - buffer + 1;
-      pos += svn__ui64toa(buffer + pos, entry->node->target.offset - entry->node->revision->target.offset);
-      new_val = svn_string_ncreate(buffer, pos, hash_pool);
-
-      apr_hash_set(hash, entry->name, entry->name_len, new_val);
-    }
-
-  result = svn_stringbuf_create_ensure(representation->target.size, pool);
-  stream = svn_stream_from_stringbuf(result, hash_pool);
-  SVN_ERR(svn_hash_write2(hash, stream, SVN_HASH_TERMINATOR, hash_pool));
-  svn_pool_destroy(hash_pool);
-
-  *content = svn_stringbuf__morph_into_string(result);
+  representation_stats_t file_rep_stats = { { 0 } };
+  representation_stats_t dir_rep_stats = { { 0 } };
+  representation_stats_t file_prop_rep_stats = { { 0 } };
+  representation_stats_t dir_prop_rep_stats = { { 0 } };
+  representation_stats_t total_rep_stats = { { 0 } };
+
+  node_stats_t dir_node_stats = { 0 };
+  node_stats_t file_node_stats = { 0 };
+  node_stats_t total_node_stats = { 0 };
+
+  apr_int64_t total_size = 0;
+  apr_int64_t change_count = 0;
+  apr_int64_t change_len = 0;
   
-  return SVN_NO_ERROR;
-}
-
-struct diff_write_baton_t
-{
-  svn_stream_t *stream;
-  apr_size_t size;
-};
-
-static svn_error_t *
-diff_write_handler(void *baton,
-                   const char *data,
-                   apr_size_t *len)
-{
-  struct diff_write_baton_t *whb = baton;
-
-  SVN_ERR(svn_stream_write(whb->stream, data, len));
-  whb->size += *len;
-
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-diff_stringbufs(svn_stringbuf_t *diff,
-                apr_size_t *inflated_size,
-                svn_string_t *base,
-                svn_string_t *content,
-                apr_pool_t *pool)
-{
-  svn_txdelta_window_handler_t diff_wh;
-  void *diff_whb;
-  struct diff_write_baton_t whb;
-
-  svn_stream_t *stream;
-  svn_stream_t *source = svn_stream_from_string(base, pool);
-  svn_stream_t *target = svn_stream_from_stringbuf(diff, pool);
-
-  /* Prepare to write the svndiff data. */
-  svn_txdelta_to_svndiff3(&diff_wh,
-                          &diff_whb,
-                          target,
-                          1,
-                          SVN_DELTA_COMPRESSION_LEVEL_DEFAULT,
-                          pool);
-
-  whb.stream = svn_txdelta_target_push(diff_wh, diff_whb, source, pool);
-  whb.size = 0;
-
-  stream = svn_stream_create(&whb, pool);
-  svn_stream_set_write(stream, diff_write_handler);
-
-  SVN_ERR(svn_stream_write(stream, content->data, &content->len));
-  SVN_ERR(svn_stream_close(whb.stream));
-  SVN_ERR(svn_stream_close(stream));
-
-  *inflated_size = whb.size;
-  return SVN_NO_ERROR;
-}
-
-static void
-update_id(svn_stringbuf_t *node_rev,
-          const char *key,
-          noderev_t *node)
-{
-  char *newline_pos = 0;
-  char *pos;
-
-  pos = strstr(node_rev->data, key);
-  if (pos)
-    pos = strchr(pos, '/');
-  if (pos)
-    newline_pos = strchr(++pos, '\n');
-
-  if (pos && newline_pos)
-    {
-      char temp[SVN_INT64_BUFFER_SIZE];
-      apr_size_t len = svn__i64toa(temp, node->target.offset - node->revision->target.offset);
-      svn_stringbuf_replace(node_rev,
-                            pos - node_rev->data, newline_pos - pos,
-                            temp, len);
-    }
-}
-
-static void
-update_text(svn_stringbuf_t *node_rev,
-            const char *key,
-            representation_t *representation,
-            apr_pool_t *scratch_pool)
-{
-  apr_size_t key_len = strlen(key);
-  char *pos = strstr(node_rev->data, key);
-  char *val_pos;
-
-  if (!pos)
-    return;
-
-  val_pos = pos + key_len;
-  if (representation->dir)
-    {
-      char *newline_pos = strchr(val_pos, '\n');
-      svn_checksum_t checksum = {representation->dir->target_md5,
-                                 svn_checksum_md5};
-      const char* temp = apr_psprintf(scratch_pool, "%ld %" APR_UINT64_T_FMT " %" 
-                                      APR_UINT64_T_FMT" %" APR_SIZE_T_FMT " %s",
-                                      representation->revision->revision,
-                                      representation->target.offset - representation->revision->target.offset,
-                                      representation->target.size,
-                                      representation->dir->size,
-                                      svn_checksum_to_cstring(&checksum,
-                                                              scratch_pool));
-
-      svn_stringbuf_replace(node_rev,
-                            val_pos - node_rev->data, newline_pos - val_pos,
-                            temp, strlen(temp));
-    }
-  else
+  for (i = 0; i < fs->revisions->nelts; ++i)
     {
-      const char* temp;
-      char *end_pos = strchr(val_pos, ' ');
+      revision_info_t *revision = APR_ARRAY_IDX(fs->revisions, i,
+                                                revision_info_t *);
+      change_count += revision->change_count;
+      change_len += revision->changes_len;
+      total_size += revision->end - revision->offset;
+
+      dir_node_stats.count += revision->dir_noderev_count;
+      dir_node_stats.size += revision->dir_noderev_size;
+      file_node_stats.count += revision->file_noderev_count;
+      file_node_stats.size += revision->file_noderev_size;
+      total_node_stats.count += revision->dir_noderev_count
+                              + revision->file_noderev_count;
+      total_node_stats.size += revision->dir_noderev_size
+                             + revision->file_noderev_size;
       
-      val_pos = end_pos + 1;
-      end_pos = strchr(strchr(val_pos, ' ') + 1, ' ');
-      temp = apr_psprintf(scratch_pool, "%" APR_UINT64_T_FMT " %" APR_UINT64_T_FMT,
-                          representation->target.offset - representation->revision->target.offset,
-                          representation->target.size);
-
-      svn_stringbuf_replace(node_rev,
-                            val_pos - node_rev->data, end_pos - val_pos,
-                            temp, strlen(temp));
-    }
-}
-
-static svn_error_t *
-get_fragment_content(svn_string_t **content,
-                     fs_fs_t *fs,
-                     fragment_t *fragment,
-                     apr_pool_t *pool)
-{
-  revision_info_t *info;
-  representation_t *representation;
-  noderev_t *node;
-  svn_string_t *revision_content, *base_content;
-  svn_stringbuf_t *header, *node_rev, *text;
-  apr_size_t header_size;
-  svn_checksum_t *checksum = NULL;
-
-  switch (fragment->kind)
-    {
-      case header_fragment:
-        info = fragment->data;
-        *content = svn_string_createf(pool,
-                                      "\n%" APR_UINT64_T_FMT " %" APR_UINT64_T_FMT "\n",
-                                      info->root_noderev->target.offset - info->target.offset,
-                                      info->target.changes);
-        return SVN_NO_ERROR;
-
-      case changes_fragment:
-        info = fragment->data;
-        SVN_ERR(get_content(&revision_content, fs, info->revision, pool));
-        
-        *content = svn_string_create_empty(pool);
-        (*content)->data = revision_content->data + info->original.changes;
-        (*content)->len = info->target.changes_len;
-        return SVN_NO_ERROR;
-
-      case property_fragment:
-      case file_fragment:
-        representation = fragment->data;
-        SVN_ERR(get_content(&revision_content, fs,
-                            representation->revision->revision, pool));
-
-        if (representation->delta_base)
-          if (representation->delta_base->dir)
+      for (k = 0; k < revision->representations->nelts; ++k)
+        {
+          representation_t *rep = APR_ARRAY_IDX(revision->representations,
+                                                k, representation_t *);
+          switch(rep->kind)
             {
-              SVN_ERR(get_combined_window(&text, fs, representation, pool));
-              representation->target.size = text->len;
-
-              svn_stringbuf_insert(text, 0, "PLAIN\n", 6);
-              svn_stringbuf_appendcstr(text, "ENDREP\n");
-              *content = svn_stringbuf__morph_into_string(text);
-
-              return SVN_NO_ERROR;
+              case file_rep:
+                add_rep_stats(&file_rep_stats, rep);
+                break;
+              case dir_rep:
+                add_rep_stats(&dir_rep_stats, rep);
+                break;
+              case file_property_rep:
+                add_rep_stats(&file_prop_rep_stats, rep);
+                break;
+              case dir_property_rep:
+                add_rep_stats(&dir_prop_rep_stats, rep);
+                break;
+              default:
+                break;
             }
-          else
-            if (representation->delta_base == fs->null_base)
-              header = svn_stringbuf_create("DELTA\n", pool);
-            else
-              header = svn_stringbuf_createf(pool,
-                                             "DELTA %ld %" APR_UINT64_T_FMT " %" APR_UINT64_T_FMT "\n",
-                                             representation->delta_base->revision->revision,
-                                             representation->delta_base->target.offset
-                                             - representation->delta_base->revision->target.offset,
-                                             representation->delta_base->target.size);
-        else
-          header = svn_stringbuf_create("PLAIN\n", pool);
-
-        header_size = strchr(revision_content->data +
-                             representation->original.offset, '\n') -
-                      revision_content->data -
-                      representation->original.offset + 1;
-        svn_stringbuf_appendbytes(header,
-                                  revision_content->data +
-                                  representation->original.offset +
-                                  header_size,
-                                  representation->original.size);
-        svn_stringbuf_appendcstr(header, "ENDREP\n");
-        *content = svn_stringbuf__morph_into_string(header);
-        return SVN_NO_ERROR;
-
-      case dir_fragment:
-        representation = fragment->data;
-        SVN_ERR(get_updated_dir(&revision_content, fs, representation,
-                                pool, pool));
-        SVN_ERR(svn_checksum(&checksum, svn_checksum_md5,
-                             revision_content->data, revision_content->len,
-                             pool));
-        memcpy(representation->dir->target_md5,
-               checksum->digest,
-               sizeof(representation->dir->target_md5));
-
-        if (representation->delta_base)
-          {
-            if (representation->delta_base->dir == NULL)
-              {
-                header = svn_stringbuf_create("DELTA\n", pool);
-                base_content = svn_string_create_empty(pool);
-              }
-            else
-              {
-                representation_t *base_rep = representation->delta_base;
-                header = svn_stringbuf_createf(pool,
-                                               "DELTA %ld %" APR_UINT64_T_FMT " %" APR_UINT64_T_FMT "\n",
-                                               base_rep->revision->revision,
-                                               base_rep->target.offset - base_rep->revision->target.offset,
-                                               base_rep->target.size);
-                SVN_ERR(get_updated_dir(&base_content, fs, base_rep,
-                                        pool, pool));
-              }
-
-            header_size = header->len;
-            SVN_ERR(diff_stringbufs(header, &representation->dir->size,
-                                    base_content,
-                                    revision_content, pool));
-            representation->target.size = header->len - header_size;
-            svn_stringbuf_appendcstr(header, "ENDREP\n");
-            *content = svn_stringbuf__morph_into_string(header);
-          }
-        else
-          {
-            representation->target.size = revision_content->len;
-            representation->dir->size = revision_content->len;
-            *content = svn_string_createf(pool, "PLAIN\n%sENDREP\n",
-                                          revision_content->data);
-          }
-
-        return SVN_NO_ERROR;
-
-      case noderev_fragment:
-        node = fragment->data;
-        SVN_ERR(get_content(&revision_content, fs,
-                            node->revision->revision, pool));
-        node_rev = svn_stringbuf_ncreate(revision_content->data +
-                                         node->original.offset,
-                                         node->original.size,
-                                         pool);
-
-        update_id(node_rev, "id: ", node);
-        update_id(node_rev, "pred: ", node->predecessor);
-        update_text(node_rev, "text: ", node->text, pool);
-        update_text(node_rev, "props: ", node->props, pool);
-
-        *content = svn_stringbuf__morph_into_string(node_rev);
-        return SVN_NO_ERROR;
-    }
-
-  SVN_ERR_ASSERT(0);
-
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-prepare_repo(const char *path, apr_pool_t *pool)
-{
-  svn_node_kind_t kind;
-  
-  const char *old_path = svn_dirent_join(path, "db/old", pool);
-  const char *new_path = svn_dirent_join(path, "new", pool);
-  const char *revs_path = svn_dirent_join(path, "db/revs", pool);
-  const char *old_rep_cache_path = svn_dirent_join(path, "db/rep-cache.db.old", pool);
-  const char *rep_cache_path = svn_dirent_join(path, "db/rep-cache.db", pool);
-  
-  SVN_ERR(svn_io_check_path(old_path, &kind, pool));
-  if (kind == svn_node_dir)
-    {
-      SVN_ERR(svn_io_remove_dir2(new_path, TRUE, NULL, NULL, pool));
-      SVN_ERR(svn_io_file_move(revs_path, new_path, pool));
-      SVN_ERR(svn_io_file_move(old_path, revs_path, pool));
-      SVN_ERR(svn_io_remove_dir2(new_path, TRUE, NULL, NULL, pool));
-    }
 
-  SVN_ERR(svn_io_check_path(old_rep_cache_path, &kind, pool));
-  if (kind == svn_node_file)
-    SVN_ERR(svn_io_file_move(old_rep_cache_path, rep_cache_path, pool));
-
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-activate_new_revs(const char *path, apr_pool_t *pool)
-{
-  svn_node_kind_t kind;
-
-  const char *old_path = svn_dirent_join(path, "db/old", pool);
-  const char *new_path = svn_dirent_join(path, "new", pool);
-  const char *revs_path = svn_dirent_join(path, "db/revs", pool);
-  const char *old_rep_cache_path = svn_dirent_join(path, "db/rep-cache.db.old", pool);
-  const char *rep_cache_path = svn_dirent_join(path, "db/rep-cache.db", pool);
-
-  SVN_ERR(svn_io_check_path(old_path, &kind, pool));
-  if (kind == svn_node_none)
-    {
-      SVN_ERR(svn_io_file_move(revs_path, old_path, pool));
-      SVN_ERR(svn_io_file_move(new_path, revs_path, pool));
+          add_rep_stats(&total_rep_stats, rep);
+        }
     }
 
-  SVN_ERR(svn_io_check_path(old_rep_cache_path, &kind, pool));
-  if (kind == svn_node_none)
-    SVN_ERR(svn_io_file_move(rep_cache_path, old_rep_cache_path, pool));
-
-  return SVN_NO_ERROR;
+  printf("\nGlobal statistics:\n");
+  printf(_("%20s bytes in %12s revisions\n"
+           "%20s bytes in %12s changes\n"
+           "%20s bytes in %12s node revision records\n"
+           "%20s bytes in %12s representations\n"
+           "%20s bytes expanded representation size\n"
+           "%20s bytes with rep-sharing off\n"),
+         svn__i64toa_sep(total_size, ',', pool),
+         svn__i64toa_sep(fs->revisions->nelts, ',', pool),
+         svn__i64toa_sep(change_len, ',', pool),
+         svn__i64toa_sep(change_count, ',', pool),
+         svn__i64toa_sep(total_node_stats.size, ',', pool),
+         svn__i64toa_sep(total_node_stats.count, ',', pool),
+         svn__i64toa_sep(total_rep_stats.total.packed_size, ',', pool),
+         svn__i64toa_sep(total_rep_stats.total.count, ',', pool),
+         svn__i64toa_sep(total_rep_stats.total.expanded_size, ',', pool),
+         svn__i64toa_sep(total_rep_stats.expanded_size, ',', pool));
+
+  printf("\nNoderev statistics:\n");
+  printf(_("%20s bytes in %12s nodes total\n"
+           "%20s bytes in %12s directory noderevs\n"
+           "%20s bytes in %12s file noderevs\n"),
+         svn__i64toa_sep(total_node_stats.size, ',', pool),
+         svn__i64toa_sep(total_node_stats.count, ',', pool),
+         svn__i64toa_sep(dir_node_stats.size, ',', pool),
+         svn__i64toa_sep(dir_node_stats.count, ',', pool),
+         svn__i64toa_sep(file_node_stats.size, ',', pool),
+         svn__i64toa_sep(file_node_stats.count, ',', pool));
+
+  printf("\nRepresentation statistics:\n");
+  printf(_("%20s bytes in %12s representations total\n"
+           "%20s bytes in %12s directory representations\n"
+           "%20s bytes in %12s file representations\n"
+           "%20s bytes in %12s directory property representations\n"
+           "%20s bytes in %12s file property representations\n"
+           "%20s bytes in header & footer overhead\n"),
+         svn__i64toa_sep(total_rep_stats.total.packed_size, ',', pool),
+         svn__i64toa_sep(total_rep_stats.total.count, ',', pool),
+         svn__i64toa_sep(dir_rep_stats.total.packed_size, ',', pool),
+         svn__i64toa_sep(dir_rep_stats.total.count, ',', pool),
+         svn__i64toa_sep(file_rep_stats.total.packed_size, ',', pool),
+         svn__i64toa_sep(file_rep_stats.total.count, ',', pool),
+         svn__i64toa_sep(dir_prop_rep_stats.total.packed_size, ',', pool),
+         svn__i64toa_sep(dir_prop_rep_stats.total.count, ',', pool),
+         svn__i64toa_sep(file_prop_rep_stats.total.packed_size, ',', pool),
+         svn__i64toa_sep(file_prop_rep_stats.total.count, ',', pool),
+         svn__i64toa_sep(total_rep_stats.total.overhead_size, ',', pool));
+
+  printf("\nDirectory representation statistics:\n");
+  print_rep_stats(&dir_rep_stats, pool);
+  printf("\nFile representation statistics:\n");
+  print_rep_stats(&file_rep_stats, pool);
+  printf("\nDirectory property representation statistics:\n");
+  print_rep_stats(&dir_prop_rep_stats, pool);
+  printf("\nFile property representation statistics:\n");
+  print_rep_stats(&file_prop_rep_stats, pool);
 }
 
 static void
@@ -2707,16 +1397,13 @@ print_usage(svn_stream_t *ostream, const
 {
   svn_error_clear(svn_stream_printf(ostream, pool,
      "\n"
-     "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! WARNING !!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
-     "!!! This is an experimental tool. Don't use it on production data !!!\n"
-     "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
-     "\n"
      "Usage: %s <repo> <cachesize>\n"
      "\n"
-     "Optimize the repository at local path <repo> staring from revision 0.\n"
+     "Read the repository at local path <repo> starting at revision 0,\n"
+     "count statistical information and write that data to stdout.\n"
      "Use up to <cachesize> MB of memory for caching. This does not include\n"
      "temporary representation of the repository structure, i.e. the actual\n"
-     "memory will be higher and <cachesize> be the lower limit.\n",
+     "memory may be considerably higher.\n",
      progname));
 }
 
@@ -2761,33 +1448,12 @@ int main(int argc, const char *argv[])
   repo_path = argv[1];
   start_revision = 0;
 
-  printf("\nPreparing repository\n");
-  svn_err = prepare_repo(repo_path, pool);
+  printf("Reading revisions\n");
+  svn_err = read_revisions(&fs, repo_path, start_revision, memsize, pool);
+  printf("\n");
 
-  if (!svn_err)
-    {
-      printf("Reading revisions\n");
-      svn_err = read_revisions(&fs, repo_path, start_revision, memsize, pool);
-    }
+  print_stats(fs, pool);
   
-  if (!svn_err)
-    {
-      printf("\nReordering revision content\n");
-      svn_err = reorder_revisions(fs, pool);
-    }
-    
-  if (!svn_err)
-    {
-      printf("\nPacking and writing revisions\n");
-      svn_err = pack_and_write_revisions(fs, pool);
-    }
-
-  if (!svn_err)
-    {
-      printf("\nSwitch to new revs\n");
-      svn_err = activate_new_revs(repo_path, pool);
-    }
-
   if (svn_err)
     {

[... 2 lines stripped ...]


Mime
View raw message