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/11 09:01:43 UTC

svn commit: r1444674 - in /subversion/branches/fsfs-format7/subversion/libsvn_fs_fs: caching.c fs.h index.c index.h

Author: stefan2
Date: Mon Feb 11 08:01:42 2013
New Revision: 1444674

URL: http://svn.apache.org/r1444674
Log:
On the fsfs-format7 branch:  minimize the impact of logical
addressing, cache the index info.  Also, optimize cache access
code to not copy unnecessary data, i.e. use __get_partial
instead of __get.  Finally, when having loaded index data from 
disk, parse more of it than actually requested and cache it
for future reference. 

* subversion/libsvn_fs_fs/fs.h
  (fs_fs_data_t): add 4 new caches

* subversion/libsvn_fs_fs/caching.c
  (svn_fs_fs__initialize_caches): initialize new caches

* subversion/libsvn_fs_fs/index.c
  (l2p_page_info_baton_t,
   l2p_page_baton_t,
   l2p_page_table_baton_t,
   p2l_page_info_baton_t): new cache access data structures

  (base_revision,
   l2p_header_copy,
   l2p_header_access_func,
   get_l2p_page_info,
   get_l2p_header,
   l2p_page_get_offset,
   l2p_page_access_func,
   l2p_page_table_access_func,
   get_l2p_page_table,
   prefetch_l2p_pages,
   p2l_page_info_copy,
   p2l_page_info_func,
   prefetch_p2l_page): new utility & cache access functions

  (get_l2p_header): rename to ...
  (get_l2p_header_body): ... this; add caching support
  (svn_fs_fs__l2p_get_max_ids): update; improve pool usage
  (get_p2l_header): superseeded by ...
  (get_p2l_page_info): ... this one
  (l2p_index_lookup,
   svn_fs_fs__p2l_index_lookup): use caches and prefetch data

  (svn_fs_fs__serialize_l2p_header,
   svn_fs_fs__deserialize_l2p_header,
   svn_fs_fs__serialize_l2p_page,
   svn_fs_fs__deserialize_l2p_page,
   svn_fs_fs__serialize_p2l_header,
   svn_fs_fs__deserialize_p2l_header,
   svn_fs_fs__serialize_p2l_page,
   svn_fs_fs__deserialize_p2l_page): implement caches access functions

* subversion/libsvn_fs_fs/index.h
  (svn_fs_fs__page_cache_key_t): declare new cache key type
  (svn_fs_fs__serialize_l2p_header,
   svn_fs_fs__deserialize_l2p_header,
   svn_fs_fs__serialize_l2p_page,
   svn_fs_fs__deserialize_l2p_page,
   svn_fs_fs__serialize_p2l_header,
   svn_fs_fs__deserialize_p2l_header,
   svn_fs_fs__serialize_p2l_page,
   svn_fs_fs__deserialize_p2l_page): declare caches access functions

Modified:
    subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/caching.c
    subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/fs.h
    subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/index.c
    subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/index.h

Modified: subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/caching.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/caching.c?rev=1444674&r1=1444673&r2=1444674&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/caching.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/caching.c Mon Feb 11 08:01:42 2013
@@ -25,6 +25,7 @@
 #include "id.h"
 #include "dag.h"
 #include "tree.h"
+#include "index.h"
 #include "temp_serializer.h"
 #include "../libsvn_fs/fs-loader.h"
 
@@ -555,6 +556,69 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
       ffd->combined_window_cache = NULL;
     }
 
+  if (ffd->format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT)
+    {
+      SVN_ERR(create_cache(&(ffd->l2p_header_cache),
+                           NULL,
+                           membuffer,
+                           0, 0, /* Do not use inprocess cache */
+                           svn_fs_fs__serialize_l2p_header,
+                           svn_fs_fs__deserialize_l2p_header,
+                           sizeof(pair_cache_key_t),
+                           apr_pstrcat(pool, prefix, "L2P_HEADER",
+                                       (char *)NULL),
+                           SVN_CACHE__MEMBUFFER_HIGH_PRIORITY,
+                           fs,
+                           no_handler,
+                           fs->pool));
+      SVN_ERR(create_cache(&(ffd->l2p_page_cache),
+                           NULL,
+                           membuffer,
+                           0, 0, /* Do not use inprocess cache */
+                           svn_fs_fs__serialize_l2p_page,
+                           svn_fs_fs__deserialize_l2p_page,
+                           sizeof(svn_fs_fs__page_cache_key_t),
+                           apr_pstrcat(pool, prefix, "L2P_PAGE",
+                                       (char *)NULL),
+                           SVN_CACHE__MEMBUFFER_HIGH_PRIORITY,
+                           fs,
+                           no_handler,
+                           fs->pool));
+      SVN_ERR(create_cache(&(ffd->p2l_header_cache),
+                           NULL,
+                           membuffer,
+                           0, 0, /* Do not use inprocess cache */
+                           svn_fs_fs__serialize_p2l_header,
+                           svn_fs_fs__deserialize_p2l_header,
+                           sizeof(pair_cache_key_t),
+                           apr_pstrcat(pool, prefix, "P2L_HEADER",
+                                       (char *)NULL),
+                           SVN_CACHE__MEMBUFFER_HIGH_PRIORITY,
+                           fs,
+                           no_handler,
+                           fs->pool));
+      SVN_ERR(create_cache(&(ffd->p2l_page_cache),
+                           NULL,
+                           membuffer,
+                           0, 0, /* Do not use inprocess cache */
+                           svn_fs_fs__serialize_p2l_page,
+                           svn_fs_fs__deserialize_p2l_page,
+                           sizeof(svn_fs_fs__page_cache_key_t),
+                           apr_pstrcat(pool, prefix, "P2L_PAGE",
+                                       (char *)NULL),
+                           0,
+                           fs,
+                           no_handler,
+                           fs->pool));
+    }
+  else
+    {
+      ffd->l2p_header_cache = NULL;
+      ffd->l2p_page_cache = NULL;
+      ffd->p2l_header_cache = NULL;
+      ffd->p2l_page_cache = NULL;
+    }
+
   return SVN_NO_ERROR;
 }
 

