You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2012/03/06 18:50:31 UTC
svn commit: r1297604 [5/12] - in
/subversion/branches/reintegrate-keep-alive: ./ build/ build/ac-macros/
build/generator/ build/generator/templates/ build/win32/ notes/
notes/api-errata/1.7/ subversion/bindings/javahl/native/
subversion/bindings/javahl...
Modified: subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/fs_fs.c
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/fs_fs.c?rev=1297604&r1=1297603&r2=1297604&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/fs_fs.c (original)
+++ subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/fs_fs.c Tue Mar 6 17:50:23 2012
@@ -81,7 +81,7 @@
/* Begin deltification after a node history exceeded this this limit.
Useful values are 4 to 64 with 16 being a good compromise between
computational overhead and pository size savings.
- Should be a power of 2.
+ Should be a power of 2.
Values < 2 will result in standard skip-delta behavior. */
#define SVN_FS_FS_MAX_LINEAR_DELTIFICATION 16
@@ -225,6 +225,12 @@ path_lock(svn_fs_t *fs, apr_pool_t *pool
}
static const char *
+path_revprop_generation(svn_fs_t *fs, apr_pool_t *pool)
+{
+ return svn_dirent_join(fs->path, PATH_REVPROP_GEN, pool);
+}
+
+static const char *
path_rev_packed(svn_fs_t *fs, svn_revnum_t rev, const char *kind,
apr_pool_t *pool)
{
@@ -800,7 +806,7 @@ get_writable_proto_rev_body(svn_fs_t *fs
err = svn_error_compose_create(
err,
unlock_proto_rev_list_locked(fs, txn_id, *lockcookie, pool));
-
+
*lockcookie = NULL;
}
@@ -870,25 +876,39 @@ get_file_offset(apr_off_t *offset_p, apr
}
-/* Check that BUF, a nul-terminated buffer of text from format file PATH,
+/* Check that BUF, a nul-terminated buffer of text from file PATH,
contains only digits at OFFSET and beyond, raising an error if not.
+ TITLE contains a user-visible description of the file, usually the
+ short file name.
Uses POOL for temporary allocation. */
static svn_error_t *
-check_format_file_buffer_numeric(const char *buf, apr_off_t offset,
- const char *path, apr_pool_t *pool)
+check_file_buffer_numeric(const char *buf, apr_off_t offset,
+ const char *path, const char *title,
+ apr_pool_t *pool)
{
const char *p;
for (p = buf + offset; *p; p++)
if (!svn_ctype_isdigit(*p))
return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL,
- _("Format file '%s' contains unexpected non-digit '%c' within '%s'"),
- svn_dirent_local_style(path, pool), *p, buf);
+ _("%s file '%s' contains unexpected non-digit '%c' within '%s'"),
+ title, svn_dirent_local_style(path, pool), *p, buf);
return SVN_NO_ERROR;
}
+/* Check that BUF, a nul-terminated buffer of text from format file PATH,
+ contains only digits at OFFSET and beyond, raising an error if not.
+
+ Uses POOL for temporary allocation. */
+static svn_error_t *
+check_format_file_buffer_numeric(const char *buf, apr_off_t offset,
+ const char *path, apr_pool_t *pool)
+{
+ return check_file_buffer_numeric(buf, offset, path, "Format", pool);
+}
+
/* Read the format number and maximum number of files per directory
from PATH and return them in *PFORMAT and *MAX_FILES_PER_DIR
respectively.
@@ -1548,6 +1568,16 @@ ensure_revision_exists(svn_fs_t *fs,
_("No such revision %ld"), rev);
}
+svn_error_t *
+svn_fs_fs__revision_exists(svn_revnum_t rev,
+ svn_fs_t *fs,
+ apr_pool_t *pool)
+{
+ /* Different order of parameters. */
+ SVN_ERR(ensure_revision_exists(fs, rev, pool));
+ return SVN_NO_ERROR;
+}
+
/* Open the correct revision file for REV. If the filesystem FS has
been packed, *FILE will be set to the packed file; otherwise, set *FILE
to the revision file for REV. Return SVN_ERR_FS_NO_SUCH_REVISION if the
@@ -2677,6 +2707,105 @@ svn_fs_fs__rev_get_root(svn_fs_id_t **ro
return SVN_NO_ERROR;
}
+/* Reads the revprop_gen file and writes the content into the
+ REVPROP_GENERATION member of FS. Use pool for allocations. */
+static svn_error_t *
+read_revprop_generation(svn_fs_t *fs,
+ apr_pool_t *pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+
+ const char *path = path_revprop_generation(fs, pool);
+ svn_error_t *err;
+ char buf[80];
+ int i;
+
+ /* Read the raw data from the file, if it exists. If it does
+ not, set the generation to "1" and return.
+ We don't want to have this function fail. So, patiently
+ retry a couple of times the case the OS denied us access. */
+ apr_pool_t *iterpool = svn_pool_create(pool);
+ for (i = 0; i < RECOVERABLE_RETRY_COUNT; ++i)
+ {
+ apr_file_t *file;
+ apr_size_t len = sizeof(buf);
+
+ svn_pool_clear(iterpool);
+
+ err = svn_io_file_open(&file, path, APR_READ | APR_BUFFERED,
+ APR_OS_DEFAULT, iterpool);
+ if (err && APR_STATUS_IS_ENOENT(err->apr_err))
+ {
+ /* No-one changed a revprop -> we are still at gen 1. */
+ ffd->revprop_generation = 1;
+ svn_error_clear(err);
+ return SVN_NO_ERROR;
+ }
+ svn_error_clear(err);
+
+ RETRY_RECOVERABLE(err, file,
+ svn_io_read_length_line(file,
+ buf,
+ &len,
+ iterpool));
+ IGNORE_RECOVERABLE(err, svn_io_file_close(file,
+ iterpool));
+
+ break;
+ }
+ SVN_ERR(err);
+
+ svn_pool_destroy(iterpool);
+
+ /* Check that the first line contains only digits. */
+ SVN_ERR(check_file_buffer_numeric(buf, 0, path,
+ "Revprop generations", pool));
+ SVN_ERR(svn_cstring_atoi64(&ffd->revprop_generation, buf));
+
+ /* Graceful behavior in case someone put a "0" in the file. */
+ if (ffd->revprop_generation <= 0)
+ ffd->revprop_generation = 1;
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+check_revprop_generation(svn_fs_t *fs,
+ apr_pool_t *pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+
+ return ffd->revprop_generation == 0
+ ? read_revprop_generation(fs, pool)
+ : SVN_NO_ERROR;
+}
+
+static svn_error_t *
+increment_revprop_generation(svn_fs_t *fs,
+ apr_pool_t *pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+
+ const char *path = path_revprop_generation(fs, pool);
+ const char *tmp_filename;
+ svn_string_t *generation;
+
+ SVN_ERR(read_revprop_generation(fs, pool));
+
+ /* Increment the key and add a trailing \n to the string so the
+ txn-current file has a newline in it. */
+ ++ffd->revprop_generation;
+ generation = svn_string_createf(pool, "%" APR_INT64_T_FMT "\n",
+ ffd->revprop_generation);
+
+ SVN_ERR(svn_io_write_unique(&tmp_filename,
+ svn_dirent_dirname(path, pool),
+ generation->data, generation->len,
+ svn_io_file_del_none, pool));
+ return move_into_place(tmp_filename, path,
+ svn_fs_fs__path_current(fs, pool), pool);
+}
+
/* Set the revision property list of revision REV in filesystem FS to
PROPLIST. Use POOL for temporary allocations. */
static svn_error_t *
@@ -2693,6 +2822,10 @@ set_revision_proplist(svn_fs_t *fs,
const char *tmp_path;
const char *perms_reference;
svn_stream_t *stream;
+ svn_node_kind_t kind;
+
+ /* test whether revprops already exist for this revision */
+ SVN_ERR(svn_io_check_path(final_path, &kind, pool));
/* ### do we have a directory sitting around already? we really shouldn't
### have to get the dirname here. */
@@ -2709,6 +2842,12 @@ set_revision_proplist(svn_fs_t *fs,
SVN_ERR(svn_fs_fs__path_rev_absolute(&perms_reference, fs, rev, pool));
SVN_ERR(move_into_place(tmp_path, final_path, perms_reference, pool));
+ /* Invalidate all cached revprops for this FS and for all other
+ users that haven't read any revprops, YET. Since writing revprops
+ implies a write lock, there can be no races. */
+ if (kind != svn_node_none)
+ SVN_ERR(increment_revprop_generation(fs, pool));
+
return SVN_NO_ERROR;
}
@@ -2722,8 +2861,22 @@ revision_proplist(apr_hash_t **proplist_
apr_pool_t *pool)
{
apr_hash_t *proplist;
+ fs_fs_data_t *ffd = fs->fsap_data;
+ const char *key;
SVN_ERR(ensure_revision_exists(fs, rev, pool));
+ SVN_ERR(check_revprop_generation(fs, pool));
+
+ /* Try cache lookup first. */
+ key = svn_fs_fs__combine_two_numbers(rev, ffd->revprop_generation, pool);
+ if (ffd->revprop_cache)
+ {
+ svn_boolean_t is_cached;
+ SVN_ERR(svn_cache__get((void **) proplist_p, &is_cached,
+ ffd->revprop_cache, key, pool));
+ if (is_cached)
+ return SVN_NO_ERROR;
+ }
/* if (1); null condition for easier merging to revprop-packing */
{
@@ -2779,6 +2932,10 @@ revision_proplist(apr_hash_t **proplist_
svn_pool_destroy(iterpool);
}
+ /* Cache the result, if caching has been activated. */
+ if (ffd->revprop_cache)
+ SVN_ERR(svn_cache__set(ffd->revprop_cache, key, proplist, pool));
+
*proplist_p = proplist;
return SVN_NO_ERROR;
@@ -2802,6 +2959,8 @@ struct rep_state
apr_file_t *file;
/* The txdelta window cache to use or NULL. */
svn_cache__t *window_cache;
+ /* Caches un-deltified windows. May be NULL. */
+ svn_cache__t *combined_cache;
apr_off_t start; /* The starting offset for the raw
svndiff/plaintext data minus header. */
apr_off_t off; /* The current offset into the file. */
@@ -2825,6 +2984,7 @@ create_rep_state_body(struct rep_state *
SVN_ERR(open_and_seek_representation(&rs->file, fs, rep, pool));
rs->window_cache = ffd->txdelta_window_cache;
+ rs->combined_cache = ffd->combined_window_cache;
SVN_ERR(read_rep_line(&ra, rs->file, pool));
SVN_ERR(get_file_offset(&rs->start, rs->file, pool));
@@ -2876,7 +3036,7 @@ create_rep_state(struct rep_state **rep_
### going to jump straight to this comment anyway! */
return svn_error_createf(SVN_ERR_FS_CORRUPT, err,
"Corrupt representation '%s'",
- rep
+ rep
? representation_string(rep, ffd->format, TRUE,
TRUE, pool)
: "(null)");
@@ -2885,57 +3045,14 @@ create_rep_state(struct rep_state **rep_
return svn_error_trace(err);
}
-/* Build an array of rep_state structures in *LIST giving the delta
- reps from first_rep to a plain-text or self-compressed rep. Set
- *SRC_STATE to the plain-text rep we find at the end of the chain,
- or to NULL if the final delta representation is self-compressed.
- The representation to start from is designated by filesystem FS, id
- ID, and representation REP. */
-static svn_error_t *
-build_rep_list(apr_array_header_t **list,
- struct rep_state **src_state,
- svn_fs_t *fs,
- representation_t *first_rep,
- apr_pool_t *pool)
-{
- representation_t rep;
- struct rep_state *rs;
- struct rep_args *rep_args;
-
- *list = apr_array_make(pool, 1, sizeof(struct rep_state *));
- rep = *first_rep;
-
- while (1)
- {
- SVN_ERR(create_rep_state(&rs, &rep_args, &rep, fs, pool));
- if (rep_args->is_delta == FALSE)
- {
- /* This is a plaintext, so just return the current rep_state. */
- *src_state = rs;
- return SVN_NO_ERROR;
- }
-
- /* Push this rep onto the list. If it's self-compressed, we're done. */
- APR_ARRAY_PUSH(*list, struct rep_state *) = rs;
- if (rep_args->is_delta_vs_empty)
- {
- *src_state = NULL;
- return SVN_NO_ERROR;
- }
-
- rep.revision = rep_args->base_revision;
- rep.offset = rep_args->base_offset;
- rep.size = rep_args->base_length;
- rep.txn_id = NULL;
- }
-}
-
-
struct rep_read_baton
{
/* The FS from which we're reading. */
svn_fs_t *fs;
+ /* If not NULL, this is the base for the first delta window in rs_list */
+ svn_stringbuf_t *base_window;
+
/* The state of all prior delta representations. */
apr_array_header_t *rs_list;
@@ -2980,51 +3097,9 @@ struct rep_read_baton
apr_pool_t *filehandle_pool;
};
-/* Create a rep_read_baton structure for node revision NODEREV in
- filesystem FS and store it in *RB_P. If FULLTEXT_CACHE_KEY is not
- NULL, it is the rep's key in the fulltext cache, and a stringbuf
- must be allocated to store the text. Perform all allocations in
- POOL. If rep is mutable, it must be for file contents. */
-static svn_error_t *
-rep_read_get_baton(struct rep_read_baton **rb_p,
- svn_fs_t *fs,
- representation_t *rep,
- const char *fulltext_cache_key,
- apr_pool_t *pool)
-{
- struct rep_read_baton *b;
-
- b = apr_pcalloc(pool, sizeof(*b));
- b->fs = fs;
- b->chunk_index = 0;
- b->buf = NULL;
- b->md5_checksum_ctx = svn_checksum_ctx_create(svn_checksum_md5, pool);
- b->checksum_finalized = FALSE;
- b->md5_checksum = svn_checksum_dup(rep->md5_checksum, pool);
- b->len = rep->expanded_size ? rep->expanded_size : rep->size;
- b->off = 0;
- b->fulltext_cache_key = fulltext_cache_key;
- b->pool = svn_pool_create(pool);
- b->filehandle_pool = svn_pool_create(pool);
-
- if (fulltext_cache_key)
- b->current_fulltext = svn_stringbuf_create_ensure
- ((apr_size_t)b->len,
- b->filehandle_pool);
- else
- b->current_fulltext = NULL;
-
- SVN_ERR(build_rep_list(&b->rs_list, &b->src_state, fs, rep,
- b->filehandle_pool));
-
- /* Save our output baton. */
- *rb_p = b;
-
- return SVN_NO_ERROR;
-}
-
/* Combine the name of the rev file in RS with the given OFFSET to form
- * a cache lookup key. Allocations will be made from POOL. */
+ * a cache lookup key. Allocations will be made from POOL. May return
+ * NULL if the key cannot be constructed. */
static const char*
get_window_key(struct rep_state *rs, apr_off_t offset, apr_pool_t *pool)
{
@@ -3038,7 +3113,7 @@ get_window_key(struct rep_state *rs, apr
* comparison _will_ find them.
*/
if (apr_file_name_get(&name, rs->file))
- return "";
+ return NULL;
/* Handle packed files as well by scanning backwards until we find the
* revision or pack number. */
@@ -3105,35 +3180,194 @@ get_cached_window(svn_txdelta_window_t *
rs->chunk_index++;
rs->off = cached_window->end_offset;
- /* manipulate the rev file as if we just read from it */
- SVN_ERR(svn_io_file_seek(rs->file, APR_SET, &rs->off, pool));
- }
- }
+ /* manipulate the rev file as if we just read from it */
+ SVN_ERR(svn_io_file_seek(rs->file, APR_SET, &rs->off, pool));
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Store the WINDOW read at OFFSET for the rep state RS in the current
+ * FSFS session's cache. This will be a no-op if no cache has been given.
+ * Temporary allocations will be made from SCRATCH_POOL. */
+static svn_error_t *
+set_cached_window(svn_txdelta_window_t *window,
+ struct rep_state *rs,
+ apr_off_t offset,
+ apr_pool_t *scratch_pool)
+{
+ if (rs->window_cache)
+ {
+ /* store the window and the first offset _past_ it */
+ svn_fs_fs__txdelta_cached_window_t cached_window = { window, rs->off };
+
+ /* but key it with the start offset because that is the known state
+ * when we will look it up */
+ return svn_cache__set(rs->window_cache,
+ get_window_key(rs, offset, scratch_pool),
+ &cached_window,
+ scratch_pool);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Read the WINDOW_P for the rep state RS from the current FSFS session's
+ * cache. This will be a no-op and IS_CACHED will be set to FALSE if no
+ * cache has been given. If a cache is available IS_CACHED will inform
+ * the caller about the success of the lookup. Allocations (of the window
+ * in particualar) will be made from POOL.
+ */
+static svn_error_t *
+get_cached_combined_window(svn_stringbuf_t **window_p,
+ struct rep_state *rs,
+ 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 */
+ return svn_cache__get((void **)window_p,
+ is_cached,
+ rs->combined_cache,
+ get_window_key(rs, rs->start, pool),
+ pool);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Store the WINDOW read at OFFSET for the rep state RS in the current
+ * FSFS session's cache. This will be a no-op if no cache has been given.
+ * Temporary allocations will be made from SCRATCH_POOL. */
+static svn_error_t *
+set_cached_combined_window(svn_stringbuf_t *window,
+ struct rep_state *rs,
+ apr_off_t offset,
+ 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 */
+ return svn_cache__set(rs->combined_cache,
+ get_window_key(rs, offset, scratch_pool),
+ window,
+ scratch_pool);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Build an array of rep_state structures in *LIST giving the delta
+ reps from first_rep to a plain-text or self-compressed rep. Set
+ *SRC_STATE to the plain-text rep we find at the end of the chain,
+ or to NULL if the final delta representation is self-compressed.
+ The representation to start from is designated by filesystem FS, id
+ ID, and representation REP.
+ Also, set *WINDOW_P to the base window content for *LIST, if it
+ could be found in cache. Otherwise, *LIST will contain the base
+ representation for the whole delta chain. */
+static svn_error_t *
+build_rep_list(apr_array_header_t **list,
+ svn_stringbuf_t **window_p,
+ struct rep_state **src_state,
+ svn_fs_t *fs,
+ representation_t *first_rep,
+ apr_pool_t *pool)
+{
+ representation_t rep;
+ struct rep_state *rs;
+ struct rep_args *rep_args;
+ svn_boolean_t is_cached = FALSE;
+
+ *list = apr_array_make(pool, 1, sizeof(struct rep_state *));
+ rep = *first_rep;
+
+ while (1)
+ {
+ SVN_ERR(create_rep_state(&rs, &rep_args, &rep, fs, pool));
+ SVN_ERR(get_cached_combined_window(window_p, rs, &is_cached, pool));
+ if (is_cached)
+ {
+ /* We already have a reconstructed window in our cache.
+ Write a pseudo rep_state with the full length. */
+ rs->off = rs->start;
+ rs->end = rs->start + (*window_p)->len;
+ *src_state = rs;
+ return SVN_NO_ERROR;
+ }
+
+ if (rep_args->is_delta == FALSE)
+ {
+ /* This is a plaintext, so just return the current rep_state. */
+ *src_state = rs;
+ return SVN_NO_ERROR;
+ }
+
+ /* Push this rep onto the list. If it's self-compressed, we're done. */
+ APR_ARRAY_PUSH(*list, struct rep_state *) = rs;
+ if (rep_args->is_delta_vs_empty)
+ {
+ *src_state = NULL;
+ return SVN_NO_ERROR;
+ }
+
+ rep.revision = rep_args->base_revision;
+ rep.offset = rep_args->base_offset;
+ rep.size = rep_args->base_length;
+ rep.txn_id = NULL;
+ }
+}
+
+
+/* Create a rep_read_baton structure for node revision NODEREV in
+ filesystem FS and store it in *RB_P. If FULLTEXT_CACHE_KEY is not
+ NULL, it is the rep's key in the fulltext cache, and a stringbuf
+ must be allocated to store the text. Perform all allocations in
+ POOL. If rep is mutable, it must be for file contents. */
+static svn_error_t *
+rep_read_get_baton(struct rep_read_baton **rb_p,
+ svn_fs_t *fs,
+ representation_t *rep,
+ const char *fulltext_cache_key,
+ apr_pool_t *pool)
+{
+ struct rep_read_baton *b;
+
+ b = apr_pcalloc(pool, sizeof(*b));
+ b->fs = fs;
+ b->base_window = NULL;
+ b->chunk_index = 0;
+ b->buf = NULL;
+ b->md5_checksum_ctx = svn_checksum_ctx_create(svn_checksum_md5, pool);
+ b->checksum_finalized = FALSE;
+ b->md5_checksum = svn_checksum_dup(rep->md5_checksum, pool);
+ b->len = rep->expanded_size ? rep->expanded_size : rep->size;
+ b->off = 0;
+ b->fulltext_cache_key = fulltext_cache_key;
+ b->pool = svn_pool_create(pool);
+ b->filehandle_pool = svn_pool_create(pool);
- return SVN_NO_ERROR;
-}
+ if (fulltext_cache_key)
+ b->current_fulltext = svn_stringbuf_create_ensure
+ ((apr_size_t)b->len,
+ b->filehandle_pool);
+ else
+ b->current_fulltext = NULL;
-/* Store the WINDOW read at OFFSET for the rep state RS in the current
- * FSFS session's cache. This will be a no-op if no cache has been given.
- * Temporary allocations will be made from SCRATCH_POOL. */
-static svn_error_t *
-set_cached_window(svn_txdelta_window_t *window,
- struct rep_state *rs,
- apr_off_t offset,
- apr_pool_t *scratch_pool)
-{
- if (rs->window_cache)
- {
- /* store the window and the first offset _past_ it */
- svn_fs_fs__txdelta_cached_window_t cached_window = { window, rs->off };
+ SVN_ERR(build_rep_list(&b->rs_list, &b->base_window,
+ &b->src_state, fs, rep,
+ b->filehandle_pool));
- /* but key it with the start offset because that is the known state
- * when we will look it up */
- return svn_cache__set(rs->window_cache,
- get_window_key(rs, offset, scratch_pool),
- &cached_window,
- scratch_pool);
- }
+ /* Save our output baton. */
+ *rb_p = b;
return SVN_NO_ERROR;
}
@@ -3185,45 +3419,73 @@ read_window(svn_txdelta_window_t **nwin,
return set_cached_window(*nwin, rs, old_offset, pool);
}
-/* Get one delta window that is a result of combining all but the last deltas
- from the current desired representation identified in *RB, to its
- final base representation. Store the window in *RESULT. */
+/* Get the undeltified window that is a result of combining all deltas
+ from the current desired representation identified in *RB with its
+ base representation. Store the window in *RESULT. */
static svn_error_t *
-get_combined_window(svn_txdelta_window_t **result,
+get_combined_window(svn_stringbuf_t **result,
struct rep_read_baton *rb)
{
- apr_pool_t *pool, *new_pool;
+ apr_pool_t *pool, *new_pool, *window_pool;
int i;
- svn_txdelta_window_t *window, *nwin;
+ svn_txdelta_window_t *window;
+ apr_array_header_t *windows;
+ svn_stringbuf_t *source, *buf = rb->base_window;
struct rep_state *rs;
- SVN_ERR_ASSERT(rb->rs_list->nelts >= 2);
-
- pool = svn_pool_create(rb->pool);
-
- /* Read the next window from the original rep. */
- rs = APR_ARRAY_IDX(rb->rs_list, 0, struct rep_state *);
- SVN_ERR(read_window(&window, rb->chunk_index, rs, pool));
-
- /* Combine in the windows from the other delta reps, if needed. */
- for (i = 1; i < rb->rs_list->nelts - 1; i++)
+ /* Read all windows that we need to combine. This is fine because
+ the size of each window is relatively small (100kB) and skip-
+ delta limits the number of deltas in a chain to well under 100.
+ Stop early if one of them does not depend on its predecessors. */
+ window_pool = svn_pool_create(rb->pool);
+ windows = apr_array_make(window_pool, 0, sizeof(svn_txdelta_window_t *));
+ for (i = 0; i < rb->rs_list->nelts; ++i)
{
+ rs = APR_ARRAY_IDX(rb->rs_list, i, struct rep_state *);
+ SVN_ERR(read_window(&window, rb->chunk_index, rs, window_pool));
+
+ APR_ARRAY_PUSH(windows, svn_txdelta_window_t *) = window;
if (window->src_ops == 0)
- break;
+ {
+ ++i;
+ break;
+ }
+ }
+ /* Combine in the windows from the other delta reps. */
+ pool = svn_pool_create(rb->pool);
+ for (--i; i >= 0; --i)
+ {
rs = APR_ARRAY_IDX(rb->rs_list, i, struct rep_state *);
+ window = APR_ARRAY_IDX(windows, i, svn_txdelta_window_t *);
- SVN_ERR(read_window(&nwin, rb->chunk_index, rs, pool));
-
- /* Combine this window with the current one. Cycle pools so that we
- only need to hold three windows at a time. */
+ /* Combine this window with the current one. */
+ source = buf;
new_pool = svn_pool_create(rb->pool);
- window = svn_txdelta_compose_windows(nwin, window, new_pool);
+ buf = svn_stringbuf_create_ensure(window->tview_len, new_pool);
+ buf->len = window->tview_len;
+
+ svn_txdelta_apply_instructions(window, source ? source->data : NULL,
+ buf->data, &buf->len);
+ if (buf->len != window->tview_len)
+ return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
+ _("svndiff window length is "
+ "corrupt"));
+
+ /* Cache windows only if the whole rep content could be read as a
+ single chunk. Only then will no other chunk need a deeper RS
+ list than the cached chunk. */
+ if ((rb->chunk_index == 0) && (rs->off == rs->end))
+ SVN_ERR(set_cached_combined_window(buf, rs, rs->start, new_pool));
+
+ /* Cycle pools so that we only need to hold three windows at a time. */
svn_pool_destroy(pool);
pool = new_pool;
}
- *result = window;
+ svn_pool_destroy(window_pool);
+
+ *result = buf;
return SVN_NO_ERROR;
}
@@ -3256,10 +3518,9 @@ get_contents(struct rep_read_baton *rb,
char *buf,
apr_size_t *len)
{
- apr_size_t copy_len, remaining = *len, tlen;
- char *sbuf, *tbuf, *cur = buf;
+ apr_size_t copy_len, remaining = *len, offset;
+ char *cur = buf;
struct rep_state *rs;
- svn_txdelta_window_t *cwindow, *lwindow;
/* Special case for when there are no delta reps, only a plain
text. */
@@ -3267,10 +3528,28 @@ get_contents(struct rep_read_baton *rb,
{
copy_len = remaining;
rs = rb->src_state;
- if (((apr_off_t) copy_len) > rs->end - rs->off)
- copy_len = (apr_size_t) (rs->end - rs->off);
- SVN_ERR(svn_io_file_read_full2(rs->file, cur, copy_len, NULL,
- NULL, rb->pool));
+
+ if (rb->base_window != NULL)
+ {
+ /* We got the desired rep directly from the cache.
+ This is where we need the pseudo rep_state created
+ by build_rep_list(). */
+ offset = rs->off - rs->start;
+ if (copy_len + offset > rb->base_window->len)
+ copy_len = offset < rb->base_window->len
+ ? rb->base_window->len - offset
+ : 0;
+
+ memcpy (cur, rb->base_window->data + offset, copy_len);
+ }
+ else
+ {
+ if (((apr_off_t) copy_len) > rs->end - rs->off)
+ copy_len = (apr_size_t) (rs->end - rs->off);
+ SVN_ERR(svn_io_file_read_full2(rs->file, cur, copy_len, NULL,
+ NULL, rb->pool));
+ }
+
rs->off += copy_len;
*len = copy_len;
return SVN_NO_ERROR;
@@ -3302,90 +3581,18 @@ get_contents(struct rep_read_baton *rb,
}
else
{
+ svn_stringbuf_t *sbuf = NULL;
rs = APR_ARRAY_IDX(rb->rs_list, 0, struct rep_state *);
if (rs->off == rs->end)
break;
/* Get more buffered data by evaluating a chunk. */
- if (rb->rs_list->nelts > 1)
- SVN_ERR(get_combined_window(&cwindow, rb));
- else
- cwindow = NULL;
- if (!cwindow || cwindow->src_ops > 0)
- {
- rs = APR_ARRAY_IDX(rb->rs_list, rb->rs_list->nelts - 1,
- struct rep_state *);
- /* Read window from last representation in list. */
- /* We apply this window directly instead of combining it
- with the others. We do this because vdelta used to
- be used for deltas against the empty stream, which
- will trigger quadratic behaviour in the delta
- combiner. It's still likely that we'll find such
- deltas in an old repository; it may be worth
- considering whether or not this special case is still
- needed in the future, though. */
- SVN_ERR(read_window(&lwindow, rb->chunk_index, rs, rb->pool));
-
- if (lwindow->src_ops > 0)
- {
- if (! rb->src_state)
- return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
- _("svndiff data requested "
- "non-existent source"));
- rs = rb->src_state;
- sbuf = apr_palloc(rb->pool, lwindow->sview_len);
- if (! ((rs->start + lwindow->sview_offset) < rs->end))
- return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
- _("svndiff requested position "
- "beyond end of stream"));
- if ((rs->start + lwindow->sview_offset) != rs->off)
- {
- rs->off = rs->start + lwindow->sview_offset;
- SVN_ERR(svn_io_file_seek(rs->file, APR_SET, &rs->off,
- rb->pool));
- }
- SVN_ERR(svn_io_file_read_full2(rs->file, sbuf,
- lwindow->sview_len,
- NULL, NULL, rb->pool));
- rs->off += lwindow->sview_len;
- }
- else
- sbuf = NULL;
-
- /* Apply lwindow to source. */
- tlen = lwindow->tview_len;
- tbuf = apr_palloc(rb->pool, tlen);
- svn_txdelta_apply_instructions(lwindow, sbuf, tbuf,
- &tlen);
- if (tlen != lwindow->tview_len)
- return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
- _("svndiff window length is "
- "corrupt"));
- sbuf = tbuf;
- }
- else
- sbuf = NULL;
+ SVN_ERR(get_combined_window(&sbuf, rb));
rb->chunk_index++;
-
- if (cwindow)
- {
- rb->buf_len = cwindow->tview_len;
- rb->buf = apr_palloc(rb->pool, rb->buf_len);
- svn_txdelta_apply_instructions(cwindow, sbuf, rb->buf,
- &rb->buf_len);
- if (rb->buf_len != cwindow->tview_len)
- return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
- _("svndiff window length is "
- "corrupt"));
- }
- else
- {
- rb->buf_len = lwindow->tview_len;
- rb->buf = sbuf;
- }
-
+ rb->buf_len = sbuf->len;
+ rb->buf = sbuf->data;
rb->buf_pos = 0;
}
}
@@ -3469,27 +3676,28 @@ read_representation(svn_stream_t **conte
else
{
fs_fs_data_t *ffd = fs->fsap_data;
- const char *fulltext_key = NULL;
+ const char *fulltext_cache_key = NULL;
svn_filesize_t len = rep->expanded_size ? rep->expanded_size : rep->size;
struct rep_read_baton *rb;
if (ffd->fulltext_cache && SVN_IS_VALID_REVNUM(rep->revision)
&& fulltext_size_is_cachable(ffd, len))
{
- svn_string_t *fulltext;
+ svn_stringbuf_t *fulltext;
svn_boolean_t is_cached;
- fulltext_key = apr_psprintf(pool, "%ld/%" APR_OFF_T_FMT,
+ fulltext_cache_key = apr_psprintf(pool, "%ld/%" APR_OFF_T_FMT,
rep->revision, rep->offset);
SVN_ERR(svn_cache__get((void **) &fulltext, &is_cached,
- ffd->fulltext_cache, fulltext_key, pool));
+ ffd->fulltext_cache, fulltext_cache_key,
+ pool));
if (is_cached)
{
- *contents_p = svn_stream_from_string(fulltext, pool);
+ *contents_p = svn_stream_from_stringbuf(fulltext, pool);
return SVN_NO_ERROR;
}
}
- SVN_ERR(rep_read_get_baton(&rb, fs, rep, fulltext_key, pool));
+ SVN_ERR(rep_read_get_baton(&rb, fs, rep, fulltext_cache_key, pool));
*contents_p = svn_stream_create(rb, pool);
svn_stream_set_read(*contents_p, rep_read_contents);
@@ -5218,7 +5426,7 @@ choose_delta_base(representation_t **rep
count = noderev->predecessor_count;
count = count & (count - 1);
- /* We use skip delta for limiting the number of delta operations
+ /* We use skip delta for limiting the number of delta operations
along very long node histories. Close to HEAD however, we create
a linear history to minimize delta size. */
walk = noderev->predecessor_count - count;
@@ -5327,6 +5535,80 @@ rep_write_get_baton(struct rep_write_bat
return SVN_NO_ERROR;
}
+/* For the hash REP->SHA1, try to find an already existing representation
+ in FS and return it in *OUT_REP. If no such representation exists or
+ if rep sharing has been disabled for FS, NULL will be returned. Since
+ there may be new duplicate representations within the same uncommitted
+ revision, those can be passed in REPS_HASH (maps a sha1 digest onto
+ representation_t*), otherwise pass in NULL for REPS_HASH.
+ POOL will be used for allocations. The lifetime of the returned rep is
+ limited by both, POOL and REP lifetime.
+ */
+static svn_error_t *
+get_shared_rep(representation_t **old_rep,
+ svn_fs_t *fs,
+ representation_t *rep,
+ apr_hash_t *reps_hash,
+ apr_pool_t *pool)
+{
+ svn_error_t *err;
+ fs_fs_data_t *ffd = fs->fsap_data;
+
+ /* Return NULL, if rep sharing has been disabled. */
+ *old_rep = NULL;
+ if (!ffd->rep_sharing_allowed)
+ return SVN_NO_ERROR;
+
+ /* Check and see if we already have a representation somewhere that's
+ identical to the one we just wrote out. Start with the hash lookup
+ because it is cheepest. */
+ if (reps_hash)
+ *old_rep = apr_hash_get(reps_hash,
+ rep->sha1_checksum->digest,
+ APR_SHA1_DIGESTSIZE);
+
+ /* If we haven't found anything yet, try harder and consult our DB. */
+ if (*old_rep == NULL)
+ {
+ err = svn_fs_fs__get_rep_reference(old_rep, fs, rep->sha1_checksum,
+ pool);
+ /* ### Other error codes that we shouldn't mask out? */
+ if (err == SVN_NO_ERROR
+ || err->apr_err == SVN_ERR_FS_CORRUPT
+ || SVN_ERROR_IN_CATEGORY(err->apr_err,
+ SVN_ERR_MALFUNC_CATEGORY_START))
+ {
+ /* Fatal error; don't mask it.
+
+ In particular, this block is triggered when the rep-cache refers
+ to revisions in the future. We signal that as a corruption situation
+ since, once those revisions are less than youngest (because of more
+ commits), the rep-cache would be invalid.
+ */
+ SVN_ERR(err);
+ }
+ else
+ {
+ /* Something's wrong with the rep-sharing index. We can continue
+ without rep-sharing, but warn.
+ */
+ (fs->warning)(fs->warning_baton, err);
+ svn_error_clear(err);
+ *old_rep = NULL;
+ }
+ }
+
+ /* Add information that is missing in the cached data. */
+ if (*old_rep)
+ {
+ /* Use the old rep for this content. */
+ (*old_rep)->md5_checksum = rep->md5_checksum;
+ (*old_rep)->uniquifier = rep->uniquifier;
+ }
+
+ return SVN_NO_ERROR;
+}
+
/* Close handler for the representation write stream. BATON is a
rep_write_baton. Writes out a new node-rev that correctly
references the representation we just finished writing. */
@@ -5334,7 +5616,6 @@ static svn_error_t *
rep_write_contents_close(void *baton)
{
struct rep_write_baton *b = baton;
- fs_fs_data_t *ffd = b->fs->fsap_data;
const char *unique_suffix;
representation_t *rep;
representation_t *old_rep;
@@ -5368,38 +5649,7 @@ rep_write_contents_close(void *baton)
/* Check and see if we already have a representation somewhere that's
identical to the one we just wrote out. */
- if (ffd->rep_sharing_allowed)
- {
- svn_error_t *err;
- err = svn_fs_fs__get_rep_reference(&old_rep, b->fs, rep->sha1_checksum,
- b->parent_pool);
- /* ### Other error codes that we shouldn't mask out? */
- if (err == SVN_NO_ERROR
- || err->apr_err == SVN_ERR_FS_CORRUPT
- || SVN_ERROR_IN_CATEGORY(err->apr_err,
- SVN_ERR_MALFUNC_CATEGORY_START))
- {
- /* Fatal error; don't mask it.
-
- In particular, this block is triggered when the rep-cache refers
- to revisions in the future. We signal that as a corruption situation
- since, once those revisions are less than youngest (because of more
- commits), the rep-cache would be invalid.
- */
- SVN_ERR(err);
- }
- else
- {
- /* Something's wrong with the rep-sharing index. We can continue
- without rep-sharing, but warn.
- */
- (b->fs->warning)(b->fs->warning_baton, err);
- svn_error_clear(err);
- old_rep = NULL;
- }
- }
- else
- old_rep = NULL;
+ SVN_ERR(get_shared_rep(&old_rep, b->fs, rep, NULL, b->parent_pool));
if (old_rep)
{
@@ -5407,8 +5657,6 @@ rep_write_contents_close(void *baton)
SVN_ERR(svn_io_file_trunc(b->file, b->rep_offset, b->pool));
/* Use the old rep for this content. */
- old_rep->md5_checksum = rep->md5_checksum;
- old_rep->uniquifier = rep->uniquifier;
b->noderev->data_rep = old_rep;
}
else
@@ -5575,7 +5823,8 @@ struct write_hash_baton
apr_size_t size;
- svn_checksum_ctx_t *checksum_ctx;
+ svn_checksum_ctx_t *md5_ctx;
+ svn_checksum_ctx_t *sha1_ctx;
};
/* The handler for the write_hash_rep stream. BATON is a
@@ -5588,7 +5837,8 @@ write_hash_handler(void *baton,
{
struct write_hash_baton *whb = baton;
- SVN_ERR(svn_checksum_update(whb->checksum_ctx, data, *len));
+ SVN_ERR(svn_checksum_update(whb->md5_ctx, data, *len));
+ SVN_ERR(svn_checksum_update(whb->sha1_ctx, data, *len));
SVN_ERR(svn_stream_write(whb->stream, data, len));
whb->size += *len;
@@ -5597,23 +5847,32 @@ write_hash_handler(void *baton,
}
/* Write out the hash HASH as a text representation to file FILE. In
- the process, record the total size of the dump in *SIZE, and the
- md5 digest in CHECKSUM. Perform temporary allocations in POOL. */
+ the process, record position, the total size of the dump and MD5 as
+ well as SHA1 in REP. If rep sharing has been enabled and REPS_HASH
+ is not NULL, it will be used in addition to the on-disk cache to find
+ earlier reps with the same content. When such existing reps can be
+ found, we will truncate the one just written from the file and return
+ the existing rep. Perform temporary allocations in POOL. */
static svn_error_t *
-write_hash_rep(svn_filesize_t *size,
- svn_checksum_t **checksum,
+write_hash_rep(representation_t *rep,
apr_file_t *file,
apr_hash_t *hash,
+ svn_fs_t *fs,
+ apr_hash_t *reps_hash,
apr_pool_t *pool)
{
svn_stream_t *stream;
struct write_hash_baton *whb;
+ representation_t *old_rep;
+
+ SVN_ERR(get_file_offset(&rep->offset, file, pool));
whb = apr_pcalloc(pool, sizeof(*whb));
whb->stream = svn_stream_from_aprfile2(file, TRUE, pool);
whb->size = 0;
- whb->checksum_ctx = svn_checksum_ctx_create(svn_checksum_md5, pool);
+ whb->md5_ctx = svn_checksum_ctx_create(svn_checksum_md5, pool);
+ whb->sha1_ctx = svn_checksum_ctx_create(svn_checksum_sha1, pool);
stream = svn_stream_create(whb, pool);
svn_stream_set_write(stream, write_hash_handler);
@@ -5623,15 +5882,41 @@ write_hash_rep(svn_filesize_t *size,
SVN_ERR(svn_hash_write2(hash, stream, SVN_HASH_TERMINATOR, pool));
/* Store the results. */
- SVN_ERR(svn_checksum_final(checksum, whb->checksum_ctx, pool));
- *size = whb->size;
+ SVN_ERR(svn_checksum_final(&rep->md5_checksum, whb->md5_ctx, pool));
+ SVN_ERR(svn_checksum_final(&rep->sha1_checksum, whb->sha1_ctx, pool));
+
+ /* Check and see if we already have a representation somewhere that's
+ identical to the one we just wrote out. */
+ SVN_ERR(get_shared_rep(&old_rep, fs, rep, reps_hash, pool));
+
+ if (old_rep)
+ {
+ /* We need to erase from the protorev the data we just wrote. */
+ SVN_ERR(svn_io_file_trunc(file, rep->offset, pool));
+
+ /* Use the old rep for this content. */
+ memcpy(rep, old_rep, sizeof (*rep));
+ }
+ else
+ {
+ /* Write out our cosmetic end marker. */
+ SVN_ERR(svn_stream_printf(whb->stream, pool, "ENDREP\n"));
+
+ /* update the representation */
+ rep->size = whb->size;
+ rep->expanded_size = 0;
+ }
- return svn_stream_printf(whb->stream, pool, "ENDREP\n");
+ return SVN_NO_ERROR;
}
/* Write out the hash HASH pertaining to the NODEREV in FS as a deltified
text representation to file FILE. In the process, record the total size
- and the md5 digest in REP. Perform temporary allocations in POOL. */
+ and the md5 digest in REP. If rep sharing has been enabled and REPS_HASH
+ is not NULL, it will be used in addition to the on-disk cache to find
+ earlier reps with the same content. When such existing reps can be found,
+ we will truncate the one just written from the file and return the existing
+ rep. Perform temporary allocations in POOL. */
#ifdef SVN_FS_FS_DELTIFY_DIRECTORIES
static svn_error_t *
write_hash_delta_rep(representation_t *rep,
@@ -5639,19 +5924,21 @@ write_hash_delta_rep(representation_t *r
apr_hash_t *hash,
svn_fs_t *fs,
node_revision_t *noderev,
+ apr_hash_t *reps_hash,
apr_pool_t *pool)
{
svn_txdelta_window_handler_t diff_wh;
void *diff_whb;
-
+
svn_stream_t *file_stream;
svn_stream_t *stream;
representation_t *base_rep;
+ representation_t *old_rep;
svn_stream_t *source;
const char *header;
- apr_off_t rep_end = 0;
- apr_off_t delta_start = 0;
+ apr_off_t rep_end = 0;
+ apr_off_t delta_start = 0;
struct write_hash_baton *whb;
fs_fs_data_t *ffd = fs->fsap_data;
@@ -5680,7 +5967,7 @@ write_hash_delta_rep(representation_t *r
SVN_ERR(get_file_offset(&delta_start, file, pool));
file_stream = svn_stream_from_aprfile2(file, TRUE, pool);
-
+
/* Prepare to write the svndiff data. */
svn_txdelta_to_svndiff3(&diff_wh,
&diff_whb,
@@ -5692,7 +5979,8 @@ write_hash_delta_rep(representation_t *r
whb = apr_pcalloc(pool, sizeof(*whb));
whb->stream = svn_txdelta_target_push(diff_wh, diff_whb, source, pool);
whb->size = 0;
- whb->checksum_ctx = svn_checksum_ctx_create(svn_checksum_md5, pool);
+ whb->md5_ctx = svn_checksum_ctx_create(svn_checksum_md5, pool);
+ whb->sha1_ctx = svn_checksum_ctx_create(svn_checksum_sha1, pool);
/* serialize the hash */
stream = svn_stream_create(whb, pool);
@@ -5702,15 +5990,31 @@ write_hash_delta_rep(representation_t *r
SVN_ERR(svn_stream_close(whb->stream));
/* Store the results. */
- SVN_ERR(svn_checksum_final(&rep->md5_checksum, whb->checksum_ctx, pool));
+ SVN_ERR(svn_checksum_final(&rep->md5_checksum, whb->md5_ctx, pool));
+ SVN_ERR(svn_checksum_final(&rep->sha1_checksum, whb->sha1_ctx, pool));
+
+ /* Check and see if we already have a representation somewhere that's
+ identical to the one we just wrote out. */
+ SVN_ERR(get_shared_rep(&old_rep, fs, rep, reps_hash, pool));
+
+ if (old_rep)
+ {
+ /* We need to erase from the protorev the data we just wrote. */
+ SVN_ERR(svn_io_file_trunc(file, rep->offset, pool));
+
+ /* Use the old rep for this content. */
+ memcpy(rep, old_rep, sizeof (*rep));
+ }
+ else
+ {
+ /* Write out our cosmetic end marker. */
+ SVN_ERR(get_file_offset(&rep_end, file, pool));
+ SVN_ERR(svn_stream_printf(file_stream, pool, "ENDREP\n"));
- /* Write out our cosmetic end marker. */
- SVN_ERR(get_file_offset(&rep_end, file, pool));
- SVN_ERR(svn_stream_printf(file_stream, pool, "ENDREP\n"));
-
- /* update the representation */
- rep->expanded_size = whb->size;
- rep->size = rep_end - delta_start;
+ /* update the representation */
+ rep->expanded_size = whb->size;
+ rep->size = rep_end - delta_start;
+ }
return SVN_NO_ERROR;
}
@@ -5719,6 +6023,8 @@ write_hash_delta_rep(representation_t *r
/* Sanity check ROOT_NODEREV, a candidate for being the root node-revision
of (not yet committed) revision REV in FS. Use POOL for temporary
allocations.
+
+ If you change this function, consider updating svn_fs_fs__verify() too.
*/
static svn_error_t *
validate_root_noderev(svn_fs_t *fs,
@@ -5765,8 +6071,11 @@ validate_root_noderev(svn_fs_t *fs,
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
_("predecessor count for "
"the root node-revision is wrong: "
- "found %d, committing r%ld"),
- root_noderev->predecessor_count, rev);
+ "found (%d+%ld != %d), committing r%ld"),
+ head_predecessor_count,
+ rev - head_revnum, /* This is equal to 1. */
+ root_noderev->predecessor_count,
+ rev);
}
return SVN_NO_ERROR;
@@ -5789,6 +6098,10 @@ validate_root_noderev(svn_fs_t *fs,
If REPS_TO_CACHE is not NULL, append to it a copy (allocated in
REPS_POOL) of each data rep that is new in this revision.
+ If REPS_HASH is not NULL, append copies (allocated in REPS_POOL)
+ of the representations of each property rep that is new in this
+ revision.
+
AT_ROOT is true if the node revision being written is the root
node-revision. It is only controls additional sanity checking
logic.
@@ -5804,6 +6117,7 @@ write_final_rev(const svn_fs_id_t **new_
const char *start_copy_id,
apr_off_t initial_offset,
apr_array_header_t *reps_to_cache,
+ apr_hash_t *reps_hash,
apr_pool_t *reps_pool,
svn_boolean_t at_root,
apr_pool_t *pool)
@@ -5842,7 +6156,7 @@ write_final_rev(const svn_fs_id_t **new_
svn_pool_clear(subpool);
SVN_ERR(write_final_rev(&new_id, file, rev, fs, dirent->id,
start_node_id, start_copy_id, initial_offset,
- reps_to_cache, reps_pool, FALSE,
+ reps_to_cache, reps_hash, reps_pool, FALSE,
subpool));
if (new_id && (svn_fs_fs__id_rev(new_id) == rev))
dirent->id = svn_fs_fs__id_copy(new_id, pool);
@@ -5859,13 +6173,11 @@ write_final_rev(const svn_fs_id_t **new_
#ifdef SVN_FS_FS_DELTIFY_DIRECTORIES
SVN_ERR(write_hash_delta_rep(noderev->data_rep, file,
- str_entries, fs, noderev, pool));
+ str_entries, fs, noderev, NULL,
+ pool));
#else
- SVN_ERR(get_file_offset(&noderev->data_rep->offset, file, pool));
- SVN_ERR(write_hash_rep(&noderev->data_rep->size,
- &noderev->data_rep->md5_checksum, file,
- str_entries, pool));
- noderev->data_rep->expanded_size = noderev->data_rep->size;
+ SVN_ERR(write_hash_rep(noderev->data_rep, file, str_entries,
+ fs, NULL, pool));
#endif
}
}
@@ -5896,17 +6208,17 @@ write_final_rev(const svn_fs_id_t **new_
apr_hash_t *proplist;
SVN_ERR(svn_fs_fs__get_proplist(&proplist, fs, noderev, pool));
+ noderev->prop_rep->txn_id = NULL;
+ noderev->prop_rep->revision = rev;
+
#ifdef SVN_FS_FS_DELTIFY_PROPS
SVN_ERR(write_hash_delta_rep(noderev->prop_rep, file,
- proplist, fs, noderev, pool));
+ proplist, fs, noderev, reps_hash,
+ pool));
#else
- SVN_ERR(get_file_offset(&noderev->prop_rep->offset, file, pool));
- SVN_ERR(write_hash_rep(&noderev->prop_rep->size,
- &noderev->prop_rep->md5_checksum, file,
- proplist, pool));
+ SVN_ERR(write_hash_rep(noderev->prop_rep, file, proplist,
+ fs, reps_hash, pool));
#endif
- noderev->prop_rep->txn_id = NULL;
- noderev->prop_rep->revision = rev;
}
@@ -5949,6 +6261,41 @@ write_final_rev(const svn_fs_id_t **new_
noderev->id = new_id;
+ if (ffd->rep_sharing_allowed)
+ {
+ /* Save the data representation's hash in the rep cache. */
+ if ( noderev->data_rep && noderev->kind == svn_node_file
+ && noderev->data_rep->revision == rev)
+ {
+ SVN_ERR_ASSERT(reps_to_cache && reps_pool);
+ APR_ARRAY_PUSH(reps_to_cache, representation_t *)
+ = svn_fs_fs__rep_copy(noderev->data_rep, reps_pool);
+ }
+
+ if (noderev->prop_rep && noderev->prop_rep->revision == rev)
+ {
+ /* Add new property reps to hash and on-disk cache. */
+ representation_t *copy
+ = svn_fs_fs__rep_copy(noderev->prop_rep, reps_pool);
+
+ SVN_ERR_ASSERT(reps_to_cache && reps_pool);
+ APR_ARRAY_PUSH(reps_to_cache, representation_t *) = copy;
+
+ apr_hash_set(reps_hash,
+ copy->sha1_checksum->digest,
+ APR_SHA1_DIGESTSIZE,
+ copy);
+ }
+ }
+
+ /* don't serialize SHA1 for dirs to disk (waste of space) */
+ if (noderev->data_rep && noderev->kind == svn_node_dir)
+ noderev->data_rep->sha1_checksum = NULL;
+
+ /* don't serialize SHA1 for props to disk (waste of space) */
+ if (noderev->prop_rep)
+ noderev->prop_rep->sha1_checksum = NULL;
+
/* Write out our new node-revision. */
if (at_root)
SVN_ERR(validate_root_noderev(fs, noderev, rev, pool));
@@ -5957,16 +6304,6 @@ write_final_rev(const svn_fs_id_t **new_
svn_fs_fs__fs_supports_mergeinfo(fs),
pool));
- /* Save the data representation's hash in the rep cache. */
- if (ffd->rep_sharing_allowed
- && noderev->data_rep && noderev->kind == svn_node_file
- && noderev->data_rep->revision == rev)
- {
- SVN_ERR_ASSERT(reps_to_cache && reps_pool);
- APR_ARRAY_PUSH(reps_to_cache, representation_t *)
- = svn_fs_fs__rep_copy(noderev->data_rep, reps_pool);
- }
-
/* Return our ID that references the revision file. */
*new_id_p = noderev->id;
@@ -6174,6 +6511,7 @@ struct commit_baton {
svn_fs_t *fs;
svn_fs_txn_t *txn;
apr_array_header_t *reps_to_cache;
+ apr_hash_t *reps_hash;
apr_pool_t *reps_pool;
};
@@ -6231,8 +6569,8 @@ commit_body(void *baton, apr_pool_t *poo
root_id = svn_fs_fs__id_txn_create("0", "0", cb->txn->id, pool);
SVN_ERR(write_final_rev(&new_root_id, proto_file, new_rev, cb->fs, root_id,
start_node_id, start_copy_id, initial_offset,
- cb->reps_to_cache, cb->reps_pool, TRUE,
- pool));
+ cb->reps_to_cache, cb->reps_hash, cb->reps_pool,
+ TRUE, pool));
/* Write the changed-path information. */
SVN_ERR(write_final_changed_path_info(&changed_path_offset, proto_file,
@@ -6406,11 +6744,13 @@ svn_fs_fs__commit(svn_revnum_t *new_rev_
if (ffd->rep_sharing_allowed)
{
cb.reps_to_cache = apr_array_make(pool, 5, sizeof(representation_t *));
+ cb.reps_hash = apr_hash_make(pool);
cb.reps_pool = pool;
}
else
{
cb.reps_to_cache = NULL;
+ cb.reps_hash = NULL;
cb.reps_pool = NULL;
}
@@ -6569,6 +6909,9 @@ svn_fs_fs__create(svn_fs_t *fs,
"", pool));
}
+ /* Create the revprop generation tracking file. */
+ SVN_ERR(increment_revprop_generation(fs, pool));
+
/* This filesystem is ready. Stamp it with a format number. */
SVN_ERR(write_format(path_format(fs, pool),
ffd->format, ffd->max_files_per_dir, FALSE, pool));
@@ -7701,7 +8044,7 @@ svn_fs_fs__pack(svn_fs_t *fs,
/** Verifying. **/
-/* Body of svn_fs_fs__verify().
+/* Used by svn_fs_fs__verify().
Implements svn_fs_fs__walk_rep_reference().walker. */
static svn_error_t *
verify_walker(representation_t *rep,
@@ -7722,23 +8065,93 @@ svn_error_t *
svn_fs_fs__verify(svn_fs_t *fs,
svn_cancel_func_t cancel_func,
void *cancel_baton,
+ svn_revnum_t start,
+ svn_revnum_t end,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
svn_boolean_t exists;
+ svn_revnum_t youngest = ffd->youngest_rev_cache; /* cache is current */
+ apr_pool_t *iterpool = svn_pool_create(pool);
if (ffd->format < SVN_FS_FS__MIN_REP_SHARING_FORMAT)
return SVN_NO_ERROR;
- /* Do not attempt to walk the rep-cache database if its file does not exists,
- since doing so would create it --- which may confuse the administrator. */
+ /* Input validation. */
+ if (! SVN_IS_VALID_REVNUM(start))
+ start = 0;
+ if (! SVN_IS_VALID_REVNUM(end))
+ end = youngest;
+ SVN_ERR(ensure_revision_exists(fs, start, iterpool));
+ SVN_ERR(ensure_revision_exists(fs, end, iterpool));
+
+ /* rep-cache verification. */
SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, fs, pool));
if (exists)
- /* Don't take any lock. */
+ /* Do not attempt to walk the rep-cache database if its file does not exist,
+ since doing so would create it --- which may confuse the administrator.
+ Don't take any lock. */
SVN_ERR(svn_fs_fs__walk_rep_reference(fs, verify_walker, NULL,
cancel_func, cancel_baton,
+ start, end,
pool));
+ /* Issue #4129: bogus pred-counts on the root node-rev. */
+ {
+ svn_revnum_t i;
+ int predecessor_predecessor_count;
+
+ /* Compute PREDECESSOR_PREDECESSOR_COUNT. */
+ if (start == 0)
+ /* The value that passes the if() at the end of the loop. */
+ predecessor_predecessor_count = -1;
+ else
+ {
+ svn_fs_id_t *root_id;
+ node_revision_t *root_noderev;
+ SVN_ERR(svn_fs_fs__rev_get_root(&root_id, fs, start-1, iterpool));
+ SVN_ERR(svn_fs_fs__get_node_revision(&root_noderev, fs, root_id,
+ iterpool));
+ predecessor_predecessor_count = root_noderev->predecessor_count;
+ }
+
+ for (i = start; i <= end; i++)
+ {
+ /* ### Caching.
+
+ svn_fs_fs__rev_get_root() consults caches, which in verify we
+ don't want. But we can't easily bypass that, as
+ svn_fs_revision_root()+svn_fs_node_id()'s implementation uses
+ svn_fs_fs__rev_get_root() too.
+
+ ### A future revision will make fs_verify() disable caches when it
+ ### opens ffd.
+ */
+ svn_fs_id_t *root_id;
+ node_revision_t *root_noderev;
+
+ if ((i % 128) == 0) /* uneducated guess */
+ svn_pool_clear(iterpool);
+
+ /* Fetch ROOT_NODEREV. */
+ SVN_ERR(svn_fs_fs__rev_get_root(&root_id, fs, i, iterpool));
+ SVN_ERR(svn_fs_fs__get_node_revision(&root_noderev, fs, root_id,
+ iterpool));
+
+ /* Check correctness. (Compare validate_root_noderev().) */
+ if (1+predecessor_predecessor_count != root_noderev->predecessor_count)
+ return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+ _("predecessor count for "
+ "the root node-revision is wrong: "
+ "r%ld has %d, but r%ld has %d"),
+ i, root_noderev->predecessor_count,
+ i-1, predecessor_predecessor_count);
+
+ predecessor_predecessor_count = root_noderev->predecessor_count;
+ }
+ }
+
+ svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
@@ -7902,7 +8315,7 @@ hotcopy_io_copy_dir_recursively(const ch
else if (this_entry.filetype == APR_DIR) /* recurse */
{
const char *src_target;
-
+
/* Prevent infinite recursion by filtering off our
newly created destination path. */
if (strcmp(src, dst_parent) == 0
@@ -8107,7 +8520,7 @@ hotcopy_remove_rev_files(svn_fs_t *dst_f
for (rev = start_rev; rev < end_rev; rev++)
{
const char *rev_path;
-
+
svn_pool_clear(iterpool);
/* If necessary, update paths for shard. */
@@ -8549,6 +8962,7 @@ hotcopy_create_empty_dest(svn_fs_t *src_
{
fs_fs_data_t *src_ffd = src_fs->fsap_data;
fs_fs_data_t *dst_ffd = dst_fs->fsap_data;
+ svn_node_kind_t kind;
dst_fs->path = apr_pstrdup(pool, dst_path);
@@ -8612,6 +9026,14 @@ hotcopy_create_empty_dest(svn_fs_t *src_
"", pool));
}
+ /* Copy the revprop generation file if it exists in SRC_FS. */
+ SVN_ERR(svn_io_check_path(path_revprop_generation(src_fs, pool),
+ &kind, pool));
+ if (kind == svn_node_file)
+ SVN_ERR(svn_io_copy_file(path_revprop_generation(src_fs, pool),
+ path_revprop_generation(dst_fs, pool),
+ TRUE, pool));
+
dst_ffd->youngest_rev_cache = 0;
return SVN_NO_ERROR;
}
Modified: subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/fs_fs.h
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/fs_fs.h?rev=1297604&r1=1297603&r2=1297604&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/fs_fs.h (original)
+++ subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/fs_fs.h Tue Mar 6 17:50:23 2012
@@ -42,6 +42,8 @@ svn_error_t *svn_fs_fs__upgrade(svn_fs_t
svn_error_t *svn_fs_fs__verify(svn_fs_t *fs,
svn_cancel_func_t cancel_func,
void *cancel_baton,
+ svn_revnum_t start,
+ svn_revnum_t end,
apr_pool_t *pool);
/* Copy the fsfs filesystem SRC_FS at SRC_PATH into a new copy DST_FS at
@@ -106,6 +108,12 @@ svn_error_t *svn_fs_fs__youngest_rev(svn
svn_fs_t *fs,
apr_pool_t *pool);
+/* Return an error iff REV does not exist in FS. */
+svn_error_t *
+svn_fs_fs__revision_exists(svn_revnum_t rev,
+ svn_fs_t *fs,
+ apr_pool_t *pool);
+
/* Set *ROOT_ID to the node-id for the root of revision REV in
filesystem FS. Do any allocations in POOL. */
svn_error_t *svn_fs_fs__rev_get_root(svn_fs_id_t **root_id,
Modified: subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/rep-cache-db.sql
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/rep-cache-db.sql?rev=1297604&r1=1297603&r2=1297604&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/rep-cache-db.sql (original)
+++ subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/rep-cache-db.sql Tue Mar 6 17:50:23 2012
@@ -33,6 +33,11 @@ CREATE TABLE rep_cache (
expanded_size INTEGER NOT NULL
);
+/* There isn't an implicit index on a TEXT column, so create an explicit one. */
+CREATE INDEX I_HASH on REP_CACHE (hash);
+
+/* The index didn't exist until 1.7.5; therefore, some USER_VERSION=1
+ rep-cache.db files out there DO NOT contain an I_HASH index. */
PRAGMA USER_VERSION = 1;
@@ -47,9 +52,15 @@ INSERT OR FAIL INTO rep_cache (hash, rev
VALUES (?1, ?2, ?3, ?4, ?5)
--- STMT_GET_ALL_REPS
+-- STMT_GET_REPS_FOR_RANGE
SELECT hash, revision, offset, size, expanded_size
FROM rep_cache
+WHERE revision >= ?1 AND revision <= ?2
+
+
+-- STMT_GET_MAX_REV
+SELECT MAX(revision)
+FROM rep_cache
-- STMT_DEL_REPS_YOUNGER_THAN_REV
Modified: subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/rep-cache.c
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/rep-cache.c?rev=1297604&r1=1297603&r2=1297604&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/rep-cache.c (original)
+++ subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/rep-cache.c Tue Mar 6 17:50:23 2012
@@ -56,24 +56,9 @@ rep_has_been_born(representation_t *rep,
svn_fs_t *fs,
apr_pool_t *pool)
{
- fs_fs_data_t *ffd = fs->fsap_data;
- svn_revnum_t youngest;
-
SVN_ERR_ASSERT(rep);
- youngest = ffd->youngest_rev_cache;
- if (youngest < rep->revision)
- {
- /* Stale cache. */
- SVN_ERR(svn_fs_fs__youngest_rev(&youngest, fs, pool));
-
- /* Fresh cache. */
- if (youngest < rep->revision)
- return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
- _("Youngest revision is r%ld, but "
- "rep-cache contains r%ld"),
- youngest, rep->revision);
- }
+ SVN_ERR(svn_fs_fs__revision_exists(rep->revision, fs, pool));
return SVN_NO_ERROR;
}
@@ -141,11 +126,13 @@ svn_error_t *
svn_fs_fs__walk_rep_reference(svn_fs_t *fs,
svn_error_t *(*walker)(representation_t *,
void *,
- svn_fs_t *,
+ svn_fs_t *,
apr_pool_t *),
void *walker_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
+ svn_revnum_t start,
+ svn_revnum_t end,
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
@@ -161,9 +148,25 @@ svn_fs_fs__walk_rep_reference(svn_fs_t *
if (! ffd->rep_cache_db)
SVN_ERR(svn_fs_fs__open_rep_cache(fs, pool));
+ /* Check global invariants. */
+ if (start == 0)
+ {
+ svn_sqlite__stmt_t *stmt2;
+ svn_revnum_t max;
+
+ SVN_ERR(svn_sqlite__get_statement(&stmt2, ffd->rep_cache_db,
+ STMT_GET_MAX_REV));
+ SVN_ERR(svn_sqlite__step(&have_row, stmt2));
+ max = svn_sqlite__column_revnum(stmt2, 0);
+ SVN_ERR(svn_fs_fs__revision_exists(max, fs, iterpool));
+ SVN_ERR(svn_sqlite__reset(stmt2));
+ }
+
/* Get the statement. (There are no arguments to bind.) */
SVN_ERR(svn_sqlite__get_statement(&stmt, ffd->rep_cache_db,
- STMT_GET_ALL_REPS));
+ STMT_GET_REPS_FOR_RANGE));
+ SVN_ERR(svn_sqlite__bindf(stmt, "rr",
+ start, end));
/* Walk the cache entries. */
SVN_ERR(svn_sqlite__step(&have_row, stmt));
@@ -171,7 +174,7 @@ svn_fs_fs__walk_rep_reference(svn_fs_t *
{
representation_t *rep;
const char *sha1_digest;
-
+
/* Clear ITERPOOL occasionally. */
if (iterations++ % 16 == 0)
svn_pool_clear(iterpool);
@@ -191,10 +194,6 @@ svn_fs_fs__walk_rep_reference(svn_fs_t *
rep->size = svn_sqlite__column_int64(stmt, 3);
rep->expanded_size = svn_sqlite__column_int64(stmt, 4);
- /* Sanity check. */
- if (rep)
- SVN_ERR(rep_has_been_born(rep, fs, iterpool));
-
/* Walk. */
SVN_ERR(walker(rep, walker_baton, fs, iterpool));
Modified: subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/rep-cache.h
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/rep-cache.h?rev=1297604&r1=1297603&r2=1297604&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/rep-cache.h (original)
+++ subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/rep-cache.h Tue Mar 6 17:50:23 2012
@@ -50,11 +50,13 @@ svn_error_t *
svn_fs_fs__walk_rep_reference(svn_fs_t *fs,
svn_error_t *(*walker)(representation_t *rep,
void *walker_baton,
- svn_fs_t *fs,
+ svn_fs_t *fs,
apr_pool_t *scratch_pool),
void *walker_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
+ svn_revnum_t start,
+ svn_revnum_t end,
apr_pool_t *pool);
/* Return the representation REP in FS which has fulltext CHECKSUM.
Modified: subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/structure
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/structure?rev=1297604&r1=1297603&r2=1297604&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/structure (original)
+++ subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/structure Tue Mar 6 17:50:23 2012
@@ -266,7 +266,7 @@ Within a revision:
"r<rev>/<offset>" txn-id fields.
In Format 3 and above, this uniqueness is done by changing a temporary
- id of "_<base36>" to "<rev>-<base36>". Note that this means that the
+ id of "_<base36>" to "<base36>-<rev>". Note that this means that the
originating revision of a line of history or a copy can be determined
by looking at the node ID.
Modified: subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/temp_serializer.c
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/temp_serializer.c?rev=1297604&r1=1297603&r2=1297604&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/temp_serializer.c (original)
+++ subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/temp_serializer.c Tue Mar 6 17:50:23 2012
@@ -136,7 +136,7 @@ serialize_svn_string(svn_temp_serializer
* Thus, we cannot use svn_temp_serializer__add_string. */
svn_temp_serializer__push(context,
(const void * const *)&string->data,
- string->len);
+ string->len + 1);
/* back to the caller's nesting level */
svn_temp_serializer__pop(context);
@@ -579,6 +579,142 @@ svn_fs_fs__deserialize_manifest(void **o
return SVN_NO_ERROR;
}
+/* Auxilliary structure representing the content of a properties hash.
+ This structure is much easier to (de-)serialize than an apr_hash.
+ */
+typedef struct properties_data_t
+{
+ /* number of entries in the hash */
+ apr_size_t count;
+
+ /* reference to the keys */
+ const char **keys;
+
+ /* reference to the values */
+ const svn_string_t **values;
+} properties_data_t;
+
+/* Serialize COUNT C-style strings from *STRINGS into CONTEXT. */
+static void
+serialize_cstring_array(svn_temp_serializer__context_t *context,
+ const char ***strings,
+ apr_size_t count)
+{
+ apr_size_t i;
+ const char **entries = *strings;
+
+ /* serialize COUNT entries pointers (the array) */
+ svn_temp_serializer__push(context,
+ (const void * const *)strings,
+ count * sizeof(const char*));
+
+ /* serialize array elements */
+ for (i = 0; i < count; ++i)
+ svn_temp_serializer__add_string(context, &entries[i]);
+
+ svn_temp_serializer__pop(context);
+}
+
+/* Serialize COUNT svn_string_t* items from *STRINGS into CONTEXT. */
+static void
+serialize_svn_string_array(svn_temp_serializer__context_t *context,
+ const svn_string_t ***strings,
+ apr_size_t count)
+{
+ apr_size_t i;
+ const svn_string_t **entries = *strings;
+
+ /* serialize COUNT entries pointers (the array) */
+ svn_temp_serializer__push(context,
+ (const void * const *)strings,
+ count * sizeof(const char*));
+
+ /* serialize array elements */
+ for (i = 0; i < count; ++i)
+ serialize_svn_string(context, &entries[i]);
+
+ svn_temp_serializer__pop(context);
+}
+
+svn_error_t *
+svn_fs_fs__serialize_properties(char **data,
+ apr_size_t *data_len,
+ void *in,
+ apr_pool_t *pool)
+{
+ apr_hash_t *hash = in;
+ properties_data_t properties;
+ svn_temp_serializer__context_t *context;
+ apr_hash_index_t *hi;
+ svn_stringbuf_t *serialized;
+ apr_size_t i;
+
+ /* create our auxilliary data structure */
+ properties.count = apr_hash_count(hash);
+ properties.keys = apr_palloc(pool, sizeof(const char*) * (properties.count + 1));
+ properties.values = apr_palloc(pool, sizeof(const char*) * properties.count);
+
+ /* populate it with the hash entries */
+ for (hi = apr_hash_first(pool, hash), i=0; hi; hi = apr_hash_next(hi), ++i)
+ {
+ properties.keys[i] = svn__apr_hash_index_key(hi);
+ properties.values[i] = svn__apr_hash_index_val(hi);
+ }
+
+ /* serialize it */
+ context = svn_temp_serializer__init(&properties,
+ sizeof(properties),
+ properties.count * 100,
+ pool);
+
+ properties.keys[i] = "";
+ serialize_cstring_array(context, &properties.keys, properties.count + 1);
+ serialize_svn_string_array(context, &properties.values, properties.count);
+
+ /* 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_properties(void **out,
+ char *data,
+ apr_size_t data_len,
+ apr_pool_t *pool)
+{
+ apr_hash_t *hash = apr_hash_make(pool);
+ properties_data_t *properties = (properties_data_t *)data;
+ size_t i;
+
+ /* de-serialize our auxilliary data structure */
+ svn_temp_deserializer__resolve(properties, (void**)&properties->keys);
+ svn_temp_deserializer__resolve(properties, (void**)&properties->values);
+
+ /* de-serialize each entry and put it into the hash */
+ for (i = 0; i < properties->count; ++i)
+ {
+ apr_size_t len = properties->keys[i+1] - properties->keys[i] - 1;
+ svn_temp_deserializer__resolve(properties->keys,
+ (void**)&properties->keys[i]);
+
+ deserialize_svn_string(properties->values,
+ (svn_string_t **)&properties->values[i]);
+
+ apr_hash_set(hash,
+ properties->keys[i], len,
+ properties->values[i]);
+ }
+
+ /* done */
+ *out = hash;
+
+ return SVN_NO_ERROR;
+}
+
svn_error_t *
svn_fs_fs__serialize_id(char **data,
apr_size_t *data_len,
Modified: subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/temp_serializer.h
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/temp_serializer.h?rev=1297604&r1=1297603&r2=1297604&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/temp_serializer.h (original)
+++ subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/temp_serializer.h Tue Mar 6 17:50:23 2012
@@ -113,6 +113,26 @@ svn_fs_fs__deserialize_manifest(void **o
apr_pool_t *pool);
/**
+ * Implements #svn_cache__serialize_func_t for a properties hash
+ * (@a in is an #apr_hash_t of svn_string_t elements, keyed by const char*).
+ */
+svn_error_t *
+svn_fs_fs__serialize_properties(char **data,
+ apr_size_t *data_len,
+ void *in,
+ apr_pool_t *pool);
+
+/**
+ * Implements #svn_cache__deserialize_func_t for a properties hash
+ * (@a *out is an #apr_hash_t of svn_string_t elements, keyed by const char*).
+ */
+svn_error_t *
+svn_fs_fs__deserialize_properties(void **out,
+ char *data,
+ apr_size_t data_len,
+ apr_pool_t *pool);
+
+/**
* Implements #svn_cache__serialize_func_t for #svn_fs_id_t
*/
svn_error_t *
Modified: subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/tree.c
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/tree.c?rev=1297604&r1=1297603&r2=1297604&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/tree.c (original)
+++ subversion/branches/reintegrate-keep-alive/subversion/libsvn_fs_fs/tree.c Tue Mar 6 17:50:23 2012
@@ -115,7 +115,7 @@ typedef struct fs_rev_root_data_t
typedef struct fs_txn_root_data_t
{
/* Cache of txn DAG nodes (without their nested noderevs, because
- * it's mutable). */
+ * it's mutable). Same keys/values as ffd->rev_node_cache. */
svn_cache__t *txn_node_cache;
} fs_txn_root_data_t;
@@ -2807,8 +2807,10 @@ find_youngest_copyroot(svn_revnum_t *rev
parent_path_t *parent_path,
apr_pool_t *pool)
{
- svn_revnum_t rev_mine, rev_parent = -1;
- const char *path_mine, *path_parent;
+ svn_revnum_t rev_mine;
+ svn_revnum_t rev_parent = SVN_INVALID_REVNUM;
+ const char *path_mine;
+ const char *path_parent = NULL;
/* First find our parent's youngest copyroot. */
if (parent_path->parent)
Modified: subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra/ra_loader.c
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra/ra_loader.c?rev=1297604&r1=1297603&r2=1297604&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra/ra_loader.c (original)
+++ subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra/ra_loader.c Tue Mar 6 17:50:23 2012
@@ -1304,6 +1304,15 @@ svn_ra_print_ra_libraries(svn_stringbuf_
}
+svn_error_t *
+svn_ra__register_editor_shim_callbacks(svn_ra_session_t *session,
+ svn_delta_shim_callbacks_t *callbacks)
+{
+ SVN_ERR(session->vtable->register_editor_shim_callbacks(session, callbacks));
+ return SVN_NO_ERROR;
+}
+
+
/* Return the library version number. */
const svn_version_t *
svn_ra_version(void)
Modified: subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra/ra_loader.h
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra/ra_loader.h?rev=1297604&r1=1297603&r2=1297604&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra/ra_loader.h (original)
+++ subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra/ra_loader.h Tue Mar 6 17:50:23 2012
@@ -293,6 +293,8 @@ typedef struct svn_ra__vtable_t {
svn_revnum_t end_revision,
svn_revnum_t *revision_deleted,
apr_pool_t *pool);
+ svn_error_t *(*register_editor_shim_callbacks)(svn_ra_session_t *session,
+ svn_delta_shim_callbacks_t *callbacks);
} svn_ra__vtable_t;
Modified: subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra_local/ra_plugin.c
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra_local/ra_plugin.c?rev=1297604&r1=1297603&r2=1297604&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra_local/ra_plugin.c (original)
+++ subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra_local/ra_plugin.c Tue Mar 6 17:50:23 2012
@@ -153,7 +153,7 @@ cache_init(void *baton, apr_pool_t *pool
SVN_ERR(svn_error_quick_wrap(svn_cstring_atoui64(&memory_cache_size,
memory_cache_size_str),
_("memory-cache-size invalid")));
- settings.cache_size = 1024 * 1024 * memory_cache_size;
+ settings.cache_size = 1024 * 1024 * memory_cache_size;
svn_cache_config_set(&settings);
}
@@ -1505,6 +1505,15 @@ svn_ra_local__get_deleted_rev(svn_ra_ses
return SVN_NO_ERROR;
}
+static svn_error_t *
+svn_ra_local__register_editor_shim_callbacks(svn_ra_session_t *session,
+ svn_delta_shim_callbacks_t *callbacks)
+{
+ /* This is currenly a no-op, since we don't provide our own editor, just
+ use the one the libsvn_repos hands back to us. */
+ return SVN_NO_ERROR;
+}
+
/*----------------------------------------------------------------*/
static const svn_version_t *
@@ -1551,7 +1560,8 @@ static const svn_ra__vtable_t ra_local_v
svn_ra_local__replay,
svn_ra_local__has_capability,
svn_ra_local__replay_range,
- svn_ra_local__get_deleted_rev
+ svn_ra_local__get_deleted_rev,
+ svn_ra_local__register_editor_shim_callbacks
};
Modified: subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra_neon/commit.c
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra_neon/commit.c?rev=1297604&r1=1297603&r2=1297604&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra_neon/commit.c (original)
+++ subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra_neon/commit.c Tue Mar 6 17:50:23 2012
@@ -1570,8 +1570,6 @@ svn_error_t * svn_ra_neon__get_commit_ed
svn_delta_editor_t *commit_editor;
commit_ctx_t *cc;
apr_hash_index_t *hi;
- svn_delta_shim_callbacks_t *shim_callbacks =
- svn_delta_shim_callbacks_default(pool);
/* Build the main commit editor's baton. */
cc = apr_pcalloc(pool, sizeof(*cc));
@@ -1622,7 +1620,7 @@ svn_error_t * svn_ra_neon__get_commit_ed
*edit_baton = cc;
SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
- shim_callbacks, pool, pool));
+ ras->shim_callbacks, pool, pool));
return SVN_NO_ERROR;
}
Modified: subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra_neon/log.c
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra_neon/log.c?rev=1297604&r1=1297603&r2=1297604&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra_neon/log.c (original)
+++ subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra_neon/log.c Tue Mar 6 17:50:23 2012
@@ -166,7 +166,7 @@ log_start_element(int *elem, void *baton
lb->want_cdata = lb->cdata;
svn_stringbuf_setempty(lb->cdata);
lb->cdata_encoding = NULL;
-
+
/* Some tags might contain encoded CDATA. */
if ((elm->id == ELEM_comment) ||
(elm->id == ELEM_creator_displayname) ||
@@ -280,7 +280,7 @@ maybe_decode_log_cdata(const svn_string_
return SVN_NO_ERROR;
}
-
+
/*
* This implements the `svn_ra_neon__xml_endelm_cb' prototype.
Modified: subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra_neon/merge.c
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra_neon/merge.c?rev=1297604&r1=1297603&r2=1297604&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra_neon/merge.c (original)
+++ subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra_neon/merge.c Tue Mar 6 17:50:23 2012
@@ -576,29 +576,22 @@ svn_error_t * svn_ra_neon__assemble_lock
apr_hash_t *lock_tokens,
apr_pool_t *pool)
{
- apr_hash_index_t *hi;
- apr_size_t buf_size;
- const char *closing_tag = "</S:lock-token-list>";
- apr_size_t closing_tag_size = strlen(closing_tag);
- apr_pool_t *tmppool = svn_pool_create(pool);
- apr_hash_t *xml_locks = apr_hash_make(tmppool);
- svn_stringbuf_t *lockbuf = svn_stringbuf_create
- ("<S:lock-token-list xmlns:S=\"" SVN_XML_NAMESPACE "\">" DEBUG_CR, pool);
-
- buf_size = lockbuf->len;
-
+#define SVN_LOCK_TOKEN_LIST \
+ "<S:lock-token-list xmlns:S=\"" SVN_XML_NAMESPACE "\">" DEBUG_CR
#define SVN_LOCK "<S:lock>" DEBUG_CR
-#define SVN_LOCK_LEN sizeof(SVN_LOCK)-1
#define SVN_LOCK_CLOSE "</S:lock>" DEBUG_CR
-#define SVN_LOCK_CLOSE_LEN sizeof(SVN_LOCK_CLOSE)-1
#define SVN_LOCK_PATH "<S:lock-path>"
-#define SVN_LOCK_PATH_LEN sizeof(SVN_LOCK_PATH)-1
#define SVN_LOCK_PATH_CLOSE "</S:lock-path>" DEBUG_CR
-#define SVN_LOCK_PATH_CLOSE_LEN sizeof(SVN_LOCK_CLOSE)-1
#define SVN_LOCK_TOKEN "<S:lock-token>"
-#define SVN_LOCK_TOKEN_LEN sizeof(SVN_LOCK_TOKEN)-1
#define SVN_LOCK_TOKEN_CLOSE "</S:lock-token>" DEBUG_CR
-#define SVN_LOCK_TOKEN_CLOSE_LEN sizeof(SVN_LOCK_TOKEN_CLOSE)-1
+#define SVN_LOCK_TOKEN_LIST_CLOSE "</S:lock-token-list>"
+#define SVN_LEN(str) (sizeof(str) - 1)
+
+ apr_hash_index_t *hi;
+ apr_pool_t *tmppool = svn_pool_create(pool);
+ apr_hash_t *xml_locks = apr_hash_make(tmppool);
+ svn_stringbuf_t *lockbuf = svn_stringbuf_create(SVN_LOCK_TOKEN_LIST, pool);
+ apr_size_t buf_size = lockbuf->len;
/* First, figure out how much string data we're talking about,
and allocate a stringbuf big enough to hold it all... we *never*
@@ -622,17 +615,17 @@ svn_error_t * svn_ra_neon__assemble_lock
apr_hash_set(xml_locks, lock_path_xml->data, lock_path_xml->len, val);
/* Now, on with the stringbuf calculations. */
- buf_size += SVN_LOCK_LEN;
- buf_size += SVN_LOCK_PATH_LEN;
+ buf_size += SVN_LEN(SVN_LOCK);
+ buf_size += SVN_LEN(SVN_LOCK_PATH);
buf_size += lock_path_xml->len;
- buf_size += SVN_LOCK_PATH_CLOSE_LEN;
- buf_size += SVN_LOCK_TOKEN_LEN;
+ buf_size += SVN_LEN(SVN_LOCK_PATH_CLOSE);
+ buf_size += SVN_LEN(SVN_LOCK_TOKEN);
buf_size += strlen(val);
- buf_size += SVN_LOCK_TOKEN_CLOSE_LEN;
- buf_size += SVN_LOCK_CLOSE_LEN;
+ buf_size += SVN_LEN(SVN_LOCK_TOKEN_CLOSE);
+ buf_size += SVN_LEN(SVN_LOCK_CLOSE);
}
- buf_size += closing_tag_size;
+ buf_size += SVN_LEN(SVN_LOCK_TOKEN_LIST_CLOSE);
svn_stringbuf_ensure(lockbuf, buf_size + 1);
@@ -659,25 +652,25 @@ svn_error_t * svn_ra_neon__assemble_lock
svn_stringbuf_appendcstr(lockbuf, SVN_LOCK_CLOSE);
}
- svn_stringbuf_appendcstr(lockbuf, closing_tag);
+ svn_stringbuf_appendcstr(lockbuf, SVN_LOCK_TOKEN_LIST_CLOSE);
+
+ /* Check our size calculation was correct */
+ SVN_ERR_ASSERT(lockbuf->len == buf_size);
+ *body = lockbuf;
+
+ svn_pool_destroy(tmppool);
+ return SVN_NO_ERROR;
+
+#undef SVN_LOCK_TOKEN_LIST
#undef SVN_LOCK
-#undef SVN_LOCK_LEN
#undef SVN_LOCK_CLOSE
-#undef SVN_LOCK_CLOSE_LEN
#undef SVN_LOCK_PATH
-#undef SVN_LOCK_PATH_LEN
#undef SVN_LOCK_PATH_CLOSE
-#undef SVN_LOCK_PATH_CLOSE_LEN
#undef SVN_LOCK_TOKEN
-#undef SVN_LOCK_TOKEN_LEN
#undef SVN_LOCK_TOKEN_CLOSE
-#undef SVN_LOCK_TOKEN_CLOSE_LEN
-
- *body = lockbuf;
-
- svn_pool_destroy(tmppool);
- return SVN_NO_ERROR;
+#undef SVN_LOCK_TOKEN_LIST_CLOSE
+#undef SVN_LEN
}
Modified: subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra_neon/ra_neon.h
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra_neon/ra_neon.h?rev=1297604&r1=1297603&r2=1297604&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra_neon/ra_neon.h (original)
+++ subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra_neon/ra_neon.h Tue Mar 6 17:50:23 2012
@@ -131,6 +131,9 @@ typedef struct svn_ra_neon__session_t {
deadprop-count property.*/
svn_tristate_t supports_deadprop_count;
+ /* Ev2 shim callbacks. */
+ svn_delta_shim_callbacks_t *shim_callbacks;
+
/*** HTTP v2 protocol stuff. ***
*
* We assume that if mod_dav_svn sends one of the special v2 OPTIONs
@@ -1181,6 +1184,10 @@ svn_ra_neon__get_deadprop_count_support(
const char *final_url,
apr_pool_t *pool);
+svn_error_t *
+svn_ra_neon__register_editor_shim_callbacks(svn_ra_session_t *session,
+ svn_delta_shim_callbacks_t *callbacks);
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
Modified: subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra_neon/session.c
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra_neon/session.c?rev=1297604&r1=1297603&r2=1297604&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra_neon/session.c (original)
+++ subversion/branches/reintegrate-keep-alive/subversion/libsvn_ra_neon/session.c Tue Mar 6 17:50:23 2012
@@ -1229,7 +1229,8 @@ static const svn_ra__vtable_t neon_vtabl
svn_ra_neon__replay,
svn_ra_neon__has_capability,
svn_ra_neon__replay_range,
- svn_ra_neon__get_deleted_rev
+ svn_ra_neon__get_deleted_rev,
+ svn_ra_neon__register_editor_shim_callbacks
};
svn_error_t *