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 2013/02/06 22:11:36 UTC

svn commit: r1443210 - /subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/cached_data.c

Author: stefan2
Date: Wed Feb  6 21:11:35 2013
New Revision: 1443210

URL: http://svn.apache.org/viewvc?rev=1443210&view=rev
Log:
On the fsfs-format7 branch:  implement the 'block-read' feature.

Whenever we need to read data from the rev / pack file, we read
a whole block (64kB by default), parse all its contents and put
that into our caches.  The data that was actually requested will
be returned directly by block_read for most item types, i.e. no
extra read required.

The integration of this feature still needs some touch-up and
the block_read() function group needs docstrings.

* subversion/libsvn_fs_fs/cached_data.c
  (get_cached_node_revision_body): fuse with ...
  (get_node_revision_body): ... this one; clearly separate code
   paths for txn and committed data; invoke block-read
  (create_rep_state_body,
   svn_fs_fs__get_changes): invoke block-read
  (read_delta_window): ditto; optimize cache lookup sequence
  (block_read_rep_header,
   block_read_windows,
   block_read_contents,
   auto_select_stream,
   block_read_changes,
   block_read_noderev,
   block_read): implement the new feature

Modified:
    subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/cached_data.c

Modified: subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/cached_data.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/cached_data.c?rev=1443210&r1=1443209&r2=1443210&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/cached_data.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/cached_data.c Wed Feb  6 21:11:35 2013
@@ -38,6 +38,16 @@
 
 #include "svn_private_config.h"
 
+/* forward-declare */
+static svn_error_t *
+block_read(void **result,
+           svn_fs_t *fs,
+           svn_revnum_t revision,
+           apr_uint64_t item_index,
+           apr_file_t *revision_file,
+           apr_pool_t *result_pool,
+           apr_pool_t *scratch_pool);
+
 /* Convenience wrapper around svn_io_file_aligned_seek, taking filesystem
    FS instead of a block size. */
 static svn_error_t *
@@ -132,68 +142,6 @@ err_dangling_id(svn_fs_t *fs, const svn_
      id_str->data, fs->path);
 }
 