Modified: subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/fs.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/fs.h?rev=1444674&r1=1444673&r2=1444674&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/fs.h (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/fs.h Mon Feb 11 08:01:42 2013
@@ -358,6 +358,23 @@ typedef struct fs_fs_data_t
      if the node has mergeinfo, "0" if it doesn't. */
   svn_cache__t *mergeinfo_existence_cache;
 
+  /* Cache for l2p_header_t objects; the key is (revision, is-packed).
+     Will be NULL for pre-format7 repos */
+  svn_cache__t *l2p_header_cache;
+
+  /* Cache for l2p_page_t objects; the key is svn_fs_fs__page_cache_key_t.
+     Will be NULL for pre-format7 repos */
+  svn_cache__t *l2p_page_cache;
+
+  /* Cache for p2l_header_t objects; the key is (revision, is-packed).
+     Will be NULL for pre-format7 repos */
+  svn_cache__t *p2l_header_cache;
+
+  /* Cache for apr_array_header_t objects containing svn_fs_fs__p2l_entry_t
+     elements; the key is svn_fs_fs__page_cache_key_t.
+     Will be NULL for pre-format7 repos */
+  svn_cache__t *p2l_page_cache;
+
   /* TRUE while the we hold a lock on the write lock file. */
   svn_boolean_t has_write_lock;
 

Modified: subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/index.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/index.c?rev=1444674&r1=1444673&r2=1444674&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/index.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/index.c Mon Feb 11 08:01:42 2013
@@ -604,16 +604,133 @@ svn_fs_fs__l2p_index_create(svn_fs_t *fs
   return SVN_NO_ERROR;
 }
 
+/* Return the base revision used to identify the p2l or lp2 index covering
+ * REVISION in FS.
+ */
+static svn_revnum_t
+base_revision(svn_fs_t *fs, svn_revnum_t revision)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  return is_packed_rev(fs, revision)
+       ? revision - (revision % ffd->max_files_per_dir)
+       : revision;
+}
+
+/* Data structure that describes which l2p page info shall be extracted
+ * from the cache and contains the fields that receive the result.
+ */
+typedef struct l2p_page_info_baton_t
+{
+  /* input data: we want the page covering (REVISION,ITEM_INDEX) */
+  svn_revnum_t revision;
+  apr_uint64_t item_index;
+
+  /* out data */
+  /* page location and size of the page within the l2p index file */
+  l2p_page_table_entry_t entry;
+
+  /* page number within the pages for REVISION (not l2p index global!) */
+  apr_size_t page_no;
+
+  /* offset of ITEM_INDEX within that page */
+  apr_uint32_t page_offset;
+
+  /* revision identifying the l2p index file, also the first rev in that */
+  svn_revnum_t first_revision;
+} l2p_page_info_baton_t;
+
+
+/* Utility function that copies the info requested by BATON->REVISION and
+ * BATON->ITEM_INDEX and from HEADER and PAGE_TABLE into the output fields
+ * of *BATON.
+ */
+static svn_error_t *
+l2p_header_copy(l2p_page_info_baton_t *baton,
+                const l2p_header_t *header,
+                const l2p_page_table_entry_t *page_table,
+                const apr_size_t *page_table_index)
+{
+  /* revision offset within the index file */
+  apr_size_t rel_revision = baton->revision - header->first_revision;
+  if (rel_revision >= header->revision_count)
+    return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_REVISION , NULL,
+                             _("Revision %ld not covered by item index"),
+                             baton->revision);
+
+  /* select the relevant page */
+  if (baton->item_index < header->page_size)
+    {
+      /* most revs fit well into a single page */
+      baton->page_offset = (apr_size_t)baton->item_index;
+      baton->page_no = 0;
+      baton->entry = page_table[page_table_index[rel_revision]];
+    }
+  else
+    {
+      const l2p_page_table_entry_t *first_entry;
+      const l2p_page_table_entry_t *last_entry;
+      
+      /* all pages are of the same size and full, except for the last one */
+      baton->page_offset = (apr_size_t)(baton->item_index % header->page_size);
+      baton->page_no = (apr_uint32_t)(baton->item_index / header->page_size);
+
+      /* range of pages for this rev */
+      first_entry = page_table + page_table_index[rel_revision];
+      last_entry = page_table + page_table_index[rel_revision + 1];
+
+      if (last_entry - first_entry > baton->page_no)
+        {
+          baton->entry = first_entry[baton->page_no];
+        }
+      else
+        {
+          /* limit page index to the valid range */
+          baton->entry = last_entry[-1];
+
+          /* cause index overflow further down the road */
+          baton->page_offset = header->page_size + 1;
+        }
+    }
+    
+  baton->first_revision = header->first_revision;
+
+  return SVN_NO_ERROR;
+}
+
+/* Implement svn_cache__partial_getter_func_t: copy the data requested in
+ * l2p_page_info_baton_t *BATON from l2p_header_t *DATA into the output
+ * fields in *BATON.
+ */
+static svn_error_t *
+l2p_header_access_func(void **out,
+                       const void *data,
+                       apr_size_t data_len,
+                       void *baton,
+                       apr_pool_t *result_pool)
+{
+  /* resolve all pointer values of in-cache data */
+  const l2p_header_t *header = data;
+  const l2p_page_table_entry_t *page_table
+    = svn_temp_deserializer__ptr(header,
+                                 (const void *const *)&header->page_table);
+  const apr_size_t *page_table_index
+    = svn_temp_deserializer__ptr(header,
+                           (const void *const *)&header->page_table_index);
+
+  /* copy the info */
+  return l2p_header_copy(baton, header, page_table, page_table_index);
+}
+
 /* Read the header data structure of the log-to-phys index for REVISION
  * in FS and return it in *HEADER.  To maximize efficiency, use or return
  * the data stream in *STREAM.  Use POOL for allocations.
  */
 static svn_error_t *
