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/01/16 22:26:05 UTC
svn commit: r1434413 - in
/subversion/branches/fsfs-format7/subversion/libsvn_fs_fs: index.c index.h
Author: stefan2
Date: Wed Jan 16 21:26:05 2013
New Revision: 1434413
URL: http://svn.apache.org/viewvc?rev=1434413&view=rev
Log:
On the fsfs-format7 branch: complete the index implemenation, in particular
fix packing problems and add transaction support. Also, properly document
all of it.
* subversion/libsvn_fs_fs/index.h
(SVN_FS_FS__ITEM_INDEX_UNUSED,
SVN_FS_FS__ITEM_INDEX_CHANGES,
SVN_FS_FS__ITEM_INDEX_ROOT_NODE,
SVN_FS_FS__ITEM_INDEX_FIRST_USER,
SVN_FS_FS__ITEM_TYPE_UNUSED,
SVN_FS_FS__ITEM_TYPE_REP,
SVN_FS_FS__ITEM_TYPE_NODEREV,
SVN_FS_FS__ITEM_TYPE_CHANGES): define new constants
(svn_fs_fs__p2l_entry_t,
svn_fs_fs__l2p_proto_index_open,
svn_fs_fs__l2p_proto_index_add_revision,
svn_fs_fs__l2p_proto_index_add_entry,
svn_fs_fs__p2l_proto_index_open,
svn_fs_fs__p2l_proto_index_add_entry,
svn_fs_fs__p2l_index_lookup): document
(svn_fs_fs__l2p_index_create,
svn_fs_fs__p2l_index_create): document; explicitly specify input and
output file names which is useful during rev file packing
(svn_fs_fs__l2p_index_lookup): drop; replaced by svn_fs_fs__item_offset
(svn_fs_fs__item_offset): document; add support for data still in txns
* subversion/libsvn_fs_fs/index.c
(ENCODED_INT_LENGTH,
l2_index_page_table_entry,
l2p_index_header_t,
l2p_index_page_t,
l2_proto_index_entry_t,
p2l_index_header_t,
encode_uint,
get_l2p_header,
get_p2l_header,
read_entry): document
(svn_fs_fs__l2p_proto_index_open,
svn_fs_fs__p2l_proto_index_open): update
(svn_fs_fs__l2p_proto_index_add_entry): document; map offset '-1' to uint64
(svn_fs_fs__l2p_index_create): update, fix and document
(read_number): document and fix
(get_l2p_page): document; fix offset table reading
(svn_fs_fs__l2p_index_lookup): document; no duplicate offset correction
(l2p_proto_index_lookup): new internal utility function
(svn_fs_fs__p2l_index_create): update; fix page boundary detection
(get_p2l_page): document; fix page end detection
(svn_fs_fs__item_offset): update
Modified:
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/index.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/index.c?rev=1434413&r1=1434412&r2=1434413&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/index.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/index.c Wed Jan 16 21:26:05 2013
@@ -35,39 +35,81 @@
#include "../libsvn_fs/fs-loader.h"
+/* maximum length of a uint64 in an 7/8b encoding */
#define ENCODED_INT_LENGTH 10
+/* Page tables in the log-to-phys index file exclusively contain entries
+ * of this type to describe position and size of a given page.
+ */
typedef struct l2_index_page_table_entry_t
{
+ /* global offset on the page within the index file */
apr_uint64_t offset;
+
+ /* number of mapping entries in that page */
apr_uint32_t entry_count;
+
+ /* size of the page on disk (in the index file) */
apr_uint32_t size;
} l2_index_page_table_entry_t;
+/* Master run-time data structure of an log-to-phys index. It contains
+ * the page tables of every revision covered by that index - but not the
+ * pages themselves.
+ */
typedef struct l2p_index_header_t
{
+ /* first revision covered by this index */
svn_revnum_t first_revision;
+
+ /* number of revisions covered */
apr_size_t revision_count;
+
+ /* pointers into PAGE_TABLE that mark the first page of the respective
+ * revision. PAGE_TABLES[REVISION_COUNT] points to the end of PAGE_TABLE.
+ */
l2_index_page_table_entry_t ** page_tables;
+
+ /* Page table covering all pages in the index */
l2_index_page_table_entry_t * page_table;
} l2p_index_header_t;
+/* Run-time data structure containing a single log-to-phys index page.
+ */
typedef struct l2p_index_page_t
{
+ /* number of entries in the OFFSETS array */
apr_uint32_t entry_count;
+
+ /* global file offsets (item index is the array index) within the
+ * packed or non-packed rev file. Offset will be -1 for unused /
+ * invalid item index values. */
apr_off_t *offsets;
} l2p_index_page_t;
+/* All of the log-to-phys proto index file consist of entires of this type.
+ */
typedef struct l2_proto_index_entry_t
{
+ /* phys offset + 1. 0 for "new revision" entries. */
apr_uint64_t offset;
+
+ /* corresponding item index. 0 for "new revision" entries. */
apr_uint64_t item_index;
} l2_proto_index_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_index_header_t
{
+ /* first revision covered by the index (and rev file) */
svn_revnum_t first_revision;
+
+ /* 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_index_header_t;
@@ -76,14 +118,16 @@ svn_fs_fs__l2p_proto_index_open(apr_file
const char *file_name,
apr_pool_t *pool)
{
- SVN_ERR(svn_io_file_open(proto_index, file_name,
- APR_READ | APR_WRITE
+ 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;
}
+/* Write ENTRY to log-to-phys PROTO_INDEX file and verify the results.
+ * Use POOL for allocations.
+ */
static svn_error_t *
write_entry_to_proto_index(apr_file_t *proto_index,
l2_proto_index_entry_t entry,
@@ -118,8 +162,10 @@ svn_fs_fs__l2p_proto_index_add_entry(apr
l2_proto_index_entry_t entry;
/* make sure the conversion to uint64 works */
- SVN_ERR_ASSERT(offset >= 0);
- entry.offset = (apr_uint64_t)offset;
+ SVN_ERR_ASSERT(offset >= -1);
+
+ /* we support offset '-1' as a "not used" indication */
+ entry.offset = (apr_uint64_t)offset + 1;
/* make sure we can use item_index as an array index when building the
* final index file */
@@ -130,6 +176,8 @@ svn_fs_fs__l2p_proto_index_add_entry(apr
pool));
}
+/* Encode VALUE as 7/8b into P and return the number of bytes written.
+ */
static apr_size_t
encode_uint(unsigned char *p, apr_uint64_t value)
{
@@ -146,12 +194,12 @@ encode_uint(unsigned char *p, apr_uint64
}
svn_error_t *
-svn_fs_fs__l2p_index_create(apr_file_t *proto_index,
- svn_fs_t *fs,
+svn_fs_fs__l2p_index_create(const char *file_name,
+ const char *proto_file_name,
svn_revnum_t revision,
apr_pool_t *pool)
{
- apr_off_t offset = 0;
+ apr_file_t *proto_index = NULL;
int i;
apr_uint64_t entry;
svn_boolean_t eof = FALSE;
@@ -180,8 +228,9 @@ svn_fs_fs__l2p_index_create(apr_file_t *
= svn_spillbuf__create(0x10000, 0x1000000, local_pool);
/* start at the beginning of the source file */
- offset = 0;
- SVN_ERR(svn_io_file_seek(proto_index, SEEK_SET, &offset, local_pool));
+ 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 */
for (entry = 0; !eof; ++entry)
@@ -193,10 +242,10 @@ svn_fs_fs__l2p_index_create(apr_file_t *
SVN_ERR(svn_io_file_read_full2(proto_index,
&proto_entry, sizeof(proto_entry),
&read, &eof, local_pool));
- SVN_ERR_ASSERT(read == sizeof(proto_entry));
+ SVN_ERR_ASSERT(eof || read == sizeof(proto_entry));
/* handle new revision */
- if ((entry > 0 && proto_entry.offset == -1) || eof)
+ if ((entry > 0 && proto_entry.offset == 0) || eof)
{
/* dump entries, grouped into pages */
@@ -222,6 +271,8 @@ svn_fs_fs__l2p_index_create(apr_file_t *
= svn_spillbuf__get_size(buffer) - last_buffer_size;
}
+ apr_array_clear(offsets);
+
/* store the number of pages in this revision */
APR_ARRAY_PUSH(page_counts, apr_uint64_t)
= page_sizes->nelts - last_page_count;
@@ -230,30 +281,34 @@ svn_fs_fs__l2p_index_create(apr_file_t *
}
else
{
+ /* store the mapping in our array */
int idx = (apr_size_t)proto_entry.item_index;
- while (idx <= offsets->nelts)
- APR_ARRAY_PUSH(offsets, apr_uint64_t) = 0;
+ while (idx >= offsets->nelts)
+ APR_ARRAY_PUSH(offsets, apr_uint64_t) = 0; /* defaults to offset
+ '-1' / 'invalid' */
- APR_ARRAY_IDX(offsets, idx, apr_uint64_t) = proto_entry.offset + 1;
+ SVN_ERR_ASSERT(APR_ARRAY_IDX(offsets, idx, apr_uint64_t) == 0);
+ APR_ARRAY_IDX(offsets, idx, apr_uint64_t) = proto_entry.offset;
}
}
/* create the target file */
- SVN_ERR(svn_io_file_open(&index_file,
- path_l2p_index(fs, revision, local_pool),
- APR_WRITE | APR_CREATE | APR_TRUNCATE
- | APR_BUFFERED,
+ 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 */
+ /* write header info */
SVN_ERR(svn_io_file_write_full(index_file, encoded,
encode_uint(encoded, revision),
NULL, local_pool));
-
- /* write the revision table */
SVN_ERR(svn_io_file_write_full(index_file, encoded,
encode_uint(encoded, page_counts->nelts),
NULL, local_pool));
+ SVN_ERR(svn_io_file_write_full(index_file, encoded,
+ encode_uint(encoded, page_sizes->nelts),
+ NULL, local_pool));
+
+ /* write the revision table */
for (i = 0; i < page_counts->nelts; ++i)
{
apr_uint64_t value = APR_ARRAY_IDX(page_counts, i, apr_uint64_t);
@@ -263,9 +318,6 @@ svn_fs_fs__l2p_index_create(apr_file_t *
}
/* write the page table */
- SVN_ERR(svn_io_file_write_full(index_file, encoded,
- encode_uint(encoded, page_sizes->nelts),
- NULL, local_pool));
for (i = 0; i < page_sizes->nelts; ++i)
{
apr_uint64_t value = APR_ARRAY_IDX(page_sizes, i, apr_uint64_t);
@@ -283,12 +335,18 @@ svn_fs_fs__l2p_index_create(apr_file_t *
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(local_pool);
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,
@@ -305,7 +363,7 @@ read_number(apr_uint64_t *value,
_("Corrupt index: number too large"));
SVN_ERR(svn_io_file_getc((char*)&byte, file, pool));
- *value += ((apr_uint64_t)byte) << shift;
+ *value += ((apr_uint64_t)byte & 0x7f) << shift;
shift += 7;
}
while (byte >= 0x80);
@@ -313,6 +371,9 @@ read_number(apr_uint64_t *value,
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.
+ */
static svn_error_t *
get_l2p_header(l2p_index_header_t **header,
svn_fs_t *fs,
@@ -329,6 +390,7 @@ get_l2p_header(l2p_index_header_t **head
SVN_ERR(svn_io_file_open(&file, path_l2p_index(fs, revision, pool),
APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool));
+ /* read the table sizes */
SVN_ERR(read_number(&value, file, pool));
result->first_revision = (svn_revnum_t)value;
SVN_ERR(read_number(&value, file, pool));
@@ -336,12 +398,14 @@ get_l2p_header(l2p_index_header_t **head
SVN_ERR(read_number(&value, file, pool));
page_count = (apr_size_t)value;
+ /* allocate the page tables */
result->page_table
= apr_pcalloc(pool, page_count * sizeof(*result->page_table));
result->page_tables
= apr_pcalloc(pool, (result->revision_count + 1)
* sizeof(*result->page_tables));
+ /* read per-revision page table sizes */
result->page_tables[0] = result->page_table;
for (i = 0; i < result->revision_count; ++i)
{
@@ -349,6 +413,7 @@ get_l2p_header(l2p_index_header_t **head
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));
@@ -357,6 +422,7 @@ get_l2p_header(l2p_index_header_t **head
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));
for (page = 0; page < page_count; ++page)
@@ -371,6 +437,10 @@ get_l2p_header(l2p_index_header_t **head
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.
+ * Use POOL for allocations.
+ */
static svn_error_t *
get_l2p_page(l2p_index_page_t **page,
svn_fs_t *fs,
@@ -383,6 +453,7 @@ get_l2p_page(l2p_index_page_t **page,
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));
@@ -390,14 +461,17 @@ get_l2p_page(l2p_index_page_t **page,
offset = table_entry->offset;
SVN_ERR(svn_io_file_seek(file, SEEK_SET, &offset, pool));
+ /* initialize the page content */
result->entry_count = table_entry->entry_count;
- result->offsets = apr_pcalloc(pool, (result->entry_count + 1)
+ result->offsets = apr_pcalloc(pool, result->entry_count
* sizeof(*result->offsets));
+
+ /* read all page entries (offsets in rev file) */
for (i = 0; i < result->entry_count; ++i)
{
- result->offsets[i] = offset;
SVN_ERR(read_number(&value, file, pool));
- offset += (apr_off_t)value;
+ 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));
@@ -406,7 +480,11 @@ get_l2p_page(l2p_index_page_t **page,
return SVN_NO_ERROR;
}
-svn_error_t *
+/* 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.
+ */
+static svn_error_t *
svn_fs_fs__l2p_index_lookup(apr_off_t *offset,
svn_fs_t *fs,
svn_revnum_t revision,
@@ -417,6 +495,7 @@ svn_fs_fs__l2p_index_lookup(apr_off_t *o
l2p_index_page_t *page = NULL;
l2_index_page_table_entry_t *entry, *first_entry, *last_entry;
+ /* read index master data structure */
SVN_ERR(get_l2p_header(&header, fs, revision, pool));
if ( (header->first_revision > revision)
|| (header->first_revision + header->revision_count <= revision))
@@ -424,6 +503,7 @@ svn_fs_fs__l2p_index_lookup(apr_off_t *o
_("Revision %ld not covered by item index"),
revision);
+ /* iterate to the relevant page (fast enough for even 1 mio items / rev) */
first_entry = header->page_tables[revision - header->first_revision];
last_entry = header->page_tables[revision + 1 - header->first_revision];
for (entry = first_entry; entry < last_entry; ++entry)
@@ -432,6 +512,7 @@ svn_fs_fs__l2p_index_lookup(apr_off_t *o
else
item_index -= entry->entry_count;
+ /* read the relevant page */
SVN_ERR(get_l2p_page(&page, fs, header->first_revision, entry, pool));
if (page->entry_count <= item_index)
return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_OVERFLOW , NULL,
@@ -439,8 +520,51 @@ svn_fs_fs__l2p_index_lookup(apr_off_t *o
" too large in revision %ld"),
item_index, revision);
- *offset = page->offsets[item_index] - 1;
+ /* return the result */
+ *offset = page->offsets[item_index];
+
+ return SVN_NO_ERROR;
+}
+
+/* Using the log-to-phys proto index in transaction TXN_ID in FS, find the
+ * absolute offset in the proto rev file for the given ITEM_IDEX and return
+ * it in *OFFSET. Use POOL for allocations.
+ */
+static svn_error_t *
+l2p_proto_index_lookup(apr_off_t *offset,
+ svn_fs_t *fs,
+ const char *txn_id,
+ apr_uint64_t item_index,
+ apr_pool_t *pool)
+{
+ svn_boolean_t eof = FALSE;
+ apr_file_t *file = NULL;
+ SVN_ERR(svn_io_file_open(&file,
+ path_l2p_proto_index(fs, txn_id, pool),
+ APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool));
+
+ /* process all entries until we fail due to EOF */
+ *offset = -1;
+ while (!eof)
+ {
+ l2_proto_index_entry_t entry;
+ apr_size_t read = 0;
+ /* (attempt to) read the next entry from the source */
+ SVN_ERR(svn_io_file_read_full2(file, &entry, sizeof(entry),
+ &read, &eof, pool));
+ SVN_ERR_ASSERT(eof || read == sizeof(entry));
+
+ /* handle new revision */
+ if (!eof && entry.item_index == item_index)
+ {
+ *offset = (apr_off_t)entry.offset - 1;
+ break;
+ }
+ }
+
+ SVN_ERR(svn_io_file_close(file, pool));
+
return SVN_NO_ERROR;
}
@@ -449,8 +573,7 @@ svn_fs_fs__p2l_proto_index_open(apr_file
const char *file_name,
apr_pool_t *pool)
{
- SVN_ERR(svn_io_file_open(proto_index, file_name,
- APR_READ | APR_WRITE
+ SVN_ERR(svn_io_file_open(proto_index, file_name, APR_READ | APR_WRITE
| APR_CREATE | APR_APPEND | APR_BUFFERED,
APR_OS_DEFAULT, pool));
@@ -473,12 +596,12 @@ svn_fs_fs__p2l_proto_index_add_entry(apr
}
svn_error_t *
-svn_fs_fs__p2l_index_create(apr_file_t *proto_index,
- svn_fs_t *fs,
+svn_fs_fs__p2l_index_create(const char *file_name,
+ const char *proto_file_name,
svn_revnum_t revision,
apr_pool_t *pool)
{
- apr_off_t offset = 0;
+ apr_file_t *proto_index = NULL;
int i;
svn_boolean_t eof = FALSE;
apr_file_t *index_file;
@@ -501,8 +624,9 @@ svn_fs_fs__p2l_index_create(apr_file_t *
= svn_spillbuf__create(0x10000, 0x1000000, local_pool);
/* start at the beginning of the source file */
- offset = 0;
- SVN_ERR(svn_io_file_seek(proto_index, SEEK_SET, &offset, local_pool));
+ 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)
@@ -510,12 +634,12 @@ svn_fs_fs__p2l_index_create(apr_file_t *
svn_fs_fs__p2l_entry_t entry;
apr_size_t read = 0;
apr_uint64_t entry_end;
- svn_boolean_t new_page = table_sizes->nelts > 0;
+ svn_boolean_t new_page = svn_spillbuf__get_size(buffer) == 0;
/* (attempt to) read the next entry from the source */
SVN_ERR(svn_io_file_read_full2(proto_index, &entry, sizeof(entry),
&read, &eof, local_pool));
- SVN_ERR_ASSERT(read == sizeof(entry));
+ 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. */
@@ -528,6 +652,9 @@ svn_fs_fs__p2l_index_create(apr_file_t *
entry.item_index = 0;
}
+ if (entry.revision == SVN_INVALID_REVNUM)
+ entry.revision = revision;
+
/* end tables while entry is extending behind them */
entry_end = entry.offset + entry.size;
while (entry_end - last_page_end > page_size)
@@ -569,18 +696,16 @@ svn_fs_fs__p2l_index_create(apr_file_t *
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 */
SVN_ERR(svn_io_file_write_full(index_file, encoded,
encode_uint(encoded, revision),
NULL, local_pool));
- /* create the target file */
- SVN_ERR(svn_io_file_open(&index_file,
- path_l2p_index(fs, revision, local_pool),
- APR_WRITE | APR_CREATE | APR_TRUNCATE
- | APR_BUFFERED,
- APR_OS_DEFAULT, 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),
@@ -599,11 +724,18 @@ svn_fs_fs__p2l_index_create(apr_file_t *
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(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. Use POOL for allocations.
+ */
static svn_error_t *
get_p2l_header(p2l_index_header_t **header,
svn_fs_t *fs,
@@ -615,10 +747,12 @@ get_p2l_header(p2l_index_header_t **head
apr_off_t offset;
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));
+ /* read table sizes and allocate page array */
SVN_ERR(read_number(&value, file, pool));
result->first_revision = (svn_revnum_t)value;
SVN_ERR(read_number(&value, file, pool));
@@ -626,6 +760,7 @@ get_p2l_header(p2l_index_header_t **head
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)
{
@@ -633,6 +768,7 @@ get_p2l_header(p2l_index_header_t **head
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));
for (i = 0; i < result->page_count; ++i)
@@ -644,6 +780,10 @@ get_p2l_header(p2l_index_header_t **head
return SVN_NO_ERROR;
}
+/* Read a mapping entry from the phys-to-log index FILE 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,
apr_off_t *item_offset,
@@ -670,6 +810,12 @@ read_entry(apr_file_t *file,
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. Use POOL for allocations.
+ */
static svn_error_t *
get_p2l_page(apr_array_header_t **entries,
svn_fs_t *fs,
@@ -686,24 +832,32 @@ get_p2l_page(apr_array_header_t **entrie
apr_uint64_t page_size = 0x10000;
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));
+ /* read rev file offset of the first page entry (all page entries will
+ * only store their sizes). */
SVN_ERR(read_number(&value, file, pool));
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));
}
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(read_number(&value, file, pool));
+ item_offset = (apr_off_t)value;
SVN_ERR(read_entry(file, &item_offset, result, pool));
}
@@ -744,14 +898,16 @@ svn_error_t *
svn_fs_fs__item_offset(apr_off_t *offset,
svn_fs_t *fs,
svn_revnum_t revision,
+ const char *txn_id,
apr_uint64_t item_index,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
if (ffd->format < SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT)
{
- *offset = (apr_off_t)item_index - 1;
- if (is_packed_rev(fs, revision))
+ /* older fsfs formats use the manifest file to re-map the offsets */
+ *offset = (apr_off_t)item_index;
+ if (!txn_id && is_packed_rev(fs, revision))
{
apr_off_t rev_offset;
@@ -759,10 +915,13 @@ svn_fs_fs__item_offset(apr_off_t *offset
pool));
*offset += rev_offset;
}
-
- return SVN_NO_ERROR;
}
+ else
+ if (txn_id)
+ SVN_ERR(l2p_proto_index_lookup(offset, fs, txn_id, item_index, pool));
+ else
+ SVN_ERR(svn_fs_fs__l2p_index_lookup(offset, fs, revision,
+ item_index, pool));
- return svn_error_trace(svn_fs_fs__l2p_index_lookup(offset, fs, revision,
- item_index, pool));
+ 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=1434413&r1=1434412&r2=1434413&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/index.h (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_fs_fs/index.h Wed Jan 16 21:26:05 2013
@@ -25,59 +25,124 @@
#include "fs.h"
+/* Per-defined item index values. They are used to identify empty or
+ * mandatory items.
+ */
+#define SVN_FS_FS__ITEM_INDEX_UNUSED 0 /* invalid / reserved value */
+#define SVN_FS_FS__ITEM_INDEX_CHANGES 1 /* list of changed paths */
+#define SVN_FS_FS__ITEM_INDEX_ROOT_NODE 2 /* the root noderev */
+#define SVN_FS_FS__ITEM_INDEX_FIRST_USER 3 /* first noderev to be freely
+ assigned */
+
+/* Data / item types as stored in the phys-to-log index.
+ */
+#define SVN_FS_FS__ITEM_TYPE_UNUSED 0 /* file section not used */
+#define SVN_FS_FS__ITEM_TYPE_REP 1 /* item is a representation */
+#define SVN_FS_FS__ITEM_TYPE_NODEREV 2 /* item is a noderev */
+#define SVN_FS_FS__ITEM_TYPE_CHANGES 3 /* item is a changed paths list */
+
+/* (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;
+
+ /* revision that the item belongs to */
+ svn_revnum_t revision;
+
+ /* logical index of the item within that revision */
+ apr_uint64_t item_index;
+} 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.
+ */
svn_error_t *
svn_fs_fs__l2p_proto_index_open(apr_file_t **proto_index,
const char *file_name,
apr_pool_t *pool);
+/* Call this function before adding entries for the next revision to the
+ * log-to-phys index file in PROTO_INDEX. Use POOL for allocations.
+ */
svn_error_t *
svn_fs_fs__l2p_proto_index_add_revision(apr_file_t *proto_index,
apr_pool_t *pool);
+/* Add a new mapping, ITEM_INDEX to OFFSET, to log-to-phys index file in
+ * PROTO_INDEX. Please note that mappings may be added in any order but
+ * duplicate entries for the same ITEM_INDEX are not supported. Not all
+ * possible index values need to be used. OFFSET may be -1 to mark
+ * 'invalid' item indexes but that is already implied for all item indexes
+ * not explicitly given a mapping.
+ *
+ * Use POOL for allocations.
+ */
svn_error_t *
svn_fs_fs__l2p_proto_index_add_entry(apr_file_t *proto_index,
apr_off_t offset,
apr_uint64_t item_index,
apr_pool_t *pool);
+/* Use the proto index file stored at PROTO_FILE_NAME and construct the
+ * final log-to-phys index file at FILE_NAME. The first revision will
+ * be REVISION, entries to the next revision will be assigned to REVISION+1
+ * and so forth. Use POOL for allocations.
+ */
svn_error_t *
-svn_fs_fs__l2p_index_create(apr_file_t *proto_index,
- svn_fs_t *fs,
- svn_revnum_t revision,
- apr_pool_t *pool);
-
-svn_error_t *
-svn_fs_fs__l2p_index_lookup(apr_off_t *offset,
- svn_fs_t *fs,
+svn_fs_fs__l2p_index_create(const char *file_name,
+ const char *proto_file_name,
svn_revnum_t revision,
- apr_uint64_t item_index,
apr_pool_t *pool);
-typedef struct svn_fs_fs__p2l_entry_t
-{
- apr_off_t offset;
- apr_off_t size;
- unsigned type;
- svn_revnum_t revision;
- apr_uint64_t item_index;
-} svn_fs_fs__p2l_entry_t;
-
+/* 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(apr_file_t *proto_index,
- svn_fs_t *fs,
+svn_fs_fs__p2l_index_create(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.
+ * 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,
@@ -85,10 +150,18 @@ svn_fs_fs__p2l_index_lookup(apr_array_he
apr_off_t offset,
apr_pool_t *pool);
+/* Use the log-to-phys mapping files in FS to find the packed / non-packed /
+ * proto-rev file offset of either (REVISION, ITEM_INDEX) or (TXN_ID,
+ * ITEM_INDEX). For committed revision, TXN_ID must be NULL. For format 6
+ * and older repositories, we simply map the revision local offset given
+ * as ITEM_INDEX to the actual file offset (when packed).
+ * Use POOL for allocations.
+ */
svn_error_t *
svn_fs_fs__item_offset(apr_off_t *offset,
svn_fs_t *fs,
svn_revnum_t revision,
+ const char *txn_id,
apr_uint64_t item_index,
apr_pool_t *pool);