-/* Look up the NODEREV_P for ID in FS' node revsion cache. If noderev
- * caching has been enabled and the data can be found, IS_CACHED will
- * be set to TRUE. The noderev will be allocated from POOL.
- *
- * Non-permanent ids (e.g. ids within a TXN) will not be cached.
- */
-static svn_error_t *
-get_cached_node_revision_body(node_revision_t **noderev_p,
-                              svn_fs_t *fs,
-                              const svn_fs_id_t *id,
-                              svn_boolean_t *is_cached,
-                              apr_pool_t *pool)
-{
-  fs_fs_data_t *ffd = fs->fsap_data;
-  if (! ffd->node_revision_cache || svn_fs_fs__id_txn_id(id))
-    {
-      *is_cached = FALSE;
-    }
-  else
-    {
-      pair_cache_key_t key = { 0 };
-
-      key.revision = svn_fs_fs__id_rev(id);
-      key.second = svn_fs_fs__id_item(id);
-      SVN_ERR(svn_cache__get((void **) noderev_p,
-                            is_cached,
-                            ffd->node_revision_cache,
-                            &key,
-                            pool));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* If noderev caching has been enabled, store the NODEREV_P for the given ID
- * in FS' node revsion cache. SCRATCH_POOL is used for temporary allcations.
- *
- * Non-permanent ids (e.g. ids within a TXN) will not be cached.
- */
-static svn_error_t *
-set_cached_node_revision_body(node_revision_t *noderev_p,
-                              svn_fs_t *fs,
-                              const svn_fs_id_t *id,
-                              apr_pool_t *scratch_pool)
-{
-  fs_fs_data_t *ffd = fs->fsap_data;
-
-  if (ffd->node_revision_cache && !svn_fs_fs__id_txn_id(id))
-    {
-      pair_cache_key_t key = { 0 };
-
-      key.revision = svn_fs_fs__id_rev(id);
-      key.second = svn_fs_fs__id_item(id);
-      return svn_cache__set(ffd->node_revision_cache,
-                            &key,
-                            noderev_p,
-                            scratch_pool);
-    }
-
-  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
@@ -207,45 +155,90 @@ get_node_revision_body(node_revision_t *
   apr_file_t *revision_file;
   svn_error_t *err;
   svn_boolean_t is_cached = FALSE;
+  fs_fs_data_t *ffd = fs->fsap_data;
+  const char *txn_id = svn_fs_fs__id_txn_id(id);
 
-  /* First, try a cache lookup. If that succeeds, we are done here. */
-  SVN_ERR(get_cached_node_revision_body(noderev_p, fs, id, &is_cached, pool));
-  if (is_cached)
-    return SVN_NO_ERROR;
-
-  if (svn_fs_fs__id_txn_id(id))
+  if (txn_id)
     {
-      /* This is a transaction node-rev. */
+      /* This is a transaction node-rev.  Its storage logic is very
+         different from that of rev / pack files. */
       err = svn_io_file_open(&revision_file, path_txn_node_rev(fs, id, pool),
                              APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool);
+      if (err)
+        {
+          if (APR_STATUS_IS_ENOENT(err->apr_err))
+            {
+              svn_error_clear(err);
+              return svn_error_trace(err_dangling_id(fs, id));
+            }
+
+          return svn_error_trace(err);
+        }
+
+      SVN_ERR(svn_fs_fs__read_noderev(noderev_p,
+                                      svn_stream_from_aprfile2(revision_file,
+                                                               FALSE,
+                                                               pool),
+                                      pool));
     }
   else
     {
-      /* This is a revision node-rev. */
+      /* noderevs in rev / pack files can be cached */
+      pair_cache_key_t key;
+      key.revision = svn_fs_fs__id_rev(id);
+      key.second = svn_fs_fs__id_item(id);
+    
+      /* First, try a cache lookup. If that succeeds, we are done here. */
+      if (ffd->node_revision_cache)
+        {
+          SVN_ERR(svn_cache__get((void **) noderev_p,
+                                 &is_cached,
+                                 ffd->node_revision_cache,
+                                 &key,
+                                 pool));
+          if (is_cached)
+            return SVN_NO_ERROR;
+        }
+
+      /* someone needs to read the data from this file: */
       err = open_and_seek_revision(&revision_file, fs,
                                    svn_fs_fs__id_rev(id),
                                    svn_fs_fs__id_item(id),
                                    pool);
-    }
 
-  if (err)
-    {
-      if (APR_STATUS_IS_ENOENT(err->apr_err))
+      if (ffd->format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT)
         {
-          svn_error_clear(err);
-          return svn_error_trace(err_dangling_id(fs, id));
+          /* block-read will parse the whole block and will also return
+             the one noderev that we need right now. */
+          SVN_ERR(block_read((void **)noderev_p, fs,
+                             svn_fs_fs__id_rev(id),
+                             svn_fs_fs__id_item(id),
+                             revision_file,
+                             pool,
+                             pool));
+          SVN_ERR(svn_io_file_close(revision_file, pool));
         }
+      else
+        {
+          /* pre-format7 reading, parsing and caching */
+          SVN_ERR(svn_fs_fs__read_noderev(noderev_p,
+                                          svn_stream_from_aprfile2(revision_file,
+                                                                   FALSE,
+                                                                   pool),
+                                          pool));
+          /* Workaround issue #4031: is-fresh-txn-root in revision files. */
+          (*noderev_p)->is_fresh_txn_root = FALSE;
 
-      return svn_error_trace(err);
+          /* The noderev is not in cache, yet. Add it, if caching has been enabled. */
+          if (ffd->node_revision_cache)
+            SVN_ERR(svn_cache__set(ffd->node_revision_cache,
+                                   &key,
+                                   *noderev_p,
+                                   pool));
+        }
     }
 
-  SVN_ERR(svn_fs_fs__read_noderev(noderev_p,
-                                  svn_stream_from_aprfile2(revision_file, FALSE,
-                                                           pool),
-                                  pool));
-
-  /* The noderev is not in cache, yet. Add it, if caching has been enabled. */
-  return set_cached_node_revision_body(*noderev_p, fs, id, pool);
+  return SVN_NO_ERROR;
 }
 
 svn_error_t *
@@ -570,6 +563,9 @@ create_rep_state_body(rep_state_t **rep_
 
       if (!rep->txn_id)
         {
+          if (ffd->format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT)
+            SVN_ERR(block_read(NULL, fs, rep->revision, rep->item_index,
+                               rs->file->file, pool, pool));
           if (ffd->rep_header_cache)
             SVN_ERR(svn_cache__set(ffd->rep_header_cache, &key, rh, pool));
         }
@@ -1101,7 +1097,34 @@ read_delta_window(svn_txdelta_window_t *
   apr_off_t offset;
   SVN_ERR_ASSERT(rs->chunk_index <= this_chunk);
 
+  /* Read the next window.  But first, try to find it in the cache. */
+  SVN_ERR(get_cached_window(nwin, rs, this_chunk, &is_cached, pool));
+  if (is_cached)
+    return SVN_NO_ERROR;
+
+  /* someone has to actually read the data from file.  Open it */
   SVN_ERR(auto_open_shared_file(rs->file));
+
+  /* invoke the 'block-read' feature for non-txn data.
+     However, don't do that if we are in the middle of some representation,
+     because the block is unlikely to contain other data. */
+  if (rs->chunk_index == 0 && SVN_IS_VALID_REVNUM(rs->revision))
+    {
+      fs_fs_data_t *ffd = rs->file->fs->fsap_data;
+      
+      if (ffd->format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT)
+        SVN_ERR(block_read(NULL, rs->file->fs, rs->revision, rs->item_index,
+                           rs->file->file, pool, pool));
+
+      /* reading the whole block probably also provided us with the
+         desired txdelta window */
+      SVN_ERR(get_cached_window(nwin, rs, this_chunk, &is_cached, pool));
+      if (is_cached)
+        return SVN_NO_ERROR;
+    }
+
+  /* data is still not cached -> we need to read it.
+     Make sure we have all the necessary info. */
   SVN_ERR(auto_set_start_offset(rs, pool));
   SVN_ERR(auto_read_diff_version(rs, pool));
 
@@ -1125,11 +1148,6 @@ read_delta_window(svn_txdelta_window_t *
                                   "representation"));
     }
 
-  /* Read the next window. But first, try to find it in the cache. */
-  SVN_ERR(get_cached_window(nwin, rs, this_chunk, &is_cached, pool));
-  if (is_cached)
-    return SVN_NO_ERROR;
-
   /* Actually read the next window. */
   SVN_ERR(svn_txdelta_read_svndiff_window(nwin, rs->file->stream, rs->ver,
                                           pool));
@@ -1960,28 +1978,359 @@ svn_fs_fs__get_changes(apr_array_header_
   /* read changes from revision file */
   
   SVN_ERR(svn_fs_fs__ensure_revision_exists(rev, fs, pool));
-
   SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&revision_file, fs, rev, pool));
 
-  if (ffd->format < SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT)
-    SVN_ERR(get_root_changes_offset(NULL, &changes_offset, revision_file,
-                                    fs, rev, pool));
+  if (ffd->format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT)
+    {
+      /* 'block-read' will also provide us with the desired data */
+      SVN_ERR(block_read((void **)changes, fs,
+                         rev, SVN_FS_FS__ITEM_INDEX_CHANGES,
+                         revision_file, pool, pool));
+    }
   else
-    SVN_ERR(svn_fs_fs__item_offset(&changes_offset, fs, rev, NULL,
-                                   SVN_FS_FS__ITEM_INDEX_CHANGES, pool));
+    {
+      /* pre-format7 code path */
+      SVN_ERR(get_root_changes_offset(NULL, &changes_offset, revision_file,
+                                      fs, rev, pool));
+      SVN_ERR(svn_io_file_seek(revision_file, APR_SET, &changes_offset, pool));
+      SVN_ERR(svn_fs_fs__read_changes(changes,
+                                      svn_stream_from_aprfile2(revision_file,
+                                                               TRUE, pool),
+                                      pool));
+
+      /* cache for future reference */
+
+      if (ffd->changes_cache)
+        SVN_ERR(svn_cache__set(ffd->changes_cache, &rev, *changes, pool));
+    }
 
-  SVN_ERR(svn_io_file_seek(revision_file, APR_SET, &changes_offset, pool));
-  SVN_ERR(svn_fs_fs__read_changes(changes,
-                                  svn_stream_from_aprfile2(revision_file,
-                                                           TRUE, pool),
-                                  pool));
   SVN_ERR(svn_io_file_close(revision_file, pool));
 
-  /* cache for future reference */
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+block_read_rep_header(svn_fs_fs__rep_header_t **rep_header,
+                      svn_fs_t *fs,
+                      svn_stream_t *stream,
+                      pair_cache_key_t *key,
+                      apr_pool_t *pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  SVN_ERR(svn_fs_fs__read_rep_header(rep_header, stream, pool));
+
+  if (ffd->rep_header_cache)
+    SVN_ERR(svn_cache__set(ffd->rep_header_cache, key, *rep_header, pool));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+block_read_windows(svn_fs_fs__rep_header_t *rep_header,
+                   svn_fs_t *fs,
+                   apr_file_t *file,
+                   svn_stream_t *stream,
+                   svn_fs_fs__p2l_entry_t* entry,
+                   apr_pool_t *pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  shared_file_t shared_file = { 0 };
+  rep_state_t rs = { 0 };
+  apr_off_t offset;
+  svn_boolean_t is_cached = FALSE;
+  window_cache_key_t key = { 0 };
+
+  if (   (rep_header->is_delta && !ffd->txdelta_window_cache)
+      || (!rep_header->is_delta && !ffd->combined_window_cache))
+    return SVN_NO_ERROR;
+
+  shared_file.file = file;
+  shared_file.stream = stream;
+  shared_file.fs = fs;
+  shared_file.revision = entry->revision;
+  shared_file.pool = pool;
+
+  rs.file = &shared_file;
+  rs.revision = entry->revision;
+  rs.item_index = entry->item_index;
+  rs.header_size = rep_header->header_size;
+  rs.start = entry->offset + rs.header_size;
+  rs.current = rep_header->is_delta ? 4 : 0;
+  rs.size = entry->size - rep_header->header_size - 7;
+  rs.ver = 1;
+  rs.chunk_index = 0;
+  rs.window_cache = ffd->txdelta_window_cache;
+  rs.combined_cache = ffd->combined_window_cache;
   
+  /* RS->FILE may be shared between RS instances -> make sure we point
+   * to the right data. */
+  offset = rs.start + rs.current;
+  if (!rep_header->is_delta)
+    {
+      svn_stringbuf_t *plaintext;
+      
+      /* already in cache? */
+      SVN_ERR(svn_cache__has_key(&is_cached, rs.combined_cache,
+                                 get_window_key(&key, &rs), pool));
+      if (is_cached)
+        return SVN_NO_ERROR;
+
+      SVN_ERR(svn_io_file_seek(file, APR_SET, &offset, pool));
+
+      plaintext = svn_stringbuf_create_ensure(rs.size, pool);
+      SVN_ERR(svn_io_file_read_full2(file, plaintext->data, rs.size,
+                                     &plaintext->len, NULL, pool));
+      plaintext->data[plaintext->len] = 0;
+      rs.current += rs.size;
+
+      SVN_ERR(set_cached_combined_window(plaintext, &rs, pool));
+    }
+  else
+    {
+      SVN_ERR(svn_cache__has_key(&is_cached, rs.window_cache,
+                                 get_window_key(&key, &rs), pool));
+      if (is_cached)
+        return SVN_NO_ERROR;
+
+      SVN_ERR(svn_io_file_seek(file, APR_SET, &offset, pool));
+      while (rs.current < rs.size)
+        {
+          svn_txdelta_window_t *window;
+          /* Actually read the next window. */
+          SVN_ERR(svn_txdelta_read_svndiff_window(&window, stream, rs.ver,
+                                                  pool));
+          SVN_ERR(get_file_offset(&offset, file, pool));
+          rs.current = offset - rs.start;
+          if (rs.current > rs.size)
+            return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
+                                    _("Reading one svndiff window read beyond "
+                                      "the end of the representation"));
+
+          /* the window has not been cached before, thus cache it now
+           * (if caching is used for them at all) */
+          SVN_ERR(set_cached_window(window, &rs, pool));
+          rs.chunk_index++;
+        }
+    }
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+block_read_contents(svn_stringbuf_t **item,
+                    svn_fs_t *fs,
+                    apr_file_t *file,
+                    svn_stream_t *stream,
+                    svn_fs_fs__p2l_entry_t* entry,
+                    pair_cache_key_t *key,
+                    apr_pool_t *pool)
+{
+  svn_fs_fs__rep_header_t *rep_header;
+  SVN_ERR(block_read_rep_header(&rep_header, fs, stream, key, pool));
+  SVN_ERR(block_read_windows(rep_header, fs, file, stream, entry, pool));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+auto_select_stream(svn_stream_t **stream,
+                   svn_fs_t *fs,
+                   apr_file_t *file,
+                   svn_stream_t *file_stream,
+                   svn_fs_fs__p2l_entry_t* entry,
+                   apr_pool_t *pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+
+  if (((entry->offset + entry->size) ^ entry->offset) >= ffd->block_size)
+    {
+      svn_stringbuf_t *text = svn_stringbuf_create_ensure(entry->size, pool);
+      text->len = entry->size;
+      text->data[text->len] = 0;
+      SVN_ERR(svn_io_file_read_full2(file, text->data, text->len, NULL,
+                                     NULL, pool));
+      *stream = svn_stream_from_stringbuf(text, pool);
+    }
+  else
+    {
+      *stream = file_stream;
+    }
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+block_read_changes(apr_array_header_t **changes,
+                   svn_fs_t *fs,
+                   apr_file_t *file,
+                   svn_stream_t *file_stream,
+                   svn_fs_fs__p2l_entry_t* entry,
+                   svn_boolean_t must_read,
+                   apr_pool_t *pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  svn_stream_t *stream;
+  if (!must_read && !ffd->changes_cache)
+    return SVN_NO_ERROR;
+
+  /* already in cache? */
+  if (!must_read && ffd->changes_cache)
+    {
+      svn_boolean_t is_cached = FALSE;
+      SVN_ERR(svn_cache__has_key(&is_cached, ffd->changes_cache,
+                                 &entry->revision, pool));
+      if (is_cached)
+        return SVN_NO_ERROR;
+    }
+
+  SVN_ERR(auto_select_stream(&stream, fs, file, file_stream, entry, pool));
+
+  /* read changes from revision file */
+
+  SVN_ERR(svn_fs_fs__read_changes(changes, stream, pool));
+
+  /* cache for future reference */
+
   if (ffd->changes_cache)
-    SVN_ERR(svn_cache__set(ffd->changes_cache, &rev, *changes, pool));
+    SVN_ERR(svn_cache__set(ffd->changes_cache, &entry->revision, *changes, pool));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+block_read_noderev(node_revision_t **noderev_p,
+                   svn_fs_t *fs,
+                   apr_file_t *file,
+                   svn_stream_t *file_stream,
+                   svn_fs_fs__p2l_entry_t* entry,
+                   pair_cache_key_t *key,
+                   svn_boolean_t must_read,
+                   apr_pool_t *pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  svn_stream_t *stream;
+  if (!must_read && !ffd->node_revision_cache)
+    return SVN_NO_ERROR;
+
+  /* already in cache? */
+  if (!must_read && ffd->node_revision_cache)
+    {
+      svn_boolean_t is_cached = FALSE;
+      SVN_ERR(svn_cache__has_key(&is_cached, ffd->node_revision_cache, key,
+                                 pool));
+      if (is_cached)
+        return SVN_NO_ERROR;
+    }
+
+  SVN_ERR(auto_select_stream(&stream, fs, file, file_stream, entry, pool));
+
+  /* read node rev from revision file */
+
+  SVN_ERR(svn_fs_fs__read_noderev(noderev_p, stream, pool));
+
+  /* Workaround issue #4031: is-fresh-txn-root in revision files. */
+  (*noderev_p)->is_fresh_txn_root = FALSE;
+
+  if (ffd->node_revision_cache)
+    SVN_ERR(svn_cache__set(ffd->node_revision_cache, key, *noderev_p, pool));
+
+  if (   entry->item_index == SVN_FS_FS__ITEM_INDEX_ROOT_NODE
+      && ffd->rev_root_id_cache)
+    SVN_ERR(svn_cache__set(ffd->rev_root_id_cache, &entry->revision,
+                           (void *)(*noderev_p)->id, pool));
 
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+block_read(void **result,
+           svn_fs_t *fs,
+           svn_revnum_t revision,
+           apr_uint64_t item_index,
+           apr_file_t *revision_file,
+           apr_pool_t *result_pool,
+           apr_pool_t *scratch_pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  apr_off_t offset = 0;
+  apr_off_t block_start = 0;
+  apr_array_header_t *entries;
+  int i;
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+  svn_stream_t *stream = svn_stream_from_aprfile2(revision_file, TRUE,
+                                                  scratch_pool);
+
+  /* don't try this on transaction protorev files */
+  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
+  
+  /* index lookup: find the OFFSET of the item we *must* read plus
+   * the list of items in the same block. */
+  SVN_ERR(svn_fs_fs__item_offset(&offset, fs, revision, NULL, item_index,
+                                 iterpool));
+  SVN_ERR(svn_fs_fs__p2l_index_lookup(&entries, fs, revision, offset,
+                                      scratch_pool));
+  SVN_ERR(aligned_seek(fs, revision_file, &block_start, offset, iterpool));
+  
+  /* read all items from the block */
+  for (i = 0; i < entries->nelts; ++i)
+    {
+      svn_fs_fs__p2l_entry_t* entry
+        = &APR_ARRAY_IDX(entries, i, svn_fs_fs__p2l_entry_t);
+      svn_boolean_t is_result = result && entry->revision == revision
+                                       && entry->item_index == item_index;
+
+      /* select the pool that we want the item to be allocated in */
+      apr_pool_t *pool = is_result ? result_pool : iterpool;
+
+      /* handle all items that start within this block and are relatively
+       * small (i.e. < block size).  Always read the item we need to return.
+       */
+      if (is_result || (   entry->offset >= block_start
+                        && entry->size < ffd->block_size))
+        {
+          void *item = NULL;
+          pair_cache_key_t key;
+          key.revision = entry->revision;
+          key.second = entry->item_index;
+
+          SVN_ERR(svn_io_file_seek(revision_file, SEEK_SET, &entry->offset,
+                                   iterpool));
+          switch (entry->type)
+            {
+              case SVN_FS_FS__ITEM_TYPE_FILE_REP:
+              case SVN_FS_FS__ITEM_TYPE_DIR_REP:
+              case SVN_FS_FS__ITEM_TYPE_FILE_PROPS:
+              case SVN_FS_FS__ITEM_TYPE_DIR_PROPS:
+                SVN_ERR(block_read_contents((svn_stringbuf_t **)&item,
+                                            fs, revision_file, stream,
+                                            entry, &key, pool));
+                break;
+
+              case SVN_FS_FS__ITEM_TYPE_NODEREV:
+                if (ffd->node_revision_cache || is_result)
+                  SVN_ERR(block_read_noderev((node_revision_t **)&item,
+                                             fs, revision_file, stream,
+                                             entry, &key, is_result, pool));
+                break;
+
+              case SVN_FS_FS__ITEM_TYPE_CHANGES:
+                SVN_ERR(block_read_changes((apr_array_header_t **)&item,
+                                           fs, revision_file,  stream,
+                                           entry, is_result, pool));
+                break;
+
+              default:
+                break;
+            }
+
+          if (is_result)
+            *result = item;
+
+          svn_pool_clear(iterpool);
+        }
+    }
+
+  SVN_ERR(svn_stream_close(stream));
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
\ No newline at end of file