Author: stefan2
Date: Sat Jan 26 23:07:23 2013
New Revision: 1438988

URL: http://svn.apache.org/viewvc?rev=1438988&view=rev
Log:
On the fsfs-format7:  Significantly speed up l2p and p2l index parsing.

First of all, reading numbers one byte at a time is extremely expensive.
We will therefore introduce a buffering stream-y object that reads and
decodes a whole bunch of numbers at a time while still providing full
random access to the underlying data.

Secondly, we try to reuse open files / streams between different stages
of index access, i.e. header and page access.

* subversion/libsvn_fs_fs/index.c
  (MAX_NUMBER_PREFETCH,
   value_position_pair_t,
   packed_number_stream_t): new types and definitions
  (packed_stream_read,
   packed_stream_open,
   packed_stream_close,
   packed_stream_get,
   packed_stream_seek,
   packed_stream_offset): implement the packed numbers stream
  (read_number): drop; superseded by packed numbers stream
  (get_l2p_header,
   get_l2p_page,
   l2p_index_lookup,
   svn_fs_fs__l2p_get_max_ids,
   get_p2l_header,
   read_entry,
   get_p2l_page,
   svn_fs_fs__p2l_index_lookup): switch to stream; reuse open streams

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

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=1438988&r1=1438987&r2=1438988&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/index.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/index.c Sat Jan 26 
23:07:23 2013
@@ -119,6 +119,237 @@ typedef struct p2l_index_header_t
   apr_off_t *offsets;
 } p2l_index_header_t;
 
