aboutsummaryrefslogtreecommitdiff
path: root/subversion/libsvn_fs_x/cached_data.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_fs_x/cached_data.c')
-rw-r--r--subversion/libsvn_fs_x/cached_data.c1219
1 files changed, 700 insertions, 519 deletions
diff --git a/subversion/libsvn_fs_x/cached_data.c b/subversion/libsvn_fs_x/cached_data.c
index 2fdf5699962b..909842e5c456 100644
--- a/subversion/libsvn_fs_x/cached_data.c
+++ b/subversion/libsvn_fs_x/cached_data.c
@@ -30,6 +30,7 @@
#include "private/svn_io_private.h"
#include "private/svn_sorts_private.h"
+#include "private/svn_string_private.h"
#include "private/svn_subr_private.h"
#include "private/svn_temp_serializer.h"
@@ -54,6 +55,7 @@ block_read(void **result,
svn_fs_t *fs,
const svn_fs_x__id_t *id,
svn_fs_x__revision_file_t *revision_file,
+ void *baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
@@ -67,7 +69,7 @@ block_read(void **result,
* contents if not NULL. Use SCRATCH_POOL for temporary allocations.
*/
static svn_error_t *
-dgb__log_access(svn_fs_t *fs,
+dbg__log_access(svn_fs_t *fs,
const svn_fs_x__id_t *id,
void *item,
apr_uint32_t item_type,
@@ -173,21 +175,6 @@ dgb__log_access(svn_fs_t *fs,
return SVN_NO_ERROR;
}
-/* Convenience wrapper around svn_io_file_aligned_seek, taking filesystem
- FS instead of a block size. */
-static svn_error_t *
-aligned_seek(svn_fs_t *fs,
- apr_file_t *file,
- apr_off_t *buffer_start,
- apr_off_t offset,
- apr_pool_t *scratch_pool)
-{
- svn_fs_x__data_t *ffd = fs->fsap_data;
- return svn_error_trace(svn_io_file_aligned_seek(file, ffd->block_size,
- buffer_start, offset,
- scratch_pool));
-}
-
/* Open the revision file for the item given by ID in filesystem FS and
store the newly opened file in FILE. Seek to the item's location before
returning.
@@ -207,11 +194,10 @@ open_and_seek_revision(svn_fs_x__revision_file_t **file,
SVN_ERR(svn_fs_x__ensure_revision_exists(rev, fs, scratch_pool));
- SVN_ERR(svn_fs_x__open_pack_or_rev_file(&rev_file, fs, rev, result_pool,
- scratch_pool));
+ SVN_ERR(svn_fs_x__rev_file_init(&rev_file, fs, rev, result_pool));
SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, rev_file, id,
scratch_pool));
- SVN_ERR(aligned_seek(fs, rev_file->file, NULL, offset, scratch_pool));
+ SVN_ERR(svn_fs_x__rev_file_seek(rev_file, NULL, offset));
*file = rev_file;
@@ -233,12 +219,12 @@ open_and_seek_transaction(svn_fs_x__revision_file_t **file,
apr_uint32_t sub_item = 0;
apr_int64_t txn_id = svn_fs_x__get_txn_id(rep->id.change_set);
- SVN_ERR(svn_fs_x__open_proto_rev_file(file, fs, txn_id, result_pool,
- scratch_pool));
+ SVN_ERR(svn_fs_x__rev_file_open_proto_rev(file, fs, txn_id, result_pool,
+ scratch_pool));
SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, *file, &rep->id,
scratch_pool));
- SVN_ERR(aligned_seek(fs, (*file)->file, NULL, offset, scratch_pool));
+ SVN_ERR(svn_fs_x__rev_file_seek(*file, NULL, offset));
return SVN_NO_ERROR;
}
@@ -294,6 +280,7 @@ get_node_revision_body(svn_fs_x__noderev_t **noderev_p,
if (svn_fs_x__is_txn(id->change_set))
{
apr_file_t *file;
+ svn_stream_t *stream;
/* This is a transaction node-rev. Its storage logic is very
different from that of rev / pack files. */
@@ -303,21 +290,19 @@ get_node_revision_body(svn_fs_x__noderev_t **noderev_p,
scratch_pool),
APR_READ | APR_BUFFERED, APR_OS_DEFAULT,
scratch_pool);
- if (err)
+ if (err && APR_STATUS_IS_ENOENT(err->apr_err))
+ {
+ svn_error_clear(err);
+ return svn_error_trace(err_dangling_id(fs, id));
+ }
+ else if (err)
{
- if (APR_STATUS_IS_ENOENT(err->apr_err))
- {
- svn_error_clear(err);
- return svn_error_trace(err_dangling_id(fs, id));
- }
-
return svn_error_trace(err);
}
- SVN_ERR(svn_fs_x__read_noderev(noderev_p,
- svn_stream_from_aprfile2(file,
- FALSE,
- scratch_pool),
+ /* Be sure to close the file ASAP. */
+ stream = svn_stream_from_aprfile2(file, FALSE, scratch_pool);
+ SVN_ERR(svn_fs_x__read_noderev(noderev_p, stream,
result_pool, scratch_pool));
}
else
@@ -328,8 +313,8 @@ get_node_revision_body(svn_fs_x__noderev_t **noderev_p,
svn_revnum_t revision = svn_fs_x__get_revnum(id->change_set);
svn_fs_x__pair_cache_key_t key;
- SVN_ERR(svn_fs_x__open_pack_or_rev_file(&revision_file, fs, revision,
- scratch_pool, scratch_pool));
+ SVN_ERR(svn_fs_x__rev_file_init(&revision_file, fs, revision,
+ scratch_pool));
/* First, try a noderevs container cache lookup. */
if ( svn_fs_x__is_packed_rev(fs, revision)
@@ -355,22 +340,20 @@ get_node_revision_body(svn_fs_x__noderev_t **noderev_p,
/* Not found or not applicable. Try a noderev cache lookup.
* If that succeeds, we are done here. */
- if (ffd->node_revision_cache)
- {
- SVN_ERR(svn_cache__get((void **) noderev_p,
- &is_cached,
- ffd->node_revision_cache,
- &key,
- result_pool));
- if (is_cached)
- return SVN_NO_ERROR;
- }
+ SVN_ERR(svn_cache__get((void **) noderev_p,
+ &is_cached,
+ ffd->node_revision_cache,
+ &key,
+ result_pool));
+ if (is_cached)
+ return SVN_NO_ERROR;
/* block-read will parse the whole block and will also return
the one noderev that we need right now. */
SVN_ERR(block_read((void **)noderev_p, fs,
id,
revision_file,
+ NULL,
result_pool,
scratch_pool));
SVN_ERR(svn_fs_x__close_revision_file(revision_file));
@@ -396,7 +379,7 @@ svn_fs_x__get_node_revision(svn_fs_x__noderev_t **noderev_p,
id_string->data);
}
- SVN_ERR(dgb__log_access(fs, id, *noderev_p,
+ SVN_ERR(dbg__log_access(fs, id, *noderev_p,
SVN_FS_X__ITEM_TYPE_NODEREV, scratch_pool));
return svn_error_trace(err);
@@ -423,8 +406,8 @@ svn_fs_x__get_mergeinfo_count(apr_int64_t *count,
svn_revnum_t revision = svn_fs_x__get_revnum(id->change_set);
svn_fs_x__revision_file_t *rev_file;
- SVN_ERR(svn_fs_x__open_pack_or_rev_file(&rev_file, fs, revision,
- scratch_pool, scratch_pool));
+ SVN_ERR(svn_fs_x__rev_file_init(&rev_file, fs, revision,
+ scratch_pool));
if ( svn_fs_x__is_packed_rev(fs, revision)
&& ffd->noderevs_container_cache)
@@ -505,39 +488,13 @@ typedef struct rep_state_t
int chunk_index; /* number of the window to read */
} rep_state_t;
-/* Simple wrapper around svn_fs_x__get_file_offset to simplify callers. */
-static svn_error_t *
-get_file_offset(apr_off_t *offset,
- rep_state_t *rs,
- apr_pool_t *scratch_pool)
-{
- return svn_error_trace(svn_fs_x__get_file_offset(offset,
- rs->sfile->rfile->file,
- scratch_pool));
-}
-
-/* Simple wrapper around svn_io_file_aligned_seek to simplify callers. */
-static svn_error_t *
-rs_aligned_seek(rep_state_t *rs,
- apr_off_t *buffer_start,
- apr_off_t offset,
- apr_pool_t *scratch_pool)
-{
- svn_fs_x__data_t *ffd = rs->sfile->fs->fsap_data;
- return svn_error_trace(svn_io_file_aligned_seek(rs->sfile->rfile->file,
- ffd->block_size,
- buffer_start, offset,
- scratch_pool));
-}
-
/* Open FILE->FILE and FILE->STREAM if they haven't been opened, yet. */
static svn_error_t*
auto_open_shared_file(shared_file_t *file)
{
if (file->rfile == NULL)
- SVN_ERR(svn_fs_x__open_pack_or_rev_file(&file->rfile, file->fs,
- file->revision, file->pool,
- file->pool));
+ SVN_ERR(svn_fs_x__rev_file_init(&file->rfile, file->fs,
+ file->revision, file->pool));
return SVN_NO_ERROR;
}
@@ -571,9 +528,8 @@ auto_read_diff_version(rep_state_t *rs,
if (rs->ver == -1)
{
char buf[4];
- SVN_ERR(rs_aligned_seek(rs, NULL, rs->start, scratch_pool));
- SVN_ERR(svn_io_file_read_full2(rs->sfile->rfile->file, buf,
- sizeof(buf), NULL, NULL, scratch_pool));
+ SVN_ERR(svn_fs_x__rev_file_seek(rs->sfile->rfile, NULL, rs->start));
+ SVN_ERR(svn_fs_x__rev_file_read(rs->sfile->rfile, buf, sizeof(buf)));
/* ### Layering violation */
if (! ((buf[0] == 'S') && (buf[1] == 'V') && (buf[2] == 'N')))
@@ -655,7 +611,7 @@ create_rep_state_body(rep_state_t **rep_state,
: NULL;
/* cache lookup, i.e. skip reading the rep header if possible */
- if (ffd->rep_header_cache && SVN_IS_VALID_REVNUM(revision))
+ if (SVN_IS_VALID_REVNUM(revision))
SVN_ERR(svn_cache__get((void **) &rh, &is_cached,
ffd->rep_header_cache, &key, result_pool));
@@ -680,6 +636,8 @@ create_rep_state_body(rep_state_t **rep_state,
/* read rep header, if necessary */
if (!is_cached)
{
+ svn_stream_t *stream;
+
/* we will need the on-disk location for non-txn reps */
apr_off_t offset;
svn_boolean_t in_container = TRUE;
@@ -731,26 +689,26 @@ create_rep_state_body(rep_state_t **rep_state,
return SVN_NO_ERROR;
}
- SVN_ERR(rs_aligned_seek(rs, NULL, offset, scratch_pool));
+ SVN_ERR(svn_fs_x__rev_file_seek(rs->sfile->rfile, NULL, offset));
}
- SVN_ERR(svn_fs_x__read_rep_header(&rh, rs->sfile->rfile->stream,
+ SVN_ERR(svn_fs_x__rev_file_stream(&stream, rs->sfile->rfile));
+ SVN_ERR(svn_fs_x__read_rep_header(&rh, stream,
result_pool, scratch_pool));
- SVN_ERR(get_file_offset(&rs->start, rs, result_pool));
+ SVN_ERR(svn_fs_x__rev_file_offset(&rs->start, rs->sfile->rfile));
/* populate the cache if appropriate */
if (SVN_IS_VALID_REVNUM(revision))
{
- SVN_ERR(block_read(NULL, fs, &rs->rep_id, rs->sfile->rfile,
+ SVN_ERR(block_read(NULL, fs, &rs->rep_id, rs->sfile->rfile, NULL,
result_pool, scratch_pool));
- if (ffd->rep_header_cache)
- SVN_ERR(svn_cache__set(ffd->rep_header_cache, &key, rh,
- scratch_pool));
+ SVN_ERR(svn_cache__set(ffd->rep_header_cache, &key, rh,
+ scratch_pool));
}
}
/* finalize */
- SVN_ERR(dgb__log_access(fs, &rs->rep_id, rh, SVN_FS_X__ITEM_TYPE_ANY_REP,
+ SVN_ERR(dbg__log_access(fs, &rs->rep_id, rh, SVN_FS_X__ITEM_TYPE_ANY_REP,
scratch_pool));
rs->header_size = rh->header_size;
@@ -820,8 +778,7 @@ svn_fs_x__check_rep(svn_fs_x__representation_t *rep,
svn_revnum_t revision = svn_fs_x__get_revnum(rep->id.change_set);
svn_fs_x__revision_file_t *rev_file;
- SVN_ERR(svn_fs_x__open_pack_or_rev_file(&rev_file, fs, revision,
- scratch_pool, scratch_pool));
+ SVN_ERR(svn_fs_x__rev_file_init(&rev_file, fs, revision, scratch_pool));
/* Does REP->ID refer to an actual item? Which one is it? */
SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, rev_file, &rep->id,
@@ -1075,22 +1032,14 @@ get_cached_window_sizes(window_sizes_t **sizes,
svn_boolean_t *is_cached,
apr_pool_t *pool)
{
- if (! rs->window_cache)
- {
- /* txdelta window has not been enabled */
- *is_cached = FALSE;
- }
- else
- {
- svn_fs_x__window_cache_key_t key = { 0 };
- SVN_ERR(svn_cache__get_partial((void **)sizes,
- is_cached,
- rs->window_cache,
- get_window_key(&key, rs),
- get_cached_window_sizes_func,
- NULL,
- pool));
- }
+ svn_fs_x__window_cache_key_t key = { 0 };
+ SVN_ERR(svn_cache__get_partial((void **)sizes,
+ is_cached,
+ rs->window_cache,
+ get_window_key(&key, rs),
+ get_cached_window_sizes_func,
+ NULL,
+ pool));
return SVN_NO_ERROR;
}
@@ -1103,33 +1052,25 @@ get_cached_window(svn_txdelta_window_t **window_p,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- if (! rs->window_cache)
- {
- /* txdelta window has not been enabled */
- *is_cached = FALSE;
- }
- else
- {
- /* ask the cache for the desired txdelta window */
- svn_fs_x__txdelta_cached_window_t *cached_window;
- svn_fs_x__window_cache_key_t key = { 0 };
- get_window_key(&key, rs);
- key.chunk_index = chunk_index;
- SVN_ERR(svn_cache__get((void **) &cached_window,
- is_cached,
- rs->window_cache,
- &key,
- result_pool));
+ /* ask the cache for the desired txdelta window */
+ svn_fs_x__txdelta_cached_window_t *cached_window;
+ svn_fs_x__window_cache_key_t key = { 0 };
+ get_window_key(&key, rs);
+ key.chunk_index = chunk_index;
+ SVN_ERR(svn_cache__get((void **) &cached_window,
+ is_cached,
+ rs->window_cache,
+ &key,
+ result_pool));
- if (*is_cached)
- {
- /* found it. Pass it back to the caller. */
- *window_p = cached_window->window;
+ if (*is_cached)
+ {
+ /* found it. Pass it back to the caller. */
+ *window_p = cached_window->window;
- /* manipulate the RS as if we just read the data */
- rs->current = cached_window->end_offset;
- rs->chunk_index = chunk_index;
- }
+ /* manipulate the RS as if we just read the data */
+ rs->current = cached_window->end_offset;
+ rs->chunk_index = chunk_index;
}
return SVN_NO_ERROR;
@@ -1145,23 +1086,20 @@ set_cached_window(svn_txdelta_window_t *window,
apr_off_t start_offset,
apr_pool_t *scratch_pool)
{
- if (rs->window_cache)
- {
- /* store the window and the first offset _past_ it */
- svn_fs_x__txdelta_cached_window_t cached_window;
- svn_fs_x__window_cache_key_t key = {0};
+ /* store the window and the first offset _past_ it */
+ svn_fs_x__txdelta_cached_window_t cached_window;
+ svn_fs_x__window_cache_key_t key = {0};
- cached_window.window = window;
- cached_window.start_offset = start_offset - rs->start;
- cached_window.end_offset = rs->current;
+ cached_window.window = window;
+ cached_window.start_offset = start_offset - rs->start;
+ cached_window.end_offset = rs->current;
- /* but key it with the start offset because that is the known state
- * when we will look it up */
- SVN_ERR(svn_cache__set(rs->window_cache,
- get_window_key(&key, rs),
- &cached_window,
- scratch_pool));
- }
+ /* but key it with the start offset because that is the known state
+ * when we will look it up */
+ SVN_ERR(svn_cache__set(rs->window_cache,
+ get_window_key(&key, rs),
+ &cached_window,
+ scratch_pool));
return SVN_NO_ERROR;
}
@@ -1178,23 +1116,13 @@ get_cached_combined_window(svn_stringbuf_t **window_p,
svn_boolean_t *is_cached,
apr_pool_t *pool)
{
- if (! rs->combined_cache)
- {
- /* txdelta window has not been enabled */
- *is_cached = FALSE;
- }
- else
- {
- /* ask the cache for the desired txdelta window */
- svn_fs_x__window_cache_key_t key = { 0 };
- return svn_cache__get((void **)window_p,
- is_cached,
- rs->combined_cache,
- get_window_key(&key, rs),
- pool);
- }
-
- return SVN_NO_ERROR;
+ /* ask the cache for the desired txdelta window */
+ svn_fs_x__window_cache_key_t key = { 0 };
+ return svn_cache__get((void **)window_p,
+ is_cached,
+ rs->combined_cache,
+ get_window_key(&key, rs),
+ pool);
}
/* Store the WINDOW read for the rep state RS in the current FSX session's
@@ -1205,18 +1133,13 @@ set_cached_combined_window(svn_stringbuf_t *window,
rep_state_t *rs,
apr_pool_t *scratch_pool)
{
- if (rs->combined_cache)
- {
- /* but key it with the start offset because that is the known state
- * when we will look it up */
- svn_fs_x__window_cache_key_t key = { 0 };
- return svn_cache__set(rs->combined_cache,
- get_window_key(&key, rs),
- window,
- scratch_pool);
- }
-
- return SVN_NO_ERROR;
+ /* but key it with the start offset because that is the known state
+ * when we will look it up */
+ svn_fs_x__window_cache_key_t key = { 0 };
+ return svn_cache__set(rs->combined_cache,
+ get_window_key(&key, rs),
+ window,
+ scratch_pool);
}
/* Build an array of rep_state structures in *LIST giving the delta
@@ -1264,7 +1187,8 @@ build_rep_list(apr_array_header_t **list,
/* for txn reps and containered reps, there won't be a cached
* combined window */
if (svn_fs_x__is_revision(rep.id.change_set)
- && rep_header->type != svn_fs_x__rep_container)
+ && rep_header->type != svn_fs_x__rep_container
+ && rs->combined_cache)
SVN_ERR(get_cached_combined_window(window_p, rs, &is_cached,
result_pool));
@@ -1364,30 +1288,37 @@ read_delta_window(svn_txdelta_window_t **nwin, int this_chunk,
apr_off_t start_offset;
apr_off_t end_offset;
apr_pool_t *iterpool;
+ svn_stream_t *stream;
+ svn_fs_x__revision_file_t *file;
+ svn_boolean_t cacheable = rs->chunk_index == 0
+ && svn_fs_x__is_revision(rs->rep_id.change_set)
+ && rs->window_cache;
SVN_ERR_ASSERT(rs->chunk_index <= this_chunk);
- SVN_ERR(dgb__log_access(rs->sfile->fs, &rs->rep_id, NULL,
+ SVN_ERR(dbg__log_access(rs->sfile->fs, &rs->rep_id, NULL,
SVN_FS_X__ITEM_TYPE_ANY_REP, scratch_pool));
/* Read the next window. But first, try to find it in the cache. */
- SVN_ERR(get_cached_window(nwin, rs, this_chunk, &is_cached,
- result_pool, scratch_pool));
- if (is_cached)
- return SVN_NO_ERROR;
+ if (cacheable)
+ {
+ SVN_ERR(get_cached_window(nwin, rs, this_chunk, &is_cached,
+ result_pool, scratch_pool));
+ if (is_cached)
+ return SVN_NO_ERROR;
+ }
/* someone has to actually read the data from file. Open it */
SVN_ERR(auto_open_shared_file(rs->sfile));
+ file = rs->sfile->rfile;
/* invoke the 'block-read' feature for non-txn data.
However, don't do that if we are in the middle of some representation,
because the block is unlikely to contain other data. */
- if ( rs->chunk_index == 0
- && svn_fs_x__is_revision(rs->rep_id.change_set)
- && rs->window_cache)
+ if (cacheable)
{
- SVN_ERR(block_read(NULL, rs->sfile->fs, &rs->rep_id,
- rs->sfile->rfile, result_pool, scratch_pool));
+ SVN_ERR(block_read(NULL, rs->sfile->fs, &rs->rep_id, file, NULL,
+ result_pool, scratch_pool));
/* reading the whole block probably also provided us with the
desired txdelta window */
@@ -1405,18 +1336,19 @@ read_delta_window(svn_txdelta_window_t **nwin, int this_chunk,
/* RS->FILE may be shared between RS instances -> make sure we point
* to the right data. */
start_offset = rs->start + rs->current;
- SVN_ERR(rs_aligned_seek(rs, NULL, start_offset, scratch_pool));
+ SVN_ERR(svn_fs_x__rev_file_seek(file, NULL, start_offset));
/* Skip windows to reach the current chunk if we aren't there yet. */
iterpool = svn_pool_create(scratch_pool);
while (rs->chunk_index < this_chunk)
{
- apr_file_t *file = rs->sfile->rfile->file;
+ apr_file_t *apr_file;
svn_pool_clear(iterpool);
- SVN_ERR(svn_txdelta_skip_svndiff_window(file, rs->ver, iterpool));
+ SVN_ERR(svn_fs_x__rev_file_get(&apr_file, file));
+ SVN_ERR(svn_txdelta_skip_svndiff_window(apr_file, rs->ver, iterpool));
rs->chunk_index++;
- SVN_ERR(svn_fs_x__get_file_offset(&start_offset, file, iterpool));
+ SVN_ERR(svn_io_file_get_offset(&start_offset, apr_file, iterpool));
rs->current = start_offset - rs->start;
if (rs->current >= rs->size)
@@ -1428,9 +1360,10 @@ read_delta_window(svn_txdelta_window_t **nwin, int this_chunk,
svn_pool_destroy(iterpool);
/* Actually read the next window. */
- SVN_ERR(svn_txdelta_read_svndiff_window(nwin, rs->sfile->rfile->stream,
- rs->ver, result_pool));
- SVN_ERR(get_file_offset(&end_offset, rs, scratch_pool));
+ SVN_ERR(svn_fs_x__rev_file_stream(&stream, file));
+ SVN_ERR(svn_txdelta_read_svndiff_window(nwin, stream, rs->ver,
+ result_pool));
+ SVN_ERR(svn_fs_x__rev_file_offset(&end_offset, file));
rs->current = end_offset - rs->start;
if (rs->current > rs->size)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
@@ -1439,7 +1372,7 @@ read_delta_window(svn_txdelta_window_t **nwin, int this_chunk,
/* the window has not been cached before, thus cache it now
* (if caching is used for them at all) */
- if (svn_fs_x__is_revision(rs->rep_id.change_set))
+ if (cacheable)
SVN_ERR(set_cached_window(*nwin, rs, start_offset, scratch_pool));
return SVN_NO_ERROR;
@@ -1458,31 +1391,29 @@ read_container_window(svn_stringbuf_t **nwin,
svn_fs_x__data_t *ffd = fs->fsap_data;
svn_fs_x__pair_cache_key_t key;
svn_revnum_t revision = svn_fs_x__get_revnum(rs->rep_id.change_set);
+ svn_boolean_t is_cached = FALSE;
+ svn_fs_x__reps_baton_t baton;
SVN_ERR(auto_set_start_offset(rs, scratch_pool));
key.revision = svn_fs_x__packed_base_rev(fs, revision);
key.second = rs->start;
/* already in cache? */
- if (ffd->reps_container_cache)
- {
- svn_boolean_t is_cached = FALSE;
- svn_fs_x__reps_baton_t baton;
- baton.fs = fs;
- baton.idx = rs->sub_item;
+ baton.fs = fs;
+ baton.idx = rs->sub_item;
- SVN_ERR(svn_cache__get_partial((void**)&extractor, &is_cached,
- ffd->reps_container_cache, &key,
- svn_fs_x__reps_get_func, &baton,
- result_pool));
- }
+ SVN_ERR(svn_cache__get_partial((void**)&extractor, &is_cached,
+ ffd->reps_container_cache, &key,
+ svn_fs_x__reps_get_func, &baton,
+ result_pool));
/* read from disk, if necessary */
if (extractor == NULL)
{
SVN_ERR(auto_open_shared_file(rs->sfile));
SVN_ERR(block_read((void **)&extractor, fs, &rs->rep_id,
- rs->sfile->rfile, result_pool, scratch_pool));
+ rs->sfile->rfile, NULL,
+ result_pool, scratch_pool));
}
SVN_ERR(svn_fs_x__extractor_drive(nwin, extractor, rs->current, size,
@@ -1569,7 +1500,8 @@ get_combined_window(svn_stringbuf_t **result,
single chunk. Only then will no other chunk need a deeper RS
list than the cached chunk. */
if ( (rb->chunk_index == 0) && (rs->current == rs->size)
- && svn_fs_x__is_revision(rs->rep_id.change_set))
+ && svn_fs_x__is_revision(rs->rep_id.change_set)
+ && rs->combined_cache)
SVN_ERR(set_cached_combined_window(buf, rs, new_pool));
rs->chunk_index++;
@@ -1587,7 +1519,7 @@ get_combined_window(svn_stringbuf_t **result,
}
/* Returns whether or not the expanded fulltext of the file is cachable
- * based on its size SIZE. The decision depends on the cache used by RB.
+ * based on its size SIZE. The decision depends on the cache used by FFD.
*/
static svn_boolean_t
fulltext_size_is_cachable(svn_fs_x__data_t *ffd,
@@ -1690,23 +1622,23 @@ cache_windows(svn_filesize_t *fulltext_len,
else
{
svn_txdelta_window_t *window;
+ svn_fs_x__revision_file_t *file = rs->sfile->rfile;
+ svn_stream_t *stream;
apr_off_t start_offset = rs->start + rs->current;
apr_off_t end_offset;
apr_off_t block_start;
/* navigate to & read the current window */
- SVN_ERR(rs_aligned_seek(rs, &block_start, start_offset, iterpool));
- SVN_ERR(svn_txdelta_read_svndiff_window(&window,
- rs->sfile->rfile->stream,
- rs->ver, iterpool));
+ SVN_ERR(svn_fs_x__rev_file_stream(&stream, file));
+ SVN_ERR(svn_fs_x__rev_file_seek(file, &block_start, start_offset));
+ SVN_ERR(svn_txdelta_read_svndiff_window(&window, stream, rs->ver,
+ iterpool));
/* aggregate expanded window size */
*fulltext_len += window->tview_len;
/* determine on-disk window size */
- SVN_ERR(svn_fs_x__get_file_offset(&end_offset,
- rs->sfile->rfile->file,
- iterpool));
+ SVN_ERR(svn_fs_x__rev_file_offset(&end_offset, rs->sfile->rfile));
rs->current = end_offset - rs->start;
if (rs->current > rs->size)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
@@ -1735,25 +1667,22 @@ cache_windows(svn_filesize_t *fulltext_len,
static svn_error_t *
read_rep_header(svn_fs_x__rep_header_t **rep_header,
svn_fs_t *fs,
- svn_stream_t *stream,
+ svn_fs_x__revision_file_t *file,
svn_fs_x__representation_cache_key_t *key,
apr_pool_t *pool)
{
svn_fs_x__data_t *ffd = fs->fsap_data;
+ svn_stream_t *stream;
svn_boolean_t is_cached = FALSE;
- if (ffd->rep_header_cache)
- {
- SVN_ERR(svn_cache__get((void**)rep_header, &is_cached,
- ffd->rep_header_cache, key, pool));
- if (is_cached)
- return SVN_NO_ERROR;
- }
+ SVN_ERR(svn_cache__get((void**)rep_header, &is_cached,
+ ffd->rep_header_cache, key, pool));
+ if (is_cached)
+ return SVN_NO_ERROR;
+ SVN_ERR(svn_fs_x__rev_file_stream(&stream, file));
SVN_ERR(svn_fs_x__read_rep_header(rep_header, stream, pool, pool));
-
- if (ffd->rep_header_cache)
- SVN_ERR(svn_cache__set(ffd->rep_header_cache, key, *rep_header, pool));
+ SVN_ERR(svn_cache__set(ffd->rep_header_cache, key, *rep_header, pool));
return SVN_NO_ERROR;
}
@@ -1779,8 +1708,7 @@ svn_fs_x__get_representation_length(svn_filesize_t *packed_len,
key.revision = svn_fs_x__get_revnum(entry->items[0].change_set);
key.is_packed = svn_fs_x__is_packed_rev(fs, key.revision);
key.item_index = entry->items[0].number;
- SVN_ERR(read_rep_header(&rep_header, fs, rev_file->stream, &key,
- scratch_pool));
+ SVN_ERR(read_rep_header(&rep_header, fs, rev_file, &key, scratch_pool));
/* prepare representation reader state (rs) structure */
SVN_ERR(init_rep_state(&rs, rep_header, fs, rev_file, entry,
@@ -1829,10 +1757,10 @@ get_contents_from_windows(rep_read_baton_t *rb,
This is where we need the pseudo rep_state created
by build_rep_list(). */
apr_size_t offset = (apr_size_t)rs->current;
- if (copy_len + offset > rb->base_window->len)
- copy_len = offset < rb->base_window->len
- ? rb->base_window->len - offset
- : 0ul;
+ if (offset >= rb->base_window->len)
+ copy_len = 0ul;
+ else if (copy_len > rb->base_window->len - offset)
+ copy_len = rb->base_window->len - offset;
memcpy (cur, rb->base_window->data + offset, copy_len);
}
@@ -2052,6 +1980,16 @@ skip_contents(rep_read_baton_t *baton,
len -= to_read;
buffer += to_read;
}
+
+ /* Make the MD5 calculation catch up with the data delivered
+ * (we did not run MD5 on the data that we took from the cache). */
+ if (!err)
+ {
+ SVN_ERR(svn_checksum_update(baton->md5_checksum_ctx,
+ baton->current_fulltext->data,
+ baton->current_fulltext->len));
+ baton->off += baton->current_fulltext->len;
+ }
}
else if (len > 0)
{
@@ -2067,6 +2005,15 @@ skip_contents(rep_read_baton_t *baton,
err = get_contents_from_windows(baton, buffer, &to_read);
len -= to_read;
+
+ /* Make the MD5 calculation catch up with the data delivered
+ * (we did not run MD5 on the data that we took from the cache). */
+ if (!err)
+ {
+ SVN_ERR(svn_checksum_update(baton->md5_checksum_ctx,
+ buffer, to_read));
+ baton->off += to_read;
+ }
}
svn_pool_destroy(subpool);
@@ -2112,8 +2059,13 @@ rep_read_contents(void *baton,
SVN_ERR(skip_contents(rb, rb->fulltext_delivered));
}
- /* Get the next block of data. */
- SVN_ERR(get_contents_from_windows(rb, buf, len));
+ /* Get the next block of data.
+ * Keep in mind that the representation might be empty and leave us
+ * already positioned at the end of the rep. */
+ if (rb->off == rb->len)
+ *len = 0;
+ else
+ SVN_ERR(get_contents_from_windows(rb, buf, len));
if (rb->current_fulltext)
svn_stringbuf_appendbytes(rb->current_fulltext, buf, *len);
@@ -2186,7 +2138,7 @@ svn_fs_x__get_contents(svn_stream_t **contents_p,
/* Make the stream attempt fulltext cache lookups if the fulltext
* is cacheable. If it is not, then also don't try to buffer and
* cache it. */
- if (ffd->fulltext_cache && cache_fulltext
+ if ( cache_fulltext
&& SVN_IS_VALID_REVNUM(revision)
&& fulltext_size_is_cachable(ffd, len))
{
@@ -2208,6 +2160,86 @@ svn_fs_x__get_contents(svn_stream_t **contents_p,
return SVN_NO_ERROR;
}
+svn_error_t *
+svn_fs_x__get_contents_from_file(svn_stream_t **contents_p,
+ svn_fs_t *fs,
+ svn_fs_x__representation_t *rep,
+ apr_file_t *file,
+ apr_off_t offset,
+ apr_pool_t *pool)
+{
+ rep_read_baton_t *rb;
+ svn_fs_x__pair_cache_key_t fulltext_cache_key = { SVN_INVALID_REVNUM, 0 };
+ rep_state_t *rs = apr_pcalloc(pool, sizeof(*rs));
+ svn_fs_x__rep_header_t *rh;
+ svn_stream_t *stream;
+
+ /* Initialize the reader baton. Some members may added lazily
+ * while reading from the stream. */
+ SVN_ERR(rep_read_get_baton(&rb, fs, rep, fulltext_cache_key, pool));
+
+ /* Continue constructing RS. Leave caches as NULL. */
+ rs->size = rep->size;
+ rs->rep_id = rep->id;
+ rs->ver = -1;
+ rs->start = -1;
+
+ /* Provide just enough file access info to allow for a basic read from
+ * FILE but leave all index / footer info with empty values b/c FILE
+ * probably is not a complete revision file. */
+ rs->sfile = apr_pcalloc(pool, sizeof(*rs->sfile));
+ rs->sfile->revision = SVN_INVALID_REVNUM;
+ rs->sfile->pool = pool;
+ rs->sfile->fs = fs;
+ SVN_ERR(svn_fs_x__rev_file_wrap_temp(&rs->sfile->rfile, fs, file, pool));
+
+ /* Read the rep header. */
+ SVN_ERR(svn_fs_x__rev_file_seek(rs->sfile->rfile, NULL, offset));
+ SVN_ERR(svn_fs_x__rev_file_stream(&stream, rs->sfile->rfile));
+ SVN_ERR(svn_fs_x__read_rep_header(&rh, stream, pool, pool));
+ SVN_ERR(svn_fs_x__rev_file_offset(&rs->start, rs->sfile->rfile));
+ rs->header_size = rh->header_size;
+
+ /* Log the access. */
+ SVN_ERR(dbg__log_access(fs, &rep->id, rh,
+ SVN_FS_X__ITEM_TYPE_ANY_REP, pool));
+
+ /* Build the representation list (delta chain). */
+ if (rh->type == svn_fs_x__rep_self_delta)
+ {
+ rb->rs_list = apr_array_make(pool, 1, sizeof(rep_state_t *));
+ APR_ARRAY_PUSH(rb->rs_list, rep_state_t *) = rs;
+ rb->src_state = NULL;
+ }
+ else
+ {
+ svn_fs_x__representation_t next_rep = { 0 };
+
+ /* skip "SVNx" diff marker */
+ rs->current = 4;
+
+ /* REP's base rep is inside a proper revision.
+ * It can be reconstructed in the usual way. */
+ next_rep.id.change_set = svn_fs_x__change_set_by_rev(rh->base_revision);
+ next_rep.id.number = rh->base_item_index;
+ next_rep.size = rh->base_length;
+
+ SVN_ERR(build_rep_list(&rb->rs_list, &rb->base_window,
+ &rb->src_state, rb->fs, &next_rep,
+ rb->filehandle_pool, rb->scratch_pool));
+
+ /* Insert the access to REP as the first element of the delta chain. */
+ svn_sort__array_insert(rb->rs_list, &rs, 0);
+ }
+
+ /* Now, the baton is complete and we can assemble the stream around it. */
+ *contents_p = svn_stream_create(rb, pool);
+ svn_stream_set_read2(*contents_p, NULL /* only full read support */,
+ rep_read_contents);
+ svn_stream_set_close(*contents_p, rep_read_contents_close);
+
+ return SVN_NO_ERROR;
+}
/* Baton for cache_access_wrapper. Wraps the original parameters of
* svn_fs_x__try_process_file_content().
@@ -2257,8 +2289,7 @@ svn_fs_x__try_process_file_contents(svn_boolean_t *success,
fulltext_cache_key.revision = svn_fs_x__get_revnum(rep->id.change_set);
fulltext_cache_key.second = rep->id.number;
- if (ffd->fulltext_cache
- && SVN_IS_VALID_REVNUM(fulltext_cache_key.revision)
+ if ( SVN_IS_VALID_REVNUM(fulltext_cache_key.revision)
&& fulltext_size_is_cachable(ffd, rep->expanded_size))
{
cache_access_wrapper_baton_t wrapper_baton;
@@ -2344,12 +2375,11 @@ svn_fs_x__get_file_delta_stream(svn_txdelta_stream_t **stream_p,
svn_stream_t *source_stream, *target_stream;
rep_state_t *rep_state;
svn_fs_x__rep_header_t *rep_header;
- svn_fs_x__data_t *ffd = fs->fsap_data;
/* Try a shortcut: if the target is stored as a delta against the source,
then just use that delta. However, prefer using the fulltext cache
whenever that is available. */
- if (target->data_rep && (source || !ffd->fulltext_cache))
+ if (target->data_rep && source)
{
/* Read target's base rep if any. */
SVN_ERR(create_rep_state(&rep_state, &rep_header, NULL,
@@ -2449,117 +2479,147 @@ compare_dirent_name(const void *a,
return strcmp(lhs->name, rhs);
}
-/* Into ENTRIES, read all directories entries from the key-value text in
- * STREAM. If INCREMENTAL is TRUE, read until the end of the STREAM and
+/* Into ENTRIES, parse all directories entries from the serialized form in
+ * DATA. If INCREMENTAL is TRUE, read until the end of the STREAM and
* update the data. ID is provided for nicer error messages.
+ *
+ * The contents of DATA will be shared with the items in ENTRIES, i.e. it
+ * must not be modified afterwards and must remain valid as long as ENTRIES
+ * is valid. Use SCRATCH_POOL for temporary allocations.
*/
static svn_error_t *
-read_dir_entries(apr_array_header_t *entries,
- svn_stream_t *stream,
- svn_boolean_t incremental,
- const svn_fs_x__id_t *id,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+parse_dir_entries(apr_array_header_t **entries_p,
+ const svn_stringbuf_t *data,
+ svn_boolean_t incremental,
+ const svn_fs_x__id_t *id,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ const apr_byte_t *p = (const apr_byte_t *)data->data;
+ const apr_byte_t *end = p + data->len;
+ apr_uint64_t count;
apr_hash_t *hash = incremental ? svn_hash__make(scratch_pool) : NULL;
- const char *terminator = SVN_HASH_TERMINATOR;
-
- /* Read until the terminator (non-incremental) or the end of STREAM
- (incremental mode). In the latter mode, we use a temporary HASH
- to make updating and removing entries cheaper. */
- while (1)
- {
- svn_hash__entry_t entry;
- svn_fs_x__dirent_t *dirent;
- char *str;
-
- svn_pool_clear(iterpool);
- SVN_ERR(svn_hash__read_entry(&entry, stream, terminator,
- incremental, iterpool));
+ apr_array_header_t *entries;
- /* End of directory? */
- if (entry.key == NULL)
- {
- /* In incremental mode, we skip the terminator and read the
- increments following it until the end of the stream. */
- if (incremental && terminator)
- terminator = NULL;
- else
- break;
- }
+ /* Construct the resulting container. */
+ p = svn__decode_uint(&count, p, end);
+ if (count > INT_MAX)
+ return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+ _("Directory for '%s' is too large"),
+ svn_fs_x__id_unparse(id, scratch_pool)->data);
- /* Deleted entry? */
- if (entry.val == NULL)
- {
- /* We must be in incremental mode */
- assert(hash);
- apr_hash_set(hash, entry.key, entry.keylen, NULL);
- continue;
- }
+ entries = apr_array_make(result_pool, (int)count,
+ sizeof(svn_fs_x__dirent_t *));
- /* Add a new directory entry. */
+ while (p != end)
+ {
+ apr_size_t len;
+ svn_fs_x__dirent_t *dirent;
dirent = apr_pcalloc(result_pool, sizeof(*dirent));
- dirent->name = apr_pstrmemdup(result_pool, entry.key, entry.keylen);
- str = svn_cstring_tokenize(" ", &entry.val);
- if (str == NULL)
+ /* The part of the serialized entry that is not the name will be
+ * about 6 bytes or less. Since APR allocates with an 8 byte
+ * alignment (4 bytes loss on average per string), simply using
+ * the name string in DATA already gives us near-optimal memory
+ * usage. */
+ dirent->name = (const char *)p;
+ len = strlen(dirent->name);
+ p += len + 1;
+ if (p == end)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
- _("Directory entry corrupt in '%s'"),
- svn_fs_x__id_unparse(id, scratch_pool)->data);
+ _("Directory entry missing kind in '%s'"),
+ svn_fs_x__id_unparse(id, scratch_pool)->data);
- if (strcmp(str, SVN_FS_X__KIND_FILE) == 0)
- {
- dirent->kind = svn_node_file;
- }
- else if (strcmp(str, SVN_FS_X__KIND_DIR) == 0)
- {
- dirent->kind = svn_node_dir;
- }
- else
- {
- return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
- _("Directory entry corrupt in '%s'"),
- svn_fs_x__id_unparse(id, scratch_pool)->data);
- }
+ dirent->kind = (svn_node_kind_t)*(p++);
+ if (p == end)
+ return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+ _("Directory entry missing change set in '%s'"),
+ svn_fs_x__id_unparse(id, scratch_pool)->data);
- str = svn_cstring_tokenize(" ", &entry.val);
- if (str == NULL)
+ p = svn__decode_int(&dirent->id.change_set, p, end);
+ if (p == end)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
- _("Directory entry corrupt in '%s'"),
- svn_fs_x__id_unparse(id, scratch_pool)->data);
+ _("Directory entry missing item number in '%s'"),
+ svn_fs_x__id_unparse(id, scratch_pool)->data);
- SVN_ERR(svn_fs_x__id_parse(&dirent->id, str));
+ p = svn__decode_uint(&dirent->id.number, p, end);
/* In incremental mode, update the hash; otherwise, write to the
* final array. */
if (incremental)
- apr_hash_set(hash, dirent->name, entry.keylen, dirent);
+ {
+ /* Insertion / update or a deletion? */
+ if (svn_fs_x__id_used(&dirent->id))
+ apr_hash_set(hash, dirent->name, len, dirent);
+ else
+ apr_hash_set(hash, dirent->name, len, NULL);
+ }
else
- APR_ARRAY_PUSH(entries, svn_fs_x__dirent_t *) = dirent;
+ {
+ APR_ARRAY_PUSH(entries, svn_fs_x__dirent_t *) = dirent;
+ }
}
- /* Convert container to a sorted array. */
if (incremental)
{
+ /* Convert container into a sorted array. */
apr_hash_index_t *hi;
- for (hi = apr_hash_first(iterpool, hash); hi; hi = apr_hash_next(hi))
+ for (hi = apr_hash_first(scratch_pool, hash); hi; hi = apr_hash_next(hi))
APR_ARRAY_PUSH(entries, svn_fs_x__dirent_t *) = apr_hash_this_val(hi);
+
+ if (!sorted(entries))
+ svn_sort__array(entries, compare_dirents);
+ }
+ else
+ {
+ /* Check that we read the expected amount of entries. */
+ if ((apr_uint64_t)entries->nelts != count)
+ return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+ _("Directory length mismatch in '%s'"),
+ svn_fs_x__id_unparse(id, scratch_pool)->data);
}
- if (!sorted(entries))
- svn_sort__array(entries, compare_dirents);
+ *entries_p = entries;
- svn_pool_destroy(iterpool);
+ return SVN_NO_ERROR;
+}
+
+/* For directory NODEREV in FS, return the *FILESIZE of its in-txn
+ * representation. If the directory representation is comitted data,
+ * set *FILESIZE to SVN_INVALID_FILESIZE. Use SCRATCH_POOL for temporaries.
+ */
+static svn_error_t *
+get_txn_dir_info(svn_filesize_t *filesize,
+ svn_fs_t *fs,
+ svn_fs_x__noderev_t *noderev,
+ apr_pool_t *scratch_pool)
+{
+ if (noderev->data_rep
+ && ! svn_fs_x__is_revision(noderev->data_rep->id.change_set))
+ {
+ const svn_io_dirent2_t *dirent;
+ const char *filename;
+
+ filename = svn_fs_x__path_txn_node_children(fs, &noderev->noderev_id,
+ scratch_pool, scratch_pool);
+
+ SVN_ERR(svn_io_stat_dirent2(&dirent, filename, FALSE, FALSE,
+ scratch_pool, scratch_pool));
+ *filesize = dirent->filesize;
+ }
+ else
+ {
+ *filesize = SVN_INVALID_FILESIZE;
+ }
return SVN_NO_ERROR;
}
-/* Fetch the contents of a directory into ENTRIES. Values are stored
+/* Fetch the contents of a directory into DIR. Values are stored
as filename to string mappings; further conversion is necessary to
convert them into svn_fs_x__dirent_t values. */
static svn_error_t *
-get_dir_contents(apr_array_header_t **entries,
+get_dir_contents(svn_fs_x__dir_data_t *dir,
svn_fs_t *fs,
svn_fs_x__noderev_t *noderev,
apr_pool_t *result_pool,
@@ -2567,44 +2627,67 @@ get_dir_contents(apr_array_header_t **entries,
{
svn_stream_t *contents;
const svn_fs_x__id_t *id = &noderev->noderev_id;
+ apr_size_t len;
+ svn_stringbuf_t *text;
+ svn_boolean_t incremental;
- *entries = apr_array_make(result_pool, 16, sizeof(svn_fs_x__dirent_t *));
+ /* Initialize the result. */
+ dir->txn_filesize = SVN_INVALID_FILESIZE;
+
+ /* Read dir contents - unless there is none in which case we are done. */
if (noderev->data_rep
&& ! svn_fs_x__is_revision(noderev->data_rep->id.change_set))
{
- const char *filename
- = svn_fs_x__path_txn_node_children(fs, id, scratch_pool,
- scratch_pool);
+ /* Get location & current size of the directory representation. */
+ const char *filename;
+ apr_file_t *file;
+
+ filename = svn_fs_x__path_txn_node_children(fs, id, scratch_pool,
+ scratch_pool);
/* The representation is mutable. Read the old directory
contents from the mutable children file, followed by the
changes we've made in this transaction. */
- SVN_ERR(svn_stream_open_readonly(&contents, filename, scratch_pool,
- scratch_pool));
- SVN_ERR(read_dir_entries(*entries, contents, TRUE, id,
- result_pool, scratch_pool));
- SVN_ERR(svn_stream_close(contents));
+ SVN_ERR(svn_io_file_open(&file, filename, APR_READ | APR_BUFFERED,
+ APR_OS_DEFAULT, scratch_pool));
+
+ /* Obtain txn children file size. */
+ SVN_ERR(svn_io_file_size_get(&dir->txn_filesize, file, scratch_pool));
+ len = (apr_size_t)dir->txn_filesize;
+
+ /* Finally, provide stream access to FILE. */
+ contents = svn_stream_from_aprfile2(file, FALSE, scratch_pool);
+ incremental = TRUE;
}
else if (noderev->data_rep)
{
- /* Undeltify content before parsing it. Otherwise, we could only
- * parse it byte-by-byte.
- */
- apr_size_t len = noderev->data_rep->expanded_size;
- svn_stringbuf_t *text;
-
/* The representation is immutable. Read it normally. */
+ len = noderev->data_rep->expanded_size;
SVN_ERR(svn_fs_x__get_contents(&contents, fs, noderev->data_rep,
FALSE, scratch_pool));
- SVN_ERR(svn_stringbuf_from_stream(&text, contents, len, scratch_pool));
- SVN_ERR(svn_stream_close(contents));
-
- /* de-serialize hash */
- contents = svn_stream_from_stringbuf(text, scratch_pool);
- SVN_ERR(read_dir_entries(*entries, contents, FALSE, id,
- result_pool, scratch_pool));
+ incremental = FALSE;
+ }
+ else
+ {
+ /* Empty representation == empty directory. */
+ dir->entries = apr_array_make(result_pool, 0,
+ sizeof(svn_fs_x__dirent_t *));
+ return SVN_NO_ERROR;
}
+ /* Read the whole stream contents into a single buffer.
+ * Due to our LEN hint, no allocation overhead occurs.
+ *
+ * Also, a large portion of TEXT will be file / dir names which we
+ * directly reference from DIR->ENTRIES instead of copying them.
+ * Hence, we need to use the RESULT_POOL here. */
+ SVN_ERR(svn_stringbuf_from_stream(&text, contents, len, result_pool));
+ SVN_ERR(svn_stream_close(contents));
+
+ /* de-serialize hash */
+ SVN_ERR(parse_dir_entries(&dir->entries, text, incremental, id,
+ result_pool, scratch_pool));
+
return SVN_NO_ERROR;
}
@@ -2618,26 +2701,24 @@ locate_dir_cache(svn_fs_t *fs,
svn_fs_x__noderev_t *noderev)
{
svn_fs_x__data_t *ffd = fs->fsap_data;
- if (svn_fs_x__is_txn(noderev->noderev_id.change_set))
+
+ if (!noderev->data_rep)
{
- /* data in txns must be addressed by ID since the representation has
- not been created, yet. */
+ /* no data rep -> empty directory.
+ Use a key that does definitely not clash with non-NULL reps. */
+ key->change_set = SVN_FS_X__INVALID_CHANGE_SET;
+ key->number = SVN_FS_X__ITEM_INDEX_UNUSED;
+ }
+ else if (svn_fs_x__is_txn(noderev->noderev_id.change_set))
+ {
+ /* data in txns must be addressed by noderev ID since the
+ representation has not been created, yet. */
*key = noderev->noderev_id;
}
else
{
/* committed data can use simple rev,item pairs */
- if (noderev->data_rep)
- {
- *key = noderev->data_rep->id;
- }
- else
- {
- /* no data rep -> empty directory.
- Use a key that does definitely not clash with non-NULL reps. */
- key->change_set = SVN_FS_X__INVALID_CHANGE_SET;
- key->number = SVN_FS_X__ITEM_INDEX_UNUSED;
- }
+ *key = noderev->data_rep->id;
}
return ffd->dir_cache;
@@ -2651,26 +2732,40 @@ svn_fs_x__rep_contents_dir(apr_array_header_t **entries_p,
apr_pool_t *scratch_pool)
{
svn_fs_x__id_t key;
+ svn_fs_x__dir_data_t *dir;
/* find the cache we may use */
svn_cache__t *cache = locate_dir_cache(fs, &key, noderev);
- if (cache)
+ svn_boolean_t found;
+
+ SVN_ERR(svn_cache__get((void **)&dir, &found, cache, &key, result_pool));
+ if (found)
{
- svn_boolean_t found;
+ /* Verify that the cached dir info is not stale
+ * (no-op for committed data). */
+ svn_filesize_t filesize;
+ SVN_ERR(get_txn_dir_info(&filesize, fs, noderev, scratch_pool));
- SVN_ERR(svn_cache__get((void **)entries_p, &found, cache, &key,
- result_pool));
- if (found)
- return SVN_NO_ERROR;
+ if (filesize == dir->txn_filesize)
+ {
+ /* Still valid. Done. */
+ *entries_p = dir->entries;
+ return SVN_NO_ERROR;
+ }
}
/* Read in the directory contents. */
- SVN_ERR(get_dir_contents(entries_p, fs, noderev, result_pool,
- scratch_pool));
+ dir = apr_pcalloc(scratch_pool, sizeof(*dir));
+ SVN_ERR(get_dir_contents(dir, fs, noderev, result_pool, scratch_pool));
+ *entries_p = dir->entries;
- /* Update the cache, if we are to use one. */
- if (cache)
- SVN_ERR(svn_cache__set(cache, &key, *entries_p, scratch_pool));
+ /* Update the cache, if we are to use one.
+ *
+ * Don't even attempt to serialize very large directories; it would cause
+ * an unnecessary memory allocation peak. 100 bytes/entry is about right.
+ */
+ if (svn_cache__is_cachable(cache, 100 * dir->entries->nelts))
+ SVN_ERR(svn_cache__set(cache, &key, dir, scratch_pool));
return SVN_NO_ERROR;
}
@@ -2699,40 +2794,49 @@ svn_fs_x__rep_contents_dir_entry(svn_fs_x__dirent_t **dirent,
/* find the cache we may use */
svn_fs_x__id_t key;
svn_cache__t *cache = locate_dir_cache(fs, &key, noderev);
- if (cache)
- {
- svn_fs_x__ede_baton_t baton;
- baton.hint = *hint;
- baton.name = name;
-
- /* Cache lookup. */
- SVN_ERR(svn_cache__get_partial((void **)dirent,
- &found,
- cache,
- &key,
- svn_fs_x__extract_dir_entry,
- &baton,
- result_pool));
-
- /* Remember the new clue only if we found something at that spot. */
- if (found)
- *hint = baton.hint;
- }
+ svn_fs_x__ede_baton_t baton;
+
+ svn_filesize_t filesize;
+ SVN_ERR(get_txn_dir_info(&filesize, fs, noderev, scratch_pool));
+
+ /* Cache lookup. */
+ baton.hint = *hint;
+ baton.name = name;
+ baton.txn_filesize = filesize;
+
+ SVN_ERR(svn_cache__get_partial((void **)dirent,
+ &found,
+ cache,
+ &key,
+ svn_fs_x__extract_dir_entry,
+ &baton,
+ result_pool));
+
+ /* Remember the new clue only if we found something at that spot. */
+ if (found)
+ *hint = baton.hint;
/* fetch data from disk if we did not find it in the cache */
- if (! found)
+ if (! found || baton.out_of_date)
{
- apr_array_header_t *entries;
svn_fs_x__dirent_t *entry;
svn_fs_x__dirent_t *entry_copy = NULL;
+ svn_fs_x__dir_data_t dir;
+
+ /* Read in the directory contents. */
+ SVN_ERR(get_dir_contents(&dir, fs, noderev, scratch_pool,
+ scratch_pool));
- /* read the dir from the file system. It will probably be put it
- into the cache for faster lookup in future calls. */
- SVN_ERR(svn_fs_x__rep_contents_dir(&entries, fs, noderev,
- scratch_pool, scratch_pool));
+ /* Update the cache, if we are to use one.
+ *
+ * Don't even attempt to serialize very large directories; it would
+ * cause an unnecessary memory allocation peak. 150 bytes / entry is
+ * about right. */
+ if (cache && svn_cache__is_cachable(cache, 150 * dir.entries->nelts))
+ SVN_ERR(svn_cache__set(cache, &key, &dir, scratch_pool));
/* find desired entry and return a copy in POOL, if found */
- entry = svn_fs_x__find_dir_entry(entries, name, NULL);
+ entry = svn_fs_x__find_dir_entry(dir.entries, name, NULL);
if (entry)
{
entry_copy = apr_pmemdup(result_pool, entry, sizeof(*entry_copy));
@@ -2746,140 +2850,168 @@ svn_fs_x__rep_contents_dir_entry(svn_fs_x__dirent_t **dirent,
}
svn_error_t *
-svn_fs_x__get_proplist(apr_hash_t **proplist_p,
+svn_fs_x__get_proplist(apr_hash_t **proplist,
svn_fs_t *fs,
svn_fs_x__noderev_t *noderev,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- apr_hash_t *proplist;
svn_stream_t *stream;
const svn_fs_x__id_t *noderev_id = &noderev->noderev_id;
if (noderev->prop_rep
&& !svn_fs_x__is_revision(noderev->prop_rep->id.change_set))
{
+ svn_stringbuf_t *content;
+ svn_string_t *as_string;
const char *filename = svn_fs_x__path_txn_node_props(fs, noderev_id,
scratch_pool,
scratch_pool);
- proplist = apr_hash_make(result_pool);
+ SVN_ERR(svn_stringbuf_from_file2(&content, filename, result_pool));
- SVN_ERR(svn_stream_open_readonly(&stream, filename, scratch_pool,
- scratch_pool));
- SVN_ERR(svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR,
- result_pool));
- SVN_ERR(svn_stream_close(stream));
+ as_string = svn_stringbuf__morph_into_string(content);
+ SVN_ERR_W(svn_fs_x__parse_properties(proplist, as_string, result_pool),
+ apr_psprintf(scratch_pool,
+ "malformed property list for node-revision '%s' in '%s'",
+ svn_fs_x__id_unparse(&noderev->noderev_id,
+ scratch_pool)->data,
+ filename));
}
else if (noderev->prop_rep)
{
svn_fs_x__data_t *ffd = fs->fsap_data;
svn_fs_x__representation_t *rep = noderev->prop_rep;
svn_fs_x__pair_cache_key_t key = { 0 };
+ svn_string_t *content;
+ svn_boolean_t is_cached;
key.revision = svn_fs_x__get_revnum(rep->id.change_set);
key.second = rep->id.number;
- if (ffd->properties_cache && SVN_IS_VALID_REVNUM(key.revision))
- {
- svn_boolean_t is_cached;
- SVN_ERR(svn_cache__get((void **) proplist_p, &is_cached,
- ffd->properties_cache, &key, result_pool));
- if (is_cached)
- return SVN_NO_ERROR;
- }
+ SVN_ERR(svn_cache__get((void **) proplist, &is_cached,
+ ffd->properties_cache, &key, result_pool));
+ if (is_cached)
+ return SVN_NO_ERROR;
- proplist = apr_hash_make(result_pool);
- SVN_ERR(svn_fs_x__get_contents(&stream, fs, noderev->prop_rep, FALSE,
- scratch_pool));
- SVN_ERR(svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR,
- result_pool));
- SVN_ERR(svn_stream_close(stream));
+ SVN_ERR(svn_fs_x__get_contents(&stream, fs, rep, FALSE, scratch_pool));
+ SVN_ERR(svn_string_from_stream2(&content, stream, rep->expanded_size,
+ result_pool));
- if (ffd->properties_cache && SVN_IS_VALID_REVNUM(rep->id.change_set))
- SVN_ERR(svn_cache__set(ffd->properties_cache, &key, proplist,
- scratch_pool));
+ SVN_ERR_W(svn_fs_x__parse_properties(proplist, content, result_pool),
+ apr_psprintf(scratch_pool,
+ "malformed property list for node-revision '%s'",
+ svn_fs_x__id_unparse(&noderev->noderev_id,
+ scratch_pool)->data));
+
+ SVN_ERR(svn_cache__set(ffd->properties_cache, &key, *proplist,
+ scratch_pool));
}
else
{
/* return an empty prop list if the node doesn't have any props */
- proplist = apr_hash_make(result_pool);
+ *proplist = apr_hash_make(result_pool);
}
- *proplist_p = proplist;
-
return SVN_NO_ERROR;
}
+svn_error_t *
+svn_fs_x__create_changes_context(svn_fs_x__changes_context_t **context,
+ svn_fs_t *fs,
+ svn_revnum_t rev,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_fs_x__changes_context_t *result = apr_pcalloc(result_pool,
+ sizeof(*result));
+ result->fs = fs;
+ result->revision = rev;
+ SVN_ERR(svn_fs_x__ensure_revision_exists(rev, fs, scratch_pool));
+ SVN_ERR(svn_fs_x__rev_file_init(&result->revision_file, fs, rev,
+ result_pool));
+
+ *context = result;
+ return SVN_NO_ERROR;
+}
svn_error_t *
svn_fs_x__get_changes(apr_array_header_t **changes,
- svn_fs_t *fs,
- svn_revnum_t rev,
- apr_pool_t *result_pool)
+ svn_fs_x__changes_context_t *context,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- svn_fs_x__revision_file_t *revision_file;
svn_boolean_t found;
- svn_fs_x__data_t *ffd = fs->fsap_data;
- apr_pool_t *scratch_pool = svn_pool_create(result_pool);
+ svn_fs_x__data_t *ffd = context->fs->fsap_data;
svn_fs_x__id_t id;
- id.change_set = svn_fs_x__change_set_by_rev(rev);
+ id.change_set = svn_fs_x__change_set_by_rev(context->revision);
id.number = SVN_FS_X__ITEM_INDEX_CHANGES;
- /* Provide revision file. */
-
- SVN_ERR(svn_fs_x__ensure_revision_exists(rev, fs, scratch_pool));
- SVN_ERR(svn_fs_x__open_pack_or_rev_file(&revision_file, fs, rev,
- scratch_pool, scratch_pool));
-
/* try cache lookup first */
- if (ffd->changes_container_cache && svn_fs_x__is_packed_rev(fs, rev))
+ if (svn_fs_x__is_packed_rev(context->fs, context->revision))
{
apr_off_t offset;
- apr_uint32_t sub_item;
svn_fs_x__pair_cache_key_t key;
+ svn_fs_x__changes_get_list_baton_t baton;
+ baton.start = (int)context->next;
+ baton.eol = &context->eol;
- SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, revision_file,
+ SVN_ERR(svn_fs_x__item_offset(&offset, &baton.sub_item, context->fs,
+ context->revision_file,
&id, scratch_pool));
- key.revision = svn_fs_x__packed_base_rev(fs, rev);
+ key.revision = svn_fs_x__packed_base_rev(context->fs,
+ context->revision);
key.second = offset;
SVN_ERR(svn_cache__get_partial((void **)changes, &found,
ffd->changes_container_cache, &key,
svn_fs_x__changes_get_list_func,
- &sub_item, result_pool));
- }
- else if (ffd->changes_cache)
- {
- SVN_ERR(svn_cache__get((void **) changes, &found, ffd->changes_cache,
- &rev, result_pool));
+ &baton, result_pool));
}
else
{
- found = FALSE;
+ svn_fs_x__changes_list_t *changes_list;
+ svn_fs_x__pair_cache_key_t key;
+ key.revision = context->revision;
+ key.second = context->next;
+
+ SVN_ERR(svn_cache__get((void **)&changes_list, &found,
+ ffd->changes_cache, &key, result_pool));
+
+ if (found)
+ {
+ /* Where to look next - if there is more data. */
+ context->eol = changes_list->eol;
+ context->next_offset = changes_list->end_offset;
+
+ /* Return the block as a "proper" APR array. */
+ (*changes) = apr_array_make(result_pool, 0, sizeof(void *));
+ (*changes)->elts = (char *)changes_list->changes;
+ (*changes)->nelts = changes_list->count;
+ (*changes)->nalloc = changes_list->count;
+ }
}
if (!found)
{
/* 'block-read' will also provide us with the desired data */
- SVN_ERR(block_read((void **)changes, fs, &id, revision_file,
+ SVN_ERR(block_read((void **)changes, context->fs, &id,
+ context->revision_file, context,
result_pool, scratch_pool));
-
- SVN_ERR(svn_fs_x__close_revision_file(revision_file));
}
- SVN_ERR(dgb__log_access(fs, &id, *changes, SVN_FS_X__ITEM_TYPE_CHANGES,
- scratch_pool));
+ context->next += (*changes)->nelts;
+
+ SVN_ERR(dbg__log_access(context->fs, &id, *changes,
+ SVN_FS_X__ITEM_TYPE_CHANGES, scratch_pool));
- svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
/* Fetch the representation data (header, txdelta / plain windows)
- * addressed by ENTRY->ITEM in FS and cache it if caches are enabled.
- * Read the data from the already open FILE and the wrapping
- * STREAM object. If MAX_OFFSET is not -1, don't read windows that start
+ * addressed by ENTRY->ITEM in FS and cache it under KEY. Read the data
+ * from REV_FILE. If MAX_OFFSET is not -1, don't read windows that start
* at or beyond that offset. Use SCRATCH_POOL for temporary allocations.
*/
static svn_error_t *
@@ -2890,20 +3022,16 @@ block_read_contents(svn_fs_t *fs,
apr_off_t max_offset,
apr_pool_t *scratch_pool)
{
- svn_fs_x__data_t *ffd = fs->fsap_data;
svn_fs_x__representation_cache_key_t header_key = { 0 };
rep_state_t rs = { 0 };
svn_filesize_t fulltext_len;
svn_fs_x__rep_header_t *rep_header;
- if (!ffd->txdelta_window_cache || !ffd->combined_window_cache)
- return SVN_NO_ERROR;
-
header_key.revision = (apr_int32_t)key->revision;
header_key.is_packed = svn_fs_x__is_packed_rev(fs, header_key.revision);
header_key.item_index = key->second;
- SVN_ERR(read_rep_header(&rep_header, fs, rev_file->stream, &header_key,
+ SVN_ERR(read_rep_header(&rep_header, fs, rev_file, &header_key,
scratch_pool));
SVN_ERR(init_rep_state(&rs, rep_header, fs, rev_file, entry, scratch_pool));
SVN_ERR(cache_windows(&fulltext_len, fs, &rs, max_offset, scratch_pool));
@@ -2913,28 +3041,28 @@ block_read_contents(svn_fs_t *fs,
/* For the given REV_FILE in FS, in *STREAM return a stream covering the
* item specified by ENTRY. Also, verify the item's content by low-level
- * checksum. Allocate the result in POOL.
+ * checksum. Allocate the result in RESULT_POOL.
*/
static svn_error_t *
read_item(svn_stream_t **stream,
svn_fs_t *fs,
svn_fs_x__revision_file_t *rev_file,
svn_fs_x__p2l_entry_t* entry,
- apr_pool_t *pool)
+ apr_pool_t *result_pool)
{
apr_uint32_t digest;
svn_checksum_t *expected, *actual;
apr_uint32_t plain_digest;
+ svn_stringbuf_t *text;
/* Read item into string buffer. */
- svn_stringbuf_t *text = svn_stringbuf_create_ensure(entry->size, pool);
+ text = svn_stringbuf_create_ensure(entry->size, result_pool);
text->len = entry->size;
text->data[text->len] = 0;
- SVN_ERR(svn_io_file_read_full2(rev_file->file, text->data, text->len,
- NULL, NULL, pool));
+ SVN_ERR(svn_fs_x__rev_file_read(rev_file, text->data, text->len));
/* Return (construct, calculate) stream and checksum. */
- *stream = svn_stream_from_stringbuf(text, pool);
+ *stream = svn_stream_from_stringbuf(text, result_pool);
digest = svn__fnv1a_32x4(text->data, text->len);
/* Checksums will match most of the time. */
@@ -2945,84 +3073,114 @@ read_item(svn_stream_t **stream,
* nice error messages. */
plain_digest = htonl(entry->fnv1_checksum);
expected = svn_checksum__from_digest_fnv1a_32x4(
- (const unsigned char *)&plain_digest, pool);
+ (const unsigned char *)&plain_digest, result_pool);
plain_digest = htonl(digest);
actual = svn_checksum__from_digest_fnv1a_32x4(
- (const unsigned char *)&plain_digest, pool);
+ (const unsigned char *)&plain_digest, result_pool);
/* Construct the full error message with all the info we have. */
- return svn_checksum_mismatch_err(expected, actual, pool,
+ return svn_checksum_mismatch_err(expected, actual, result_pool,
_("Low-level checksum mismatch while reading\n"
"%s bytes of meta data at offset %s "),
- apr_psprintf(pool, "%" APR_OFF_T_FMT, entry->size),
- apr_psprintf(pool, "%" APR_OFF_T_FMT, entry->offset));
+ apr_off_t_toa(result_pool, entry->size),
+ apr_off_t_toa(result_pool, entry->offset));
}
-/* Read all txdelta / plain windows following REP_HEADER in FS as described
- * by ENTRY. Read the data from the already open FILE and the wrapping
- * STREAM object. If MAX_OFFSET is not -1, don't read windows that start
- * at or beyond that offset. Use SCRATCH_POOL for temporary allocations.
- * If caching is not enabled, this is a no-op.
+/* If not already cached or if MUST_READ is set, read the changed paths
+ * list addressed by ENTRY in FS and retúrn it in *CHANGES. Cache the
+ * result if caching is enabled. Read the data from REV_FILE. Trim the
+ * data in *CHANGES to the range given by CONTEXT. Allocate *CHANGES in
+ * RESUSLT_POOL and allocate temporaries in SCRATCH_POOL.
*/
static svn_error_t *
block_read_changes(apr_array_header_t **changes,
svn_fs_t *fs,
svn_fs_x__revision_file_t *rev_file,
svn_fs_x__p2l_entry_t* entry,
+ svn_fs_x__changes_context_t *context,
svn_boolean_t must_read,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_fs_x__data_t *ffd = fs->fsap_data;
svn_stream_t *stream;
- svn_revnum_t revision = svn_fs_x__get_revnum(entry->items[0].change_set);
- if (!must_read && !ffd->changes_cache)
- return SVN_NO_ERROR;
+ svn_fs_x__pair_cache_key_t key;
+ svn_fs_x__changes_list_t changes_list;
+
+ /* If we don't have to return any data, just read and cache the first
+ block. This means we won't cache the remaining blocks from longer
+ lists right away but only if they are actually needed. */
+ apr_size_t next = must_read ? context->next : 0;
+ apr_size_t next_offset = must_read ? context->next_offset : 0;
/* we don't support containers, yet */
SVN_ERR_ASSERT(entry->item_count == 1);
+ /* The item to read / write. */
+ key.revision = svn_fs_x__get_revnum(entry->items[0].change_set);
+ key.second = next;
+
/* already in cache? */
- if (!must_read && ffd->changes_cache)
+ if (!must_read)
{
svn_boolean_t is_cached = FALSE;
- SVN_ERR(svn_cache__has_key(&is_cached, ffd->changes_cache, &revision,
+ SVN_ERR(svn_cache__has_key(&is_cached, ffd->changes_cache, &key,
scratch_pool));
if (is_cached)
return SVN_NO_ERROR;
}
- SVN_ERR(read_item(&stream, fs, rev_file, entry, scratch_pool));
+ /* Verify the whole list only once. We don't use the STREAM any further. */
+ if (!must_read || next == 0)
+ SVN_ERR(read_item(&stream, fs, rev_file, entry, scratch_pool));
+
+ /* Seek to the block to read within the changes list. */
+ SVN_ERR(svn_fs_x__rev_file_seek(rev_file, NULL,
+ entry->offset + next_offset));
+ SVN_ERR(svn_fs_x__rev_file_stream(&stream, rev_file));
/* read changes from revision file */
+ SVN_ERR(svn_fs_x__read_changes(changes, stream, SVN_FS_X__CHANGES_BLOCK_SIZE,
+ result_pool, scratch_pool));
- SVN_ERR(svn_fs_x__read_changes(changes, stream, result_pool, scratch_pool));
+ SVN_ERR(svn_fs_x__rev_file_offset(&changes_list.end_offset, rev_file));
+ changes_list.end_offset -= entry->offset;
+ changes_list.start_offset = next_offset;
+ changes_list.count = (*changes)->nelts;
+ changes_list.changes = (svn_fs_x__change_t **)(*changes)->elts;
+ changes_list.eol = (changes_list.count < SVN_FS_X__CHANGES_BLOCK_SIZE)
+ || (changes_list.end_offset + 1 >= entry->size);
/* cache for future reference */
- if (ffd->changes_cache)
- {
- /* Guesstimate for the size of the in-cache representation. */
- apr_size_t estimated_size = (apr_size_t)250 * (*changes)->nelts;
+ SVN_ERR(svn_cache__set(ffd->changes_cache, &key, &changes_list,
+ scratch_pool));
- /* Don't even serialize data that probably won't fit into the
- * cache. This often implies that either CHANGES is very
- * large, memory is scarce or both. Having a huge temporary
- * copy would not be a good thing in either case. */
- if (svn_cache__is_cachable(ffd->changes_cache, estimated_size))
- SVN_ERR(svn_cache__set(ffd->changes_cache, &revision, *changes,
- scratch_pool));
+ /* Trim the result:
+ * Remove the entries that already been reported. */
+ if (must_read)
+ {
+ context->next_offset = changes_list.end_offset;
+ context->eol = changes_list.eol;
}
return SVN_NO_ERROR;
}
+/* If not already cached or if MUST_READ is set, read the changed paths
+ * list container addressed by ENTRY in FS. Return the changes list
+ * identified by SUB_ITEM in *CHANGES, using CONTEXT to select a sub-range
+ * within that list. Read the data from REV_FILE and cache the result.
+ *
+ * Allocate *CHANGES in RESUSLT_POOL and everything else in SCRATCH_POOL.
+ */
static svn_error_t *
block_read_changes_container(apr_array_header_t **changes,
svn_fs_t *fs,
svn_fs_x__revision_file_t *rev_file,
svn_fs_x__p2l_entry_t* entry,
apr_uint32_t sub_item,
+ svn_fs_x__changes_context_t *context,
svn_boolean_t must_read,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
@@ -3037,7 +3195,7 @@ block_read_changes_container(apr_array_header_t **changes,
key.second = entry->offset;
/* already in cache? */
- if (!must_read && ffd->changes_container_cache)
+ if (!must_read)
{
svn_boolean_t is_cached = FALSE;
SVN_ERR(svn_cache__has_key(&is_cached, ffd->changes_container_cache,
@@ -3057,15 +3215,19 @@ block_read_changes_container(apr_array_header_t **changes,
if (must_read)
SVN_ERR(svn_fs_x__changes_get_list(changes, container, sub_item,
- result_pool));
-
- if (ffd->changes_container_cache)
- SVN_ERR(svn_cache__set(ffd->changes_container_cache, &key, container,
- scratch_pool));
+ context, result_pool));
+ SVN_ERR(svn_cache__set(ffd->changes_container_cache, &key, container,
+ scratch_pool));
return SVN_NO_ERROR;
}
+/* If not already cached or if MUST_READ is set, read the node revision
+ * addressed by ENTRY in FS and return it in *NODEREV_P. Cache the
+ * result under KEY if caching is enabled. Read the data from REV_FILE.
+ * Allocate *NODEREV_P in RESUSLT_POOL and allocate temporaries in
+ * SCRATCH_POOL.
+ */
static svn_error_t *
block_read_noderev(svn_fs_x__noderev_t **noderev_p,
svn_fs_t *fs,
@@ -3078,14 +3240,12 @@ block_read_noderev(svn_fs_x__noderev_t **noderev_p,
{
svn_fs_x__data_t *ffd = fs->fsap_data;
svn_stream_t *stream;
- if (!must_read && !ffd->node_revision_cache)
- return SVN_NO_ERROR;
/* we don't support containers, yet */
SVN_ERR_ASSERT(entry->item_count == 1);
/* already in cache? */
- if (!must_read && ffd->node_revision_cache)
+ if (!must_read)
{
svn_boolean_t is_cached = FALSE;
SVN_ERR(svn_cache__has_key(&is_cached, ffd->node_revision_cache, key,
@@ -3100,13 +3260,18 @@ block_read_noderev(svn_fs_x__noderev_t **noderev_p,
SVN_ERR(svn_fs_x__read_noderev(noderev_p, stream, result_pool,
scratch_pool));
- if (ffd->node_revision_cache)
- SVN_ERR(svn_cache__set(ffd->node_revision_cache, key, *noderev_p,
- scratch_pool));
+ SVN_ERR(svn_cache__set(ffd->node_revision_cache, key, *noderev_p,
+ scratch_pool));
return SVN_NO_ERROR;
}
+/* If not already cached or if MUST_READ is set, read the node revision
+ * container addressed by ENTRY in FS. Return the item identified by
+ * SUB_ITEM in *NODEREV_P. Read the data from REV_FILE and cache it.
+ * Allocate *NODEREV_P in RESUSLT_POOL and allocate temporaries in
+ * SCRATCH_POOL.
+ */
static svn_error_t *
block_read_noderevs_container(svn_fs_x__noderev_t **noderev_p,
svn_fs_t *fs,
@@ -3127,7 +3292,7 @@ block_read_noderevs_container(svn_fs_x__noderev_t **noderev_p,
key.second = entry->offset;
/* already in cache? */
- if (!must_read && ffd->noderevs_container_cache)
+ if (!must_read)
{
svn_boolean_t is_cached = FALSE;
SVN_ERR(svn_cache__has_key(&is_cached, ffd->noderevs_container_cache,
@@ -3147,13 +3312,18 @@ block_read_noderevs_container(svn_fs_x__noderev_t **noderev_p,
SVN_ERR(svn_fs_x__noderevs_get(noderev_p, container, sub_item,
result_pool));
- if (ffd->noderevs_container_cache)
- SVN_ERR(svn_cache__set(ffd->noderevs_container_cache, &key, container,
- scratch_pool));
+ SVN_ERR(svn_cache__set(ffd->noderevs_container_cache, &key, container,
+ scratch_pool));
return SVN_NO_ERROR;
}
+/* If not already cached or if MUST_READ is set, read the representation
+ * container addressed by ENTRY in FS. Return an extractor object for the
+ * item identified by SUB_ITEM in *EXTRACTOR. Read the data from REV_FILE
+ * and cache it. Allocate *EXTRACTOR in RESUSLT_POOL and all temporaries
+ * in SCRATCH_POOL.
+ */
static svn_error_t *
block_read_reps_container(svn_fs_x__rep_extractor_t **extractor,
svn_fs_t *fs,
@@ -3174,7 +3344,7 @@ block_read_reps_container(svn_fs_x__rep_extractor_t **extractor,
key.second = entry->offset;
/* already in cache? */
- if (!must_read && ffd->reps_container_cache)
+ if (!must_read)
{
svn_boolean_t is_cached = FALSE;
SVN_ERR(svn_cache__has_key(&is_cached, ffd->reps_container_cache,
@@ -3195,18 +3365,30 @@ block_read_reps_container(svn_fs_x__rep_extractor_t **extractor,
SVN_ERR(svn_fs_x__reps_get(extractor, fs, container, sub_item,
result_pool));
- if (ffd->noderevs_container_cache)
- SVN_ERR(svn_cache__set(ffd->reps_container_cache, &key, container,
- scratch_pool));
+ SVN_ERR(svn_cache__set(ffd->reps_container_cache, &key, container,
+ scratch_pool));
return SVN_NO_ERROR;
}
+/* Read the whole (e.g. 64kB) block containing the item identified by ID in
+ * FS and put all data into cache. If necessary and depending on heuristics,
+ * neighboring blocks may also get read. The data is being read from
+ * already open REVISION_FILE, which must be the correct rev / pack file
+ * w.r.t. ID->CHANGE_SET.
+ *
+ * For noderevs and changed path lists, the item fetched can be allocated
+ * RESULT_POOL and returned in *RESULT. Otherwise, RESULT must be NULL.
+ * The BATON is passed along to the extractor sub-functions and will be
+ * used only when constructing the *RESULT. SCRATCH_POOL will be used for
+ * all temporary allocations.
+ */
static svn_error_t *
block_read(void **result,
svn_fs_t *fs,
const svn_fs_x__id_t *id,
svn_fs_x__revision_file_t *revision_file,
+ void *baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
@@ -3232,8 +3414,7 @@ block_read(void **result,
do
{
/* fetch list of items in the block surrounding OFFSET */
- SVN_ERR(aligned_seek(fs, revision_file->file, &block_start, offset,
- iterpool));
+ SVN_ERR(svn_fs_x__rev_file_seek(revision_file, &block_start, offset));
SVN_ERR(svn_fs_x__p2l_index_lookup(&entries, fs, revision_file,
revision, block_start,
ffd->block_size, scratch_pool,
@@ -3272,8 +3453,8 @@ block_read(void **result,
key.revision = svn_fs_x__get_revnum(entry->items[0].change_set);
key.second = entry->items[0].number;
- SVN_ERR(svn_io_file_seek(revision_file->file, SEEK_SET,
- &entry->offset, iterpool));
+ SVN_ERR(svn_fs_x__rev_file_seek(revision_file, NULL,
+ entry->offset));
switch (entry->type)
{
case SVN_FS_X__ITEM_TYPE_FILE_REP:
@@ -3289,17 +3470,16 @@ block_read(void **result,
break;
case SVN_FS_X__ITEM_TYPE_NODEREV:
- if (ffd->node_revision_cache || is_result)
- SVN_ERR(block_read_noderev((svn_fs_x__noderev_t **)&item,
- fs, revision_file,
- entry, &key, is_result,
- pool, iterpool));
+ SVN_ERR(block_read_noderev((svn_fs_x__noderev_t **)&item,
+ fs, revision_file,
+ entry, &key, is_result,
+ pool, iterpool));
break;
case SVN_FS_X__ITEM_TYPE_CHANGES:
SVN_ERR(block_read_changes((apr_array_header_t **)&item,
fs, revision_file,
- entry, is_result,
+ entry, baton, is_result,
pool, iterpool));
break;
@@ -3308,7 +3488,8 @@ block_read(void **result,
((apr_array_header_t **)&item,
fs, revision_file,
entry, wanted_sub_item,
- is_result, pool, iterpool));
+ baton, is_result,
+ pool, iterpool));
break;
case SVN_FS_X__ITEM_TYPE_NODEREVS_CONT:
@@ -3337,7 +3518,7 @@ block_read(void **result,
/* if we crossed a block boundary, read the remainder of
* the last block as well */
offset = entry->offset + entry->size;
- if (offset > block_start + ffd->block_size)
+ if (offset - block_start > ffd->block_size)
++run_count;
svn_pool_clear(iterpool);