-get_l2p_header(l2p_header_t **header,
-               packed_number_stream_t **stream,
-               svn_fs_t *fs,
-               svn_revnum_t revision,
-               apr_pool_t *pool)
+get_l2p_header_body(l2p_header_t **header,
+                    packed_number_stream_t **stream,
+                    svn_fs_t *fs,
+                    svn_revnum_t revision,
+                    apr_pool_t *pool)
 {
   fs_fs_data_t *ffd = fs->fsap_data;
   apr_uint64_t value;
@@ -623,6 +740,10 @@ get_l2p_header(l2p_header_t **header,
   l2p_header_t *result = apr_pcalloc(pool, sizeof(*result));
   apr_size_t page_table_index;
 
+  pair_cache_key_t key;
+  key.revision = base_revision(fs, revision);
+  key.second = is_packed_rev(fs, revision);
+
   if (*stream == NULL)
     SVN_ERR(packed_stream_open(stream, path_l2p_index(fs, revision, pool),
                                ffd->block_size, pool));
@@ -671,7 +792,73 @@ get_l2p_header(l2p_header_t **header,
       offset += result->page_table[page].size;
     }
 
+  /* return and cache the header */
   *header = result;
+  SVN_ERR(svn_cache__set(ffd->l2p_header_cache, &key, result, pool));
+
+  return SVN_NO_ERROR;
+}
+
+/* Get the page info requested in *BATON from FS and set the output fields
+ * in *BATON.
+ * To maximize efficiency, use or return the data stream in *STREAM.
+ * Use POOL for allocations.
+ */
+static svn_error_t *
+get_l2p_page_info(l2p_page_info_baton_t *baton,
+                  packed_number_stream_t **stream,
+                  svn_fs_t *fs,
+                  apr_pool_t *pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  l2p_header_t *result;
+  svn_boolean_t is_cached = FALSE;
+  void *dummy = NULL;
+
+  /* try to find the info in the cache */
+  pair_cache_key_t key;
+  key.revision = base_revision(fs, baton->revision);
+  key.second = is_packed_rev(fs, baton->revision);
+  SVN_ERR(svn_cache__get_partial((void**)&dummy, &is_cached,
+                                 ffd->l2p_header_cache, &key,
+                                 l2p_header_access_func, baton,
+                                 pool));
+  if (is_cached)
+    return SVN_NO_ERROR;
+
+  /* read from disk, cache and copy the result */
+  SVN_ERR(get_l2p_header_body(&result, stream, fs, baton->revision, pool));
+  SVN_ERR(l2p_header_copy(baton, result, result->page_table,
+                          result->page_table_index));
+
+  return SVN_NO_ERROR;
+}
+
+/* Read the log-to-phys header info of the index covering REVISION from FS
+ * and return it in *HEADER.  To maximize efficiency, use or return the
+ * data stream in *STREAM.  Use POOL for allocations.
+ */
+static svn_error_t *
+get_l2p_header(l2p_header_t **header,
+               packed_number_stream_t **stream,
+               svn_fs_t *fs,
+               svn_revnum_t revision,
+               apr_pool_t *pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  svn_boolean_t is_cached = FALSE;
+
+  /* first, try cache lookop */
+  pair_cache_key_t key;
+  key.revision = base_revision(fs, revision);
+  key.second = is_packed_rev(fs, revision);
+  SVN_ERR(svn_cache__get((void**)header, &is_cached, ffd->l2p_header_cache,
+                         &key, pool));
+  if (is_cached)
+    return SVN_NO_ERROR;
+
+  /* read from disk and cache the result */
+  SVN_ERR(get_l2p_header_body(header, stream, fs, revision, pool));
 
   return SVN_NO_ERROR;
 }
@@ -721,6 +908,224 @@ get_l2p_page(l2p_page_t **page,
   return SVN_NO_ERROR;
 }
 
+/* Request data structure for l2p_page_access_func.  This one is input
+ * parameters only, i.e. the request results will be passed back otherwise.
+ */
+typedef struct l2p_page_baton_t
+{
+  /* revision. Used for error messages only */
+  svn_revnum_t revision;
+
+  /* item index to look up. Used for error messages only */
+  apr_uint64_t item_index;
+
+  /* offset within the cached page */
+  apr_uint32_t page_offset;
+} l2p_page_baton_t;
+
+/* Return the rev / pack file offset of the item at BATON->PAGE_OFFSET in
+ * OFFSETS of PAGE and write it to *OFFSET.
+ */
+static svn_error_t *
+l2p_page_get_offset(apr_off_t *offset,
+                    l2p_page_baton_t *baton,
+                    const l2p_page_t *page,
+                    const apr_off_t *offsets)
+{
+  /* overflow check */
+  if (page->entry_count <= baton->page_offset)
+    return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_OVERFLOW , NULL,
+                             _("Item index %" APR_UINT64_T_FMT
+                               " too large in revision %ld"),
+                             baton->item_index, baton->revision);
+
+  /* return the result */
+  *offset = offsets[baton->page_offset];
+
+  return SVN_NO_ERROR;
+}
+
+/* Implement svn_cache__partial_getter_func_t: copy the data requested in
+ * l2p_page_baton_t *BATON from l2p_page_t *DATA into apr_off_t *OUT.
+ */
+static svn_error_t *
+l2p_page_access_func(void **out,
+                     const void *data,
+                     apr_size_t data_len,
+                     void *baton,
+                     apr_pool_t *result_pool)
+{
+  /* resolve all in-cache pointers */
+  const l2p_page_t *page = data;
+  const apr_off_t *offsets
+    = svn_temp_deserializer__ptr(page, (const void *const *)&page->offsets);
+
+  /* return the requested data */
+  return l2p_page_get_offset((apr_off_t *)out, baton, page, offsets);
+}
+
+/* Data request structure used by l2p_page_table_access_func.
+ */
+typedef struct l2p_page_table_baton_t
+{
+  /* revision for which to read the page table */
+  svn_revnum_t revision;
+
+  /* page table entries (of type l2p_page_table_entry_t).
+   * Must be created by caller and will be filled by callee. */
+  apr_array_header_t *pages;
+} l2p_page_table_baton_t;
+
+/* Implement svn_cache__partial_getter_func_t: copy the data requested in
+ * l2p_page_baton_t *BATON from l2p_page_t *DATA into apr_off_t *OUT.
+ */
+static svn_error_t *
+l2p_page_table_access_func(void **out,
+                           const void *data,
+                           apr_size_t data_len,
+                           void *baton,
+                           apr_pool_t *result_pool)
+{
+  /* resolve in-cache pointers */
+  l2p_page_table_baton_t *table_baton = baton;
+  const l2p_header_t *header = (const l2p_header_t *)data;
+  const l2p_page_table_entry_t *page_table
+    = svn_temp_deserializer__ptr(header,
+                                 (const void *const *)&header->page_table);
+  const apr_size_t *page_table_index
+    = svn_temp_deserializer__ptr(header,
+                           (const void *const *)&header->page_table_index);
+
+  /* copy the revision's page table into BATON */
+  apr_size_t rel_revision = table_baton->revision - header->first_revision;
+  if (rel_revision < header->revision_count)
+    {
+      const l2p_page_table_entry_t *entry
+        = page_table + page_table_index[rel_revision];
+      const l2p_page_table_entry_t *last_entry
+        = page_table + page_table_index[rel_revision + 1];
+
+      for (; entry < last_entry; ++entry)
+        APR_ARRAY_PUSH(table_baton->pages, l2p_page_table_entry_t)
+          = *entry;
+    }
+
+  /* set output as a courtesy to the caller */
+  *out = table_baton->pages;
+  
+  return SVN_NO_ERROR;
+}
+
+/* Read the l2p index page table for REVISION in FS from cache and return
+ * it in PAGES.  The later must be provided by the caller (and can be
+ * re-used); existing entries will be removed before writing the result.
+ * If the data cannot be found in the cache, the result will be empty
+ * (it never can be empty for a valid REVISION if the data is cached).
+ * Use POOL for temporary allocations.
+ */
+static svn_error_t *
+get_l2p_page_table(apr_array_header_t *pages,
+                   svn_fs_t *fs,
+                   svn_revnum_t revision,
+                   apr_pool_t *pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  svn_boolean_t is_cached = FALSE;
+  l2p_page_table_baton_t baton;
+
+  pair_cache_key_t key;
+  key.revision = base_revision(fs, revision);
+  key.second = is_packed_rev(fs, revision);
+
+  apr_array_clear(pages);
+  baton.revision = revision;
+  baton.pages = pages;
+  SVN_ERR(svn_cache__get_partial((void**)&pages, &is_cached,
+                                 ffd->l2p_header_cache, &key,
+                                 l2p_page_table_access_func, &baton, pool));
+
+  return SVN_NO_ERROR;
+}
+
+/* Utility function.  Read the l2p index pages for REVISION in FS from
+ * STREAM and put them into the cache.  Skip page number EXLCUDED_PAGE_NO
+ * (use -1 for 'skip none') and pages outside the MIN_OFFSET, MAX_OFFSET
+ * range in the l2p index file.  The index is being identified by
+ * FIRST_REVISION.  PAGES is a scratch container provided by the caller.
+ * SCRATCH_POOL is used for temporary allocations.
+ */
+static svn_error_t *
+prefetch_l2p_pages(svn_boolean_t *end,
+                   svn_fs_t *fs,
+                   packed_number_stream_t *stream,
+                   svn_revnum_t first_revision,
+                   svn_revnum_t revision,
+                   apr_array_header_t *pages,
+                   int exlcuded_page_no,
+                   apr_off_t min_offset,
+                   apr_off_t max_offset,
+                   apr_pool_t *scratch_pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  int i;
+  apr_pool_t *iterpool;
+  svn_fs_fs__page_cache_key_t key = { 0 };
+
+  /* get the page table for REVISION from cache */
+  *end = FALSE;
+  SVN_ERR(get_l2p_page_table(pages, fs, revision, scratch_pool));
+  if (pages->nelts == 0)
+    {
+      /* not found -> we can't continue without hitting the disk again */
+      *end = TRUE;
+      return SVN_NO_ERROR;
+    }
+
+  /* prefetch pages individually until all are done or we found one in
+   * the cache */
+  iterpool = svn_pool_create(scratch_pool);
+  key.revision = revision;
+  key.is_packed = is_packed_rev(fs, revision);
+
+  for (i = 0; i < pages->nelts && !*end; ++i)
+    {
+      l2p_page_table_entry_t *entry
+        = &APR_ARRAY_IDX(pages, i, l2p_page_table_entry_t);
+      if (i == exlcuded_page_no)
+        continue;
+
+      /* skip pages outside the specified index file range */
+      if (   entry->offset < min_offset
+          || entry->offset + entry->size > max_offset)
+        {
+          *end = TRUE;
+          continue;
+        }
+
+      /* page already in cache? */
+      key.page = i;
+      SVN_ERR(svn_cache__has_key(end, ffd->l2p_page_cache,
+                                 &key, iterpool));
+      if (!*end)
+        {
+          /* no in cache -> read from stream (data already buffered in APR)
+           * and cache the result */
+          l2p_page_t *page = NULL;
+          SVN_ERR(get_l2p_page(&page, &stream, fs, first_revision,
+                               entry, iterpool));
+
+          SVN_ERR(svn_cache__set(ffd->l2p_page_cache, &key, page,
+                                 iterpool));
+        }
+
+      svn_pool_clear(iterpool);
+    }
+
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
 /* Using the log-to-phys indexes in FS, find the absolute offset in the
  * rev file for (REVISION, ITEM_INDEX) and return it in *OFFSET.
  * Use POOL for allocations.
@@ -732,72 +1137,90 @@ l2p_index_lookup(apr_off_t *offset,
                  apr_uint64_t item_index,
                  apr_pool_t *pool)
 {
-  l2p_header_t *header = NULL;
+  fs_fs_data_t *ffd = fs->fsap_data;
+  l2p_page_info_baton_t info_baton;
+  l2p_page_baton_t page_baton;
   l2p_page_t *page = NULL;
-  l2p_page_table_entry_t *entry, *first_entry, *last_entry;
-  apr_size_t page_no;
-  apr_uint32_t page_offset;
   packed_number_stream_t *stream = NULL;
+  svn_fs_fs__page_cache_key_t key = { 0 };
+  svn_boolean_t is_cached = FALSE;
 
-  /* read index master data structure */
-  SVN_ERR(get_l2p_header(&header, &stream, fs, revision, pool));
-  if (   (header->first_revision > revision)
-      || (header->first_revision + header->revision_count <= revision))
+  /* read index master data structure and extract the info required to
+   * access the l2p index page for (REVISION,ITEM_INDEX)*/
+  info_baton.revision = revision;
+  info_baton.item_index = item_index;
+  SVN_ERR(get_l2p_page_info(&info_baton, &stream, fs, pool));
+
+  /* try to find the page in the cache and get the OFFSET from it */
+  page_baton.revision = revision;
+  page_baton.item_index = item_index;
+  page_baton.page_offset = info_baton.page_offset;
+
+  key.revision = revision;
+  key.is_packed = is_packed_rev(fs, revision);
+  key.page = info_baton.page_no;
+
+  SVN_ERR(svn_cache__get_partial((void**)offset, &is_cached,
+                                 ffd->l2p_page_cache, &key,
+                                 l2p_page_access_func, &page_baton, pool));
+  if (!is_cached)
     {
-      SVN_ERR(packed_stream_close(stream));
-      return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_REVISION , NULL,
-                               _("Revision %ld not covered by item index"),
-                               revision);
-    }
-
-  /* select the relevant page */
-  if (item_index < header->page_size)
-    {
-      /* most revs fit well into a single page */
-      page_offset = (apr_size_t)item_index;
-      page_no = 0;
-      entry = header->page_table
-            + header->page_table_index[revision - header->first_revision];
-    }
-  else
-    {
-      /* all pages are of the same size and full, except for the last one */
-      page_offset = (apr_size_t)(item_index % header->page_size);
-      page_no = (apr_uint32_t)(item_index / header->page_size);
-
-      /* range of pages for this rev */
-      first_entry = header->page_table
-            + header->page_table_index[revision - header->first_revision];
-      last_entry = header->page_table
-            + header->page_table_index[revision + 1 - header->first_revision];
-
-      if (last_entry - first_entry > page_no)
+      /* we need to read the info from disk (might already be in the
+       * APR file buffer, though) */
+      apr_array_header_t *pages;
+      svn_revnum_t prefetch_revision;
+      svn_revnum_t last_revision
+        = info_baton.first_revision
+          + (key.is_packed ? ffd->max_files_per_dir : 1);
+      apr_pool_t *iterpool = svn_pool_create(pool);
+      svn_boolean_t end;
+      apr_off_t max_offset
+        = APR_ALIGN(info_baton.entry.offset + info_baton.entry.size,
+                    0x10000);
+      apr_off_t min_offset = max_offset - 0x10000;
+
+      /* read the relevant page */
+      SVN_ERR(get_l2p_page(&page, &stream, fs, info_baton.first_revision,
+                           &info_baton.entry, pool));
+
+      /* cache the page and extract the result we need */
+      SVN_ERR(svn_cache__set(ffd->l2p_page_cache, &key, page, pool));
+      SVN_ERR(l2p_page_get_offset(offset, &page_baton, page, page->offsets));
+
+      /* prefetch pages from following and preceding revisions */
+      pages = apr_array_make(pool, 16, sizeof(l2p_page_table_entry_t));
+      end = FALSE;
+      for (prefetch_revision = revision;
+           prefetch_revision < last_revision && !end;
+           ++prefetch_revision)
         {
-          entry = first_entry + page_no;
+          int excluded_page_no = prefetch_revision == revision
+                               ? info_baton.page_no
+                               : -1;
+          SVN_ERR(prefetch_l2p_pages(&end, fs, stream,
+                                     info_baton.first_revision,
+                                     prefetch_revision, pages,
+                                     excluded_page_no, min_offset,
+                                     max_offset, iterpool));
+          svn_pool_clear(iterpool);
         }
-      else
-        {
-          /* limit page index to the valid range */
-          entry = last_entry - 1;
 
-          /* cause index overflow further down the road */
-          page_offset = header->page_size;
+      end = FALSE;
+      for (prefetch_revision = revision-1;
+           prefetch_revision >= info_baton.first_revision && !end;
+           --prefetch_revision)
+        {
+          SVN_ERR(prefetch_l2p_pages(&end, fs, stream,
+                                     info_baton.first_revision,
+                                     prefetch_revision, pages, -1,
+                                     min_offset, max_offset, iterpool));
+          svn_pool_clear(iterpool);
         }
+
+      svn_pool_destroy(iterpool);
     }
 
-  /* read the relevant page */
-  SVN_ERR(get_l2p_page(&page, &stream, fs, header->first_revision, entry,
-                       pool));
   SVN_ERR(packed_stream_close(stream));
-   
-  if (page->entry_count <= page_offset)
-    return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_OVERFLOW , NULL,
-                             _("Item index %" APR_UINT64_T_FMT
-                               " too large in revision %ld"),
-                             item_index, revision);
-
-  /* return the result */
-  *offset = page->offsets[item_index];
 
   return SVN_NO_ERROR;
 }
