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.