Author: stefan2
Date: Thu Aug  8 17:17:24 2013
New Revision: 1511886

URL: http://svn.apache.org/r1511886
Log:
On the log-addressing branch:  Implement the reverse index with all
caching infrastructure but don't create nor use it right now.

* subversion/libsvn_fs_fs/fs.h
  (PATH_EXT_P2L_INDEX): declare a new file name extension
  (CONFIG_OPTION_P2L_PAGE_SIZE): new config file option
  (fs_fs_data_t): add p2l index page size member;
                  add caches for p2l index header and pages

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

* subversion/libsvn_fs_fs/fs_fs.c
  (read_config): read new config options
  (write_config): add new options to config template

* subversion/libsvn_fs_fs/index.h
  (svn_fs_fs__p2l_entry_t
   svn_fs_fs__p2l_proto_index_open,
   svn_fs_fs__p2l_proto_index_add_entry,
   svn_fs_fs__p2l_index_create): declare p2l index creation API
  (svn_fs_fs__p2l_index_lookup,
   svn_fs_fs__p2l_entry_lookup,
   svn_fs_fs__p2l_item_lookup): declare p2l index lookup API
  (svn_fs_fs__serialize_p2l_header,
   svn_fs_fs__deserialize_p2l_header,
   svn_fs_fs__serialize_p2l_page,
   svn_fs_fs__deserialize_p2l_page): cache (de-)serialization support

* subversion/libsvn_fs_fs/index.c
  (p2l_header_t): declare index header runtime structure

  (svn_fs_fs__p2l_proto_index_open,
   svn_fs_fs__p2l_proto_index_add_entry,
   svn_fs_fs__p2l_index_create): implement p2l index creation API

  (get_p2l_header): parse index header from stream

  (p2l_page_info_baton_t,
   p2l_page_info_copy,
   p2l_page_info_func,
   get_p2l_page_info): get page info from header cache;
                       populate cache as necessary

  (read_entry,
   get_p2l_page): read an index page from stream
   prefetch_p2l_page): read index page(s) and populate the cache

  (get_p2l_keys,
   p2l_index_lookup,
   svn_fs_fs__p2l_index_lookup): index lookup returning a whole page

  (compare_p2l_entry_offsets,
   get_p2l_entry_from_cached_page,
   p2l_entry_lookup_func,
   p2l_entry_lookup,
   svn_fs_fs__p2l_entry_lookup): index lookup returning a single entry

  (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 cache (de-)serialization support

* subversion/libsvn_fs_fs/util.h
  (svn_fs_fs__path_p2l_index,
   svn_fs_fs__path_p2l_proto_index): declare new file path constructors

* subversion/libsvn_fs_fs/util.c
  (svn_fs_fs__path_p2l_index,
   svn_fs_fs__path_p2l_proto_index): implement

Modified:
    subversion/branches/log-addressing/subversion/libsvn_fs_fs/caching.c
    subversion/branches/log-addressing/subversion/libsvn_fs_fs/fs.h
    subversion/branches/log-addressing/subversion/libsvn_fs_fs/fs_fs.c
    subversion/branches/log-addressing/subversion/libsvn_fs_fs/index.c
    subversion/branches/log-addressing/subversion/libsvn_fs_fs/index.h
    subversion/branches/log-addressing/subversion/libsvn_fs_fs/util.c
    subversion/branches/log-addressing/subversion/libsvn_fs_fs/util.h

Modified: subversion/branches/log-addressing/subversion/libsvn_fs_fs/caching.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_fs_fs/caching.c?rev=1511886&r1=1511885&r2=1511886&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_fs_fs/caching.c 
(original)
+++ subversion/branches/log-addressing/subversion/libsvn_fs_fs/caching.c Thu 
Aug  8 17:17:24 2013
@@ -684,11 +684,39 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
                            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),
+                           SVN_CACHE__MEMBUFFER_HIGH_PRIORITY,
+                           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/log-addressing/subversion/libsvn_fs_fs/fs.h
URL: 
http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_fs_fs/fs.h?rev=1511886&r1=1511885&r2=1511886&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_fs_fs/fs.h (original)
+++ subversion/branches/log-addressing/subversion/libsvn_fs_fs/fs.h Thu Aug  8 
17:17:24 2013
@@ -73,6 +73,8 @@ extern "C" {
                                                     shards */
 #define PATH_EXT_L2P_INDEX    ".l2p"             /* extension of the log-
                                                     to-phys index */
+#define PATH_EXT_P2L_INDEX    ".p2l"             /* extension of the phys-
+                                                    to-log index */
 /* If you change this, look at tests/svn_test_fs.c(maybe_install_fsfs_conf) */
 #define PATH_CONFIG           "fsfs.conf"        /* Configuration */
 
@@ -110,6 +112,7 @@ extern "C" {
 #define CONFIG_SECTION_IO                "io"
 #define CONFIG_OPTION_BLOCK_SIZE         "block-size"
 #define CONFIG_OPTION_L2P_PAGE_SIZE      "l2p-page-size"
+#define CONFIG_OPTION_P2L_PAGE_SIZE      "p2l-page-size"
 
 /* The format number of this filesystem.
    This is independent of the repository format number, and
@@ -297,6 +300,9 @@ typedef struct fs_fs_data_t
   /* Capacity in entries of log-to-phys index pages */
   apr_int64_t l2p_page_size;
 
+  /* Rev / pack file granularity covered by phys-to-log index pages */
+  apr_int64_t p2l_page_size;
+  
   /* The revision that was youngest, last time we checked. */
   svn_revnum_t youngest_rev_cache;
 
@@ -386,6 +392,15 @@ typedef struct fs_fs_data_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/log-addressing/subversion/libsvn_fs_fs/fs_fs.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_fs_fs/fs_fs.c?rev=1511886&r1=1511885&r2=1511886&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_fs_fs/fs_fs.c 
(original)
+++ subversion/branches/log-addressing/subversion/libsvn_fs_fs/fs_fs.c Thu Aug  
8 17:17:24 2013
@@ -535,14 +535,20 @@ read_config(fs_fs_data_t *ffd,
                                    CONFIG_SECTION_IO,
                                    CONFIG_OPTION_L2P_PAGE_SIZE,
                                    0x2000));
+      SVN_ERR(svn_config_get_int64(ffd->config, &ffd->p2l_page_size,
+                                   CONFIG_SECTION_IO,
+                                   CONFIG_OPTION_P2L_PAGE_SIZE,
+                                   64));
 
       ffd->block_size *= 0x400;
+      ffd->p2l_page_size *= 0x400;
     }
   else
     {
       /* should be irrelevant but we initialize them anyway */
       ffd->block_size = 0x1000;
       ffd->l2p_page_size = 0x2000;
+      ffd->p2l_page_size = 0x1000;
     }
   
   return SVN_NO_ERROR;