@@ -855,37 +1278,47 @@ svn_fs_fs__l2p_get_max_ids(apr_array_hea
   svn_revnum_t revision;
   svn_revnum_t last_rev = (svn_revnum_t)(start_rev + count);
   packed_number_stream_t *stream = NULL;
+  apr_pool_t *header_pool = svn_pool_create(pool);
 
-  /* read index master data structure */
-  SVN_ERR(get_l2p_header(&header, &stream, fs, start_rev, pool));
+  /* read index master data structure for the index covering START_REV */
+  SVN_ERR(get_l2p_header(&header, &stream, fs, start_rev, header_pool));
   SVN_ERR(packed_stream_close(stream));
   stream = NULL;
 
+  /* Determine the length of the item index list for each rev.
+   * Read new index headers as required. */
   *max_ids = apr_array_make(pool, (int)count, sizeof(apr_uint64_t));
   for (revision = start_rev; revision < last_rev; ++revision)
     {
-      apr_uint64_t page_count;
+      apr_uint64_t full_page_count;
       apr_uint64_t item_count;
       apr_size_t first_page_index, last_page_index;
 
       if (revision >= header->first_revision + header->revision_count)
         {
-          SVN_ERR(get_l2p_header(&header, &stream, fs, revision, pool));
+          /* need to read the next index. Clear up memory used for the
+           * previous one. */
+          svn_pool_clear(header_pool);
+          SVN_ERR(get_l2p_header(&header, &stream, fs, revision,
+                                 header_pool));
           SVN_ERR(packed_stream_close(stream));
           stream = NULL;
         }
 
+      /* in a revision with N index pages, the first N-1 index pages are
+       * "full", i.e. contain HEADER->PAGE_SIZE entries */
       first_page_index
          = header->page_table_index[revision - header->first_revision];
       last_page_index
          = header->page_table_index[revision - header->first_revision + 1];
-      page_count = first_page_index - last_page_index - 1;
-      item_count = page_count * header->page_size
+      full_page_count = first_page_index - last_page_index - 1;
+      item_count = full_page_count * header->page_size
                  + header->page_table[last_page_index - 1].entry_count;
 
       APR_ARRAY_PUSH(*max_ids, apr_uint64_t) = item_count;
     }
 
+  svn_pool_destroy(header_pool);
   return SVN_NO_ERROR;
 }
 
