You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by hw...@apache.org on 2012/01/13 22:40:38 UTC
svn commit: r1231318 [11/35] - in /subversion/branches/revprop-packing: ./
build/ build/ac-macros/ build/generator/ build/generator/templates/
build/win32/ contrib/client-side/emacs/ contrib/server-side/mod_dontdothat/
notes/ notes/http-and-webdav/ not...
Modified: subversion/branches/revprop-packing/subversion/libsvn_delta/compat.c
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/libsvn_delta/compat.c?rev=1231318&r1=1231317&r2=1231318&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/libsvn_delta/compat.c (original)
+++ subversion/branches/revprop-packing/subversion/libsvn_delta/compat.c Fri Jan 13 21:40:26 2012
@@ -21,11 +21,15 @@
* ====================================================================
*/
-#include <apr_pools.h>
-
#include "svn_types.h"
#include "svn_error.h"
#include "svn_delta.h"
+#include "svn_sorts.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_hash.h"
+#include "svn_props.h"
+#include "svn_pools.h"
struct file_rev_handler_wrapper_baton {
@@ -67,7 +71,7 @@ svn_compat_wrap_file_rev_handler(svn_fil
void *handler_baton,
apr_pool_t *pool)
{
- struct file_rev_handler_wrapper_baton *fwb = apr_palloc(pool, sizeof(*fwb));
+ struct file_rev_handler_wrapper_baton *fwb = apr_pcalloc(pool, sizeof(*fwb));
/* Set the user provided old format callback in the baton. */
fwb->baton = handler_baton;
@@ -76,3 +80,1664 @@ svn_compat_wrap_file_rev_handler(svn_fil
*handler2_baton = fwb;
*handler2 = file_rev_handler_wrapper;
}
+
+
+/* The following code maps the calls to a traditional delta editor to an
+ * Editorv2 editor. It does this by keeping track of a lot of state, and
+ * then communicating that state to Ev2 upon closure of the file or dir (or
+ * edit). Note that Ev2 calls add_symlink() and set_target() are not present
+ * in the delta editor paradigm, so we never call them.
+ *
+ * The general idea here is that we have to see *all* the actions on a node's
+ * parent before we can process that node, which means we need to buffer a
+ * large amount of information in the dir batons, and then process it in the
+ * close_directory() handler. */
+
+typedef svn_error_t *(*start_edit_func_t)(
+ void *baton,
+ svn_revnum_t base_revision);
+
+typedef svn_error_t *(*target_revision_func_t)(
+ void *baton,
+ svn_revnum_t target_revision,
+ apr_pool_t *scratch_pool);
+
+/* svn_editor__See insert_shims() for more information. */
+struct extra_baton
+{
+ start_edit_func_t start_edit;
+ target_revision_func_t target_revision;
+ void *baton;
+};
+
+struct ev2_edit_baton
+{
+ svn_editor_t *editor;
+ apr_hash_t *paths;
+ apr_pool_t *edit_pool;
+ struct extra_baton *exb;
+
+ svn_boolean_t *found_abs_paths; /* Did we strip an incoming '/' from the
+ paths? */
+
+ svn_delta_fetch_props_func_t fetch_props_func;
+ void *fetch_props_baton;
+
+ svn_delta_fetch_base_func_t fetch_base_func;
+ void *fetch_base_baton;
+};
+
+struct ev2_dir_baton
+{
+ struct ev2_edit_baton *eb;
+ const char *path;
+ svn_revnum_t base_revision;
+
+ const char *copyfrom_path;
+ svn_revnum_t copyfrom_rev;
+};
+
+struct ev2_file_baton
+{
+ struct ev2_edit_baton *eb;
+ const char *path;
+ svn_revnum_t base_revision;
+ const char *delta_base;
+};
+
+enum action_code_t
+{
+ ACTION_MOVE,
+ ACTION_MKDIR,
+ ACTION_COPY,
+ ACTION_PROPSET,
+ ACTION_PUT,
+ ACTION_ADD,
+ ACTION_DELETE,
+ ACTION_ADD_ABSENT,
+ ACTION_SET_TEXT
+};
+
+struct path_action
+{
+ enum action_code_t action;
+ void *args;
+};
+
+struct prop_args
+{
+ const char *name;
+ svn_revnum_t base_revision;
+ const svn_string_t *value;
+};
+
+struct copy_args
+{
+ const char *copyfrom_path;
+ svn_revnum_t copyfrom_rev;
+};
+
+struct path_checksum_args
+{
+ const char *path;
+ svn_revnum_t base_revision;
+ svn_checksum_t *checksum;
+};
+
+static svn_error_t *
+add_action(struct ev2_edit_baton *eb,
+ const char *path,
+ enum action_code_t action,
+ void *args)
+{
+ struct path_action *p_action;
+ apr_array_header_t *action_list = apr_hash_get(eb->paths, path,
+ APR_HASH_KEY_STRING);
+
+ p_action = apr_palloc(eb->edit_pool, sizeof(*p_action));
+ p_action->action = action;
+ p_action->args = args;
+
+ if (action_list == NULL)
+ {
+ action_list = apr_array_make(eb->edit_pool, 1,
+ sizeof(struct path_action *));
+ apr_hash_set(eb->paths, apr_pstrdup(eb->edit_pool, path),
+ APR_HASH_KEY_STRING, action_list);
+ }
+
+ APR_ARRAY_PUSH(action_list, struct path_action *) = p_action;
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+process_actions(void *edit_baton,
+ const char *path,
+ apr_array_header_t *actions,
+ apr_pool_t *scratch_pool)
+{
+ struct ev2_edit_baton *eb = edit_baton;
+ apr_hash_t *props = NULL;
+ svn_boolean_t need_add = FALSE;
+ svn_boolean_t need_delete = FALSE;
+ svn_boolean_t need_copy = FALSE;
+ const char *copyfrom_path;
+ svn_revnum_t copyfrom_rev;
+ apr_array_header_t *children;
+ svn_stream_t *contents = NULL;
+ svn_checksum_t *checksum = NULL;
+ svn_revnum_t delete_revnum = SVN_INVALID_REVNUM;
+ svn_revnum_t props_base_revision = SVN_INVALID_REVNUM;
+ svn_revnum_t text_base_revision = SVN_INVALID_REVNUM;
+ svn_kind_t kind;
+ int i;
+
+ if (*path == '/')
+ {
+ path++;
+ *eb->found_abs_paths = TRUE;
+ }
+
+ /* Go through all of our actions, populating various datastructures
+ * dependent on them. */
+ for (i = 0; i < actions->nelts; i++)
+ {
+ const struct path_action *action = APR_ARRAY_IDX(actions, i,
+ struct path_action *);
+
+ switch (action->action)
+ {
+ case ACTION_PROPSET:
+ {
+ const struct prop_args *p_args = action->args;
+
+ if (!SVN_IS_VALID_REVNUM(props_base_revision))
+ props_base_revision = p_args->base_revision;
+ else
+ SVN_ERR_ASSERT(p_args->base_revision == props_base_revision);
+
+ if (!props)
+ {
+ /* Fetch the original props. We can then apply each of
+ the modifications to it. */
+ SVN_ERR(eb->fetch_props_func(&props,
+ eb->fetch_props_baton,
+ path, props_base_revision,
+ scratch_pool, scratch_pool));
+ }
+
+ /* Note that p_args->value may be NULL. */
+ apr_hash_set(props, p_args->name, APR_HASH_KEY_STRING,
+ p_args->value);
+ break;
+ }
+
+ case ACTION_DELETE:
+ {
+ delete_revnum = *((svn_revnum_t *) action->args);
+ need_delete = TRUE;
+ break;
+ }
+
+ case ACTION_ADD:
+ {
+ kind = *((svn_kind_t *) action->args);
+ need_add = TRUE;
+
+ if (kind == svn_kind_dir)
+ {
+ children = apr_array_make(scratch_pool, 1,
+ sizeof(const char *));
+ }
+ else
+ {
+ /* The default is an empty file. */
+ contents = svn_stream_empty(scratch_pool);
+ checksum = svn_checksum_empty_checksum(svn_checksum_sha1,
+ scratch_pool);
+ }
+ break;
+ }
+
+ case ACTION_SET_TEXT:
+ {
+ struct path_checksum_args *pca = action->args;
+
+ SVN_ERR(svn_stream_open_readonly(&contents, pca->path,
+ scratch_pool, scratch_pool));
+ checksum = pca->checksum;
+
+ if (!SVN_IS_VALID_REVNUM(text_base_revision))
+ text_base_revision = pca->base_revision;
+ else
+ SVN_ERR_ASSERT(pca->base_revision == text_base_revision);
+
+ break;
+ }
+
+ case ACTION_COPY:
+ {
+ struct copy_args *c_args = action->args;
+
+ copyfrom_path = c_args->copyfrom_path;
+ copyfrom_rev = c_args->copyfrom_rev;
+ need_copy = TRUE;
+ break;
+ }
+
+ case ACTION_ADD_ABSENT:
+ {
+ kind = *((svn_kind_t *) action->args);
+ SVN_ERR(svn_editor_add_absent(eb->editor, path, kind,
+ SVN_INVALID_REVNUM));
+ break;
+ }
+
+ default:
+ SVN_ERR_MALFUNCTION();
+ }
+ }
+
+ /* We've now got a wholistic view of what has happened to this node,
+ * so we can call our own editor APIs on it. */
+
+ if (need_delete && !need_add && !need_copy)
+ {
+ /* If we're only doing a delete, do it here. */
+ SVN_ERR(svn_editor_delete(eb->editor, path, delete_revnum));
+ }
+
+ if (need_add)
+ {
+ if (kind == svn_kind_dir)
+ {
+ SVN_ERR(svn_editor_add_directory(eb->editor, path, children,
+ props, delete_revnum));
+ }
+ else
+ {
+ SVN_ERR(svn_editor_add_file(eb->editor, path, checksum, contents,
+ props, delete_revnum));
+ }
+
+ return SVN_NO_ERROR;
+ }
+
+ if (need_copy)
+ {
+ SVN_ERR(svn_editor_copy(eb->editor, copyfrom_path, copyfrom_rev, path,
+ delete_revnum));
+ }
+
+ if (props)
+ {
+ /* We fetched and modified the props in some way. Apply 'em now that
+ we have the new set. */
+ SVN_ERR(svn_editor_set_props(eb->editor, path, props_base_revision,
+ props, contents == NULL));
+ }
+
+ if (contents)
+ {
+ /* If we have an content for this node, set it now. */
+ SVN_ERR(svn_editor_set_text(eb->editor, path, text_base_revision,
+ checksum, contents));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+run_ev2_actions(void *edit_baton,
+ apr_pool_t *scratch_pool)
+{
+ struct ev2_edit_baton *eb = edit_baton;
+ apr_array_header_t *sorted_hash;
+ apr_pool_t *iterpool;
+ int i;
+
+ /* Sort the paths touched by this edit.
+ * Ev2 doesn't really have any particular need for depth-first-ness, but
+ * we want to ensure all parent directories are handled before children in
+ * the case of adds (which does introduce an element of depth-first-ness). */
+ sorted_hash = svn_sort__hash(eb->paths, svn_sort_compare_items_as_paths,
+ scratch_pool);
+
+ iterpool = svn_pool_create(scratch_pool);
+ for (i = 0; i < sorted_hash->nelts; i++)
+ {
+ svn_sort__item_t *item = &APR_ARRAY_IDX(sorted_hash, i, svn_sort__item_t);
+ apr_array_header_t *actions = item->value;
+ const char *path = item->key;
+
+ svn_pool_clear(iterpool);
+ SVN_ERR(process_actions(edit_baton, path, actions, iterpool));
+
+ /* Remove this item from the hash. */
+ apr_hash_set(eb->paths, path, APR_HASH_KEY_STRING, NULL);
+ }
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+ev2_set_target_revision(void *edit_baton,
+ svn_revnum_t target_revision,
+ apr_pool_t *scratch_pool)
+{
+ struct ev2_edit_baton *eb = edit_baton;
+
+ if (eb->exb->target_revision)
+ SVN_ERR(eb->exb->target_revision(eb->exb->baton, target_revision,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+ev2_open_root(void *edit_baton,
+ svn_revnum_t base_revision,
+ apr_pool_t *result_pool,
+ void **root_baton)
+{
+ struct ev2_dir_baton *db = apr_pcalloc(result_pool, sizeof(*db));
+ struct ev2_edit_baton *eb = edit_baton;
+
+ db->eb = eb;
+ db->path = "";
+ db->base_revision = base_revision;
+
+ *root_baton = db;
+
+ if (eb->exb->start_edit)
+ SVN_ERR(eb->exb->start_edit(eb->exb->baton, base_revision));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+ev2_delete_entry(const char *path,
+ svn_revnum_t revision,
+ void *parent_baton,
+ apr_pool_t *scratch_pool)
+{
+ struct ev2_dir_baton *pb = parent_baton;
+ svn_revnum_t *revnum = apr_palloc(pb->eb->edit_pool, sizeof(*revnum));
+
+ *revnum = revision;
+ SVN_ERR(add_action(pb->eb, path, ACTION_DELETE, revnum));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+ev2_add_directory(const char *path,
+ void *parent_baton,
+ const char *copyfrom_path,
+ svn_revnum_t copyfrom_revision,
+ apr_pool_t *result_pool,
+ void **child_baton)
+{
+ struct ev2_dir_baton *pb = parent_baton;
+ struct ev2_dir_baton *cb = apr_pcalloc(result_pool, sizeof(*cb));
+
+ cb->eb = pb->eb;
+ cb->path = apr_pstrdup(result_pool, path);
+ cb->base_revision = pb->base_revision;
+ *child_baton = cb;
+
+ if (!copyfrom_path)
+ {
+ /* A simple add. */
+ svn_kind_t *kind = apr_palloc(pb->eb->edit_pool, sizeof(*kind));
+
+ *kind = svn_kind_dir;
+ SVN_ERR(add_action(pb->eb, path, ACTION_ADD, kind));
+
+ if (pb->copyfrom_path)
+ {
+ const char *name = svn_relpath_basename(path, result_pool);
+ cb->copyfrom_path = apr_pstrcat(result_pool, pb->copyfrom_path,
+ "/", name, NULL);
+ cb->copyfrom_rev = pb->copyfrom_rev;
+ }
+ }
+ else
+ {
+ /* A copy */
+ struct copy_args *args = apr_palloc(pb->eb->edit_pool, sizeof(*args));
+
+ args->copyfrom_path = apr_pstrdup(pb->eb->edit_pool, copyfrom_path);
+ args->copyfrom_rev = copyfrom_revision;
+ SVN_ERR(add_action(pb->eb, path, ACTION_COPY, args));
+
+ cb->copyfrom_path = args->copyfrom_path;
+ cb->copyfrom_rev = args->copyfrom_rev;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+ev2_open_directory(const char *path,
+ void *parent_baton,
+ svn_revnum_t base_revision,
+ apr_pool_t *result_pool,
+ void **child_baton)
+{
+ struct ev2_dir_baton *pb = parent_baton;
+ struct ev2_dir_baton *db = apr_pcalloc(result_pool, sizeof(*db));
+
+ db->eb = pb->eb;
+ db->path = apr_pstrdup(result_pool, path);
+ db->base_revision = base_revision;
+
+ if (pb->copyfrom_path)
+ {
+ /* We are inside a copy. */
+ const char *name = svn_relpath_basename(path, result_pool);
+
+ db->copyfrom_path = apr_pstrcat(result_pool, pb->copyfrom_path,
+ "/", name, NULL);
+ db->copyfrom_rev = pb->copyfrom_rev;
+ }
+
+ *child_baton = db;
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+ev2_change_dir_prop(void *dir_baton,
+ const char *name,
+ const svn_string_t *value,
+ apr_pool_t *scratch_pool)
+{
+ struct ev2_dir_baton *db = dir_baton;
+ struct prop_args *p_args = apr_palloc(db->eb->edit_pool, sizeof(*p_args));
+
+ p_args->name = apr_pstrdup(db->eb->edit_pool, name);
+ p_args->value = value ? svn_string_dup(value, db->eb->edit_pool) : NULL;
+ p_args->base_revision = db->base_revision;
+
+ SVN_ERR(add_action(db->eb, db->path, ACTION_PROPSET, p_args));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+ev2_close_directory(void *dir_baton,
+ apr_pool_t *scratch_pool)
+{
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+ev2_absent_directory(const char *path,
+ void *parent_baton,
+ apr_pool_t *scratch_pool)
+{
+ struct ev2_dir_baton *pb = parent_baton;
+ svn_kind_t *kind = apr_palloc(pb->eb->edit_pool, sizeof(*kind));
+
+ *kind = svn_kind_dir;
+ SVN_ERR(add_action(pb->eb, path, ACTION_ADD_ABSENT, kind));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+ev2_add_file(const char *path,
+ void *parent_baton,
+ const char *copyfrom_path,
+ svn_revnum_t copyfrom_revision,
+ apr_pool_t *result_pool,
+ void **file_baton)
+{
+ struct ev2_file_baton *fb = apr_pcalloc(result_pool, sizeof(*fb));
+ struct ev2_dir_baton *pb = parent_baton;
+
+ fb->eb = pb->eb;
+ fb->path = apr_pstrdup(result_pool, path);
+ fb->base_revision = pb->base_revision;
+ *file_baton = fb;
+
+ if (!copyfrom_path)
+ {
+ /* A simple add. */
+ svn_kind_t *kind = apr_palloc(pb->eb->edit_pool, sizeof(*kind));
+
+ /* Don't bother fetching the base, as in an add we don't have a base. */
+ fb->delta_base = NULL;
+
+ *kind = svn_kind_file;
+ SVN_ERR(add_action(pb->eb, path, ACTION_ADD, kind));
+ }
+ else
+ {
+ /* A copy */
+ struct copy_args *args = apr_palloc(pb->eb->edit_pool, sizeof(*args));
+
+ SVN_ERR(fb->eb->fetch_base_func(&fb->delta_base,
+ fb->eb->fetch_base_baton,
+ copyfrom_path, copyfrom_revision,
+ result_pool, result_pool));
+
+ args->copyfrom_path = apr_pstrdup(pb->eb->edit_pool, copyfrom_path);
+ args->copyfrom_rev = copyfrom_revision;
+ SVN_ERR(add_action(pb->eb, path, ACTION_COPY, args));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+ev2_open_file(const char *path,
+ void *parent_baton,
+ svn_revnum_t base_revision,
+ apr_pool_t *result_pool,
+ void **file_baton)
+{
+ struct ev2_file_baton *fb = apr_pcalloc(result_pool, sizeof(*fb));
+ struct ev2_dir_baton *pb = parent_baton;
+
+ fb->eb = pb->eb;
+ fb->path = apr_pstrdup(result_pool, path);
+ fb->base_revision = base_revision;
+
+ if (pb->copyfrom_path)
+ {
+ /* We're in a copied directory, so the delta base is going to be
+ based up on the copy source. */
+ const char *name = svn_relpath_basename(path, result_pool);
+ const char *copyfrom_path = apr_pstrcat(result_pool, pb->copyfrom_path,
+ "/", name, NULL);
+
+ SVN_ERR(fb->eb->fetch_base_func(&fb->delta_base,
+ fb->eb->fetch_base_baton,
+ copyfrom_path, pb->copyfrom_rev,
+ result_pool, result_pool));
+ }
+ else
+ {
+ SVN_ERR(fb->eb->fetch_base_func(&fb->delta_base,
+ fb->eb->fetch_base_baton,
+ path, base_revision,
+ result_pool, result_pool));
+ }
+
+ *file_baton = fb;
+ return SVN_NO_ERROR;
+}
+
+struct handler_baton
+{
+ svn_txdelta_window_handler_t apply_handler;
+ void *apply_baton;
+
+ apr_pool_t *pool;
+};
+
+static svn_error_t *
+window_handler(svn_txdelta_window_t *window, void *baton)
+{
+ struct handler_baton *hb = baton;
+ svn_error_t *err;
+
+ err = hb->apply_handler(window, hb->apply_baton);
+ if (window != NULL && !err)
+ return SVN_NO_ERROR;
+
+ svn_pool_destroy(hb->pool);
+
+ return svn_error_trace(err);
+}
+
+
+static svn_error_t *
+ev2_apply_textdelta(void *file_baton,
+ const char *base_checksum,
+ apr_pool_t *result_pool,
+ svn_txdelta_window_handler_t *handler,
+ void **handler_baton)
+{
+ struct ev2_file_baton *fb = file_baton;
+ apr_pool_t *handler_pool = svn_pool_create(fb->eb->edit_pool);
+ struct handler_baton *hb = apr_pcalloc(handler_pool, sizeof(*hb));
+ svn_stream_t *source;
+ svn_stream_t *target;
+ struct path_checksum_args *pca = apr_pcalloc(fb->eb->edit_pool,
+ sizeof(*pca));
+
+ pca->base_revision = fb->base_revision;
+
+ if (! fb->delta_base)
+ source = svn_stream_empty(handler_pool);
+ else
+ SVN_ERR(svn_stream_open_readonly(&source, fb->delta_base, handler_pool,
+ result_pool));
+
+ SVN_ERR(svn_stream_open_unique(&target, &pca->path, NULL,
+ svn_io_file_del_on_pool_cleanup,
+ fb->eb->edit_pool, result_pool));
+
+ /* Wrap our target with a checksum'ing stream. */
+ target = svn_stream_checksummed2(target, NULL, &pca->checksum,
+ svn_checksum_sha1, TRUE,
+ fb->eb->edit_pool);
+
+ svn_txdelta_apply(source, target,
+ NULL, NULL,
+ handler_pool,
+ &hb->apply_handler, &hb->apply_baton);
+
+ hb->pool = handler_pool;
+
+ *handler_baton = hb;
+ *handler = window_handler;
+
+ SVN_ERR(add_action(fb->eb, fb->path, ACTION_SET_TEXT, pca));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+ev2_change_file_prop(void *file_baton,
+ const char *name,
+ const svn_string_t *value,
+ apr_pool_t *scratch_pool)
+{
+ struct ev2_file_baton *fb = file_baton;
+ struct prop_args *p_args = apr_palloc(fb->eb->edit_pool, sizeof(*p_args));
+
+ p_args->name = apr_pstrdup(fb->eb->edit_pool, name);
+ p_args->value = value ? svn_string_dup(value, fb->eb->edit_pool) : NULL;
+ p_args->base_revision = fb->base_revision;
+
+ SVN_ERR(add_action(fb->eb, fb->path, ACTION_PROPSET, p_args));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+ev2_close_file(void *file_baton,
+ const char *text_checksum,
+ apr_pool_t *scratch_pool)
+{
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+ev2_absent_file(const char *path,
+ void *parent_baton,
+ apr_pool_t *scratch_pool)
+{
+ struct ev2_dir_baton *pb = parent_baton;
+ svn_kind_t *kind = apr_palloc(pb->eb->edit_pool, sizeof(*kind));
+
+ *kind = svn_kind_file;
+ SVN_ERR(add_action(pb->eb, path, ACTION_ADD_ABSENT, kind));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+ev2_close_edit(void *edit_baton,
+ apr_pool_t *scratch_pool)
+{
+ struct ev2_edit_baton *eb = edit_baton;
+
+ SVN_ERR(run_ev2_actions(edit_baton, scratch_pool));
+ return svn_error_trace(svn_editor_complete(eb->editor));
+}
+
+static svn_error_t *
+ev2_abort_edit(void *edit_baton,
+ apr_pool_t *scratch_pool)
+{
+ struct ev2_edit_baton *eb = edit_baton;
+
+ SVN_ERR(run_ev2_actions(edit_baton, scratch_pool));
+ return svn_error_trace(svn_editor_abort(eb->editor));
+}
+
+static svn_error_t *
+delta_from_editor(const svn_delta_editor_t **deditor,
+ void **dedit_baton,
+ svn_editor_t *editor,
+ svn_boolean_t *found_abs_paths,
+ svn_delta_fetch_props_func_t fetch_props_func,
+ void *fetch_props_baton,
+ svn_delta_fetch_base_func_t fetch_base_func,
+ void *fetch_base_baton,
+ struct extra_baton *exb,
+ apr_pool_t *pool)
+{
+ /* Static 'cause we don't want it to be on the stack. */
+ static svn_delta_editor_t delta_editor = {
+ ev2_set_target_revision,
+ ev2_open_root,
+ ev2_delete_entry,
+ ev2_add_directory,
+ ev2_open_directory,
+ ev2_change_dir_prop,
+ ev2_close_directory,
+ ev2_absent_directory,
+ ev2_add_file,
+ ev2_open_file,
+ ev2_apply_textdelta,
+ ev2_change_file_prop,
+ ev2_close_file,
+ ev2_absent_file,
+ ev2_close_edit,
+ ev2_abort_edit
+ };
+ struct ev2_edit_baton *eb = apr_pcalloc(pool, sizeof(*eb));
+
+ eb->editor = editor;
+ eb->paths = apr_hash_make(pool);
+ eb->edit_pool = pool;
+ eb->found_abs_paths = found_abs_paths;
+ *eb->found_abs_paths = FALSE;
+ eb->exb = exb;
+
+ eb->fetch_props_func = fetch_props_func;
+ eb->fetch_props_baton = fetch_props_baton;
+
+ eb->fetch_base_func = fetch_base_func;
+ eb->fetch_base_baton = fetch_base_baton;
+
+ *dedit_baton = eb;
+ *deditor = &delta_editor;
+
+ return SVN_NO_ERROR;
+}
+
+
+
+
+
+struct operation {
+ enum {
+ OP_OPEN,
+ OP_DELETE,
+ OP_ADD,
+ OP_REPLACE,
+ OP_ADD_ABSENT,
+ OP_PROPSET /* only for files for which no other operation is
+ occuring; directories are OP_OPEN with non-empty
+ props */
+ } operation;
+
+ const char *path;
+ svn_kind_t kind; /* to copy, mkdir, put or set revprops */
+ svn_revnum_t base_revision; /* When committing, the base revision */
+ svn_revnum_t copyfrom_revision; /* to copy, valid for add and replace */
+ const char *copyfrom_url; /* to copy, valid for add and replace */
+ const char *src_file; /* for put, the source file for contents */
+ apr_hash_t *children; /* const char *path -> struct operation * */
+ apr_hash_t *prop_mods; /* const char *prop_name ->
+ const svn_string_t *prop_value */
+ apr_array_header_t *prop_dels; /* const char *prop_name deletions */
+ void *baton; /* as returned by the commit editor */
+};
+
+struct editor_baton
+{
+ const svn_delta_editor_t *deditor;
+ void *dedit_baton;
+
+ svn_delta_fetch_kind_func_t fetch_kind_func;
+ void *fetch_kind_baton;
+
+ svn_delta_fetch_props_func_t fetch_props_func;
+ void *fetch_props_baton;
+
+ struct operation root;
+ svn_boolean_t root_opened;
+ svn_boolean_t *make_abs_paths;
+
+ apr_hash_t *paths;
+ apr_pool_t *edit_pool;
+};
+
+
+/* Find the operation associated with PATH, which is a single-path
+ component representing a child of the path represented by
+ OPERATION. If no such child operation exists, create a new one of
+ type OP_OPEN. */
+static struct operation *
+get_operation(const char *path,
+ struct operation *operation,
+ svn_revnum_t base_revision,
+ apr_pool_t *result_pool)
+{
+ struct operation *child = apr_hash_get(operation->children, path,
+ APR_HASH_KEY_STRING);
+ if (! child)
+ {
+ child = apr_pcalloc(result_pool, sizeof(*child));
+ child->children = apr_hash_make(result_pool);
+ child->path = apr_pstrdup(result_pool, path);
+ child->operation = OP_OPEN;
+ child->copyfrom_revision = SVN_INVALID_REVNUM;
+ child->kind = svn_kind_dir;
+ child->base_revision = base_revision;
+ child->prop_mods = apr_hash_make(result_pool);
+ child->prop_dels = apr_array_make(result_pool, 1, sizeof(const char *));
+ apr_hash_set(operation->children, apr_pstrdup(result_pool, path),
+ APR_HASH_KEY_STRING, child);
+ }
+ return child;
+}
+
+/* Add PATH to the operations tree rooted at OPERATION, creating any
+ intermediate nodes that are required. Here's what's expected for
+ each action type:
+
+ ACTION URL REV SRC-FILE PROPNAME
+ ------------ ----- ------- -------- --------
+ ACTION_MKDIR NULL invalid NULL NULL
+ ACTION_COPY valid valid NULL NULL
+ ACTION_PUT NULL invalid valid NULL
+ ACTION_DELETE NULL invalid NULL NULL
+ ACTION_PROPSET valid invalid NULL valid
+
+ Node type information is obtained for any copy source (to determine
+ whether to create a file or directory) and for any deleted path (to
+ ensure it exists since svn_delta_editor_t->delete_entry doesn't
+ return an error on non-existent nodes). */
+static svn_error_t *
+build(struct editor_baton *eb,
+ enum action_code_t action,
+ const char *relpath,
+ svn_kind_t kind,
+ const char *url,
+ svn_revnum_t rev,
+ apr_hash_t *props,
+ const char *src_file,
+ svn_revnum_t head,
+ apr_pool_t *scratch_pool)
+{
+ apr_array_header_t *path_bits = svn_path_decompose(relpath, scratch_pool);
+ const char *path_so_far = "";
+ struct operation *operation = &eb->root;
+ int i;
+
+ /* We should only see PROPS when action is ACTION_PROPSET. */
+ SVN_ERR_ASSERT((props && action == ACTION_PROPSET)
+ || (!props && action != ACTION_PROPSET) );
+
+ /* Look for any previous operations we've recognized for PATH. If
+ any of PATH's ancestors have not yet been traversed, we'll be
+ creating OP_OPEN operations for them as we walk down PATH's path
+ components. */
+ for (i = 0; i < path_bits->nelts; ++i)
+ {
+ const char *path_bit = APR_ARRAY_IDX(path_bits, i, const char *);
+ path_so_far = svn_relpath_join(path_so_far, path_bit, scratch_pool);
+ operation = get_operation(path_so_far, operation, head, eb->edit_pool);
+ }
+
+ /* Handle property changes. */
+ if (props)
+ {
+ apr_hash_t *current_props;
+ apr_array_header_t *propdiffs;
+
+ if (kind == svn_kind_unknown)
+ SVN_ERR(eb->fetch_kind_func(&operation->kind, eb->fetch_kind_baton,
+ relpath, rev, scratch_pool));
+ else
+ operation->kind = kind;
+
+ SVN_ERR(eb->fetch_props_func(¤t_props, eb->fetch_props_baton,
+ relpath, rev, scratch_pool, scratch_pool));
+
+ SVN_ERR(svn_prop_diffs(&propdiffs, props, current_props, scratch_pool));
+
+ for (i = 0; i < propdiffs->nelts; i++)
+ {
+ /* Note: the array returned by svn_prop_diffs() is an array of
+ actual structures, not pointers to them. */
+ svn_prop_t *prop = &APR_ARRAY_IDX(propdiffs, i, svn_prop_t);
+ if (!prop->value)
+ APR_ARRAY_PUSH(operation->prop_dels, const char *) =
+ apr_pstrdup(eb->edit_pool, prop->name);
+ else
+ apr_hash_set(operation->prop_mods, prop->name, APR_HASH_KEY_STRING,
+ svn_string_dup(prop->value, eb->edit_pool));
+ }
+
+ /* If we're not adding this thing ourselves, check for existence. */
+ if (! ((operation->operation == OP_ADD) ||
+ (operation->operation == OP_REPLACE)))
+ {
+ if ((operation->kind == svn_kind_file)
+ && (operation->operation == OP_OPEN))
+ operation->operation = OP_PROPSET;
+ }
+ if (!operation->copyfrom_revision)
+ operation->copyfrom_revision = rev;
+ return SVN_NO_ERROR;
+ }
+
+ if (action == ACTION_DELETE)
+ operation->operation = OP_DELETE;
+
+ else if (action == ACTION_ADD_ABSENT)
+ operation->operation = OP_ADD_ABSENT;
+
+ /* Handle copy operations (which can be adds or replacements). */
+ else if (action == ACTION_COPY)
+ {
+ operation->operation =
+ operation->operation == OP_DELETE ? OP_REPLACE : OP_ADD;
+
+ if (kind == svn_kind_none || kind == svn_kind_unknown)
+ SVN_ERR(eb->fetch_kind_func(&operation->kind, eb->fetch_kind_baton,
+ url, rev, scratch_pool));
+ else
+ operation->kind = kind;
+ operation->copyfrom_url = url;
+ operation->copyfrom_revision = rev;
+ }
+ /* Handle mkdir operations (which can be adds or replacements). */
+ else if (action == ACTION_MKDIR)
+ {
+ operation->operation =
+ operation->operation == OP_DELETE ? OP_REPLACE : OP_ADD;
+ operation->kind = svn_kind_dir;
+ }
+ /* Handle put operations (which can be adds, replacements, or opens). */
+ else if (action == ACTION_PUT)
+ {
+ if (operation->operation == OP_DELETE)
+ {
+ operation->operation = OP_REPLACE;
+ }
+ else if (operation->operation == OP_OPEN)
+ {
+ if (kind == svn_kind_file)
+ operation->operation = OP_OPEN;
+ else if (kind == svn_kind_none)
+ operation->operation = OP_ADD;
+ else
+ return svn_error_createf(SVN_ERR_BAD_URL, NULL,
+ "'%s' is not a file", relpath);
+ }
+ operation->kind = svn_kind_file;
+ operation->src_file = src_file;
+ }
+ else
+ {
+ /* We shouldn't get here. */
+ SVN_ERR_MALFUNCTION();
+ }
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+ensure_root_opened(struct editor_baton *eb)
+{
+ if (!eb->root_opened)
+ {
+ SVN_ERR(eb->deditor->open_root(eb->dedit_baton, eb->root.base_revision,
+ eb->edit_pool, &eb->root.baton));
+ eb->root_opened = TRUE;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements svn_editor_cb_add_directory_t */
+static svn_error_t *
+add_directory_cb(void *baton,
+ const char *relpath,
+ const apr_array_header_t *children,
+ apr_hash_t *props,
+ svn_revnum_t replaces_rev,
+ apr_pool_t *scratch_pool)
+{
+ struct editor_baton *eb = baton;
+
+ SVN_ERR(ensure_root_opened(eb));
+
+ if (SVN_IS_VALID_REVNUM(replaces_rev))
+ {
+ /* We need to add the delete action. */
+
+ SVN_ERR(build(eb, ACTION_DELETE, relpath, svn_kind_unknown,
+ NULL, SVN_INVALID_REVNUM,
+ NULL, NULL, SVN_INVALID_REVNUM, scratch_pool));
+ }
+
+ SVN_ERR(build(eb, ACTION_MKDIR, relpath, svn_kind_dir,
+ NULL, SVN_INVALID_REVNUM,
+ NULL, NULL, SVN_INVALID_REVNUM, scratch_pool));
+
+ if (props && apr_hash_count(props) > 0)
+ SVN_ERR(build(eb, ACTION_PROPSET, relpath, svn_kind_dir,
+ NULL, SVN_INVALID_REVNUM, props,
+ NULL, SVN_INVALID_REVNUM, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements svn_editor_cb_add_file_t */
+static svn_error_t *
+add_file_cb(void *baton,
+ const char *relpath,
+ const svn_checksum_t *checksum,
+ svn_stream_t *contents,
+ apr_hash_t *props,
+ svn_revnum_t replaces_rev,
+ apr_pool_t *scratch_pool)
+{
+ struct editor_baton *eb = baton;
+ const char *tmp_filename;
+ svn_stream_t *tmp_stream;
+
+ SVN_ERR(ensure_root_opened(eb));
+
+ if (SVN_IS_VALID_REVNUM(replaces_rev))
+ {
+ /* We need to add the delete action. */
+
+ SVN_ERR(build(eb, ACTION_DELETE, relpath, svn_kind_unknown,
+ NULL, SVN_INVALID_REVNUM,
+ NULL, NULL, SVN_INVALID_REVNUM, scratch_pool));
+ }
+
+ /* Spool the contents to a tempfile, and provide that to the driver. */
+ SVN_ERR(svn_stream_open_unique(&tmp_stream, &tmp_filename, NULL,
+ svn_io_file_del_on_pool_cleanup,
+ eb->edit_pool, scratch_pool));
+ SVN_ERR(svn_stream_copy3(svn_stream_disown(contents, scratch_pool),
+ tmp_stream, NULL, NULL, scratch_pool));
+
+ SVN_ERR(build(eb, ACTION_PUT, relpath, svn_kind_none,
+ NULL, SVN_INVALID_REVNUM,
+ NULL, tmp_filename, SVN_INVALID_REVNUM, scratch_pool));
+
+ if (props && apr_hash_count(props) > 0)
+ SVN_ERR(build(eb, ACTION_PROPSET, relpath, svn_kind_file,
+ NULL, SVN_INVALID_REVNUM, props,
+ NULL, SVN_INVALID_REVNUM, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements svn_editor_cb_add_symlink_t */
+static svn_error_t *
+add_symlink_cb(void *baton,
+ const char *relpath,
+ const char *target,
+ apr_hash_t *props,
+ svn_revnum_t replaces_rev,
+ apr_pool_t *scratch_pool)
+{
+ struct editor_baton *eb = baton;
+
+ SVN_ERR(ensure_root_opened(eb));
+
+ if (SVN_IS_VALID_REVNUM(replaces_rev))
+ {
+ /* We need to add the delete action. */
+
+ SVN_ERR(build(eb, ACTION_DELETE, relpath, svn_kind_unknown,
+ NULL, SVN_INVALID_REVNUM,
+ NULL, NULL, SVN_INVALID_REVNUM, scratch_pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements svn_editor_cb_add_absent_t */
+static svn_error_t *
+add_absent_cb(void *baton,
+ const char *relpath,
+ svn_kind_t kind,
+ svn_revnum_t replaces_rev,
+ apr_pool_t *scratch_pool)
+{
+ struct editor_baton *eb = baton;
+
+ SVN_ERR(ensure_root_opened(eb));
+
+ SVN_ERR(build(eb, ACTION_ADD_ABSENT, relpath, kind,
+ NULL, SVN_INVALID_REVNUM,
+ NULL, NULL, SVN_INVALID_REVNUM, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements svn_editor_cb_set_props_t */
+static svn_error_t *
+set_props_cb(void *baton,
+ const char *relpath,
+ svn_revnum_t revision,
+ apr_hash_t *props,
+ svn_boolean_t complete,
+ apr_pool_t *scratch_pool)
+{
+ struct editor_baton *eb = baton;
+
+ SVN_ERR(ensure_root_opened(eb));
+
+ SVN_ERR(build(eb, ACTION_PROPSET, relpath, svn_kind_unknown,
+ NULL, SVN_INVALID_REVNUM,
+ props, NULL, revision, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements svn_editor_cb_set_text_t */
+static svn_error_t *
+set_text_cb(void *baton,
+ const char *relpath,
+ svn_revnum_t revision,
+ const svn_checksum_t *checksum,
+ svn_stream_t *contents,
+ apr_pool_t *scratch_pool)
+{
+ struct editor_baton *eb = baton;
+ const char *tmp_filename;
+ svn_stream_t *tmp_stream;
+
+ SVN_ERR(ensure_root_opened(eb));
+
+ /* Spool the contents to a tempfile, and provide that to the driver. */
+ SVN_ERR(svn_stream_open_unique(&tmp_stream, &tmp_filename, NULL,
+ svn_io_file_del_on_pool_cleanup,
+ eb->edit_pool, scratch_pool));
+ SVN_ERR(svn_stream_copy3(svn_stream_disown(contents, scratch_pool),
+ tmp_stream, NULL, NULL, scratch_pool));
+
+ SVN_ERR(build(eb, ACTION_PUT, relpath, svn_kind_file,
+ NULL, SVN_INVALID_REVNUM,
+ NULL, tmp_filename, revision, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements svn_editor_cb_set_target_t */
+static svn_error_t *
+set_target_cb(void *baton,
+ const char *relpath,
+ svn_revnum_t revision,
+ const char *target,
+ apr_pool_t *scratch_pool)
+{
+ struct editor_baton *eb = baton;
+
+ SVN_ERR(ensure_root_opened(eb));
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements svn_editor_cb_delete_t */
+static svn_error_t *
+delete_cb(void *baton,
+ const char *relpath,
+ svn_revnum_t revision,
+ apr_pool_t *scratch_pool)
+{
+ struct editor_baton *eb = baton;
+
+ SVN_ERR(ensure_root_opened(eb));
+
+ SVN_ERR(build(eb, ACTION_DELETE, relpath, svn_kind_unknown,
+ NULL, SVN_INVALID_REVNUM, NULL, NULL, SVN_INVALID_REVNUM,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements svn_editor_cb_copy_t */
+static svn_error_t *
+copy_cb(void *baton,
+ const char *src_relpath,
+ svn_revnum_t src_revision,
+ const char *dst_relpath,
+ svn_revnum_t replaces_rev,
+ apr_pool_t *scratch_pool)
+{
+ struct editor_baton *eb = baton;
+
+ SVN_ERR(ensure_root_opened(eb));
+
+ if (SVN_IS_VALID_REVNUM(replaces_rev))
+ {
+ /* We need to add the delete action. */
+
+ SVN_ERR(build(eb, ACTION_DELETE, dst_relpath, svn_kind_unknown,
+ NULL, SVN_INVALID_REVNUM,
+ NULL, NULL, SVN_INVALID_REVNUM, scratch_pool));
+ }
+
+ SVN_ERR(build(eb, ACTION_COPY, dst_relpath, svn_kind_unknown,
+ src_relpath, src_revision, NULL, NULL, SVN_INVALID_REVNUM,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements svn_editor_cb_move_t */
+static svn_error_t *
+move_cb(void *baton,
+ const char *src_relpath,
+ svn_revnum_t src_revision,
+ const char *dst_relpath,
+ svn_revnum_t replaces_rev,
+ apr_pool_t *scratch_pool)
+{
+ struct editor_baton *eb = baton;
+
+ SVN_ERR(ensure_root_opened(eb));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+change_props(const svn_delta_editor_t *editor,
+ void *baton,
+ const struct operation *child,
+ apr_pool_t *scratch_pool)
+{
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+
+ if (child->prop_dels)
+ {
+ int i;
+ for (i = 0; i < child->prop_dels->nelts; i++)
+ {
+ const char *prop_name;
+
+ svn_pool_clear(iterpool);
+ prop_name = APR_ARRAY_IDX(child->prop_dels, i, const char *);
+ if (child->kind == svn_kind_dir)
+ SVN_ERR(editor->change_dir_prop(baton, prop_name,
+ NULL, iterpool));
+ else
+ SVN_ERR(editor->change_file_prop(baton, prop_name,
+ NULL, iterpool));
+ }
+ }
+
+ if (apr_hash_count(child->prop_mods))
+ {
+ apr_hash_index_t *hi;
+ for (hi = apr_hash_first(scratch_pool, child->prop_mods);
+ hi; hi = apr_hash_next(hi))
+ {
+ const char *name = svn__apr_hash_index_key(hi);
+ svn_string_t *val = svn__apr_hash_index_val(hi);
+
+ svn_pool_clear(iterpool);
+ if (child->kind == svn_kind_dir)
+ SVN_ERR(editor->change_dir_prop(baton, name, val, iterpool));
+ else
+ SVN_ERR(editor->change_file_prop(baton, name, val, iterpool));
+ }
+ }
+
+ svn_pool_destroy(iterpool);
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+drive_tree(struct operation *op,
+ const struct operation *parent_op,
+ const svn_delta_editor_t *editor,
+ svn_boolean_t make_abs_paths,
+ apr_pool_t *scratch_pool)
+{
+ const char *path = op->path;
+
+ if (path[0] != '/' && make_abs_paths)
+ path = apr_pstrcat(scratch_pool, "/", path, NULL);
+
+ /* Deletes and replacements are simple -- just delete the thing. */
+ if (op->operation == OP_DELETE || op->operation == OP_REPLACE)
+ {
+ SVN_ERR(editor->delete_entry(path, SVN_INVALID_REVNUM,
+ parent_op->baton, scratch_pool));
+ }
+
+ if (op->kind == svn_kind_dir)
+ {
+ /* Open or create our baton. */
+ if (op->operation == OP_OPEN || op->operation == OP_PROPSET)
+ SVN_ERR(editor->open_directory(path, parent_op->baton,
+ parent_op->base_revision,
+ scratch_pool, &op->baton));
+
+ else if (op->operation == OP_ADD || op->operation == OP_REPLACE)
+ SVN_ERR(editor->add_directory(path, parent_op->baton,
+ op->copyfrom_url, op->copyfrom_revision,
+ scratch_pool, &op->baton));
+
+ else if (op->operation == OP_ADD_ABSENT)
+ SVN_ERR(editor->absent_directory(path, parent_op->baton,
+ scratch_pool));
+
+ if (op->baton)
+ {
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ apr_hash_index_t *hi;
+
+ /* Do any prop mods we may have. */
+ SVN_ERR(change_props(editor, op->baton, op, scratch_pool));
+
+ for (hi = apr_hash_first(scratch_pool, op->children);
+ hi; hi = apr_hash_next(hi))
+ {
+ struct operation *child = svn__apr_hash_index_val(hi);
+
+ svn_pool_clear(iterpool);
+ SVN_ERR(drive_tree(child, op, editor, make_abs_paths, iterpool));
+ }
+ svn_pool_destroy(iterpool);
+
+ /* We're done, close the directory. */
+ SVN_ERR(editor->close_directory(op->baton, scratch_pool));
+ }
+ }
+ else
+ {
+ /* This currently treats anything that isn't a directory as a file.
+ I don't know that that's a valid assumption... */
+
+ void *file_baton = NULL;
+
+ /* Open or create our baton. */
+ if (op->operation == OP_OPEN || op->operation == OP_PROPSET)
+ SVN_ERR(editor->open_file(path, parent_op->baton, op->base_revision,
+ scratch_pool, &file_baton));
+
+ else if (op->operation == OP_ADD || op->operation == OP_REPLACE)
+ SVN_ERR(editor->add_file(path, parent_op->baton, op->copyfrom_url,
+ op->copyfrom_revision, scratch_pool,
+ &file_baton));
+
+ else if (op->operation == OP_ADD_ABSENT)
+ SVN_ERR(editor->absent_file(path, parent_op->baton, scratch_pool));
+
+ if (file_baton)
+ {
+ /* Do we need to change text contents? */
+ if (op->src_file)
+ {
+ svn_txdelta_window_handler_t handler;
+ void *handler_baton;
+ svn_stream_t *contents;
+
+ SVN_ERR(editor->apply_textdelta(file_baton, NULL, scratch_pool,
+ &handler, &handler_baton));
+ SVN_ERR(svn_stream_open_readonly(&contents, op->src_file,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_txdelta_send_stream(contents, handler, handler_baton,
+ NULL, scratch_pool));
+ SVN_ERR(svn_stream_close(contents));
+ }
+
+ /* Do any prop mods we may have. */
+ SVN_ERR(change_props(editor, file_baton, op, scratch_pool));
+
+ /* Close the file. */
+ SVN_ERR(editor->close_file(file_baton, NULL, scratch_pool));
+ }
+
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* This is a special case of drive_tree(), meant to handle the root, which
+ doesn't have a parent and should already be open. */
+static svn_error_t *
+drive_root(struct operation *root,
+ const svn_delta_editor_t *editor,
+ svn_boolean_t make_abs_paths,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_index_t *hi;
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+
+ /* Early out: if we haven't opened the root yet (which would usually only
+ be the case in an abort), there isn't much we can do here. */
+ if (!root->baton)
+ return SVN_NO_ERROR;
+
+ /* Do any prop mods we may have. */
+ SVN_ERR(change_props(editor, root->baton, root, scratch_pool));
+
+ /* Now iterate over our children. */
+ for (hi = apr_hash_first(scratch_pool, root->children);
+ hi; hi = apr_hash_next(hi))
+ {
+ struct operation *child = svn__apr_hash_index_val(hi);
+
+ svn_pool_clear(iterpool);
+ SVN_ERR(drive_tree(child, root, editor, make_abs_paths, iterpool));
+ }
+
+ /* We need to close the root directory, but leave it to our caller to call
+ close_ or abort_edit(). */
+ SVN_ERR(editor->close_directory(root->baton, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements svn_editor_cb_complete_t */
+static svn_error_t *
+complete_cb(void *baton,
+ apr_pool_t *scratch_pool)
+{
+ struct editor_baton *eb = baton;
+ svn_error_t *err;
+
+ SVN_ERR(ensure_root_opened(eb));
+
+ /* Drive the tree we've created. */
+ err = drive_root(&eb->root, eb->deditor, *eb->make_abs_paths, scratch_pool);
+ if (!err)
+ {
+ err = svn_error_compose_create(err, eb->deditor->close_edit(
+ eb->dedit_baton,
+ scratch_pool));
+ }
+
+ if (err)
+ svn_error_clear(eb->deditor->abort_edit(eb->dedit_baton, scratch_pool));
+
+ return svn_error_trace(err);
+}
+
+/* This implements svn_editor_cb_abort_t */
+static svn_error_t *
+abort_cb(void *baton,
+ apr_pool_t *scratch_pool)
+{
+ struct editor_baton *eb = baton;
+ svn_error_t *err;
+ svn_error_t *err2;
+
+ /* We still need to drive anything we collected in the editor to this
+ point. */
+
+ /* Drive the tree we've created. */
+ err = drive_root(&eb->root, eb->deditor, *eb->make_abs_paths, scratch_pool);
+
+ err2 = eb->deditor->abort_edit(eb->dedit_baton, scratch_pool);
+
+ if (err2)
+ {
+ if (err)
+ svn_error_clear(err2);
+ else
+ err = err2;
+ }
+
+ return svn_error_trace(err);
+}
+
+static svn_error_t *
+start_edit_func(void *baton,
+ svn_revnum_t base_revision)
+{
+ struct editor_baton *eb = baton;
+
+ eb->root.base_revision = base_revision;
+ SVN_ERR(ensure_root_opened(eb));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+target_revision_func(void *baton,
+ svn_revnum_t target_revision,
+ apr_pool_t *scratch_pool)
+{
+ struct editor_baton *eb = baton;
+
+ SVN_ERR(eb->deditor->set_target_revision(eb->dedit_baton, target_revision,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+editor_from_delta(svn_editor_t **editor_p,
+ struct extra_baton **exb,
+ const svn_delta_editor_t *deditor,
+ void *dedit_baton,
+ svn_boolean_t *send_abs_paths,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ svn_delta_fetch_kind_func_t fetch_kind_func,
+ void *fetch_kind_baton,
+ svn_delta_fetch_props_func_t fetch_props_func,
+ void *fetch_props_baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_editor_t *editor;
+ static const svn_editor_cb_many_t editor_cbs = {
+ add_directory_cb,
+ add_file_cb,
+ add_symlink_cb,
+ add_absent_cb,
+ set_props_cb,
+ set_text_cb,
+ set_target_cb,
+ delete_cb,
+ copy_cb,
+ move_cb,
+ complete_cb,
+ abort_cb
+ };
+ struct editor_baton *eb = apr_pcalloc(result_pool, sizeof(*eb));
+ struct extra_baton *extra_baton = apr_pcalloc(result_pool,
+ sizeof(*extra_baton));
+
+ eb->deditor = deditor;
+ eb->dedit_baton = dedit_baton;
+ eb->edit_pool = result_pool;
+ eb->paths = apr_hash_make(result_pool);
+
+ eb->fetch_kind_func = fetch_kind_func;
+ eb->fetch_kind_baton = fetch_kind_baton;
+ eb->fetch_props_func = fetch_props_func;
+ eb->fetch_props_baton = fetch_props_baton;
+
+ eb->root.path = NULL;
+ eb->root.children = apr_hash_make(result_pool);
+ eb->root.kind = svn_kind_dir;
+ eb->root.operation = OP_OPEN;
+ eb->root.prop_mods = apr_hash_make(result_pool);
+ eb->root.prop_dels = apr_array_make(result_pool, 1, sizeof(const char *));
+ eb->root.copyfrom_revision = SVN_INVALID_REVNUM;
+
+ eb->root_opened = FALSE;
+ eb->make_abs_paths = send_abs_paths;
+
+ SVN_ERR(svn_editor_create(&editor, eb, cancel_func, cancel_baton,
+ result_pool, scratch_pool));
+ SVN_ERR(svn_editor_setcb_many(editor, &editor_cbs, scratch_pool));
+
+ *editor_p = editor;
+
+ extra_baton->start_edit = start_edit_func;
+ extra_baton->target_revision = target_revision_func;
+ extra_baton->baton = eb;
+
+ *exb = extra_baton;
+
+ return SVN_NO_ERROR;
+}
+
+svn_delta_shim_callbacks_t *
+svn_delta_shim_callbacks_default(apr_pool_t *result_pool)
+{
+ svn_delta_shim_callbacks_t *shim_callbacks = apr_pcalloc(result_pool,
+ sizeof(*shim_callbacks));
+ return shim_callbacks;
+}
+
+/* Uncomment below to add editor shims throughout Subversion. In it's
+ * current state, that will likely break The World. */
+/* #define ENABLE_EDITOR_SHIMS*/
+
+svn_error_t *
+svn_editor__insert_shims(const svn_delta_editor_t **deditor_out,
+ void **dedit_baton_out,
+ const svn_delta_editor_t *deditor_in,
+ void *dedit_baton_in,
+ svn_delta_shim_callbacks_t *shim_callbacks,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+#ifndef ENABLE_EDITOR_SHIMS
+ /* Shims disabled, just copy the editor and baton directly. */
+ *deditor_out = deditor_in;
+ *dedit_baton_out = dedit_baton_in;
+#else
+ /* Use our shim APIs to create an intermediate svn_editor_t, and then
+ wrap that again back into a svn_delta_editor_t. This introduces
+ a lot of overhead. */
+ svn_editor_t *editor;
+
+ /* The "extra baton" is a set of functions and a baton which allows the
+ shims to communicate additional events to each other.
+ editor_from_delta() returns a pointer to this baton, which
+ delta_from_editor() should then store. */
+ struct extra_baton *exb;
+
+ /* The reason this is a pointer is that we don't know the appropriate
+ value until we start receiving paths. So process_actions() sets the
+ flag, which drive_tree() later consumes. */
+ svn_boolean_t *found_abs_paths = apr_palloc(result_pool,
+ sizeof(*found_abs_paths));
+
+ SVN_ERR(editor_from_delta(&editor, &exb, deditor_in, dedit_baton_in,
+ found_abs_paths, NULL, NULL,
+ shim_callbacks->fetch_kind_func,
+ shim_callbacks->fetch_baton,
+ shim_callbacks->fetch_props_func,
+ shim_callbacks->fetch_baton,
+ result_pool, scratch_pool));
+ SVN_ERR(delta_from_editor(deditor_out, dedit_baton_out, editor,
+ found_abs_paths,
+ shim_callbacks->fetch_props_func,
+ shim_callbacks->fetch_baton,
+ shim_callbacks->fetch_base_func,
+ shim_callbacks->fetch_baton,
+ exb, result_pool));
+
+#endif
+ return SVN_NO_ERROR;
+}
Modified: subversion/branches/revprop-packing/subversion/libsvn_delta/compose_delta.c
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/libsvn_delta/compose_delta.c?rev=1231318&r1=1231317&r2=1231318&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/libsvn_delta/compose_delta.c (original)
+++ subversion/branches/revprop-packing/subversion/libsvn_delta/compose_delta.c Fri Jan 13 21:40:26 2012
@@ -774,7 +774,7 @@ svn_txdelta_compose_windows(const svn_tx
/* Read the description of the delta composition algorithm in
notes/fs-improvements.txt before going any further.
You have been warned. */
- build_baton.new_data = svn_stringbuf_create("", pool);
+ build_baton.new_data = svn_stringbuf_create_empty(pool);
for (i = 0; i < window_B->num_ops; ++i)
{
const svn_txdelta_op_t *const op = &window_B->ops[i];
Modified: subversion/branches/revprop-packing/subversion/libsvn_delta/delta.h
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/libsvn_delta/delta.h?rev=1231318&r1=1231317&r2=1231318&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/libsvn_delta/delta.h (original)
+++ subversion/branches/revprop-packing/subversion/libsvn_delta/delta.h Fri Jan 13 21:40:26 2012
@@ -68,6 +68,12 @@ void svn_txdelta__insert_op(svn_txdelta_
const char *new_data,
apr_pool_t *pool);
+/* Remove / truncate the last delta ops spanning the last MAX_LEN bytes
+ from the delta window being built via BUILD_BATON starting. Return the
+ number of bytes that were actually removed. */
+apr_size_t
+svn_txdelta__remove_copy(svn_txdelta__ops_baton_t *build_baton,
+ apr_size_t max_len);
/* Allocate a delta window from POOL. */
svn_txdelta_window_t *
Modified: subversion/branches/revprop-packing/subversion/libsvn_delta/editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/libsvn_delta/editor.c?rev=1231318&r1=1231317&r2=1231318&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/libsvn_delta/editor.c (original)
+++ subversion/branches/revprop-packing/subversion/libsvn_delta/editor.c Fri Jan 13 21:40:26 2012
@@ -26,8 +26,17 @@
#include "svn_types.h"
#include "svn_error.h"
#include "svn_pools.h"
+#include "svn_editor.h"
+#include "svn_dirent_uri.h"
-#include "private/svn_editor.h"
+#ifdef SVN_DEBUG
+/* This enables runtime checks of the editor API constraints. This may
+ introduce additional memory and runtime overhead, and should not be used
+ in production builds.
+
+ ### Remove before release? */
+#define ENABLE_ORDERING_CHECK
+#endif
struct svn_editor_t
@@ -44,6 +53,15 @@ struct svn_editor_t
/* This pool is used as the scratch_pool for all callbacks. */
apr_pool_t *scratch_pool;
+
+#ifdef ENABLE_ORDERING_CHECK
+ apr_hash_t *pending_incomplete_children;
+ apr_hash_t *completed_nodes;
+ apr_hash_t *needs_text_or_target;
+ svn_boolean_t finished;
+
+ apr_pool_t *result_pool;
+#endif
};
@@ -61,6 +79,13 @@ svn_editor_create(svn_editor_t **editor,
(*editor)->cancel_func = cancel_func;
(*editor)->cancel_baton = cancel_baton;
(*editor)->scratch_pool = svn_pool_create(result_pool);
+#ifdef ENABLE_ORDERING_CHECK
+ (*editor)->pending_incomplete_children = apr_hash_make(result_pool);
+ (*editor)->completed_nodes = apr_hash_make(result_pool);
+ (*editor)->needs_text_or_target = apr_hash_make(result_pool);
+ (*editor)->finished = FALSE;
+ (*editor)->result_pool = result_pool;
+#endif
return SVN_NO_ERROR;
}
@@ -222,6 +247,11 @@ svn_editor_add_directory(svn_editor_t *e
svn_error_t *err;
SVN_ERR_ASSERT(editor->funcs.cb_add_directory != NULL);
+#ifdef ENABLE_ORDERING_CHECK
+ SVN_ERR_ASSERT(!editor->finished);
+ SVN_ERR_ASSERT(!apr_hash_get(editor->completed_nodes, relpath,
+ APR_HASH_KEY_STRING));
+#endif
if (editor->cancel_func)
SVN_ERR(editor->cancel_func(editor->cancel_baton));
@@ -229,6 +259,25 @@ svn_editor_add_directory(svn_editor_t *e
err = editor->funcs.cb_add_directory(editor->baton, relpath, children,
props, replaces_rev,
editor->scratch_pool);
+#ifdef ENABLE_ORDERING_CHECK
+ apr_hash_set(editor->completed_nodes,
+ apr_pstrdup(editor->result_pool, relpath),
+ APR_HASH_KEY_STRING, (void *) 0x5ca1ab1e);
+ apr_hash_set(editor->pending_incomplete_children, relpath,
+ APR_HASH_KEY_STRING, NULL);
+ {
+ int i;
+ for (i = 0; i < children->nelts; i++)
+ {
+ const char *child_basename = APR_ARRAY_IDX(children, i, const char *);
+ const char *child = svn_relpath_join(relpath, child_basename,
+ editor->result_pool);
+
+ apr_hash_set(editor->pending_incomplete_children, child,
+ APR_HASH_KEY_STRING, (void *)0xdeadbeef);
+ }
+ }
+#endif
svn_pool_clear(editor->scratch_pool);
return err;
}
@@ -237,18 +286,33 @@ svn_editor_add_directory(svn_editor_t *e
svn_error_t *
svn_editor_add_file(svn_editor_t *editor,
const char *relpath,
+ const svn_checksum_t *checksum,
+ svn_stream_t *contents,
apr_hash_t *props,
svn_revnum_t replaces_rev)
{
svn_error_t *err;
SVN_ERR_ASSERT(editor->funcs.cb_add_file != NULL);
+#ifdef ENABLE_ORDERING_CHECK
+ SVN_ERR_ASSERT(!editor->finished);
+ SVN_ERR_ASSERT(!apr_hash_get(editor->completed_nodes, relpath,
+ APR_HASH_KEY_STRING));
+#endif
if (editor->cancel_func)
SVN_ERR(editor->cancel_func(editor->cancel_baton));
- err = editor->funcs.cb_add_file(editor->baton, relpath, props,
+ err = editor->funcs.cb_add_file(editor->baton, relpath,
+ checksum, contents, props,
replaces_rev, editor->scratch_pool);
+#ifdef ENABLE_ORDERING_CHECK
+ apr_hash_set(editor->completed_nodes,
+ apr_pstrdup(editor->result_pool, relpath),
+ APR_HASH_KEY_STRING, (void *) 0x5ca1ab1e);
+ apr_hash_set(editor->pending_incomplete_children, relpath,
+ APR_HASH_KEY_STRING, NULL);
+#endif
svn_pool_clear(editor->scratch_pool);
return err;
}
@@ -264,12 +328,24 @@ svn_editor_add_symlink(svn_editor_t *edi
svn_error_t *err;
SVN_ERR_ASSERT(editor->funcs.cb_add_symlink != NULL);
+#ifdef ENABLE_ORDERING_CHECK
+ SVN_ERR_ASSERT(!editor->finished);
+ SVN_ERR_ASSERT(!apr_hash_get(editor->completed_nodes, relpath,
+ APR_HASH_KEY_STRING));
+#endif
if (editor->cancel_func)
SVN_ERR(editor->cancel_func(editor->cancel_baton));
err = editor->funcs.cb_add_symlink(editor->baton, relpath, target, props,
replaces_rev, editor->scratch_pool);
+#ifdef ENABLE_ORDERING_CHECK
+ apr_hash_set(editor->completed_nodes,
+ apr_pstrdup(editor->result_pool, relpath),
+ APR_HASH_KEY_STRING, (void *) 0x5ca1ab1e);
+ apr_hash_set(editor->pending_incomplete_children, relpath,
+ APR_HASH_KEY_STRING, NULL);
+#endif
svn_pool_clear(editor->scratch_pool);
return err;
}
@@ -278,18 +354,30 @@ svn_editor_add_symlink(svn_editor_t *edi
svn_error_t *
svn_editor_add_absent(svn_editor_t *editor,
const char *relpath,
- svn_node_kind_t kind,
+ svn_kind_t kind,
svn_revnum_t replaces_rev)
{
svn_error_t *err;
SVN_ERR_ASSERT(editor->funcs.cb_add_absent != NULL);
+#ifdef ENABLE_ORDERING_CHECK
+ SVN_ERR_ASSERT(!editor->finished);
+ SVN_ERR_ASSERT(!apr_hash_get(editor->completed_nodes, relpath,
+ APR_HASH_KEY_STRING));
+#endif
if (editor->cancel_func)
SVN_ERR(editor->cancel_func(editor->cancel_baton));
err = editor->funcs.cb_add_absent(editor->baton, relpath, kind,
replaces_rev, editor->scratch_pool);
+#ifdef ENABLE_ORDERING_CHECK
+ apr_hash_set(editor->completed_nodes,
+ apr_pstrdup(editor->result_pool, relpath),
+ APR_HASH_KEY_STRING, (void *) 0x5ca1ab1e);
+ apr_hash_set(editor->pending_incomplete_children, relpath,
+ APR_HASH_KEY_STRING, NULL);
+#endif
svn_pool_clear(editor->scratch_pool);
return err;
}
@@ -305,12 +393,33 @@ svn_editor_set_props(svn_editor_t *edito
svn_error_t *err;
SVN_ERR_ASSERT(editor->funcs.cb_set_props != NULL);
+#ifdef ENABLE_ORDERING_CHECK
+ SVN_ERR_ASSERT(!editor->finished);
+ SVN_ERR_ASSERT(!apr_hash_get(editor->completed_nodes, relpath,
+ APR_HASH_KEY_STRING));
+#endif
if (editor->cancel_func)
SVN_ERR(editor->cancel_func(editor->cancel_baton));
err = editor->funcs.cb_set_props(editor->baton, relpath, revision, props,
complete, editor->scratch_pool);
+#ifdef ENABLE_ORDERING_CHECK
+ /* ### Some of the ordering here depends upon the kind of RELPATH, but
+ * ### we have no way of determining what that is. */
+ if (complete)
+ {
+ apr_hash_set(editor->completed_nodes,
+ apr_pstrdup(editor->result_pool, relpath),
+ APR_HASH_KEY_STRING, (void *) 0x5ca1ab1e);
+ }
+ else
+ {
+ apr_hash_set(editor->needs_text_or_target,
+ apr_pstrdup(editor->result_pool, relpath),
+ APR_HASH_KEY_STRING, (void *) 0xba5eba11);
+ }
+#endif
svn_pool_clear(editor->scratch_pool);
return err;
}
@@ -326,12 +435,24 @@ svn_editor_set_text(svn_editor_t *editor
svn_error_t *err;
SVN_ERR_ASSERT(editor->funcs.cb_set_text != NULL);
+#ifdef ENABLE_ORDERING_CHECK
+ SVN_ERR_ASSERT(!editor->finished);
+ SVN_ERR_ASSERT(!apr_hash_get(editor->completed_nodes, relpath,
+ APR_HASH_KEY_STRING));
+#endif
if (editor->cancel_func)
SVN_ERR(editor->cancel_func(editor->cancel_baton));
err = editor->funcs.cb_set_text(editor->baton, relpath, revision,
checksum, contents, editor->scratch_pool);
+#ifdef ENABLE_ORDERING_CHECK
+ apr_hash_set(editor->needs_text_or_target, relpath, APR_HASH_KEY_STRING,
+ NULL);
+ apr_hash_set(editor->completed_nodes,
+ apr_pstrdup(editor->result_pool, relpath),
+ APR_HASH_KEY_STRING, (void *) 0x5ca1ab1e);
+#endif
svn_pool_clear(editor->scratch_pool);
return err;
}
@@ -346,12 +467,24 @@ svn_editor_set_target(svn_editor_t *edit
svn_error_t *err;
SVN_ERR_ASSERT(editor->funcs.cb_set_target != NULL);
+#ifdef ENABLE_ORDERING_CHECK
+ SVN_ERR_ASSERT(!editor->finished);
+ SVN_ERR_ASSERT(!apr_hash_get(editor->completed_nodes, relpath,
+ APR_HASH_KEY_STRING));
+#endif
if (editor->cancel_func)
SVN_ERR(editor->cancel_func(editor->cancel_baton));
err = editor->funcs.cb_set_target(editor->baton, relpath, revision,
target, editor->scratch_pool);
+#ifdef ENABLE_ORDERING_CHECK
+ apr_hash_set(editor->needs_text_or_target, relpath, APR_HASH_KEY_STRING,
+ NULL);
+ apr_hash_set(editor->completed_nodes,
+ apr_pstrdup(editor->result_pool, relpath),
+ APR_HASH_KEY_STRING, (void *) 0x5ca1ab1e);
+#endif
svn_pool_clear(editor->scratch_pool);
return err;
}
@@ -365,12 +498,22 @@ svn_editor_delete(svn_editor_t *editor,
svn_error_t *err;
SVN_ERR_ASSERT(editor->funcs.cb_delete != NULL);
+#ifdef ENABLE_ORDERING_CHECK
+ SVN_ERR_ASSERT(!editor->finished);
+ SVN_ERR_ASSERT(!apr_hash_get(editor->completed_nodes, relpath,
+ APR_HASH_KEY_STRING));
+#endif
if (editor->cancel_func)
SVN_ERR(editor->cancel_func(editor->cancel_baton));
err = editor->funcs.cb_delete(editor->baton, relpath, revision,
editor->scratch_pool);
+#ifdef ENABLE_ORDERING_CHECK
+ apr_hash_set(editor->completed_nodes,
+ apr_pstrdup(editor->result_pool, relpath),
+ APR_HASH_KEY_STRING, (void *) 0x5ca1ab1e);
+#endif
svn_pool_clear(editor->scratch_pool);
return err;
}
@@ -386,6 +529,11 @@ svn_editor_copy(svn_editor_t *editor,
svn_error_t *err;
SVN_ERR_ASSERT(editor->funcs.cb_copy != NULL);
+#ifdef ENABLE_ORDERING_CHECK
+ SVN_ERR_ASSERT(!editor->finished);
+ SVN_ERR_ASSERT(!apr_hash_get(editor->completed_nodes, dst_relpath,
+ APR_HASH_KEY_STRING));
+#endif
if (editor->cancel_func)
SVN_ERR(editor->cancel_func(editor->cancel_baton));
@@ -408,6 +556,13 @@ svn_editor_move(svn_editor_t *editor,
svn_error_t *err;
SVN_ERR_ASSERT(editor->funcs.cb_move != NULL);
+#ifdef ENABLE_ORDERING_CHECK
+ SVN_ERR_ASSERT(!editor->finished);
+ SVN_ERR_ASSERT(!apr_hash_get(editor->completed_nodes, src_relpath,
+ APR_HASH_KEY_STRING));
+ SVN_ERR_ASSERT(!apr_hash_get(editor->completed_nodes, dst_relpath,
+ APR_HASH_KEY_STRING));
+#endif
if (editor->cancel_func)
SVN_ERR(editor->cancel_func(editor->cancel_baton));
@@ -415,6 +570,22 @@ svn_editor_move(svn_editor_t *editor,
err = editor->funcs.cb_move(editor->baton, src_relpath, src_revision,
dst_relpath, replaces_rev,
editor->scratch_pool);
+#ifdef ENABLE_ORDERING_CHECK
+ /* ### after moving a node away, a new one can be created. how does
+ ### affect the "replaces_rev" concept elsewhere? */
+#if 0
+ apr_hash_set(editor->completed_nodes,
+ apr_pstrdup(editor->result_pool, src_relpath),
+ APR_HASH_KEY_STRING, (void *) 0x5ca1ab1e);
+#endif
+
+ /* ### hmm. post-move, it should be possible to change props/contents. */
+#if 0
+ apr_hash_set(editor->completed_nodes,
+ apr_pstrdup(editor->result_pool, dst_relpath),
+ APR_HASH_KEY_STRING, (void *) 0x5ca1ab1e);
+#endif
+#endif
svn_pool_clear(editor->scratch_pool);
return err;
}
@@ -426,8 +597,17 @@ svn_editor_complete(svn_editor_t *editor
svn_error_t *err;
SVN_ERR_ASSERT(editor->funcs.cb_complete != NULL);
+#ifdef ENABLE_ORDERING_CHECK
+ SVN_ERR_ASSERT(!editor->finished);
+ SVN_ERR_ASSERT(apr_hash_count(editor->pending_incomplete_children) == 0);
+ SVN_ERR_ASSERT(apr_hash_count(editor->needs_text_or_target) == 0);
+#endif
err = editor->funcs.cb_complete(editor->baton, editor->scratch_pool);
+#ifdef ENABLE_ORDERING_CHECK
+ if (!err)
+ editor->finished = TRUE;
+#endif
svn_pool_clear(editor->scratch_pool);
return err;
}
@@ -439,8 +619,14 @@ svn_editor_abort(svn_editor_t *editor)
svn_error_t *err;
SVN_ERR_ASSERT(editor->funcs.cb_abort != NULL);
+#ifdef ENABLE_ORDERING_CHECK
+ SVN_ERR_ASSERT(!editor->finished);
+#endif
err = editor->funcs.cb_abort(editor->baton, editor->scratch_pool);
+#ifdef ENABLE_ORDERING_CHECK
+ editor->finished = TRUE;
+#endif
svn_pool_clear(editor->scratch_pool);
return err;
}
Modified: subversion/branches/revprop-packing/subversion/libsvn_delta/svndiff.c
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/libsvn_delta/svndiff.c?rev=1231318&r1=1231317&r2=1231318&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/libsvn_delta/svndiff.c (original)
+++ subversion/branches/revprop-packing/subversion/libsvn_delta/svndiff.c Fri Jan 13 21:40:26 2012
@@ -31,6 +31,8 @@
#include "svn_private_config.h"
#include <zlib.h>
+#include "private/svn_error_private.h"
+
/* The zlib compressBound function was not exported until 1.2.0. */
#if ZLIB_VERNUM >= 0x1200
#define svnCompressBound(LEN) compressBound(LEN)
@@ -156,15 +158,18 @@ zlib_encode(const char *data,
}
else
{
+ int zerr;
+
svn_stringbuf_ensure(out, svnCompressBound(len) + intlen);
endlen = out->blocksize;
- if (compress2((unsigned char *)out->data + intlen, &endlen,
- (const unsigned char *)data, len,
- compression_level) != Z_OK)
- return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA,
- NULL,
- _("Compression of svndiff data failed"));
+ zerr = compress2((unsigned char *)out->data + intlen, &endlen,
+ (const unsigned char *)data, len,
+ compression_level);
+ if (zerr != Z_OK)
+ return svn_error_trace(svn_error__wrap_zlib(
+ zerr, "compress2",
+ _("Compression of svndiff data failed")));
/* Compression didn't help :(, just append the original text */
if (endlen >= len)
@@ -182,9 +187,9 @@ window_handler(svn_txdelta_window_t *win
{
struct encoder_baton *eb = baton;
apr_pool_t *pool = svn_pool_create(eb->pool);
- svn_stringbuf_t *instructions = svn_stringbuf_create("", pool);
- svn_stringbuf_t *i1 = svn_stringbuf_create("", pool);
- svn_stringbuf_t *header = svn_stringbuf_create("", pool);
+ svn_stringbuf_t *instructions = svn_stringbuf_create_empty(pool);
+ svn_stringbuf_t *i1 = svn_stringbuf_create_empty(pool);
+ svn_stringbuf_t *header = svn_stringbuf_create_empty(pool);
const svn_string_t *newdata;
unsigned char ibuf[MAX_INSTRUCTION_LEN], *ip;
const svn_txdelta_op_t *op;
@@ -254,8 +259,8 @@ window_handler(svn_txdelta_window_t *win
append_encoded_int(header, instructions->len);
if (eb->version == 1)
{
- svn_stringbuf_t *temp = svn_stringbuf_create("", pool);
- svn_string_t *tempstr = svn_string_create("", pool);
+ svn_stringbuf_t *temp = svn_stringbuf_create_empty(pool);
+ svn_string_t *tempstr = svn_string_create_empty(pool);
SVN_ERR(zlib_encode(window->new_data->data, window->new_data->len,
temp, eb->compression_level));
tempstr->data = temp->data;
@@ -431,20 +436,24 @@ decode_size(apr_size_t *val,
return NULL;
}
-/* Decode the possibly-zlib compressed string that is in IN, into OUT.
- We expect an integer is prepended to IN that specifies the original
- size, and that if encoded size == original size, that the remaining
- data is not compressed. */
+/* Decode the possibly-zlib compressed string of length INLEN that is in
+ IN, into OUT. We expect an integer is prepended to IN that specifies
+ the original size, and that if encoded size == original size, that the
+ remaining data is not compressed.
+ In that case, we will simply return pointer into IN as data pointer for
+ OUT. The caller is expected not to modify the contents of OUT.
+ An error is returned if the decoded length exceeds the given LIMIT.
+ */
static svn_error_t *
-zlib_decode(svn_stringbuf_t *in, svn_stringbuf_t *out, apr_size_t limit)
+zlib_decode(const unsigned char *in, apr_size_t inLen, svn_stringbuf_t *out,
+ apr_size_t limit)
{
apr_size_t len;
- char *oldplace = in->data;
+ const unsigned char *oldplace = in;
/* First thing in the string is the original length. */
- in->data = (char *)decode_size(&len, (unsigned char *)in->data,
- (unsigned char *)in->data+in->len);
- if (in->data == NULL)
+ in = decode_size(&len, in, in + inLen);
+ if (in == NULL)
return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA, NULL,
_("Decompression of svndiff data failed: no size"));
if (len > limit)
@@ -453,33 +462,38 @@ zlib_decode(svn_stringbuf_t *in, svn_str
"size too large"));
/* We need to subtract the size of the encoded original length off the
* still remaining input length. */
- in->len -= (in->data - oldplace);
- if (in->len == len)
+ inLen -= (in - oldplace);
+ if (inLen == len)
{
- svn_stringbuf_appendstr(out, in);
+ /* "in" is no longer used but the memory remains allocated for
+ * at least as long as "out" will be used by the caller.
+ */
+ out->data = (char *)in;
+ out->len = len;
+ out->blocksize = len; /* sic! */
+
return SVN_NO_ERROR;
}
else
{
- unsigned long zliblen;
+ unsigned long zlen = len;
+ int zerr;
svn_stringbuf_ensure(out, len);
-
- zliblen = len;
- if (uncompress ((unsigned char *)out->data, &zliblen,
- (const unsigned char *)in->data, in->len) != Z_OK)
- return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA,
- NULL,
- _("Decompression of svndiff data failed"));
+ zerr = uncompress((unsigned char *)out->data, &zlen, in, inLen);
+ if (zerr != Z_OK)
+ return svn_error_trace(svn_error__wrap_zlib(
+ zerr, "uncompress",
+ _("Decompression of svndiff data failed")));
/* Zlib should not produce something that has a different size than the
original length we stored. */
- if (zliblen != len)
+ if (zlen != len)
return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA,
NULL,
_("Size of uncompressed data "
"does not match stored original length"));
- out->len = zliblen;
+ out->len = zlen;
}
return SVN_NO_ERROR;
}
@@ -627,16 +641,15 @@ decode_window(svn_txdelta_window_t *wind
if (version == 1)
{
- svn_stringbuf_t *instin, *ndin;
- svn_stringbuf_t *instout, *ndout;
+ svn_stringbuf_t *instout = svn_stringbuf_create_empty(pool);
+ svn_stringbuf_t *ndout = svn_stringbuf_create_empty(pool);
- instin = svn_stringbuf_ncreate((const char *)data, insend - data, pool);
- instout = svn_stringbuf_create("", pool);
- SVN_ERR(zlib_decode(instin, instout, MAX_INSTRUCTION_SECTION_LEN));
-
- ndin = svn_stringbuf_ncreate((const char *)insend, newlen, pool);
- ndout = svn_stringbuf_create("", pool);
- SVN_ERR(zlib_decode(ndin, ndout, SVN_DELTA_WINDOW_SIZE));
+ /* these may in fact simply return references to insend */
+
+ SVN_ERR(zlib_decode(insend, newlen, ndout,
+ SVN_DELTA_WINDOW_SIZE));
+ SVN_ERR(zlib_decode(data, insend - data, instout,
+ MAX_INSTRUCTION_SECTION_LEN));
newlen = ndout->len;
data = (unsigned char *)instout->data;
@@ -846,7 +859,7 @@ svn_txdelta_parse_svndiff(svn_txdelta_wi
db->consumer_baton = handler_baton;
db->pool = subpool;
db->subpool = svn_pool_create(subpool);
- db->buffer = svn_stringbuf_create("", db->subpool);
+ db->buffer = svn_stringbuf_create_empty(db->subpool);
db->last_sview_offset = 0;
db->last_sview_len = 0;
db->header_bytes = 0;
Modified: subversion/branches/revprop-packing/subversion/libsvn_delta/text_delta.c
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/libsvn_delta/text_delta.c?rev=1231318&r1=1231317&r2=1231318&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/libsvn_delta/text_delta.c (original)
+++ subversion/branches/revprop-packing/subversion/libsvn_delta/text_delta.c Fri Jan 13 21:40:26 2012
@@ -150,7 +150,7 @@ compute_window(const char *data, apr_siz
svn_txdelta_window_t *window;
/* Compute the delta operations. */
- build_baton.new_data = svn_stringbuf_create("", pool);
+ build_baton.new_data = svn_stringbuf_create_empty(pool);
if (source_len == 0)
svn_txdelta__insert_op(&build_baton, svn_txdelta_new, 0, target_len, data,
@@ -276,6 +276,48 @@ svn_txdelta__insert_op(svn_txdelta__ops_
++build_baton->num_ops;
}
+apr_size_t
+svn_txdelta__remove_copy(svn_txdelta__ops_baton_t *build_baton,
+ apr_size_t max_len)
+{
+ svn_txdelta_op_t *op;
+ apr_size_t len = 0;
+
+ /* remove ops back to front */
+ while (build_baton->num_ops > 0)
+ {
+ op = &build_baton->ops[build_baton->num_ops-1];
+
+ /* we can't modify svn_txdelta_target ops -> stop there */
+ if (op->action_code == svn_txdelta_target)
+ break;
+
+ /* handle the case that we cannot remove the op entirely */
+ if (op->length + len > max_len)
+ {
+ /* truncate only insertions. Copies don't benefit
+ from being truncated. */
+ if (op->action_code == svn_txdelta_new)
+ {
+ build_baton->new_data->len -= max_len - len;
+ op->length -= max_len - len;
+ len = max_len;
+ }
+
+ break;
+ }
+
+ /* drop the op entirely */
+ if (op->action_code == svn_txdelta_new)
+ build_baton->new_data->len -= op->length;
+
+ len += op->length;
+ --build_baton->num_ops;
+ }
+
+ return len;
+}
+
/* Generic delta stream functions. */
Modified: subversion/branches/revprop-packing/subversion/libsvn_delta/xdelta.c
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/libsvn_delta/xdelta.c?rev=1231318&r1=1231317&r2=1231318&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/libsvn_delta/xdelta.c (original)
+++ subversion/branches/revprop-packing/subversion/libsvn_delta/xdelta.c Fri Jan 13 21:40:26 2012
@@ -44,6 +44,10 @@
*/
#define MATCH_BLOCKSIZE 64
+/* "no" / "invalid" / "unused" value for positions within the detla windows
+ */
+#define NO_POSITION ((apr_uint32_t)-1)
+
/* Feed C_IN into the adler32 checksum and remove C_OUT at the same time.
* This function may (and will) only be called for characters that are
* MATCH_BLOCKSIZE positions apart.
@@ -96,17 +100,22 @@ init_adler32(const char *data)
struct block
{
apr_uint32_t adlersum;
- apr_size_t pos;
+
+/* Even in 64 bit systems, store only 32 bit offsets in our hash table
+ (our delta window size much much smaller then 4GB).
+ That reduces the hash table size by 50% from 32to 16KB
+ and makes it easier to fit into the CPU's L1 cache. */
+ apr_uint32_t pos; /* NO_POSITION -> block is not used */
};
/* A hash table, using open addressing, of the blocks of the source. */
struct blocks
{
/* The largest valid index of slots. */
- apr_size_t max;
+ apr_uint32_t max;
/* Source buffer that the positions in SLOTS refer to. */
const char* data;
- /* The vector of blocks. A pos value of (apr_size_t)-1 represents an unused
+ /* The vector of blocks. A pos value of NO_POSITION represents an unused
slot. */
struct block *slots;
};
@@ -114,7 +123,7 @@ struct blocks
/* Return a hash value calculated from the adler32 SUM, suitable for use with
our hash table. */
-static apr_size_t hash_func(apr_uint32_t sum)
+static apr_uint32_t hash_func(apr_uint32_t sum)
{
/* Since the adl32 checksum have a bad distribution for the 11th to 16th
bits when used for our small block size, we add some bits from the
@@ -126,12 +135,12 @@ static apr_size_t hash_func(apr_uint32_t
data into the table BLOCKS. Ignore true duplicates, i.e. blocks with
actually the same content. */
static void
-add_block(struct blocks *blocks, apr_uint32_t adlersum, apr_size_t pos)
+add_block(struct blocks *blocks, apr_uint32_t adlersum, apr_uint32_t pos)
{
- apr_size_t h = hash_func(adlersum) & blocks->max;
+ apr_uint32_t h = hash_func(adlersum) & blocks->max;
/* This will terminate, since we know that we will not fill the table. */
- for (; blocks->slots[h].pos != (apr_size_t)-1; h = (h + 1) & blocks->max)
+ for (; blocks->slots[h].pos != NO_POSITION; h = (h + 1) & blocks->max)
if (blocks->slots[h].adlersum == adlersum)
if (memcmp(blocks->data + blocks->slots[h].pos, blocks->data + pos,
MATCH_BLOCKSIZE) == 0)
@@ -143,21 +152,21 @@ add_block(struct blocks *blocks, apr_uin
/* Find a block in BLOCKS with the checksum ADLERSUM and matching the content
at DATA, returning its position in the source data. If there is no such
- block, return (apr_size_t)-1. */
-static apr_size_t
+ block, return NO_POSITION. */
+static apr_uint32_t
find_block(const struct blocks *blocks,
apr_uint32_t adlersum,
const char* data)
{
- apr_size_t h = hash_func(adlersum) & blocks->max;
+ apr_uint32_t h = hash_func(adlersum) & blocks->max;
- for (; blocks->slots[h].pos != (apr_size_t)-1; h = (h + 1) & blocks->max)
+ for (; blocks->slots[h].pos != NO_POSITION; h = (h + 1) & blocks->max)
if (blocks->slots[h].adlersum == adlersum)
if (memcmp(blocks->data + blocks->slots[h].pos, data,
MATCH_BLOCKSIZE) == 0)
return blocks->slots[h].pos;
- return (apr_size_t)-1;
+ return NO_POSITION;
}
/* Initialize the matches table from DATA of size DATALEN. This goes
@@ -187,7 +196,7 @@ init_blocks_table(const char *data,
{
/* Avoid using an indeterminate value in the lookup. */
blocks->slots[i].adlersum = 0;
- blocks->slots[i].pos = (apr_size_t)-1;
+ blocks->slots[i].pos = NO_POSITION;
}
/* If there is an odd block at the end of the buffer, we will
@@ -226,6 +235,39 @@ match_length(const char *a, const char *
return pos;
}
+/* Return the smallest byte index at which positions left of A and B differ
+ * (A[-result] != B[-result]). If no difference can be found in the first
+ * MAX_LEN characters, MAX_LEN will be returned.
+ */
+static apr_size_t
+reverse_match_length(const char *a, const char *b, apr_size_t max_len)
+{
+ apr_size_t pos = 0;
+
+#if SVN_UNALIGNED_ACCESS_IS_OK
+
+ /* Chunky processing is so much faster ...
+ *
+ * We can't make this work on architectures that require aligned access
+ * because A and B will probably have different alignment. So, skipping
+ * the first few chars until alignment is reached is not an option.
+ */
+ for (pos = sizeof(apr_size_t); pos <= max_len; pos += sizeof(apr_size_t))
+ if (*(const apr_size_t*)(a - pos) != *(const apr_size_t*)(b - pos))
+ break;
+
+ pos -= sizeof(apr_size_t);
+
+#endif
+
+ while (++pos <= max_len)
+ if (a[-pos] != b[-pos])
+ break;
+
+ return pos-1;
+}
+
+
/* Try to find a match for the target data B in BLOCKS, and then
extend the match as long as data in A and B at the match position
continues to match. We set the position in A we ended up in (in
@@ -252,7 +294,7 @@ find_match(const struct blocks *blocks,
apos = find_block(blocks, rolling, b + bpos);
/* See if we have a match. */
- if (apos == (apr_size_t)-1)
+ if (apos == NO_POSITION)
return 0;
/* Extend the match forward as far as possible */
@@ -278,6 +320,38 @@ find_match(const struct blocks *blocks,
return MATCH_BLOCKSIZE + delta;
}
+/* Utility for compute_delta() that compares the range B[START,BSIZE) with
+ * the range of similar size before A[ASIZE]. Create corresponding copy and
+ * insert operations.
+ *
+ * BUILD_BATON and POOL will be passed through from compute_delta().
+ */
+static void
+store_delta_trailer(svn_txdelta__ops_baton_t *build_baton,
+ const char *a,
+ apr_size_t asize,
+ const char *b,
+ apr_size_t bsize,
+ apr_size_t start,
+ apr_pool_t *pool)
+{
+ apr_size_t end_match;
+ apr_size_t max_len = asize > (bsize - start) ? bsize - start : asize;
+ if (max_len == 0)
+ return;
+
+ end_match = reverse_match_length(a + asize, b + bsize, max_len);
+ if (end_match <= 4)
+ end_match = 0;
+
+ if (bsize - start > end_match)
+ svn_txdelta__insert_op(build_baton, svn_txdelta_new,
+ start, bsize - start - end_match, b + start, pool);
+ if (end_match)
+ svn_txdelta__insert_op(build_baton, svn_txdelta_source,
+ asize - end_match, end_match, NULL, pool);
+}
+
/* Compute a delta from A to B using xdelta.
@@ -315,12 +389,24 @@ compute_delta(svn_txdelta__ops_baton_t *
apr_uint32_t rolling;
apr_size_t lo = 0, pending_insert_start = 0;
+ /* Optimization: directly compare window starts. If more than 4
+ * bytes match, we can immediately create a matching windows.
+ * Shorter sequences result in a net data increase. */
+ lo = match_length(a, b, asize > bsize ? bsize : asize);
+ if ((lo > 4) || (lo == bsize))
+ {
+ svn_txdelta__insert_op(build_baton, svn_txdelta_source,
+ 0, lo, NULL, pool);
+ pending_insert_start = lo;
+ }
+ else
+ lo = 0;
+
/* If the size of the target is smaller than the match blocksize, just
insert the entire target. */
- if (bsize < MATCH_BLOCKSIZE)
+ if ((bsize - lo < MATCH_BLOCKSIZE) || (asize < MATCH_BLOCKSIZE))
{
- svn_txdelta__insert_op(build_baton, svn_txdelta_new,
- 0, bsize, b, pool);
+ store_delta_trailer(build_baton, a, asize, b, bsize, lo, pool);
return;
}
@@ -328,7 +414,7 @@ compute_delta(svn_txdelta__ops_baton_t *
init_blocks_table(a, asize, &blocks, pool);
/* Initialize our rolling checksum. */
- rolling = init_adler32(b);
+ rolling = init_adler32(b + lo);
while (lo < bsize)
{
apr_size_t matchlen = 0;
@@ -356,6 +442,19 @@ compute_delta(svn_txdelta__ops_baton_t *
svn_txdelta__insert_op(build_baton, svn_txdelta_new,
0, lo - pending_insert_start,
b + pending_insert_start, pool);
+ else
+ {
+ /* the match borders on the previous op. Maybe, we found a
+ * match that is better than / overlapping the previous one. */
+ apr_size_t len = reverse_match_length(a + apos, b + lo, apos < lo ? apos : lo);
+ if (len > 0)
+ {
+ len = svn_txdelta__remove_copy(build_baton, len);
+ apos -= len;
+ matchlen += len;
+ lo -= len;
+ }
+ }
/* Reset the pending insert start to immediately after the
match. */
@@ -373,12 +472,7 @@ compute_delta(svn_txdelta__ops_baton_t *
}
/* If we still have an insert pending at the end, throw it in. */
- if (lo - pending_insert_start > 0)
- {
- svn_txdelta__insert_op(build_baton, svn_txdelta_new,
- 0, lo - pending_insert_start,
- b + pending_insert_start, pool);
- }
+ store_delta_trailer(build_baton, a, asize, b, bsize, pending_insert_start, pool);
}
void