@@ -714,6 +720,23 @@ write_config(svn_fs_t *fs,
 "### This is an expert setting.  Any non-zero value is possible."            NL
 "### l2p-page-size is 8192 entries by default."                              NL
 "# " CONFIG_OPTION_L2P_PAGE_SIZE " = 8192"                                   NL
+"###"                                                                        NL
+"### The phys-to-log index maps positions within the rev or pack file to"    NL
+"### to data items,  i.e. describes what piece of information is being"      NL
+"### stored at that particular offset.  The index describes the rev file"    NL
+"### in chunks (pages) and keeps a global list of all those pages.  Large"   NL
+"### pages mean a shorter page table but a larger per-page description of"   NL
+"### data items in it.  The latency sweetspot depends on the change size"    NL
+"### distribution but is relatively wide."                                   NL
+"### If the repository contains very large files,  i.e. individual changes"  NL
+"### of tens of MB each,  increasing the page size will shorten the index"   NL
+"### file at the expense of a slightly increased latency in sections with"   NL
+"### smaller changes."                                                       NL
+"### For practical reasons,  this should match block-size.  Differing"       NL
+"### values are perfectly legal but may result in some processing overhead." NL
+"### Must be a power of 2."                                                  NL
+"### p2l-page-size is 64 kBytes by default."                                 NL
+"# " CONFIG_OPTION_P2L_PAGE_SIZE " = 64"                                     NL
 ;
 #undef NL
   return svn_io_file_create(svn_dirent_join(fs->path, PATH_CONFIG, pool),

Modified: subversion/branches/log-addressing/subversion/libsvn_fs_fs/index.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_fs_fs/index.c?rev=1511886&r1=1511885&r2=1511886&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_fs_fs/index.c 
(original)
+++ subversion/branches/log-addressing/subversion/libsvn_fs_fs/index.c Thu Aug  
8 17:17:24 2013
@@ -105,6 +105,24 @@ typedef struct l2p_proto_entry_t
   apr_uint64_t item_index;
 } l2p_proto_entry_t;
 
+/* Master run-time data structure of an phys-to-log index.  It contains
+ * an array with one offset value for each rev file cluster.
+ */
+typedef struct p2l_header_t
+{
+  /* first revision covered by the index (and rev file) */
+  svn_revnum_t first_revision;
+
+  /* number of bytes in the rev files covered by each p2l page */
+  apr_uint64_t page_size;
+
+  /* number of pages / clusters in that rev file */
+  apr_size_t page_count;
+
+  /* offsets of the pages / cluster descriptions within the index file */
+  apr_off_t *offsets;
+} p2l_header_t;
+
 /*
  * packed stream
  *
@@ -1376,6 +1394,865 @@ svn_fs_fs__item_offset(apr_off_t *absolu
 }
 
 /*
+ * phys-to-log index
+ */
+svn_error_t *
+svn_fs_fs__p2l_proto_index_open(apr_file_t **proto_index,
+                                const char *file_name,
+                                apr_pool_t *pool)
+{
+  SVN_ERR(svn_io_file_open(proto_index, file_name, APR_READ | APR_WRITE
+                           | APR_CREATE | APR_APPEND | APR_BUFFERED,
+                           APR_OS_DEFAULT, pool));
+
+  return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_fs_fs__p2l_proto_index_add_entry(apr_file_t *proto_index,
+                                     svn_fs_fs__p2l_entry_t *entry,
+                                     apr_pool_t *pool)
+{
+  apr_size_t written = sizeof(*entry);
+
+  SVN_ERR(svn_io_file_write_full(proto_index, entry, sizeof(*entry),
+                                 &written, pool));
+  SVN_ERR_ASSERT(written == sizeof(*entry));
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_fs__p2l_index_create(svn_fs_t *fs,
+                            const char *file_name,
+                            const char *proto_file_name,
+                            svn_revnum_t revision,
+                            apr_pool_t *pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  apr_uint64_t page_size = ffd->p2l_page_size;
+  apr_file_t *proto_index = NULL;
+  int i;
+  svn_boolean_t eof = FALSE;
+  apr_file_t *index_file;
+  unsigned char encoded[ENCODED_INT_LENGTH];
+  svn_revnum_t last_revision = revision;
+  apr_uint64_t last_compound = 0;
+
+  apr_uint64_t last_entry_end = 0;
+  apr_uint64_t last_page_end = 0;
+  apr_size_t last_buffer_size = 0;  /* byte offset in the spill buffer at
+                                       the begin of the current revision */
+
+  /* temporary data structures that collect the data which will be moved
+     to the target file in a second step */
+  apr_pool_t *local_pool = svn_pool_create(pool);
+  apr_array_header_t *table_sizes
+     = apr_array_make(local_pool, 16, sizeof(apr_uint64_t));
+
+  /* 64k blocks, spill after 16MB */
+  svn_spillbuf_t *buffer
+     = svn_spillbuf__create(0x10000, 0x1000000, local_pool);
+
+  /* for loop temps ... */
+  apr_pool_t *iter_pool = svn_pool_create(pool);
+
+  /* start at the beginning of the source file */
+  SVN_ERR(svn_io_file_open(&proto_index, proto_file_name,
+                           APR_READ | APR_CREATE | APR_BUFFERED,
+                           APR_OS_DEFAULT, pool));
+
+  /* process all entries until we fail due to EOF */
+  while (!eof)
+    {
+      svn_fs_fs__p2l_entry_t entry;
+      apr_size_t read = 0;
+      apr_uint64_t entry_end;
+      svn_boolean_t new_page = svn_spillbuf__get_size(buffer) == 0;
+      apr_uint64_t compound;
+      apr_int64_t rev_diff, compound_diff;
+
+      /* (attempt to) read the next entry from the source */
+      SVN_ERR(svn_io_file_read_full2(proto_index, &entry, sizeof(entry),
+                                     &read, &eof, iter_pool));
+      SVN_ERR_ASSERT(eof || read == sizeof(entry));
+
+      /* "unused" (and usually non-existent) section to cover the offsets
+         at the end the of the last page. */
+      if (eof)
+        {
+          entry.offset = last_entry_end;
+          entry.size = APR_ALIGN(entry.offset, page_size) - entry.offset;
+          entry.type = 0;
+          entry.item.revision = last_revision;
+          entry.item.number = 0;
+        }
+      else
+        {
+          /* fix-up items created when the txn's target rev was unknown */
+          if (entry.item.revision == SVN_INVALID_REVNUM)
+            entry.item.revision = revision;
+        }
+      
+      /* end pages if entry is extending beyond their boundaries */
+      entry_end = entry.offset + entry.size;
+      while (entry_end - last_page_end > page_size)
+        {
+          apr_uint64_t buffer_size = svn_spillbuf__get_size(buffer);
+          APR_ARRAY_PUSH(table_sizes, apr_uint64_t)
+             = buffer_size - last_buffer_size;
+
+          last_buffer_size = buffer_size;
+          last_page_end += page_size;
+          new_page = TRUE;
+        }
+
+      /* this entry starts a new table -> store its offset
+         (all following entries in the same table will store sizes only) */
+      if (new_page)
+        {
+          SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded,
+                                      encode_uint(encoded, entry.offset),
+                                      iter_pool));
+          last_revision = revision;
+          last_compound = 0;
+        }
+
+      /* write simple item entry */
+      SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded,
+                                  encode_uint(encoded, entry.size),
+                                  iter_pool));
+
+      rev_diff = entry.item.revision - last_revision;
+      last_revision = entry.item.revision;
+
+      compound = entry.item.number * 8 + entry.type;
+      compound_diff = compound - last_compound;
+      last_compound = compound;
+      
+      SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded,
+                                  encode_int(encoded, compound_diff),
+                                  iter_pool));
+      SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded,
+                                  encode_int(encoded, rev_diff),
+                                  iter_pool));
+     
+      last_entry_end = entry_end;
+
+      svn_pool_clear(iter_pool);
+    }
+
+  /* store length of last table */
+  APR_ARRAY_PUSH(table_sizes, apr_uint64_t)
+      = svn_spillbuf__get_size(buffer) - last_buffer_size;
+
+  /* create the target file */
+  SVN_ERR(svn_io_file_open(&index_file, file_name, APR_WRITE
+                           | APR_CREATE | APR_TRUNCATE | APR_BUFFERED,
+                           APR_OS_DEFAULT, local_pool));
+
+  /* write the start revision and page size */
+  SVN_ERR(svn_io_file_write_full(index_file, encoded,
+                                 encode_uint(encoded, revision),
+                                 NULL, local_pool));
+  SVN_ERR(svn_io_file_write_full(index_file, encoded,
+                                 encode_uint(encoded, page_size),
+                                 NULL, local_pool));
+
+  /* write the page table (actually, the sizes of each page description) */
+  SVN_ERR(svn_io_file_write_full(index_file, encoded,
+                                 encode_uint(encoded, table_sizes->nelts),
+                                 NULL, local_pool));
+  for (i = 0; i < table_sizes->nelts; ++i)
+    {
+      apr_uint64_t value = APR_ARRAY_IDX(table_sizes, i, apr_uint64_t);
+      SVN_ERR(svn_io_file_write_full(index_file, encoded,
+                                     encode_uint(encoded, value),
+                                     NULL, local_pool));
+    }
+
+  /* append page contents */
+  SVN_ERR(svn_stream_copy3(svn_stream__from_spillbuf(buffer, local_pool),
+                           svn_stream_from_aprfile2(index_file, TRUE,
+                                                    local_pool),
+                           NULL, NULL, local_pool));
+
+  /* finalize the index file */
+  SVN_ERR(svn_io_file_close(index_file, local_pool));
+  SVN_ERR(svn_io_set_file_read_only(file_name, FALSE, local_pool));
+
+  svn_pool_destroy(iter_pool);
+  svn_pool_destroy(local_pool);
+
+  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.
+ * If *STREAM is yet to be constructed, do so in STREAM_POOL.
+ * Use POOL for allocations.
+ */
+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 *stream_pool,
+               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;
+  svn_boolean_t is_cached = FALSE;
+
+  /* look for the header data in our cache */
+  pair_cache_key_t key;
+  key.revision = base_revision(fs, revision);
+  key.second = svn_fs_fs__is_packed_rev(fs, revision);
+
+  SVN_ERR(svn_cache__get((void**)header, &is_cached, ffd->p2l_header_cache,
+                         &key, pool));
+  if (is_cached)
+    return SVN_NO_ERROR;
+
+  /* not found -> must read it from disk.
+   * Open index file or position read pointer to the begin of the file */
+  if (*stream == NULL)
+    SVN_ERR(packed_stream_open(stream,
+                               svn_fs_fs__path_p2l_index(fs, key.revision,
+                                                         pool),
+                               ffd->block_size, stream_pool));
+  else
+    packed_stream_seek(*stream, 0);
+
+  /* allocate result data structure */
+  result = apr_pcalloc(pool, sizeof(*result));
+  
+  /* read table sizes and allocate page array */
+  SVN_ERR(packed_stream_get(&value, *stream));
+  result->first_revision = (svn_revnum_t)value;
+  SVN_ERR(packed_stream_get(&value, *stream));
+  result->page_size = value;
+  SVN_ERR(packed_stream_get(&value, *stream));
+  result->page_count = (apr_size_t)value;
+  result->offsets
+    = apr_pcalloc(pool, (result->page_count + 1) * sizeof(*result->offsets));
+
+  /* read page sizes and derive page description offsets from them */
+  result->offsets[0] = 0;
+  for (i = 0; i < result->page_count; ++i)
+    {
+      SVN_ERR(packed_stream_get(&value, *stream));
+      result->offsets[i+1] = result->offsets[i] + (apr_off_t)value;
+    }
+
+  /* correct the offset values */
+  offset = packed_stream_offset(*stream);
+  for (i = 0; i <= result->page_count; ++i)
+    result->offsets[i] += offset;
+
+  /* cache the header data */
+  SVN_ERR(svn_cache__set(ffd->p2l_header_cache, &key, result, pool));
+
+  /* return the result */
+  *header = result;
+
+  return SVN_NO_ERROR;
+}
+
+/* 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)
+{
+  /* if the requested offset is out of bounds, return info for 
+   * a zero-sized empty page right behind the last page.
+   */
+  if (baton->offset / header->page_size < header->page_count)
+    {
+      baton->page_no = baton->offset / header->page_size;
+      baton->start_offset = offsets[baton->page_no];
+      baton->next_offset = offsets[baton->page_no + 1];
+      baton->page_size = header->page_size;
+    }
+  else
+    {
+      baton->page_no = header->page_count;
+      baton->start_offset = offsets[baton->page_no];
+      baton->next_offset = offsets[baton->page_no];
+      baton->page_size = 0;
+    }
+
+  baton->first_revision = header->first_revision;
+  baton->page_start = (apr_off_t)(header->page_size * baton->page_no);
+  baton->page_count = header->page_count;
+}
+
+/* Implement svn_cache__partial_getter_func_t: extract the p2l page info
+ * requested by BATON and return it in BATON.
+ */
+static svn_error_t *
+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.
+ * If *STREAM is yet to be constructed, do so in STREAM_POOL.
+ * 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 *stream_pool,
+                  apr_pool_t *pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  p2l_header_t *header;
+  svn_boolean_t is_cached = FALSE;
+  void *dummy = NULL;
+
+  /* look for the header data in our cache */
+  pair_cache_key_t key;
+  key.revision = base_revision(fs, baton->revision);
+  key.second = svn_fs_fs__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;
+
+  SVN_ERR(get_p2l_header(&header, stream, fs, baton->revision,
+                         stream_pool, pool));
+
+  /* copy the requested info into *BATON */
+  p2l_page_info_copy(baton, header, header->offsets);
+
+  return SVN_NO_ERROR;
+}
+
+/* Read a mapping entry from the phys-to-log index STREAM and append it to
+ * RESULT.  *ITEM_INDEX contains the phys offset for the entry and will
+ * be moved forward by the size of entry.  Use POOL for allocations.
+ */
+static svn_error_t *
+read_entry(packed_number_stream_t *stream,
+           apr_off_t *item_offset,
+           svn_revnum_t *last_revision,
+           apr_uint64_t *last_compound,
+           apr_array_header_t *result,
+           apr_pool_t *pool)
+{
+  apr_uint64_t value;
+
+  svn_fs_fs__p2l_entry_t entry;
+
+  entry.offset = *item_offset;
+  SVN_ERR(packed_stream_get(&value, stream));
+  entry.size = (apr_off_t)value;
+
+  SVN_ERR(packed_stream_get(&value, stream));
+  *last_compound += decode_int(value);
+  
+  entry.type = (int)(*last_compound & 7);
+  entry.item.number = *last_compound / 8;
+
+  SVN_ERR(packed_stream_get(&value, stream));
+  *last_revision += (svn_revnum_t)decode_int(value);
+  entry.item.revision = *last_revision;
+
+  APR_ARRAY_PUSH(result, svn_fs_fs__p2l_entry_t) = entry;
+  *item_offset += entry.size;
+
+  return SVN_NO_ERROR;
+}
+
+/* Read the phys-to-log mappings for the cluster beginning at rev file
+ * offset PAGE_START from the index for START_REVISION in FS.  The data
+ * can be found in the index page beginning at START_OFFSET with the next
+ * page beginning at NEXT_OFFSET.  Return the relevant index entries in
+ * *ENTRIES.  To maximize efficiency, use or return the data stream in
+ * STREAM.  If the latter is yet to be constructed, do so in STREAM_POOL.
+ * Use POOL for other allocations.
+ */
+static svn_error_t *
+get_p2l_page(apr_array_header_t **entries,
+             packed_number_stream_t **stream,
+             svn_fs_t *fs,
+             svn_revnum_t start_revision,
+             apr_off_t start_offset,
+             apr_off_t next_offset,
+             apr_off_t page_start,
+             apr_uint64_t page_size,
+             apr_pool_t *stream_pool,
+             apr_pool_t *pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  apr_uint64_t value;
+  apr_array_header_t *result
+    = apr_array_make(pool, 16, sizeof(svn_fs_fs__p2l_entry_t));
+  apr_off_t item_offset;
+  apr_off_t offset;
+  svn_revnum_t last_revision;
+  apr_uint64_t last_compound;
+
+  /* open index and navigate to page start */
+  if (*stream == NULL)
+    SVN_ERR(packed_stream_open(stream,
+                               svn_fs_fs__path_p2l_index(fs, start_revision,
+                                                         pool),
+                               ffd->block_size, stream_pool));
+  packed_stream_seek(*stream, start_offset);
+
+  /* read rev file offset of the first page entry (all page entries will
+   * only store their sizes). */
+  SVN_ERR(packed_stream_get(&value, *stream));
+  item_offset = (apr_off_t)value;
+
+  /* read all entries of this page */
+  last_revision = start_revision;
+  last_compound = 0;
+  do
+    {
+      SVN_ERR(read_entry(*stream, &item_offset, &last_revision, &last_compound,
+                         result, pool));
+      offset = packed_stream_offset(*stream);
+    }
+  while (offset < next_offset);
+
+  /* if we haven't covered the cluster end yet, we must read the first
+   * entry of the next page */
+  if (item_offset < page_start + page_size)
+    {
+      SVN_ERR(packed_stream_get(&value, *stream));
+      item_offset = (apr_off_t)value;
+      last_revision = start_revision;
+      last_compound = 0;
+      SVN_ERR(read_entry(*stream, &item_offset, &last_revision, &last_compound,
+                         result, pool));
+    }
+
+  *entries = result;
+
+  return SVN_NO_ERROR;
+}
+
+/* If it cannot be found in FS's caches, read the p2l index page selected
+ * by BATON->OFFSET from *STREAM.  If the latter is yet to be constructed,
+ * do so in STREAM_POOL.  Don't read the page 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.  If the data is alread in the
+ * cache, descrease *LEAKING_BUCKET and increase it otherwise.  With that
+ * pattern we will still read all pages from the block even if some of
+ * them survived in the cached.
+ */
+static svn_error_t *
+prefetch_p2l_page(svn_boolean_t *end,
+                  int *leaking_bucket,
+                  svn_fs_t *fs,
+                  packed_number_stream_t **stream,
+                  p2l_page_info_baton_t *baton,
+                  apr_off_t min_offset,
+                  apr_pool_t *stream_pool,
+                  apr_pool_t *scratch_pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  svn_boolean_t already_cached;
+  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, stream_pool, 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? */
+  assert(baton->first_revision <= APR_UINT32_MAX);
+  key.revision = (apr_uint32_t)baton->first_revision;
+  key.is_packed = svn_fs_fs__is_packed_rev(fs, baton->first_revision);
+  key.page = baton->page_no;
+  SVN_ERR(svn_cache__has_key(&already_cached, ffd->p2l_page_cache,
+                             &key, scratch_pool));
+
+  /* yes, already cached */
+  if (already_cached)
+    {
+      /* stop prefetching if most pages are already cached. */
+      if (!--*leaking_bucket)
+        *end = TRUE;
+
+      return SVN_NO_ERROR;
+    }
+
+  ++*leaking_bucket;
+
+  /* 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,
+                       stream_pool,
+                       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;
+}
+
+/* Lookup & construct the baton and key information that we will need for
+ * a P2L page cache lookup.  We want the page covering OFFSET in the rev /
+ * pack file containing REVSION in FS.  Return the results in *PAGE_INFO_P
+ * and *KEY_P.  Read data through the auto-allocated *STREAM.
+ * Use POOL for allocations.
+ */
+static svn_error_t *
+get_p2l_keys(p2l_page_info_baton_t *page_info_p,
+             svn_fs_fs__page_cache_key_t *key_p,
+             packed_number_stream_t **stream,
+             svn_fs_t *fs,
+             svn_revnum_t revision,
+             apr_off_t offset,
+             apr_pool_t *pool)
+{
+  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, pool));
+
+  /* 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,
+                               _("Offset %s too large in revision %ld"),
+                               apr_off_t_toa(pool, offset), revision);
+    }
+
+  /* return results */
+  if (page_info_p)
+    *page_info_p = page_info;
+  
+  /* construct cache key */
+  if (key_p)
+    {
+      svn_fs_fs__page_cache_key_t key = { 0 };
+      assert(page_info.first_revision <= APR_UINT32_MAX);
+      key.revision = (apr_uint32_t)page_info.first_revision;
+      key.is_packed = svn_fs_fs__is_packed_rev(fs, revision);
+      key.page = page_info.page_no;
+
+      *key_p = key;  
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* Body of svn_fs_fs__p2l_index_lookup.  Use / autoconstruct *STREAM as
+ * your input based on REVISION.
+ */
+static svn_error_t *
+p2l_index_lookup(apr_array_header_t **entries,
+                 packed_number_stream_t **stream,
+                 svn_fs_t *fs,
+                 svn_revnum_t revision,
+                 apr_off_t offset,
+                 apr_pool_t *pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  svn_fs_fs__page_cache_key_t key;
+  svn_boolean_t is_cached = FALSE;
+  p2l_page_info_baton_t page_info;
+
+  /* look for this page in our cache */
+  SVN_ERR(get_p2l_keys(&page_info, &key, stream, fs, revision, offset,
+                       pool));
+  SVN_ERR(svn_cache__get((void**)entries, &is_cached, ffd->p2l_page_cache,
+                         &key, pool));
+  if (!is_cached)
+    {
+      svn_boolean_t end;
+      apr_pool_t *iterpool = svn_pool_create(pool);
+      apr_off_t original_page_start = page_info.page_start;
+      int leaking_bucket = 4;
+      p2l_page_info_baton_t prefetch_info = page_info;
+
+      apr_off_t max_offset
+        = APR_ALIGN(page_info.next_offset, ffd->block_size);
+      apr_off_t min_offset
+        = APR_ALIGN(page_info.start_offset, ffd->block_size) - ffd->block_size;
+
+      /* 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 preceding pages */
+      end = FALSE;
+      prefetch_info.offset = original_page_start;
+      while (prefetch_info.offset >= prefetch_info.page_size && !end)
+        {
+          prefetch_info.offset -= prefetch_info.page_size;
+          SVN_ERR(prefetch_p2l_page(&end, &leaking_bucket, fs, stream,
+                                    &prefetch_info, min_offset,
+                                    pool, iterpool));
+          svn_pool_clear(iterpool);
+        }
+
+      /* 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, pool));
+
+      SVN_ERR(svn_cache__set(ffd->p2l_page_cache, &key, *entries, pool));
+
+      /* pre-fetch following pages */
+      end = FALSE;
+      leaking_bucket = 4;
+      prefetch_info = page_info;
+      prefetch_info.offset = original_page_start;
+      while (   prefetch_info.next_offset < max_offset
+             && prefetch_info.page_no + 1 < prefetch_info.page_count
+             && !end)
+        {
+          prefetch_info.offset += prefetch_info.page_size;
+          SVN_ERR(prefetch_p2l_page(&end, &leaking_bucket, fs, stream,
+                                    &prefetch_info, min_offset,
+                                    pool, iterpool));
+          svn_pool_clear(iterpool);
+        }
+
+      svn_pool_destroy(iterpool);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_fs__p2l_index_lookup(apr_array_header_t **entries,
+                            svn_fs_t *fs,
+                            svn_revnum_t revision,
+                            apr_off_t offset,
+                            apr_pool_t *pool)
+{
+  packed_number_stream_t *stream = NULL;
+
+  /* look for this page in our cache */
+  SVN_ERR(p2l_index_lookup(entries, &stream, fs, revision, offset, pool));
+
+  /* make sure we close files after usage */
+  SVN_ERR(packed_stream_close(stream));
+
+  return SVN_NO_ERROR;
+}
+
+/* compare_fn_t comparing a svn_fs_fs__p2l_entry_t at LHS with an offset
+ * RHS.
+ */
+static int
+compare_p2l_entry_offsets(const void *lhs, const void *rhs)
+{
+  const svn_fs_fs__p2l_entry_t *entry = (const svn_fs_fs__p2l_entry_t *)lhs;
+  apr_off_t offset = *(const apr_off_t *)rhs;
+
+  return entry->offset < offset ? -1 : (entry->offset == offset ? 0 : 1);
+}
+
+/* Cached data extraction utility.  DATA is a P2L index page, e.g. an APR
+ * array of svn_fs_fs__p2l_entry_t elements.  Return the entry for the item
+ * starting at OFFSET or NULL if that's not an the start offset of any item.
+ */
+static svn_fs_fs__p2l_entry_t *
+get_p2l_entry_from_cached_page(const void *data,
+                               apr_uint64_t offset,
+                               apr_pool_t *pool)
+{
+  /* resolve all pointer values of in-cache data */
+  const apr_array_header_t *page = data;
+  apr_array_header_t *entries = apr_pmemdup(pool, page, sizeof(*page));
+  int idx;
+
+  entries->elts = (char *)svn_temp_deserializer__ptr(page,
+                                     (const void *const *)&page->elts);
+
+  /* search of the offset we want */
+  idx = svn_sort__bsearch_lower_bound(&offset, entries,
+      (int (*)(const void *, const void *))compare_p2l_entry_offsets);
+
+  /* return it, if it is a perfect match */
+  if (idx < entries->nelts)
+    {
+      svn_fs_fs__p2l_entry_t *entry
+        = &APR_ARRAY_IDX(entries, idx, svn_fs_fs__p2l_entry_t);
+      if (entry->offset == offset)
+        return apr_pmemdup(pool, entry, sizeof(*entry));
+    }
+
+  return NULL;
+}
+
+/* Implements svn_cache__partial_getter_func_t for P2L index pages, copying
+ * the entry for the apr_off_t at BATON into *OUT.  *OUT will be NULL if
+ * there is no matching entry in the index page at DATA.
+ */
+static svn_error_t *
+p2l_entry_lookup_func(void **out,
+                      const void *data,
+                      apr_size_t data_len,
+                      void *baton,
+                      apr_pool_t *result_pool)
+{
+  svn_fs_fs__p2l_entry_t *entry
+    = get_p2l_entry_from_cached_page(data, *(apr_off_t *)baton, result_pool);
+
+  *out = entry && entry->offset == *(apr_off_t *)baton
+       ? apr_pmemdup(result_pool, entry, sizeof(*entry))
+       : NULL;
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+p2l_entry_lookup(svn_fs_fs__p2l_entry_t **entry_p,
+                 packed_number_stream_t **stream,
+                 svn_fs_t *fs,
+                 svn_revnum_t revision,
+                 apr_off_t offset,
+                 apr_pool_t *pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  svn_fs_fs__page_cache_key_t key = { 0 };
+  svn_boolean_t is_cached = FALSE;
+  p2l_page_info_baton_t page_info;
+
+  *entry_p = NULL;
+
+  /* look for this info in our cache */
+  SVN_ERR(get_p2l_keys(&page_info, &key, stream, fs, revision, offset, pool));
+  SVN_ERR(svn_cache__get_partial((void**)entry_p, &is_cached,
+                                 ffd->p2l_page_cache, &key,
+                                 p2l_entry_lookup_func, &offset, pool));
+  if (!is_cached)
+    {
+      int idx;
+
+      /* do a standard index lookup.  This is will automatically prefetch
+       * data to speed up future lookups. */
+      apr_array_header_t *entries;
+      SVN_ERR(p2l_index_lookup(&entries, stream, fs, revision, offset, pool));
+
+      /* Find the entry that we want. */
+      idx = svn_sort__bsearch_lower_bound(&offset, entries, 
+          (int (*)(const void *, const void *))compare_p2l_entry_offsets);
+
+      /* return it, if it is a perfect match */
+      if (idx < entries->nelts)
+        {
+          svn_fs_fs__p2l_entry_t *entry
+            = &APR_ARRAY_IDX(entries, idx, svn_fs_fs__p2l_entry_t);
+          if (entry->offset == offset)
+            *entry_p = entry;
+        }
+    }
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_fs__p2l_entry_lookup(svn_fs_fs__p2l_entry_t **entry_p,
+                            svn_fs_t *fs,
+                            svn_revnum_t revision,
+                            apr_off_t offset,
+                            apr_pool_t *pool)
+{
+  packed_number_stream_t *stream = NULL;
+
+  /* look for this info in our cache */
+  SVN_ERR(p2l_entry_lookup(entry_p, &stream, fs, revision, offset, pool));
+
+  /* make sure we close files after usage */
+  SVN_ERR(packed_stream_close(stream));
+
+  return SVN_NO_ERROR;
+}
+
+/*
  * Standard (de-)serialization functions
  */
 
@@ -1484,3 +2361,103 @@ svn_fs_fs__deserialize_l2p_page(void **o
 
   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/log-addressing/subversion/libsvn_fs_fs/index.h
URL: 
http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_fs_fs/index.h?rev=1511886&r1=1511885&r2=1511886&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_fs_fs/index.h 
(original)
+++ subversion/branches/log-addressing/subversion/libsvn_fs_fs/index.h Thu Aug  
8 17:17:24 2013
@@ -47,6 +47,25 @@
 #define SVN_FS_FS__ITEM_TYPE_ANY_REP    7  /* item is any representation.
                                               Only used in pre-format7. */
 
+/* (user visible) entry in the phys-to-log index.  It describes a section
+ * of some packed / non-packed rev file as containing a specific item.
+ * There must be no overlapping / conflicting entries.
+ */
+typedef struct svn_fs_fs__p2l_entry_t
+{
+  /* offset of the first byte that belongs to the item */
+  apr_off_t offset;
+  
+  /* length of the item in bytes */
+  apr_off_t size;
+
+  /* type of the item (see SVN_FS_FS__ITEM_TYPE_*) defines */
+  unsigned type;
+
+  /* item in that block */
+  svn_fs_fs__id_part_t item;
+} svn_fs_fs__p2l_entry_t;
+
 /* Open / create a log-to-phys index file with the full file path name
  * FILE_NAME.  Return the open file in *PROTO_INDEX and use POOL for
  * allocations.
@@ -90,6 +109,65 @@ svn_fs_fs__l2p_index_create(svn_fs_t *fs
                             svn_revnum_t revision,
                             apr_pool_t *pool);
 
+/* Open / create a phys-to-log index file with the full file path name
+ * FILE_NAME.  Return the open file in *PROTO_INDEX and use POOL for
+ * allocations.
+ */
+svn_error_t *
+svn_fs_fs__p2l_proto_index_open(apr_file_t **proto_index,
+                                const char *file_name,
+                                apr_pool_t *pool);
+
+/* Add a new mapping ENTRY to the phys-to-log index file in PROTO_INDEX.
+ * The entries must be added in ascending offset order and must not leave
+ * intermittent ranges uncovered.  The revision value in ENTRY may be
+ * SVN_INVALID_REVISION.  Use POOL for allocations.
+ */
+svn_error_t *
+svn_fs_fs__p2l_proto_index_add_entry(apr_file_t *proto_index,
+                                     svn_fs_fs__p2l_entry_t *entry,
+                                     apr_pool_t *pool);
+
+/* Use the proto index file stored at PROTO_FILE_NAME and construct the
+ * final phys-to-log index file at FILE_NAME.  Entries without a valid
+ * revision will be assigned to the REVISION given here.
+ * Use POOL for allocations.
+ */
+svn_error_t *
+svn_fs_fs__p2l_index_create(svn_fs_t *fs,
+                            const char *file_name,
+                            const char *proto_file_name,
+                            svn_revnum_t revision,
+                            apr_pool_t *pool);
+
+/* Use the phys-to-log mapping files in FS to build a list of entries
+ * that (partly) share in the same cluster as the item at global OFFSET
+ * in the rep file containing REVISION.  Return the array in *ENTRIES,
+ * elements being of type svn_fs_fs__p2l_entry_t.
+ * Use POOL for allocations.
+ *
+ * Note that (only) the first and the last mapping may cross a cluster
+ * boundary.
+ */
+svn_error_t *
+svn_fs_fs__p2l_index_lookup(apr_array_header_t **entries,
+                            svn_fs_t *fs,
+                            svn_revnum_t revision,
+                            apr_off_t offset,
+                            apr_pool_t *pool);
+
+/* Use the phys-to-log mapping files in FS to return the entry for the
+ * item starting at global OFFSET in the rep file containing REVISION in
+ * *ENTRY.  Sets *ENTRY to NULL if no item starts at exactly that offset.
+ * Use POOL for allocations.
+ */
+svn_error_t *
+svn_fs_fs__p2l_entry_lookup(svn_fs_fs__p2l_entry_t **entry,
+                            svn_fs_t *fs,
+                            svn_revnum_t revision,
+                            apr_off_t offset,
+                            apr_pool_t *pool);
+
 /* For ITEM_INDEX within REV in FS, return the position in the respective
    rev or pack file in *ABSOLUTE_POSITION.  If TXN_ID is not NULL, return
    the file offset within that transaction and REV should be given as
@@ -160,4 +238,42 @@ svn_fs_fs__deserialize_l2p_page(void **o
                                 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

Modified: subversion/branches/log-addressing/subversion/libsvn_fs_fs/util.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_fs_fs/util.c?rev=1511886&r1=1511885&r2=1511886&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_fs_fs/util.c (original)
+++ subversion/branches/log-addressing/subversion/libsvn_fs_fs/util.c Thu Aug  
8 17:17:24 2013
@@ -141,6 +141,15 @@ svn_fs_fs__path_l2p_index(svn_fs_t *fs,
 }
 
 const char *
+svn_fs_fs__path_p2l_index(svn_fs_t *fs,
+                          svn_revnum_t rev,
+                          apr_pool_t *pool)
+{
+  return apr_psprintf(pool, "%s" PATH_EXT_P2L_INDEX,
+                      svn_fs_fs__path_rev_absolute(fs, rev, pool));
+}
+
+const char *
 svn_fs_fs__path_rev_absolute(svn_fs_t *fs,
                              svn_revnum_t rev,
                              apr_pool_t *pool)
@@ -232,6 +241,15 @@ svn_fs_fs__path_l2p_proto_index(svn_fs_t
                          PATH_INDEX PATH_EXT_L2P_INDEX, pool);
 }
 
+const char*
+svn_fs_fs__path_p2l_proto_index(svn_fs_t *fs,
+                                const svn_fs_fs__id_part_t *txn_id,
+                                apr_pool_t *pool)
+{
+  return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
+                         PATH_INDEX PATH_EXT_P2L_INDEX, pool);
+}
+
 const char *
 svn_fs_fs__path_txn_item_index(svn_fs_t *fs,
                                const svn_fs_fs__id_part_t *txn_id,

Modified: subversion/branches/log-addressing/subversion/libsvn_fs_fs/util.h
URL: 
http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_fs_fs/util.h?rev=1511886&r1=1511885&r2=1511886&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_fs_fs/util.h (original)
+++ subversion/branches/log-addressing/subversion/libsvn_fs_fs/util.h Thu Aug  
8 17:17:24 2013
@@ -177,6 +177,14 @@ svn_fs_fs__path_l2p_index(svn_fs_t *fs,
                           svn_revnum_t rev,
                           apr_pool_t *pool);
 
+/* Return the path of the file containing the phys-to-log index for the
+ * file containing revision REV in FS. The result will be allocated in POOL.
+ */
+const char *
+svn_fs_fs__path_p2l_index(svn_fs_t *fs,
+                          svn_revnum_t rev,
+                          apr_pool_t *pool);
+
 /* Return the path of the file storing the oldest non-packed revision in FS.
  * The result will be allocated in POOL.
  */
@@ -242,6 +250,15 @@ svn_fs_fs__path_l2p_proto_index(svn_fs_t
                                 const svn_fs_fs__id_part_t *txn_id,
                                 apr_pool_t *pool);
 
+/* Return the path of the file containing the phys-to-log index for
+ * the transaction identified by TXN_ID in FS.
+ * The result will be allocated in POOL.
+ */
+const char*
+svn_fs_fs__path_p2l_proto_index(svn_fs_t *fs,
+                                const svn_fs_fs__id_part_t *txn_id,
+                                apr_pool_t *pool);
+
 /* Return the path of the file containing item_index counter for
  * the transaction identified by TXN_ID in FS.
  * The result will be allocated in POOL.


Reply via email to