@@ -1062,26 +1495,115 @@ svn_fs_fs__p2l_index_create(svn_fs_t *fs
   return SVN_NO_ERROR;
 }
 
-/* Read the header data structure of the phys-to-log index for REVISION
- * in FS and return it in *HEADER.  To maximize efficiency, use or return
- * the data stream in *STREAM.  Use POOL for allocations.
+/* Data structure that describes which p2l page info shall be extracted
+ * from the cache and contains the fields that receive the result.
+ */
+typedef struct p2l_page_info_baton_t
+{
+  /* input variables */
+  /* revision identifying the index file */
+  svn_revnum_t revision;
+
+  /* offset within the page in rev / pack file */
+  apr_off_t offset;
+
+  /* output variables */
+  /* page containing OFFSET */
+  apr_size_t page_no;
+
+  /* first revision in this p2l index */
+  svn_revnum_t first_revision;
+
+  /* offset within the p2l index file describing this page */
+  apr_off_t start_offset;
+
+  /* offset within the p2l index file describing the following page */
+  apr_off_t next_offset;
+
+  /* PAGE_NO * PAGE_SIZE (is <= OFFSET) */
+  apr_off_t page_start;
+
+  /* total number of pages indexed */
+  apr_size_t page_count;
+
+  /* size of each page in pack / rev file */
+  apr_uint64_t page_size;
+} p2l_page_info_baton_t;
+
+/* From HEADER and the list of all OFFSETS, fill BATON with the page info
+ * requested by BATON->OFFSET.
+ */
+static void
+p2l_page_info_copy(p2l_page_info_baton_t *baton,
+                   const p2l_header_t *header,
+                   const apr_off_t *offsets)
+{
+  baton->page_no = baton->offset / header->page_size;
+  baton->first_revision = header->first_revision;
+  baton->start_offset = offsets[baton->page_no];
+  baton->next_offset = offsets[baton->page_no + 1];
+  baton->page_start = (apr_off_t)(header->page_size * baton->page_no);
+  baton->page_count = header->page_count;
+  baton->page_size = header->page_size;
+}
+
+/* Implement svn_cache__partial_getter_func_t: extract the p2l page info
+ * requested by BATON and return it in BATON.
  */
 static svn_error_t *
