diff options
author | Peter Wemm <peter@FreeBSD.org> | 2018-05-08 03:44:38 +0000 |
---|---|---|
committer | Peter Wemm <peter@FreeBSD.org> | 2018-05-08 03:44:38 +0000 |
commit | 3faf8d6bffc5d0fb2525ba37bb504c53366caf9d (patch) | |
tree | 7e47911263e75034b767fe34b2f8d3d17e91f66d /subversion/libsvn_ra_svn/editorp.c | |
parent | a55fb3c0d5eca7d887798125d5b95942b1f01d4b (diff) |
Import Subversion-1.10.0vendor/subversion/subversion-1.10.0
Notes
Notes:
svn path=/vendor/subversion/dist/; revision=333347
svn path=/vendor/subversion/subversion-1.10.0/; revision=333348; tag=vendor/subversion/subversion-1.10.0
Diffstat (limited to 'subversion/libsvn_ra_svn/editorp.c')
-rw-r--r-- | subversion/libsvn_ra_svn/editorp.c | 415 |
1 files changed, 285 insertions, 130 deletions
diff --git a/subversion/libsvn_ra_svn/editorp.c b/subversion/libsvn_ra_svn/editorp.c index 88af89859fc4..10c8a2b04a63 100644 --- a/subversion/libsvn_ra_svn/editorp.c +++ b/subversion/libsvn_ra_svn/editorp.c @@ -39,8 +39,11 @@ #include "svn_pools.h" #include "svn_private_config.h" +#include "private/svn_atomic.h" #include "private/svn_fspath.h" #include "private/svn_editor.h" +#include "private/svn_string_private.h" +#include "private/svn_subr_private.h" #include "ra_svn.h" @@ -57,7 +60,7 @@ typedef struct ra_svn_edit_baton_t { svn_ra_svn_conn_t *conn; svn_ra_svn_edit_callback callback; /* Called on successful completion. */ void *callback_baton; - int next_token; + apr_uint64_t next_token; svn_boolean_t got_status; } ra_svn_edit_baton_t; @@ -66,13 +69,19 @@ typedef struct ra_svn_baton_t { svn_ra_svn_conn_t *conn; apr_pool_t *pool; ra_svn_edit_baton_t *eb; - const char *token; + svn_string_t *token; } ra_svn_baton_t; +/* Forward declaration. */ +typedef struct ra_svn_token_entry_t ra_svn_token_entry_t; + typedef struct ra_svn_driver_state_t { const svn_delta_editor_t *editor; void *edit_baton; apr_hash_t *tokens; + + /* Entry for the last token seen. May be NULL. */ + ra_svn_token_entry_t *last_token; svn_boolean_t *aborted; svn_boolean_t done; apr_pool_t *pool; @@ -90,26 +99,33 @@ typedef struct ra_svn_driver_state_t { field in this structure is vestigial for files, and we use it for a different purpose instead: at apply-textdelta time, we set it to a subpool of the file pool, which is destroyed in textdelta-end. */ -typedef struct ra_svn_token_entry_t { +struct ra_svn_token_entry_t { svn_string_t *token; void *baton; svn_boolean_t is_file; svn_stream_t *dstream; /* svndiff stream for apply_textdelta */ apr_pool_t *pool; -} ra_svn_token_entry_t; +}; /* --- CONSUMING AN EDITOR BY PASSING EDIT OPERATIONS OVER THE NET --- */ -static const char *make_token(char type, ra_svn_edit_baton_t *eb, - apr_pool_t *pool) +static svn_string_t * +make_token(char type, + ra_svn_edit_baton_t *eb, + apr_pool_t *pool) { - return apr_psprintf(pool, "%c%d", type, eb->next_token++); + apr_size_t len; + char buffer[1 + SVN_INT64_BUFFER_SIZE]; + buffer[0] = type; + len = 1 + svn__ui64toa(&buffer[1], eb->next_token++); + + return svn_string_ncreate(buffer, len, pool); } static ra_svn_baton_t *ra_svn_make_baton(svn_ra_svn_conn_t *conn, apr_pool_t *pool, ra_svn_edit_baton_t *eb, - const char *token) + svn_string_t *token) { ra_svn_baton_t *b; @@ -171,7 +187,7 @@ static svn_error_t *ra_svn_open_root(void *edit_baton, svn_revnum_t rev, apr_pool_t *pool, void **root_baton) { ra_svn_edit_baton_t *eb = edit_baton; - const char *token = make_token('d', eb, pool); + svn_string_t *token = make_token('d', eb, pool); SVN_ERR(check_for_error(eb, pool)); SVN_ERR(svn_ra_svn__write_cmd_open_root(eb->conn, pool, rev, token)); @@ -196,7 +212,7 @@ static svn_error_t *ra_svn_add_dir(const char *path, void *parent_baton, apr_pool_t *pool, void **child_baton) { ra_svn_baton_t *b = parent_baton; - const char *token = make_token('d', b->eb, pool); + svn_string_t *token = make_token('d', b->eb, pool); SVN_ERR_ASSERT((copy_path && SVN_IS_VALID_REVNUM(copy_rev)) || (!copy_path && !SVN_IS_VALID_REVNUM(copy_rev))); @@ -212,7 +228,7 @@ static svn_error_t *ra_svn_open_dir(const char *path, void *parent_baton, void **child_baton) { ra_svn_baton_t *b = parent_baton; - const char *token = make_token('d', b->eb, pool); + svn_string_t *token = make_token('d', b->eb, pool); SVN_ERR(check_for_error(b->eb, pool)); SVN_ERR(svn_ra_svn__write_cmd_open_dir(b->conn, pool, path, b->token, @@ -265,7 +281,7 @@ static svn_error_t *ra_svn_add_file(const char *path, void **file_baton) { ra_svn_baton_t *b = parent_baton; - const char *token = make_token('c', b->eb, pool); + svn_string_t *token = make_token('c', b->eb, pool); SVN_ERR_ASSERT((copy_path && SVN_IS_VALID_REVNUM(copy_rev)) || (!copy_path && !SVN_IS_VALID_REVNUM(copy_rev))); @@ -283,7 +299,7 @@ static svn_error_t *ra_svn_open_file(const char *path, void **file_baton) { ra_svn_baton_t *b = parent_baton; - const char *token = make_token('c', b->eb, pool); + svn_string_t *token = make_token('c', b->eb, pool); SVN_ERR(check_for_error(b->eb, b->pool)); SVN_ERR(svn_ra_svn__write_cmd_open_file(b->conn, pool, path, b->token, @@ -335,15 +351,9 @@ static svn_error_t *ra_svn_apply_textdelta(void *file_baton, svn_stream_set_write(diff_stream, ra_svn_svndiff_handler); svn_stream_set_close(diff_stream, ra_svn_svndiff_close_handler); - /* If the connection does not support SVNDIFF1 or if we don't want to use - * compression, use the non-compressing "version 0" implementation */ - if ( svn_ra_svn_compression_level(b->conn) > 0 - && svn_ra_svn_has_capability(b->conn, SVN_RA_SVN_CAP_SVNDIFF1)) - svn_txdelta_to_svndiff3(wh, wh_baton, diff_stream, 1, - b->conn->compression_level, pool); - else - svn_txdelta_to_svndiff3(wh, wh_baton, diff_stream, 0, - b->conn->compression_level, pool); + svn_txdelta_to_svndiff3(wh, wh_baton, diff_stream, + svn_ra_svn__svndiff_version(b->conn), + b->conn->compression_level, pool); return SVN_NO_ERROR; } @@ -480,6 +490,7 @@ static ra_svn_token_entry_t *store_token(ra_svn_driver_state_t *ds, entry->pool = pool; apr_hash_set(ds->tokens, entry->token->data, entry->token->len, entry); + ds->last_token = entry; return entry; } @@ -489,36 +500,59 @@ static svn_error_t *lookup_token(ra_svn_driver_state_t *ds, svn_boolean_t is_file, ra_svn_token_entry_t **entry) { - *entry = apr_hash_get(ds->tokens, token->data, token->len); + if (ds->last_token && svn_string_compare(ds->last_token->token, token)) + { + *entry = ds->last_token; + } + else + { + *entry = apr_hash_get(ds->tokens, token->data, token->len); + ds->last_token = *entry; + } + if (!*entry || (*entry)->is_file != is_file) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, _("Invalid file or dir token during edit")); return SVN_NO_ERROR; } -static svn_error_t *ra_svn_handle_target_rev(svn_ra_svn_conn_t *conn, - apr_pool_t *pool, - const apr_array_header_t *params, - ra_svn_driver_state_t *ds) +/* Remove a TOKEN entry from DS. */ +static void remove_token(ra_svn_driver_state_t *ds, + svn_string_t *token) +{ + apr_hash_set(ds->tokens, token->data, token->len, NULL); + + /* Reset this unconditionally. In most cases, LAST_TOKEN->TOKEN will + match TOKEN anyway and if it doesn't, lookup_token() will suffer only + a minor performance hit. */ + ds->last_token = NULL; +} + +static svn_error_t * +ra_svn_handle_target_rev(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const svn_ra_svn__list_t *params, + ra_svn_driver_state_t *ds) { svn_revnum_t rev; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "r", &rev)); + SVN_ERR(svn_ra_svn__parse_tuple(params, "r", &rev)); SVN_CMD_ERR(ds->editor->set_target_revision(ds->edit_baton, rev, pool)); return SVN_NO_ERROR; } -static svn_error_t *ra_svn_handle_open_root(svn_ra_svn_conn_t *conn, - apr_pool_t *pool, - const apr_array_header_t *params, - ra_svn_driver_state_t *ds) +static svn_error_t * +ra_svn_handle_open_root(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const svn_ra_svn__list_t *params, + ra_svn_driver_state_t *ds) { svn_revnum_t rev; apr_pool_t *subpool; svn_string_t *token; void *root_baton; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)s", &rev, &token)); + SVN_ERR(svn_ra_svn__parse_tuple(params, "(?r)s", &rev, &token)); subpool = svn_pool_create(ds->pool); SVN_CMD_ERR(ds->editor->open_root(ds->edit_baton, rev, subpool, &root_baton)); @@ -526,17 +560,18 @@ static svn_error_t *ra_svn_handle_open_root(svn_ra_svn_conn_t *conn, return SVN_NO_ERROR; } -static svn_error_t *ra_svn_handle_delete_entry(svn_ra_svn_conn_t *conn, - apr_pool_t *pool, - const apr_array_header_t *params, - ra_svn_driver_state_t *ds) +static svn_error_t * +ra_svn_handle_delete_entry(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const svn_ra_svn__list_t *params, + ra_svn_driver_state_t *ds) { const char *path; svn_string_t *token; svn_revnum_t rev; ra_svn_token_entry_t *entry; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)s", + SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)s", &path, &rev, &token)); SVN_ERR(lookup_token(ds, token, FALSE, &entry)); path = svn_relpath_canonicalize(path, pool); @@ -544,10 +579,11 @@ static svn_error_t *ra_svn_handle_delete_entry(svn_ra_svn_conn_t *conn, return SVN_NO_ERROR; } -static svn_error_t *ra_svn_handle_add_dir(svn_ra_svn_conn_t *conn, - apr_pool_t *pool, - const apr_array_header_t *params, - ra_svn_driver_state_t *ds) +static svn_error_t * +ra_svn_handle_add_dir(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const svn_ra_svn__list_t *params, + ra_svn_driver_state_t *ds) { const char *path, *copy_path; svn_string_t *token, *child_token; @@ -556,7 +592,7 @@ static svn_error_t *ra_svn_handle_add_dir(svn_ra_svn_conn_t *conn, apr_pool_t *subpool; void *child_baton; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "css(?cr)", &path, &token, + SVN_ERR(svn_ra_svn__parse_tuple(params, "css(?cr)", &path, &token, &child_token, ©_path, ©_rev)); SVN_ERR(lookup_token(ds, token, FALSE, &entry)); subpool = svn_pool_create(entry->pool); @@ -578,10 +614,11 @@ static svn_error_t *ra_svn_handle_add_dir(svn_ra_svn_conn_t *conn, return SVN_NO_ERROR; } -static svn_error_t *ra_svn_handle_open_dir(svn_ra_svn_conn_t *conn, - apr_pool_t *pool, - const apr_array_header_t *params, - ra_svn_driver_state_t *ds) +static svn_error_t * +ra_svn_handle_open_dir(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const svn_ra_svn__list_t *params, + ra_svn_driver_state_t *ds) { const char *path; svn_string_t *token, *child_token; @@ -590,7 +627,7 @@ static svn_error_t *ra_svn_handle_open_dir(svn_ra_svn_conn_t *conn, apr_pool_t *subpool; void *child_baton; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "css(?r)", &path, &token, + SVN_ERR(svn_ra_svn__parse_tuple(params, "css(?r)", &path, &token, &child_token, &rev)); SVN_ERR(lookup_token(ds, token, FALSE, &entry)); subpool = svn_pool_create(entry->pool); @@ -601,17 +638,18 @@ static svn_error_t *ra_svn_handle_open_dir(svn_ra_svn_conn_t *conn, return SVN_NO_ERROR; } -static svn_error_t *ra_svn_handle_change_dir_prop(svn_ra_svn_conn_t *conn, - apr_pool_t *pool, - const apr_array_header_t *params, - ra_svn_driver_state_t *ds) +static svn_error_t * +ra_svn_handle_change_dir_prop(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const svn_ra_svn__list_t *params, + ra_svn_driver_state_t *ds) { svn_string_t *token; const char *name; svn_string_t *value; ra_svn_token_entry_t *entry; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "sc(?s)", &token, &name, + SVN_ERR(svn_ra_svn__parse_tuple(params, "sc(?s)", &token, &name, &value)); SVN_ERR(lookup_token(ds, token, FALSE, &entry)); SVN_CMD_ERR(ds->editor->change_dir_prop(entry->baton, name, value, @@ -619,36 +657,38 @@ static svn_error_t *ra_svn_handle_change_dir_prop(svn_ra_svn_conn_t *conn, return SVN_NO_ERROR; } -static svn_error_t *ra_svn_handle_close_dir(svn_ra_svn_conn_t *conn, - apr_pool_t *pool, - const apr_array_header_t *params, - ra_svn_driver_state_t *ds) +static svn_error_t * +ra_svn_handle_close_dir(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const svn_ra_svn__list_t *params, + ra_svn_driver_state_t *ds) { svn_string_t *token; ra_svn_token_entry_t *entry; /* Parse and look up the directory token. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "s", &token)); + SVN_ERR(svn_ra_svn__parse_tuple(params, "s", &token)); SVN_ERR(lookup_token(ds, token, FALSE, &entry)); /* Close the directory and destroy the baton. */ SVN_CMD_ERR(ds->editor->close_directory(entry->baton, pool)); - apr_hash_set(ds->tokens, token->data, token->len, NULL); + remove_token(ds, token); svn_pool_destroy(entry->pool); return SVN_NO_ERROR; } -static svn_error_t *ra_svn_handle_absent_dir(svn_ra_svn_conn_t *conn, - apr_pool_t *pool, - const apr_array_header_t *params, - ra_svn_driver_state_t *ds) +static svn_error_t * +ra_svn_handle_absent_dir(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const svn_ra_svn__list_t *params, + ra_svn_driver_state_t *ds) { const char *path; svn_string_t *token; ra_svn_token_entry_t *entry; /* Parse parameters and look up the directory token. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "cs", &path, &token)); + SVN_ERR(svn_ra_svn__parse_tuple(params, "cs", &path, &token)); SVN_ERR(lookup_token(ds, token, FALSE, &entry)); /* Call the editor. */ @@ -656,17 +696,18 @@ static svn_error_t *ra_svn_handle_absent_dir(svn_ra_svn_conn_t *conn, return SVN_NO_ERROR; } -static svn_error_t *ra_svn_handle_add_file(svn_ra_svn_conn_t *conn, - apr_pool_t *pool, - const apr_array_header_t *params, - ra_svn_driver_state_t *ds) +static svn_error_t * +ra_svn_handle_add_file(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const svn_ra_svn__list_t *params, + ra_svn_driver_state_t *ds) { const char *path, *copy_path; svn_string_t *token, *file_token; svn_revnum_t copy_rev; ra_svn_token_entry_t *entry, *file_entry; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "css(?cr)", &path, &token, + SVN_ERR(svn_ra_svn__parse_tuple(params, "css(?cr)", &path, &token, &file_token, ©_path, ©_rev)); SVN_ERR(lookup_token(ds, token, FALSE, &entry)); ds->file_refs++; @@ -691,17 +732,18 @@ static svn_error_t *ra_svn_handle_add_file(svn_ra_svn_conn_t *conn, return SVN_NO_ERROR; } -static svn_error_t *ra_svn_handle_open_file(svn_ra_svn_conn_t *conn, - apr_pool_t *pool, - const apr_array_header_t *params, - ra_svn_driver_state_t *ds) +static svn_error_t * +ra_svn_handle_open_file(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const svn_ra_svn__list_t *params, + ra_svn_driver_state_t *ds) { const char *path; svn_string_t *token, *file_token; svn_revnum_t rev; ra_svn_token_entry_t *entry, *file_entry; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "css(?r)", &path, &token, + SVN_ERR(svn_ra_svn__parse_tuple(params, "css(?r)", &path, &token, &file_token, &rev)); SVN_ERR(lookup_token(ds, token, FALSE, &entry)); ds->file_refs++; @@ -716,10 +758,11 @@ static svn_error_t *ra_svn_handle_open_file(svn_ra_svn_conn_t *conn, return SVN_NO_ERROR; } -static svn_error_t *ra_svn_handle_apply_textdelta(svn_ra_svn_conn_t *conn, - apr_pool_t *pool, - const apr_array_header_t *params, - ra_svn_driver_state_t *ds) +static svn_error_t * +ra_svn_handle_apply_textdelta(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const svn_ra_svn__list_t *params, + ra_svn_driver_state_t *ds) { svn_string_t *token; ra_svn_token_entry_t *entry; @@ -728,7 +771,7 @@ static svn_error_t *ra_svn_handle_apply_textdelta(svn_ra_svn_conn_t *conn, char *base_checksum; /* Parse arguments and look up the token. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "s(?c)", + SVN_ERR(svn_ra_svn__parse_tuple(params, "s(?c)", &token, &base_checksum)); SVN_ERR(lookup_token(ds, token, TRUE, &entry)); if (entry->dstream) @@ -741,17 +784,18 @@ static svn_error_t *ra_svn_handle_apply_textdelta(svn_ra_svn_conn_t *conn, return SVN_NO_ERROR; } -static svn_error_t *ra_svn_handle_textdelta_chunk(svn_ra_svn_conn_t *conn, - apr_pool_t *pool, - const apr_array_header_t *params, - ra_svn_driver_state_t *ds) +static svn_error_t * +ra_svn_handle_textdelta_chunk(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const svn_ra_svn__list_t *params, + ra_svn_driver_state_t *ds) { svn_string_t *token; ra_svn_token_entry_t *entry; svn_string_t *str; /* Parse arguments and look up the token. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "ss", &token, &str)); + SVN_ERR(svn_ra_svn__parse_tuple(params, "ss", &token, &str)); SVN_ERR(lookup_token(ds, token, TRUE, &entry)); if (!entry->dstream) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, @@ -760,16 +804,17 @@ static svn_error_t *ra_svn_handle_textdelta_chunk(svn_ra_svn_conn_t *conn, return SVN_NO_ERROR; } -static svn_error_t *ra_svn_handle_textdelta_end(svn_ra_svn_conn_t *conn, - apr_pool_t *pool, - const apr_array_header_t *params, - ra_svn_driver_state_t *ds) +static svn_error_t * +ra_svn_handle_textdelta_end(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const svn_ra_svn__list_t *params, + ra_svn_driver_state_t *ds) { svn_string_t *token; ra_svn_token_entry_t *entry; /* Parse arguments and look up the token. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "s", &token)); + SVN_ERR(svn_ra_svn__parse_tuple(params, "s", &token)); SVN_ERR(lookup_token(ds, token, TRUE, &entry)); if (!entry->dstream) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, @@ -780,55 +825,58 @@ static svn_error_t *ra_svn_handle_textdelta_end(svn_ra_svn_conn_t *conn, return SVN_NO_ERROR; } -static svn_error_t *ra_svn_handle_change_file_prop(svn_ra_svn_conn_t *conn, - apr_pool_t *pool, - const apr_array_header_t *params, - ra_svn_driver_state_t *ds) +static svn_error_t * +ra_svn_handle_change_file_prop(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const svn_ra_svn__list_t *params, + ra_svn_driver_state_t *ds) { const char *name; svn_string_t *token, *value; ra_svn_token_entry_t *entry; - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "sc(?s)", &token, &name, + SVN_ERR(svn_ra_svn__parse_tuple(params, "sc(?s)", &token, &name, &value)); SVN_ERR(lookup_token(ds, token, TRUE, &entry)); SVN_CMD_ERR(ds->editor->change_file_prop(entry->baton, name, value, pool)); return SVN_NO_ERROR; } -static svn_error_t *ra_svn_handle_close_file(svn_ra_svn_conn_t *conn, - apr_pool_t *pool, - const apr_array_header_t *params, - ra_svn_driver_state_t *ds) +static svn_error_t * +ra_svn_handle_close_file(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const svn_ra_svn__list_t *params, + ra_svn_driver_state_t *ds) { svn_string_t *token; ra_svn_token_entry_t *entry; const char *text_checksum; /* Parse arguments and look up the file token. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "s(?c)", + SVN_ERR(svn_ra_svn__parse_tuple(params, "s(?c)", &token, &text_checksum)); SVN_ERR(lookup_token(ds, token, TRUE, &entry)); /* Close the file and destroy the baton. */ SVN_CMD_ERR(ds->editor->close_file(entry->baton, text_checksum, pool)); - apr_hash_set(ds->tokens, token->data, token->len, NULL); + remove_token(ds, token); if (--ds->file_refs == 0) svn_pool_clear(ds->file_pool); return SVN_NO_ERROR; } -static svn_error_t *ra_svn_handle_absent_file(svn_ra_svn_conn_t *conn, - apr_pool_t *pool, - const apr_array_header_t *params, - ra_svn_driver_state_t *ds) +static svn_error_t * +ra_svn_handle_absent_file(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const svn_ra_svn__list_t *params, + ra_svn_driver_state_t *ds) { const char *path; svn_string_t *token; ra_svn_token_entry_t *entry; /* Parse parameters and look up the parent directory token. */ - SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "cs", &path, &token)); + SVN_ERR(svn_ra_svn__parse_tuple(params, "cs", &path, &token)); SVN_ERR(lookup_token(ds, token, FALSE, &entry)); /* Call the editor. */ @@ -836,22 +884,32 @@ static svn_error_t *ra_svn_handle_absent_file(svn_ra_svn_conn_t *conn, return SVN_NO_ERROR; } -static svn_error_t *ra_svn_handle_close_edit(svn_ra_svn_conn_t *conn, - apr_pool_t *pool, - const apr_array_header_t *params, - ra_svn_driver_state_t *ds) +static svn_error_t * +ra_svn_handle_close_edit(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const svn_ra_svn__list_t *params, + ra_svn_driver_state_t *ds) { SVN_CMD_ERR(ds->editor->close_edit(ds->edit_baton, pool)); ds->done = TRUE; +#ifdef SVN_DEBUG + /* Before enabling this in non-maintainer mode: + * Note that this code is used on both client *and* server */ + if (apr_hash_count(ds->tokens) != 0) + return svn_error_create( + SVN_ERR_FS_INCORRECT_EDITOR_COMPLETION, NULL, + _("Closing editor with directories or files open")); +#endif if (ds->aborted) *ds->aborted = FALSE; return svn_ra_svn__write_cmd_response(conn, pool, ""); } -static svn_error_t *ra_svn_handle_abort_edit(svn_ra_svn_conn_t *conn, - apr_pool_t *pool, - const apr_array_header_t *params, - ra_svn_driver_state_t *ds) +static svn_error_t * +ra_svn_handle_abort_edit(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const svn_ra_svn__list_t *params, + ra_svn_driver_state_t *ds) { ds->done = TRUE; if (ds->aborted) @@ -860,10 +918,11 @@ static svn_error_t *ra_svn_handle_abort_edit(svn_ra_svn_conn_t *conn, return svn_ra_svn__write_cmd_response(conn, pool, ""); } -static svn_error_t *ra_svn_handle_finish_replay(svn_ra_svn_conn_t *conn, - apr_pool_t *pool, - const apr_array_header_t *params, - ra_svn_driver_state_t *ds) +static svn_error_t * +ra_svn_handle_finish_replay(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const svn_ra_svn__list_t *params, + ra_svn_driver_state_t *ds) { if (!ds->for_replay) return svn_error_createf @@ -875,11 +934,15 @@ static svn_error_t *ra_svn_handle_finish_replay(svn_ra_svn_conn_t *conn, return SVN_NO_ERROR; } +/* Common function signature for all editor command handlers. */ +typedef svn_error_t *(*cmd_handler_t)(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const svn_ra_svn__list_t *params, + ra_svn_driver_state_t *ds); + static const struct { const char *cmd; - svn_error_t *(*handler)(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - const apr_array_header_t *params, - ra_svn_driver_state_t *ds); + cmd_handler_t handler; } ra_svn_edit_cmds[] = { { "change-file-prop", ra_svn_handle_change_file_prop }, { "open-file", ra_svn_handle_open_file }, @@ -903,12 +966,98 @@ static const struct { { NULL } }; +/* All editor commands are kept in a collision-free hash table. */ + +/* Hash table entry. + It is similar to ra_svn_edit_cmds but uses our SVN string type. */ +typedef struct cmd_t { + svn_string_t cmd; + cmd_handler_t handler; +} cmd_t; + +/* The actual hash table. It will be filled once before first usage. + + If you add more commands, you may have to tweak the table size to + eliminate collisions. Alternatively, you may modify the hash function. + + Be sure to initialize all elements with 0 as the has conflict detection + will rely on it (see init_cmd_hash). + */ +#define CMD_HASH_SIZE 67 +static cmd_t cmd_hash[CMD_HASH_SIZE] = { { { NULL } } }; + +/* Init flag that controls CMD_HASH's atomic initialization. */ +static volatile svn_atomic_t cmd_hash_initialized = FALSE; + +/* Super-fast hash function that works very well with the structure of our + command words. It produces no conflicts for them. + + Return the index within CMD_HASH that a command NAME of LEN chars would + be found. LEN > 0. + */ +static apr_size_t +cmd_hash_func(const char *name, + apr_size_t len) +{ + apr_size_t value = (apr_byte_t)(name[0] - 'a') % 8 + + 1 * (apr_byte_t)(name[len - 1] - 'a') % 8 + + 10 * (len - 7); + return value % CMD_HASH_SIZE; +} + +/* svn_atomic__init_once callback that fills the CMD_HASH table. It will + error out on hash collisions. BATON and POOL are not used. */ +static svn_error_t * +init_cmd_hash(void *baton, + apr_pool_t *pool) +{ + int i; + for (i = 0; ra_svn_edit_cmds[i].cmd; i++) + { + apr_size_t len = strlen(ra_svn_edit_cmds[i].cmd); + apr_size_t value = cmd_hash_func(ra_svn_edit_cmds[i].cmd, len); + SVN_ERR_ASSERT(cmd_hash[value].cmd.data == NULL); + + cmd_hash[value].cmd.data = ra_svn_edit_cmds[i].cmd; + cmd_hash[value].cmd.len = len; + cmd_hash[value].handler = ra_svn_edit_cmds[i].handler; + } + + return SVN_NO_ERROR; +} + +/* Return the command handler function for the command name CMD. + Return NULL if no such handler exists */ +static cmd_handler_t +cmd_lookup(const char *cmd) +{ + apr_size_t value; + apr_size_t len = strlen(cmd); + + /* Malicious data that our hash function may not like? */ + if (len == 0) + return NULL; + + /* Hash lookup. */ + value = cmd_hash_func(cmd, len); + + /* Hit? */ + if (cmd_hash[value].cmd.len != len) + return NULL; + + if (memcmp(cmd_hash[value].cmd.data, cmd, len)) + return NULL; + + /* Yes! */ + return cmd_hash[value].handler; +} + static svn_error_t *blocked_write(svn_ra_svn_conn_t *conn, apr_pool_t *pool, void *baton) { ra_svn_driver_state_t *ds = baton; const char *cmd; - apr_array_header_t *params; + svn_ra_svn__list_t *params; /* We blocked trying to send an error. Read and discard an editing * command in order to avoid deadlock. */ @@ -931,13 +1080,16 @@ svn_error_t *svn_ra_svn_drive_editor2(svn_ra_svn_conn_t *conn, ra_svn_driver_state_t state; apr_pool_t *subpool = svn_pool_create(pool); const char *cmd; - int i; svn_error_t *err, *write_err; - apr_array_header_t *params; + svn_ra_svn__list_t *params; + + SVN_ERR(svn_atomic__init_once(&cmd_hash_initialized, init_cmd_hash, NULL, + pool)); state.editor = editor; state.edit_baton = edit_baton; - state.tokens = apr_hash_make(pool); + state.tokens = svn_hash__make(pool); + state.last_token = NULL; state.aborted = aborted; state.done = FALSE; state.pool = pool; @@ -948,15 +1100,18 @@ svn_error_t *svn_ra_svn_drive_editor2(svn_ra_svn_conn_t *conn, while (!state.done) { svn_pool_clear(subpool); + + /* WRT to applying I/O limits, treat each editor command as a separate + * protocol command. */ + svn_ra_svn__reset_command_io_counters(conn); if (editor) { + cmd_handler_t handler; SVN_ERR(svn_ra_svn__read_tuple(conn, subpool, "wl", &cmd, ¶ms)); - for (i = 0; ra_svn_edit_cmds[i].cmd; i++) - if (strcmp(cmd, ra_svn_edit_cmds[i].cmd) == 0) - break; + handler = cmd_lookup(cmd); - if (ra_svn_edit_cmds[i].cmd) - err = (*ra_svn_edit_cmds[i].handler)(conn, subpool, params, &state); + if (handler) + err = (*handler)(conn, subpool, params, &state); else if (strcmp(cmd, "failure") == 0) { /* While not really an editor command this can occur when @@ -964,7 +1119,7 @@ svn_error_t *svn_ra_svn_drive_editor2(svn_ra_svn_conn_t *conn, command */ if (aborted) *aborted = TRUE; - err = svn_ra_svn__handle_failure_status(params, pool); + err = svn_ra_svn__handle_failure_status(params); return svn_error_compose_create( err, editor->abort_edit(edit_baton, subpool)); |