+/* How many numbers we will pre-fetch and buffer in a packed number stream.
+ */
+enum { MAX_NUMBER_PREFETCH = 64 };
+
+/* Prefetched number entry in a packed number stream.
+ */
+typedef struct value_position_pair_t
+{
+  /* prefetched number */
+  apr_uint64_t value;
+
+  /* number of bytes read, *including* this number, since the buffer start */
+  apr_size_t total_len;
+} value_position_pair_t;
+
+/* State of a prefetching packed number stream.  It will read compressed
+ * index data efficiently and present it as a series of non-packed uint64.
+ */
+typedef struct packed_number_stream_t
+{
+  /* underlying data file containing the packed values */
+  apr_file_t *file;
+
+  /* number of used entries in BUFFER (starting at index 0) */
+  apr_size_t used;
+
+  /* index of the next number to read from the BUFFER (0 .. USED).
+   * If CURRENT == USED, we need to read more data upon get() */
+  apr_size_t current;
+
+  /* offset in FILE from which the first entry in BUFFER has been read */
+  apr_off_t start_offset;
+
+  /* offset in FILE from which the next number has to be read */
+  apr_off_t next_offset;
+
+  /* pool to be used for file ops etc. */
+  apr_pool_t *pool;
+
+  /* buffer for prefetched values */
+  value_position_pair_t buffer[MAX_NUMBER_PREFETCH];
+} packed_number_stream_t;
+
+/* Read up to MAX_NUMBER_PREFETCH numbers from the STREAM->NEXT_OFFSET in
+ * STREAM->FILE and buffer them.
+ *
+ * We don't want GCC and others to inline this function into get() because
+ * it prevents get() from being inlined itself.
+ */
+SVN__PREVENT_INLINE
+static svn_error_t *
+packed_stream_read(packed_number_stream_t *stream)
+{
+  unsigned char buffer[MAX_NUMBER_PREFETCH];
+  apr_size_t read = 0;
+  svn_boolean_t eof;
+  apr_size_t i;
+  value_position_pair_t *target;
+
+  /* all buffered data will have been read starting here */
+  stream->start_offset = stream->next_offset;
+
+  /* packed numbers are usually not aligned to MAX_NUMBER_PREFETCH blocks,
+   * i.e. the last number has been incomplete (and not buffered in stream)
+   * and need to be re-read.  Therefore, always correct the file pointer.
+   */
+  SVN_ERR(svn_io_file_seek(stream->file, SEEK_SET, &stream->next_offset,
+                           stream->pool));
+  SVN_ERR(svn_io_file_read_full2(stream->file, buffer, sizeof(buffer),
+                                 &read, &eof, stream->pool));
+
+  /* if the last number is incomplete, trim it from the buffer */
+  while (read > 0 && buffer[read-1] >= 0x80)
+    --read;
+
+  /* we call read() only if get() requires more data.  So, there must be
+   * at least *one* further number. */
+  if SVN__PREDICT_FALSE(read == 0)
+    {
+      const char *file_name;
+      SVN_ERR(svn_io_file_name_get(&file_name, stream->file,
+                                   stream->pool));
+      return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_CORRUPTION, NULL,
+                               _("Unexpected end of index file %s"),
+                               file_name);
+    }
+
+  /* parse file buffer and expand into stream buffer */
+  target = stream->buffer;
+  for (i = 0; i < read;)
+    {
+      if (buffer[i] < 0x80)
+        {
+          /* numbers < 128 are relatively frequent and particularly easy
+           * to decode.  Give them special treatment. */
+          target->value = buffer[i];
+          ++i;
+          target->total_len = i;
+          ++target;
+        }
+      else
+        {
+          apr_uint64_t value = 0;
+          apr_uint64_t shift = 0;
+          while (buffer[i] >= 0x80)
+            {
+              value += ((apr_uint64_t)buffer[i] & 0x7f) << shift;
+              shift += 7;
+              ++i;
+            }
+
+          target->value = value + ((apr_uint64_t)buffer[i] << shift);
+          ++i;
+          target->total_len = i;
+          ++target;
+
+          /* let's catch corrupted data early.  It would surely cause
+           * havoc further down the line. */
+          if SVN__PREDICT_FALSE(shift > 8 * sizeof(value))
+            return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_CORRUPTION, NULL,
+                                     _("Corrupt index: number too large"));
+       }
+    }
+
+  /* update stream state */
+  stream->used = target - stream->buffer;
+  stream->next_offset = stream->start_offset + i;
+  stream->current = 0;
+
+  return SVN_NO_ERROR;
+};
+
+/* Create and open a packed number stream reading from FILE_NAME and
+ * return it in *STREAM.  Use POOL for allocations.
+ */
+static svn_error_t *
+packed_stream_open(packed_number_stream_t **stream,
+                   const char *file_name,
+                   apr_pool_t *pool)
+{
+  packed_number_stream_t *result = apr_palloc(pool, sizeof(*result));
+  result->pool = svn_pool_create(pool);
+
+  SVN_ERR(svn_io_file_open(&result->file, file_name,
+                           APR_READ | APR_BUFFERED, APR_OS_DEFAULT,
+                           result->pool));
+  
+  result->used = 0;
+  result->current = 0;
+  result->start_offset = 0;
+  result->next_offset = 0;
+
+  *stream = result;
+  
+  return SVN_NO_ERROR;
+}
+
+/* Close STREAM which may be NULL.
+ */
+static svn_error_t *
+packed_stream_close(packed_number_stream_t *stream)
+{
+  if (stream)
+    {
+      SVN_ERR(svn_io_file_close(stream->file, stream->pool));
+      svn_pool_destroy(stream->pool);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/*
+ * The forced inline is required as e.g. GCC would inline read() into here
+ * instead of lining the simple buffer access into callers of get().
+ */
+SVN__FORCE_INLINE
+static svn_error_t*
+packed_stream_get(apr_uint64_t *value,
+                  packed_number_stream_t *stream)
+{
+  if (stream->current == stream->used)
+    SVN_ERR(packed_stream_read(stream));
+
+  *value = stream->buffer[stream->current].value;
+  ++stream->current;
+
+  return SVN_NO_ERROR;
+}
+
+/* Navigate STREAM to packed file offset OFFSET.  There will be no checks
+ * whether the given OFFSET is valid.
+ */
+static void
+packed_stream_seek(packed_number_stream_t *stream,
+                   apr_off_t offset)
+{
+  if (   stream->used == 0
+      || offset < stream->start_offset
+      || offset >= stream->next_offset)
+    {
+      /* outside buffered data.  Next get() will read() from OFFSET. */
+      stream->start_offset = offset;
+      stream->next_offset = offset;
+      stream->current = 0;
+      stream->used = 0;
+    }
+  else
+    {
+      /* Find the suitable location in the stream buffer.
+       * Since our buffer is small, it is efficient enough to simply scan
+       * it for the desired position. */
+      apr_size_t i;
+      for (i = 0; i < stream->used; ++i)
+        if (stream->buffer[i].total_len > offset - stream->start_offset)
+          break;
+
+      stream->current = i;
+    }
+}
+
+/* Return the packed file offset of at which the next number in the stream
+ * can be found.
+ */
+static apr_off_t
+packed_stream_offset(packed_number_stream_t *stream)
+{
+  return stream->current == 0
+       ? stream->start_offset
+       : stream->buffer[stream->current-1].total_len + stream->start_offset;
+}
+
 svn_error_t *
 svn_fs_fs__l2p_proto_index_open(apr_file_t **proto_index,
                                 const char *file_name,
@@ -356,37 +587,13 @@ svn_fs_fs__l2p_index_create(svn_fs_t *fs
   return SVN_NO_ERROR;
 }
 
-/* Read an 7/8b uint64 from FILE into *VALUE.  Use POOL for allocations.
- */
-static svn_error_t *
-read_number(apr_uint64_t *value,
-            apr_file_t *file,
-            apr_pool_t *pool)
-{
-  unsigned char byte;
-  apr_uint64_t shift = 0;
-  
-  *value = 0;
-  do
-    {
-      if (shift > 8 * sizeof(value))
-        return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_CORRUPTION, NULL,
-                                 _("Corrupt index: number too large"));
-
-      SVN_ERR(svn_io_file_getc((char*)&byte, file, pool));
-      *value += ((apr_uint64_t)byte & 0x7f) << shift;
-      shift += 7;
-    }
-  while (byte >= 0x80);
-
-  return SVN_NO_ERROR;
-}            
-
 /* Read the header data structure of the log-to-phys index for REVISION
- * in FS and return it in *HEADER.  Use POOL for allocations.
+ * 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_index_header_t **header,
+               packed_number_stream_t **stream,
                svn_fs_t *fs,
                svn_revnum_t revision,
                apr_pool_t *pool)
@@ -396,19 +603,19 @@ get_l2p_header(l2p_index_header_t **head
   apr_size_t page, page_count;
   apr_off_t offset;
   l2p_index_header_t *result = apr_pcalloc(pool, sizeof(*result));
-  
-  apr_file_t *file = NULL;
-  SVN_ERR(svn_io_file_open(&file, path_l2p_index(fs, revision, pool),
-                           APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool));
+
+  if (*stream == NULL)
+    SVN_ERR(packed_stream_open(stream, path_l2p_index(fs, revision, pool),
+                               pool));
 
   /* read the table sizes */
-  SVN_ERR(read_number(&value, file, pool));
+  SVN_ERR(packed_stream_get(&value, *stream));
   result->first_revision = (svn_revnum_t)value;
-  SVN_ERR(read_number(&value, file, pool));
+  SVN_ERR(packed_stream_get(&value, *stream));
   result->revision_count = (int)value;
-  SVN_ERR(read_number(&value, file, pool));
+  SVN_ERR(packed_stream_get(&value, *stream));
   result->page_size = (apr_size_t)value;
-  SVN_ERR(read_number(&value, file, pool));
+  SVN_ERR(packed_stream_get(&value, *stream));
   page_count = (apr_size_t)value;
 
   /* allocate the page tables */
@@ -422,40 +629,40 @@ get_l2p_header(l2p_index_header_t **head
   result->page_tables[0] = result->page_table;
   for (i = 0; i < result->revision_count; ++i)
     {
-      SVN_ERR(read_number(&value, file, pool));
+      SVN_ERR(packed_stream_get(&value, *stream));
       result->page_tables[i+1] = result->page_tables[i] + (apr_size_t)value;
     }
 
   /* read actual page tables */
   for (page = 0; page < page_count; ++page)
     {
-      SVN_ERR(read_number(&value, file, pool));
+      SVN_ERR(packed_stream_get(&value, *stream));
       result->page_table[page].size = (apr_uint32_t)value;
-      SVN_ERR(read_number(&value, file, pool));
+      SVN_ERR(packed_stream_get(&value, *stream));
       result->page_table[page].entry_count = (apr_uint32_t)value;
     }
 
   /* correct the page description offsets */
-  offset = 0;
-  SVN_ERR(svn_io_file_seek(file, SEEK_CUR, &offset, pool));
+  offset = packed_stream_offset(*stream);
   for (page = 0; page < page_count; ++page)
     {
       result->page_table[page].offset = offset;
       offset += result->page_table[page].size;
     }
 
-  SVN_ERR(svn_io_file_close(file, pool));
   *header = result;
 
   return SVN_NO_ERROR;
 }
 
 /* From the log-to-phys index file starting at START_REVISION in FS, read
- * the mapping page identified by TABLE_ENTRY and return it in *PAGE.
+ * the mapping page identified by TABLE_ENTRY and return it in *PAGE.  To
+ * maximize efficiency, use or return the data stream in *STREAM.
  * Use POOL for allocations.
  */
 static svn_error_t *
 get_l2p_page(l2p_index_page_t **page,
+             packed_number_stream_t **stream,
              svn_fs_t *fs,
              svn_revnum_t start_revision,
              l2_index_page_table_entry_t *table_entry,
@@ -463,16 +670,15 @@ get_l2p_page(l2p_index_page_t **page,
 {
   apr_uint64_t value;
   apr_uint32_t i;
-  apr_off_t offset;
   l2p_index_page_t *result = apr_pcalloc(pool, sizeof(*result));
 
   /* open index file and select page */
-  apr_file_t *file = NULL;
-  SVN_ERR(svn_io_file_open(&file, path_l2p_index(fs, start_revision, pool),
-                           APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool));
+  if (*stream == NULL)
+    SVN_ERR(packed_stream_open(stream,
+                               path_l2p_index(fs, start_revision, pool),
+                               pool));
 
-  offset = table_entry->offset;
-  SVN_ERR(svn_io_file_seek(file, SEEK_SET, &offset, pool));
+  packed_stream_seek(*stream, table_entry->offset);
 
   /* initialize the page content */
   result->entry_count = table_entry->entry_count;
@@ -482,12 +688,11 @@ get_l2p_page(l2p_index_page_t **page,
   /* read all page entries (offsets in rev file) */
   for (i = 0; i < result->entry_count; ++i)
     {
-      SVN_ERR(read_number(&value, file, pool));
+      SVN_ERR(packed_stream_get(&value, *stream));
       result->offsets[i] = (apr_off_t)value - 1; /* '-1' is represented as
                                                     '0' in the index file */
     }
 
-  SVN_ERR(svn_io_file_close(file, pool));
   *page = result;
 
   return SVN_NO_ERROR;
@@ -509,14 +714,18 @@ l2p_index_lookup(apr_off_t *offset,
   l2_index_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;
 
   /* read index master data structure */
-  SVN_ERR(get_l2p_header(&header, fs, revision, pool));
+  SVN_ERR(get_l2p_header(&header, &stream, fs, revision, pool));
   if (   (header->first_revision > revision)
       || (header->first_revision + header->revision_count <= revision))
-    return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_REVISION , NULL,
-                             _("Revision %ld not covered by item index"),
-                             revision);
+    {
+      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)
@@ -551,7 +760,10 @@ l2p_index_lookup(apr_off_t *offset,
     }
 
   /* read the relevant page */
-  SVN_ERR(get_l2p_page(&page, fs, header->first_revision, entry, pool));
+  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
@@ -616,9 +828,10 @@ svn_fs_fs__l2p_get_max_ids(apr_array_hea
   l2p_index_header_t *header = NULL;
   svn_revnum_t revision;
   svn_revnum_t last_rev = (svn_revnum_t)(start_rev + count);
+  packed_number_stream_t *stream = NULL;
 
   /* read index master data structure */
-  SVN_ERR(get_l2p_header(&header, fs, start_rev, pool));
+  SVN_ERR(get_l2p_header(&header, &stream, fs, start_rev, pool));
 
   *max_ids = apr_array_make(pool, (int)count, sizeof(apr_uint64_t));
   for (revision = start_rev; revision < last_rev; ++revision)
@@ -628,7 +841,7 @@ svn_fs_fs__l2p_get_max_ids(apr_array_hea
       apr_uint64_t item_count;
 
       if (revision >= header->first_revision + header->revision_count)
-        SVN_ERR(get_l2p_header(&header, fs, revision, pool));
+        SVN_ERR(get_l2p_header(&header, &stream, fs, revision, pool));
 
       page_count = header->page_tables[revision - header->first_revision + 1]
                  - header->page_tables[revision - header->first_revision] - 1;
@@ -639,6 +852,8 @@ svn_fs_fs__l2p_get_max_ids(apr_array_hea
       APR_ARRAY_PUSH(*max_ids, apr_uint64_t) = item_count;
     }
 
+  SVN_ERR(packed_stream_close(stream));
+
   return SVN_NO_ERROR;
 }
 
@@ -813,10 +1028,12 @@ svn_fs_fs__p2l_index_create(svn_fs_t *fs
 }
 
 /* Read the header data structure of the phys-to-log index for REVISION
- * in FS and return it in *HEADER.  Use POOL for allocations.
+ * 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_p2l_header(p2l_index_header_t **header,
+               packed_number_stream_t **stream,
                svn_fs_t *fs,
                svn_revnum_t revision,
                apr_pool_t *pool)
@@ -827,16 +1044,16 @@ get_p2l_header(p2l_index_header_t **head
   p2l_index_header_t *result = apr_pcalloc(pool, sizeof(*result));
 
   /* open index file */
-  apr_file_t *file = NULL;
-  SVN_ERR(svn_io_file_open(&file, path_p2l_index(fs, revision, pool),
-                           APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool));
+  if (*stream == NULL)
+    SVN_ERR(packed_stream_open(stream, path_p2l_index(fs, revision, pool),
+                               pool));
 
   /* read table sizes and allocate page array */
-  SVN_ERR(read_number(&value, file, pool));
+  SVN_ERR(packed_stream_get(&value, *stream));
   result->first_revision = (svn_revnum_t)value;
-  SVN_ERR(read_number(&value, file, pool));
+  SVN_ERR(packed_stream_get(&value, *stream));
   result->page_size = value;
-  SVN_ERR(read_number(&value, file, pool));
+  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));
@@ -845,28 +1062,26 @@ get_p2l_header(p2l_index_header_t **head
   result->offsets[0] = 0;
   for (i = 0; i < result->page_count; ++i)
     {
-      SVN_ERR(read_number(&value, file, pool));
+      SVN_ERR(packed_stream_get(&value, *stream));
       result->offsets[i+1] = result->offsets[i] + (apr_off_t)value;
     }
 
   /* correct the offset values */
-  offset = 0;
-  SVN_ERR(svn_io_file_seek(file, SEEK_CUR, &offset, pool));
+  offset = packed_stream_offset(*stream);
   for (i = 0; i <= result->page_count; ++i)
     result->offsets[i] += offset;
 
-  SVN_ERR(svn_io_file_close(file, pool));
   *header = result;
 
   return SVN_NO_ERROR;
 }
 
-/* Read a mapping entry from the phys-to-log index FILE and append it to
+/* 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(apr_file_t *file,
+read_entry(packed_number_stream_t *stream,
            apr_off_t *item_offset,
            apr_array_header_t *result,
            apr_pool_t *pool)
@@ -876,13 +1091,13 @@ read_entry(apr_file_t *file,
   svn_fs_fs__p2l_entry_t entry;
 
   entry.offset = *item_offset;
-  SVN_ERR(read_number(&value, file, pool));
+  SVN_ERR(packed_stream_get(&value, stream));
   entry.size = (apr_off_t)value;
-  SVN_ERR(read_number(&value, file, pool));
+  SVN_ERR(packed_stream_get(&value, stream));
   entry.type = (int)value;
-  SVN_ERR(read_number(&value, file, pool));
+  SVN_ERR(packed_stream_get(&value, stream));
   entry.revision = (svn_revnum_t)value;
-  SVN_ERR(read_number(&value, file, pool));
+  SVN_ERR(packed_stream_get(&value, stream));
   entry.item_index = value;
 
   APR_ARRAY_PUSH(result, svn_fs_fs__p2l_entry_t) = entry;
@@ -895,10 +1110,12 @@ read_entry(apr_file_t *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.  Use POOL for allocations.
+ * *ENTRIES.  To maximize efficiency, use or return the data stream in
+ * STREAM.  Use POOL for 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,
@@ -914,22 +1131,22 @@ get_p2l_page(apr_array_header_t **entrie
   apr_off_t offset;
 
   /* open index and navigate to page start */
-  apr_file_t *file = NULL;
-  SVN_ERR(svn_io_file_open(&file, path_p2l_index(fs, start_revision, pool),
-                           APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool));
-  SVN_ERR(svn_io_file_seek(file, SEEK_SET, &start_offset, pool));
+  if (*stream == NULL)
+    SVN_ERR(packed_stream_open(stream,
+                               path_p2l_index(fs, start_revision, pool),
+                               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(read_number(&value, file, pool));
+  SVN_ERR(packed_stream_get(&value, *stream));
   item_offset = (apr_off_t)value;
 
   /* read all entries of this page */
   do
     {
-      SVN_ERR(read_entry(file, &item_offset, result, pool));
-      offset = 0;
-      SVN_ERR(svn_io_file_seek(file, SEEK_CUR, &offset, pool));
+      SVN_ERR(read_entry(*stream, &item_offset, result, pool));
+      offset = packed_stream_offset(*stream);
     }
   while (offset < next_offset);
 
@@ -937,12 +1154,11 @@ get_p2l_page(apr_array_header_t **entrie
    * entry of the next page */
   if (item_offset < page_start + page_size)
     {
-      SVN_ERR(read_number(&value, file, pool));
+      SVN_ERR(packed_stream_get(&value, *stream));
       item_offset = (apr_off_t)value;
-      SVN_ERR(read_entry(file, &item_offset, result, pool));
+      SVN_ERR(read_entry(*stream, &item_offset, result, pool));
     }
 
-  SVN_ERR(svn_io_file_close(file, pool));
   *entries = result;
 
   return SVN_NO_ERROR;
@@ -957,21 +1173,26 @@ svn_fs_fs__p2l_index_lookup(apr_array_he
 {
   p2l_index_header_t *header = NULL;
   apr_size_t page_no;
+  packed_number_stream_t *stream = NULL;
 
-  SVN_ERR(get_p2l_header(&header, fs, revision, pool));
+  SVN_ERR(get_p2l_header(&header, &stream, fs, revision, pool));
   page_no = offset / header->page_size;
 
   if (header->page_count <= page_no)
-    return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_OVERFLOW , NULL,
-                             _("Offset %" APR_OFF_T_FMT
-                               " too large in revision %ld"),
-                             offset, revision);
+    {
+      SVN_ERR(packed_stream_close(stream));
+      return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_OVERFLOW , NULL,
+                               _("Offset %" APR_OFF_T_FMT
+                                 " too large in revision %ld"),
+                               offset, revision);
+    }
 
-  SVN_ERR(get_p2l_page(entries, fs, header->first_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));
+  SVN_ERR(packed_stream_close(stream));
 
   return SVN_NO_ERROR;
 }


Reply via email to