-get_p2l_header(p2l_header_t **header,
-               packed_number_stream_t **stream,
-               svn_fs_t *fs,
-               svn_revnum_t revision,
-               apr_pool_t *pool)
+p2l_page_info_func(void **out,
+                   const void *data,
+                   apr_size_t data_len,
+                   void *baton,
+                   apr_pool_t *result_pool)
+{
+  /* all the pointers to cached data we need */
+  const p2l_header_t *header = data;
+  const apr_off_t *offsets
+    = svn_temp_deserializer__ptr(header,
+                                 (const void *const *)&header->offsets);
+
+  /* copy data from cache to BATON */
+  p2l_page_info_copy(baton, header, offsets);
+  return SVN_NO_ERROR;
+}
+
+/* Read the header data structure of the phys-to-log index for revision
+ * BATON->REVISION in FS.  Return in *BATON all info relevant to read the
+ * index page for the rev / pack file offset BATON->OFFSET.
+ * 
+ * To maximize efficiency, use or return the data stream in *STREAM.
+ * Use POOL for allocations.
+ */
+static svn_error_t *
+get_p2l_page_info(p2l_page_info_baton_t *baton,
+                  packed_number_stream_t **stream,
+                  svn_fs_t *fs,
+                  apr_pool_t *pool)
 {
   fs_fs_data_t *ffd = fs->fsap_data;
   apr_uint64_t value;
   apr_size_t i;
   apr_off_t offset;
   p2l_header_t *result = apr_pcalloc(pool, sizeof(*result));
+  svn_boolean_t is_cached = FALSE;
+  void *dummy = NULL;
 
-  /* open index file */
+  /* look for the header data in our cache */
+  pair_cache_key_t key;
+  key.revision = base_revision(fs, baton->revision);
+  key.second = is_packed_rev(fs, baton->revision);
+
+  SVN_ERR(svn_cache__get_partial(&dummy, &is_cached, ffd->p2l_header_cache,
+                                 &key, p2l_page_info_func, baton, pool));
+  if (is_cached)
+    return SVN_NO_ERROR;
+
+  /* not found -> must read it from disk.
+   * Open index file */
   if (*stream == NULL)
-    SVN_ERR(packed_stream_open(stream, path_p2l_index(fs, revision, pool),
+    SVN_ERR(packed_stream_open(stream,
+                               path_p2l_index(fs, baton->revision, pool),
                                ffd->block_size, pool));
 
   /* read table sizes and allocate page array */
@@ -1107,7 +1629,11 @@ get_p2l_header(p2l_header_t **header,
   for (i = 0; i <= result->page_count; ++i)
     result->offsets[i] += offset;
 
-  *header = result;
+  /* copy the requested info into *BATON */
+  p2l_page_info_copy(baton, result, result->offsets);
+
+  /* cache the header data */
+  SVN_ERR(svn_cache__set(ffd->p2l_header_cache, &key, result, pool));
 
   return SVN_NO_ERROR;
 }
@@ -1201,6 +1727,62 @@ get_p2l_page(apr_array_header_t **entrie
   return SVN_NO_ERROR;
 }
 
+/* If it cannot be found in FS's caches, read the p2l index page selected
+ * by BATON->OFFSET from STREAM.  Don't read it, if it precedes MIN_OFFSET.
+ * Set *END to TRUE if the caller should stop refeching.
+ *
+ * *BATON will be updated with the selected page's info and SCRATCH_POOL
+ * will be used for temporary allocations.
+ */
+static svn_error_t *
+prefetch_p2l_page(svn_boolean_t *end,
+                  svn_fs_t *fs,
+                  packed_number_stream_t *stream,
+                  p2l_page_info_baton_t *baton,
+                  apr_off_t min_offset,
+                  apr_pool_t *scratch_pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  apr_array_header_t *page;
+  svn_fs_fs__page_cache_key_t key = { 0 };
+
+  /* fetch the page info */
+  *end = FALSE;
+  baton->revision = baton->first_revision;
+  SVN_ERR(get_p2l_page_info(baton, &stream, fs, scratch_pool));
+  if (baton->start_offset < min_offset)
+    {
+      /* page outside limits -> stop prefetching */
+      *end = TRUE;
+      return SVN_NO_ERROR;
+    }
+
+  /* do we have that page in our caches already? */
+  key.revision = baton->first_revision;
+  key.is_packed = is_packed_rev(fs, baton->first_revision);
+  key.page = baton->page_no;
+  SVN_ERR(svn_cache__has_key(end, ffd->p2l_page_cache,
+                             &key, scratch_pool));
+  if (*end)
+    {
+      /* yes, already cached -> stop prefetching */
+      return SVN_NO_ERROR;
+    }
+
+  /* read from disk */
+  SVN_ERR(get_p2l_page(&page, &stream, fs,
+                       baton->first_revision,
+                       baton->start_offset,
+                       baton->next_offset,
+                       baton->page_start,
+                       baton->page_size, scratch_pool));
+
+  /* and put it into our cache */
+  SVN_ERR(svn_cache__set(ffd->p2l_page_cache, &key, page, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_fs_fs__p2l_index_lookup(apr_array_header_t **entries,
                             svn_fs_t *fs,
@@ -1208,14 +1790,20 @@ svn_fs_fs__p2l_index_lookup(apr_array_he
                             apr_off_t offset,
                             apr_pool_t *pool)
 {
-  p2l_header_t *header = NULL;
-  apr_size_t page_no;
+  fs_fs_data_t *ffd = fs->fsap_data;
   packed_number_stream_t *stream = NULL;
+  svn_fs_fs__page_cache_key_t key = { 0 };
+  svn_boolean_t is_cached = FALSE;
+  p2l_page_info_baton_t page_info;
+
+  /* request info for the index pages that describes the pack / rev file
+   * contents at pack / rev file position OFFSET. */
+  page_info.offset = offset;
+  page_info.revision = revision;
+  SVN_ERR(get_p2l_page_info(&page_info, &stream, fs, pool));
 
-  SVN_ERR(get_p2l_header(&header, &stream, fs, revision, pool));
-  page_no = offset / header->page_size;
-
-  if (header->page_count <= page_no)
+  /* if the offset refers to a non-existent page, bail out */
+  if (page_info.page_count <= page_info.page_no)
     {
       SVN_ERR(packed_stream_close(stream));
       return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_OVERFLOW , NULL,
@@ -1224,11 +1812,64 @@ svn_fs_fs__p2l_index_lookup(apr_array_he
                                offset, revision);
     }
 
-  SVN_ERR(get_p2l_page(entries, &stream, fs, header->first_revision,
-                       header->offsets[page_no],
-                       header->offsets[page_no + 1],
-                       (apr_off_t)(page_no * header->page_size),
-                       header->page_size, pool));
+  /* look for this page in our cache */
+  key.revision = page_info.first_revision;
+  key.is_packed = is_packed_rev(fs, revision);
+  key.page = page_info.page_no;
+
+  SVN_ERR(svn_cache__get((void**)entries, &is_cached, ffd->p2l_page_cache,
+                         &key, pool));
+  if (!is_cached)
+    {
+      apr_off_t max_offset = APR_ALIGN(page_info.next_offset, ffd->block_size);
+      apr_off_t min_offset = max_offset - ffd->block_size;
+      svn_boolean_t end;
+      apr_pool_t *iterpool = svn_pool_create(pool);
+      apr_off_t original_page_start = page_info.page_start;
+
+      /* fetch page from disk and put it into the cache */
+      SVN_ERR(get_p2l_page(entries, &stream, fs,
+                           page_info.first_revision,
+                           page_info.start_offset,
+                           page_info.next_offset,
+                           page_info.page_start,
+                           page_info.page_size, pool));
+
+      SVN_ERR(svn_cache__set(ffd->p2l_page_cache, &key, *entries, pool));
+
+      /* Since we read index data in larger chunks, we probably got more
+       * page data than we requested.  Parse & cache that until either we
+       * encounter pages already cached or reach the end of the buffer.
+       */
+
+      /* pre-fetch following pages */
+      end = FALSE;
+      page_info.offset = original_page_start;
+      while (   page_info.next_offset < max_offset
+             && page_info.page_no + 1 < page_info.page_count
+             && !end)
+        {
+          page_info.offset += page_info.page_size;
+          SVN_ERR(prefetch_p2l_page(&end, fs, stream, &page_info,
+                                    min_offset, iterpool));
+          svn_pool_clear(iterpool);
+        }
+
+      /* pre-fetch preceding pages */
+      end = FALSE;
+      page_info.offset = original_page_start;
+      while (page_info.offset >= page_info.page_size && !end)
+        {
+          page_info.offset -= page_info.page_size;
+          SVN_ERR(prefetch_p2l_page(&end, fs, stream, &page_info,
+                                    min_offset, iterpool));
+          svn_pool_clear(iterpool);
+        }
+
+      svn_pool_destroy(iterpool);
+    }
+
+  /* make sure we close files after usage */
   SVN_ERR(packed_stream_close(stream));
 
   return SVN_NO_ERROR;
@@ -1265,3 +1906,213 @@ svn_fs_fs__item_offset(apr_off_t *offset
 
   return SVN_NO_ERROR;
 }
+
+/*
+ * Standard (de-)serialization functions
+ */
+
+svn_error_t *
+svn_fs_fs__serialize_l2p_header(void **data,
+                                apr_size_t *data_len,
+                                void *in,
+                                apr_pool_t *pool)
+{
+  l2p_header_t *header = in;
+  svn_temp_serializer__context_t *context;
+  svn_stringbuf_t *serialized;
+  apr_size_t page_count = header->page_table_index[header->revision_count];
+  apr_size_t page_table_size = page_count * sizeof(*header->page_table);
+  apr_size_t index_size
+    = (header->revision_count + 1) * sizeof(*header->page_table_index);
+  apr_size_t data_size = sizeof(*header) + index_size + page_table_size;
+
+  /* serialize header and all its elements */
+  context = svn_temp_serializer__init(header,
+                                      sizeof(*header),
+                                      data_size + 32,
+                                      pool);
+
+  /* page table index array */
+  svn_temp_serializer__add_leaf(context,
+                                (const void * const *)&header->page_table_index,
+                                index_size);
+
+  /* page table array */
+  svn_temp_serializer__add_leaf(context,
+                                (const void * const *)&header->page_table,
+                                page_table_size);
+
+  /* return the serialized result */
+  serialized = svn_temp_serializer__get(context);
+
+  *data = serialized->data;
+  *data_len = serialized->len;
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_fs__deserialize_l2p_header(void **out,
+                                  void *data,
+                                  apr_size_t data_len,
+                                  apr_pool_t *pool)
+{
+  l2p_header_t *header = (l2p_header_t *)data;
+
+  /* resolve the pointers in the struct */
+  svn_temp_deserializer__resolve(header, (void**)&header->page_table_index);
+  svn_temp_deserializer__resolve(header, (void**)&header->page_table);
+
+  /* done */
+  *out = header;
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_fs__serialize_l2p_page(void **data,
+                              apr_size_t *data_len,
+                              void *in,
+                              apr_pool_t *pool)
+{
+  l2p_page_t *page = in;
+  svn_temp_serializer__context_t *context;
+  svn_stringbuf_t *serialized;
+  apr_size_t table_size = page->entry_count * sizeof(*page->offsets);
+
+  /* serialize struct and all its elements */
+  context = svn_temp_serializer__init(page,
+                                      sizeof(*page),
+                                      table_size + sizeof(*page) + 32,
+                                      pool);
+
+  /* offsets array */
+  svn_temp_serializer__add_leaf(context,
+                                (const void * const *)&page->offsets,
+                                table_size);
+
+  /* return the serialized result */
+  serialized = svn_temp_serializer__get(context);
+
+  *data = serialized->data;
+  *data_len = serialized->len;
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_fs__deserialize_l2p_page(void **out,
+                                void *data,
+                                apr_size_t data_len,
+                                apr_pool_t *pool)
+{
+  l2p_page_t *page = data;
+
+  /* resolve the only pointer in the struct */
+  svn_temp_deserializer__resolve(page, (void**)&page->offsets);
+
+  /* done */
+  *out = page;
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_fs__serialize_p2l_header(void **data,
+                                apr_size_t *data_len,
+                                void *in,
+                                apr_pool_t *pool)
+{
+  p2l_header_t *header = in;
+  svn_temp_serializer__context_t *context;
+  svn_stringbuf_t *serialized;
+  apr_size_t table_size = (header->page_count + 1) * sizeof(*header->offsets);
+
+  /* serialize header and all its elements */
+  context = svn_temp_serializer__init(header,
+                                      sizeof(*header),
+                                      table_size + sizeof(*header) + 32,
+                                      pool);
+
+  /* offsets array */
+  svn_temp_serializer__add_leaf(context,
+                                (const void * const *)&header->offsets,
+                                table_size);
+
+  /* return the serialized result */
+  serialized = svn_temp_serializer__get(context);
+
+  *data = serialized->data;
+  *data_len = serialized->len;
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_fs__deserialize_p2l_header(void **out,
+                                  void *data,
+                                  apr_size_t data_len,
+                                  apr_pool_t *pool)
+{
+  p2l_header_t *header = data;
+
+  /* resolve the only pointer in the struct */
+  svn_temp_deserializer__resolve(header, (void**)&header->offsets);
+
+  /* done */
+  *out = header;
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_fs__serialize_p2l_page(void **data,
+                              apr_size_t *data_len,
+                              void *in,
+                              apr_pool_t *pool)
+{
+  apr_array_header_t *page = in;
+  svn_temp_serializer__context_t *context;
+  svn_stringbuf_t *serialized;
+  apr_size_t table_size = page->elt_size * page->nelts;
+
+  /* serialize array header and all its elements */
+  context = svn_temp_serializer__init(page,
+                                      sizeof(*page),
+                                      table_size + sizeof(*page) + 32,
+                                      pool);
+
+  /* items in the array */
+  svn_temp_serializer__add_leaf(context,
+                                (const void * const *)&page->elts,
+                                table_size);
+
+  /* return the serialized result */
+  serialized = svn_temp_serializer__get(context);
+
+  *data = serialized->data;
+  *data_len = serialized->len;
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_fs__deserialize_p2l_page(void **out,
+                                void *data,
+                                apr_size_t data_len,
+                                apr_pool_t *pool)
+{
+  apr_array_header_t *page = (apr_array_header_t *)data;
+
+  /* resolve the only pointer in the struct */
+  svn_temp_deserializer__resolve(page, (void**)&page->elts);
+
+  /* patch up members */
+  page->pool = pool;
+  page->nalloc = page->nelts;
+
+  /* done */
+  *out = page;
+
+  return SVN_NO_ERROR;
+}

Modified: subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/index.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/index.h?rev=1444674&r1=1444673&r2=1444674&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/index.h (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/index.h Mon Feb 11 08:01:42 2013
@@ -183,4 +183,99 @@ svn_fs_fs__l2p_get_max_ids(apr_array_hea
                            apr_size_t count,
                            apr_pool_t *pool);
 
+/* Serialization and caching interface
+ */
+
+/* We use this key type to address individual pages from both index types.
+ */
+typedef struct svn_fs_fs__page_cache_key_t
+{
+  /* in l2p: this is the revision of the items being mapped
+     in p2l: this is the start revision identifying the pack / rev file */
+  svn_revnum_t revision;
+
+  /* if TRUE, this is the index to a pack file
+   */
+  svn_boolean_t is_packed;
+
+  /* in l2p: page number within the revision
+   * in p2l: page number with the rev / pack file
+   */
+  apr_uint64_t page;
+} svn_fs_fs__page_cache_key_t;
+
+/*
+ * Implements svn_cache__serialize_func_t for l2p_header_t objects.
+ */
+svn_error_t *
+svn_fs_fs__serialize_l2p_header(void **data,
+                                apr_size_t *data_len,
+                                void *in,
+                                apr_pool_t *pool);
+
+/*
+ * Implements svn_cache__deserialize_func_t for l2p_header_t objects.
+ */
+svn_error_t *
+svn_fs_fs__deserialize_l2p_header(void **out,
+                                  void *data,
+                                  apr_size_t data_len,
+                                  apr_pool_t *pool);
+
+/*
+ * Implements svn_cache__serialize_func_t for l2p_page_t objects.
+ */
+svn_error_t *
+svn_fs_fs__serialize_l2p_page(void **data,
+                              apr_size_t *data_len,
+                              void *in,
+                              apr_pool_t *pool);
+
+/*
+ * Implements svn_cache__deserialize_func_t for l2p_page_t objects.
+ */
+svn_error_t *
+svn_fs_fs__deserialize_l2p_page(void **out,
+                                void *data,
+                                apr_size_t data_len,
+                                apr_pool_t *pool);
+
+/*
+ * Implements svn_cache__serialize_func_t for p2l_header_t objects.
+ */
+svn_error_t *
+svn_fs_fs__serialize_p2l_header(void **data,
+                                apr_size_t *data_len,
+                                void *in,
+                                apr_pool_t *pool);
+
+/*
+ * Implements svn_cache__deserialize_func_t for p2l_header_t objects.
+ */
+svn_error_t *
+svn_fs_fs__deserialize_p2l_header(void **out,
+                                  void *data,
+                                  apr_size_t data_len,
+                                  apr_pool_t *pool);
+
+/*
+ * Implements svn_cache__serialize_func_t for apr_array_header_t objects
+ * with elements of type svn_fs_fs__p2l_entry_t.
+ */
+svn_error_t *
+svn_fs_fs__serialize_p2l_page(void **data,
+                              apr_size_t *data_len,
+                              void *in,
+                              apr_pool_t *pool);
+
+/*
+ * Implements svn_cache__deserialize_func_t for apr_array_header_t objects
+ * with elements of type svn_fs_fs__p2l_entry_t.
+ */
+svn_error_t *
+svn_fs_fs__deserialize_p2l_page(void **out,
+                                void *data,
+                                apr_size_t data_len,
+                                apr_pool_t *pool);
+
 #endif