You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2015/08/06 15:08:33 UTC
svn commit: r1694490 - in /subversion/trunk: ./
subversion/include/private/svn_ra_svn_private.h
subversion/libsvn_ra_svn/client.c subversion/libsvn_ra_svn/editorp.c
subversion/libsvn_ra_svn/marshal.c
Author: stefan2
Date: Thu Aug 6 13:08:33 2015
New Revision: 1694490
URL: http://svn.apache.org/r1694490
Log:
Merge the ra-svn-tuning development branch to trunk and remove the
BRANCH-README file. There were no conflicts.
Changes brought by this branch:
- More efficient protocol command dispatch.
- Efficient string handling (svn_string_t instead of const char *).
- Throughput improvements by providing optimized code paths
- Widen the internal 32 bit item ID to 64 bit to eliminate any chance of
overflow in the future.
Modified:
subversion/trunk/ (props changed)
subversion/trunk/subversion/include/private/svn_ra_svn_private.h
subversion/trunk/subversion/libsvn_ra_svn/client.c
subversion/trunk/subversion/libsvn_ra_svn/editorp.c
subversion/trunk/subversion/libsvn_ra_svn/marshal.c
Propchange: subversion/trunk/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Thu Aug 6 13:08:33 2015
@@ -64,6 +64,7 @@
/subversion/branches/performance:979193,980118,981087,981090,981189,981194,981287,981684,981827,982043,982355,983398,983406,983430,983474,983488,983490,983760,983764,983766,983770,984927,984973,984984,985014,985037,985046,985472,985477,985482,985487-985488,985493,985497,985500,985514,985601,985603,985606,985669,985673,985695,985697,986453,986465,986485,986491-986492,986517,986521,986605,986608,986817,986832,987865,987868-987869,987872,987886-987888,987893,988319,988898,990330,990533,990535-990537,990541,990568,990572,990574-990575,990600,990759,992899,992904,992911,993127,993141,994956,995478,995507,995603,998012,998858,999098,1001413,1001417,1004291,1022668,1022670,1022676,1022715,1022719,1025660,1025672,1027193,1027203,1027206,1027214,1027227,1028077,1028092,1028094,1028104,1028107,1028111,1028354,1029038,1029042-1029043,1029054-1029055,1029062-1029063,1029078,1029080,1029090,1029092-1029093,1029111,1029151,1029158,1029229-1029230,1029232,1029335-1029336,1029339-1029340,1029342,10
29344,1030763,1030827,1031203,1031235,1032285,1032333,1033040,1033057,1033294,1035869,1035882,1039511,1043705,1053735,1056015,1066452,1067683,1067697-1078365
/subversion/branches/pin-externals:1643757-1659392
/subversion/branches/py-tests-as-modules:956579-1033052
+/subversion/branches/ra-svn-tuning:1658201-1694489
/subversion/branches/ra_serf-digest-authn:875693-876404
/subversion/branches/reintegrate-improvements:873853-874164
/subversion/branches/remote-only-status:1581845-1586090
Modified: subversion/trunk/subversion/include/private/svn_ra_svn_private.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/private/svn_ra_svn_private.h?rev=1694490&r1=1694489&r2=1694490&view=diff
==============================================================================
--- subversion/trunk/subversion/include/private/svn_ra_svn_private.h (original)
+++ subversion/trunk/subversion/include/private/svn_ra_svn_private.h Thu Aug 6 13:08:33 2015
@@ -347,7 +347,7 @@ svn_error_t *
svn_ra_svn__write_cmd_open_root(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
svn_revnum_t rev,
- const char *token);
+ const svn_string_t *token);
/** Send a "delete-entry" command over connection @a conn. Delete the
* @a path at optional revision @a rev below @a parent_token.
@@ -358,7 +358,7 @@ svn_ra_svn__write_cmd_delete_entry(svn_r
apr_pool_t *pool,
const char *path,
svn_revnum_t rev,
- const char *parent_token);
+ const svn_string_t *parent_token);
/** Send a "add-dir" command over connection @a conn. Add a new directory
* node named @a path under the directory identified by @a parent_token.
@@ -370,8 +370,8 @@ svn_error_t *
svn_ra_svn__write_cmd_add_dir(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *path,
- const char *parent_token,
- const char *token,
+ const svn_string_t *parent_token,
+ const svn_string_t *token,
const char *copy_path,
svn_revnum_t copy_rev);
@@ -384,8 +384,8 @@ svn_error_t *
svn_ra_svn__write_cmd_open_dir(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *path,
- const char *parent_token,
- const char *token,
+ const svn_string_t *parent_token,
+ const svn_string_t *token,
svn_revnum_t rev);
/** Send a "change-dir-prop" command over connection @a conn. Set the
@@ -395,7 +395,7 @@ svn_ra_svn__write_cmd_open_dir(svn_ra_sv
svn_error_t *
svn_ra_svn__write_cmd_change_dir_prop(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
- const char *token,
+ const svn_string_t *token,
const char *name,
const svn_string_t *value);
@@ -406,7 +406,7 @@ svn_ra_svn__write_cmd_change_dir_prop(sv
svn_error_t *
svn_ra_svn__write_cmd_close_dir(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
- const char *token);
+ const svn_string_t *token);
/** Send a "absent-dir" command over connection @a conn. Directory node
* named @a path under the directory identified by @a parent_token is
@@ -416,7 +416,7 @@ svn_error_t *
svn_ra_svn__write_cmd_absent_dir(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *path,
- const char *parent_token);
+ const svn_string_t *parent_token);
/** Send a "add-file" command over connection @a conn. Add a new file
* node named @a path under the directory identified by @a parent_token.
@@ -428,8 +428,8 @@ svn_error_t *
svn_ra_svn__write_cmd_add_file(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *path,
- const char *parent_token,
- const char *token,
+ const svn_string_t *parent_token,
+ const svn_string_t *token,
const char *copy_path,
svn_revnum_t copy_rev);
@@ -442,8 +442,8 @@ svn_error_t *
svn_ra_svn__write_cmd_open_file(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *path,
- const char *parent_token,
- const char *token,
+ const svn_string_t *parent_token,
+ const svn_string_t *token,
svn_revnum_t rev);
/** Send a "change-file-prop" command over connection @a conn. Set the
@@ -453,7 +453,7 @@ svn_ra_svn__write_cmd_open_file(svn_ra_s
svn_error_t *
svn_ra_svn__write_cmd_change_file_prop(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
- const char *token,
+ const svn_string_t *token,
const char *name,
const svn_string_t *value);
@@ -465,7 +465,7 @@ svn_ra_svn__write_cmd_change_file_prop(s
svn_error_t *
svn_ra_svn__write_cmd_close_file(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
- const char *token,
+ const svn_string_t *token,
const char *text_checksum);
/** Send a "absent-file" command over connection @a conn. File node
@@ -476,7 +476,7 @@ svn_error_t *
svn_ra_svn__write_cmd_absent_file(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *path,
- const char *parent_token);
+ const svn_string_t *parent_token);
/** Send a "apply-textdelta" command over connection @a conn. Starts a
* series of text deltas to be applied to the file identified by @a token.
@@ -486,7 +486,7 @@ svn_ra_svn__write_cmd_absent_file(svn_ra
svn_error_t *
svn_ra_svn__write_cmd_apply_textdelta(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
- const char *token,
+ const svn_string_t *token,
const char *base_checksum);
/** Send a "textdelta-chunk" command over connection @a conn. Apply
@@ -496,7 +496,7 @@ svn_ra_svn__write_cmd_apply_textdelta(sv
svn_error_t *
svn_ra_svn__write_cmd_textdelta_chunk(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
- const char *token,
+ const svn_string_t *token,
const svn_string_t *chunk);
/** Send a "textdelta-end" command over connection @a conn. Ends the
@@ -506,7 +506,7 @@ svn_ra_svn__write_cmd_textdelta_chunk(sv
svn_error_t *
svn_ra_svn__write_cmd_textdelta_end(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
- const char *token);
+ const svn_string_t *token);
/** Send a "close-edit" command over connection @a conn. Ends the editor
* drive (successfully). Use @a pool for allocations.
@@ -790,7 +790,7 @@ svn_error_t *
svn_ra_svn__write_cmd_unlock(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *path,
- const char *token,
+ const svn_string_t *token,
svn_boolean_t break_lock);
/** Send a "get-lock" command over connection @a conn.
Modified: subversion/trunk/subversion/libsvn_ra_svn/client.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_svn/client.c?rev=1694490&r1=1694489&r2=1694490&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_svn/client.c (original)
+++ subversion/trunk/subversion/libsvn_ra_svn/client.c Thu Aug 6 13:08:33 2015
@@ -2291,7 +2291,7 @@ static svn_error_t *ra_svn_unlock_compat
const void *key;
const char *path;
void *val;
- const char *token;
+ const svn_string_t *token;
svn_error_t *err, *callback_err = NULL;
svn_pool_clear(iterpool);
@@ -2299,7 +2299,7 @@ static svn_error_t *ra_svn_unlock_compat
apr_hash_this(hi, &key, NULL, &val);
path = key;
if (strcmp(val, "") != 0)
- token = val;
+ token = svn_string_create(val, iterpool);
else
token = NULL;
Modified: subversion/trunk/subversion/libsvn_ra_svn/editorp.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_svn/editorp.c?rev=1694490&r1=1694489&r2=1694490&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_svn/editorp.c (original)
+++ subversion/trunk/subversion/libsvn_ra_svn/editorp.c Thu Aug 6 13:08:33 2015
@@ -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)
-{
- return apr_psprintf(pool, "%c%d", type, eb->next_token++);
+static svn_string_t *
+make_token(char type,
+ ra_svn_edit_baton_t *eb,
+ apr_pool_t *pool)
+{
+ 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(voi
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
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(cons
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(cons
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(con
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,
@@ -480,6 +496,7 @@ static ra_svn_token_entry_t *store_token
entry->pool = pool;
apr_hash_set(ds->tokens, entry->token->data, entry->token->len, entry);
+ ds->last_token = entry;
return entry;
}
@@ -489,7 +506,16 @@ static svn_error_t *lookup_token(ra_svn_
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"));
@@ -883,11 +909,15 @@ static svn_error_t *ra_svn_handle_finish
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 apr_array_header_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 },
@@ -911,6 +941,92 @@ 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)
{
@@ -939,13 +1055,16 @@ svn_error_t *svn_ra_svn_drive_editor2(sv
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_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;
@@ -958,13 +1077,12 @@ svn_error_t *svn_ra_svn_drive_editor2(sv
svn_pool_clear(subpool);
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
Modified: subversion/trunk/subversion/libsvn_ra_svn/marshal.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_svn/marshal.c?rev=1694490&r1=1694489&r2=1694490&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_svn/marshal.c (original)
+++ subversion/trunk/subversion/libsvn_ra_svn/marshal.c Thu Aug 6 13:08:33 2015
@@ -60,9 +60,10 @@
/* We don't use "words" longer than this in our protocol. The longest word
* we are currently using is only about 16 chars long but we leave room for
- * longer future capability and command names.
+ * longer future capability and command names. See read_item() to understand
+ * why MAX_WORD_LENGTH - 1 should be a multiple of 8.
*/
-#define MAX_WORD_LENGTH 31
+#define MAX_WORD_LENGTH 25
/* The generic parsers will use the following value to limit the recursion
* depth to some reasonable value. The current protocol implementation
@@ -512,22 +513,65 @@ svn_ra_svn__write_number(svn_ra_svn_conn
return write_number(conn, pool, number, ' ');
}
+/* Write string S of length LEN to TARGET and return the first position
+ after the written data.
+
+ NOTE: This function assumes that TARGET has enough room for S, the LEN
+ prefix and the required separators. The available buffer size
+ should be SVN_INT64_BUFFER_SIZE + LEN + 1 to avoid any chance of
+ overflow.
+ */
+static char *
+write_ncstring_quick(char *target,
+ const char *s,
+ apr_size_t len)
+{
+ /* Write string length. */
+ if (len < 10)
+ {
+ *target = (char)(len + '0');
+ target++;
+ }
+ else
+ {
+ target += svn__ui64toa(target, len);
+ }
+
+ /* Separator & contents. */
+ target[0] = ':';
+ memcpy(target + 1, s, len);
+ target[len + 1] = ' ';
+
+ /* First location after the string. */
+ return target + len + 2;
+}
+
+
static svn_error_t *
svn_ra_svn__write_ncstring(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *s,
apr_size_t len)
{
- if (len < 10)
- {
- SVN_ERR(writebuf_writechar(conn, pool, (char)(len + '0')));
- SVN_ERR(writebuf_writechar(conn, pool, ':'));
+ apr_size_t needed = SVN_INT64_BUFFER_SIZE + len + 1;
+
+ /* In most cases, there is enough left room in the WRITE_BUF
+ the we can serialize directly into it. */
+ if (conn->write_pos + needed < sizeof(conn->write_buf))
+ {
+ /* Quick path. */
+ conn->write_pos = write_ncstring_quick(conn->write_buf
+ + conn->write_pos, s, len)
+ - conn->write_buf;
}
else
- SVN_ERR(write_number(conn, pool, len, ':'));
+ {
+ /* Slower fallback code. */
+ SVN_ERR(write_number(conn, pool, len, ':'));
- SVN_ERR(writebuf_write(conn, pool, s, len));
- SVN_ERR(writebuf_writechar(conn, pool, ' '));
+ SVN_ERR(writebuf_write(conn, pool, s, len));
+ SVN_ERR(writebuf_writechar(conn, pool, ' '));
+ }
return SVN_NO_ERROR;
}
@@ -755,6 +799,52 @@ write_tuple_string_opt(svn_ra_svn_conn_t
return str ? svn_ra_svn__write_string(conn, pool, str) : SVN_NO_ERROR;
}
+/* Optimized sending code for the "(s?)" pattern. */
+static svn_error_t *
+write_tuple_string_opt_list(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ const svn_string_t *str)
+{
+ apr_size_t needed;
+
+ /* Special case. */
+ if (!str)
+ return writebuf_write(conn, pool, "( ) ", 4);
+
+ /* If there is at least this much the room left in the WRITE_BUF,
+ we can serialize directly into it. */
+ needed = 2 /* open list */
+ + SVN_INT64_BUFFER_SIZE /* string length + separator */
+ + str->len /* string contents */
+ + 2; /* close list */
+
+ if (conn->write_pos + needed <= sizeof(conn->write_buf))
+ {
+ /* Quick path. */
+ /* Open list. */
+ char *p = conn->write_buf + conn->write_pos;
+ p[0] = '(';
+ p[1] = ' ';
+
+ /* Write string. */
+ p = write_ncstring_quick(p + 2, str->data, str->len);
+
+ /* Close list. */
+ p[0] = ')';
+ p[1] = ' ';
+ conn->write_pos = p + 2 - conn->write_buf;
+ }
+ else
+ {
+ /* Standard code path (fallback). */
+ SVN_ERR(svn_ra_svn__start_list(conn, pool));
+ SVN_ERR(svn_ra_svn__write_string(conn, pool, str));
+ SVN_ERR(svn_ra_svn__end_list(conn, pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
static svn_error_t *
write_tuple_start_list(svn_ra_svn_conn_t *conn,
apr_pool_t *pool)
@@ -809,14 +899,14 @@ static svn_error_t *
write_cmd_add_node(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *path,
- const char *parent_token,
- const char *token,
+ const svn_string_t *parent_token,
+ const svn_string_t *token,
const char *copy_path,
svn_revnum_t copy_rev)
{
SVN_ERR(write_tuple_cstring(conn, pool, path));
- SVN_ERR(write_tuple_cstring(conn, pool, parent_token));
- SVN_ERR(write_tuple_cstring(conn, pool, token));
+ SVN_ERR(write_tuple_string(conn, pool, parent_token));
+ SVN_ERR(write_tuple_string(conn, pool, token));
SVN_ERR(write_tuple_start_list(conn, pool));
SVN_ERR(write_tuple_cstring_opt(conn, pool, copy_path));
SVN_ERR(write_tuple_revision_opt(conn, pool, copy_rev));
@@ -829,13 +919,13 @@ static svn_error_t *
write_cmd_open_node(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *path,
- const char *parent_token,
- const char *token,
+ const svn_string_t *parent_token,
+ const svn_string_t *token,
svn_revnum_t rev)
{
SVN_ERR(write_tuple_cstring(conn, pool, path));
- SVN_ERR(write_tuple_cstring(conn, pool, parent_token));
- SVN_ERR(write_tuple_cstring(conn, pool, token));
+ SVN_ERR(write_tuple_string(conn, pool, parent_token));
+ SVN_ERR(write_tuple_string(conn, pool, token));
SVN_ERR(write_tuple_start_list(conn, pool));
SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
SVN_ERR(write_tuple_end_list(conn, pool));
@@ -846,15 +936,13 @@ write_cmd_open_node(svn_ra_svn_conn_t *c
static svn_error_t *
write_cmd_change_node_prop(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
- const char *token,
+ const svn_string_t *token,
const char *name,
const svn_string_t *value)
{
- SVN_ERR(write_tuple_cstring(conn, pool, token));
+ SVN_ERR(write_tuple_string(conn, pool, token));
SVN_ERR(write_tuple_cstring(conn, pool, name));
- SVN_ERR(write_tuple_start_list(conn, pool));
- SVN_ERR(write_tuple_string_opt(conn, pool, value));
- SVN_ERR(write_tuple_end_list(conn, pool));
+ SVN_ERR(write_tuple_string_opt_list(conn, pool, value));
return SVN_NO_ERROR;
}
@@ -863,10 +951,10 @@ static svn_error_t *
write_cmd_absent_node(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *path,
- const char *token)
+ const svn_string_t *token)
{
SVN_ERR(write_tuple_cstring(conn, pool, path));
- SVN_ERR(write_tuple_cstring(conn, pool, token));
+ SVN_ERR(write_tuple_string(conn, pool, token));
return SVN_NO_ERROR;
}
@@ -1060,17 +1148,40 @@ static svn_error_t *read_item(svn_ra_svn
char *p = buffer + 1;
buffer[0] = c;
- while (1)
+ if (conn->read_ptr + MAX_WORD_LENGTH <= conn->read_end)
{
- SVN_ERR(readbuf_getchar(conn, pool, p));
- if (!svn_ctype_isalnum(*p) && *p != '-')
- break;
+ /* Fast path: we can simply take a chunk from the read
+ * buffer and inspect it with no overflow checks etc.
+ *
+ * Copying these 24 bytes unconditionally is also faster
+ * than a variable-sized memcpy. Note that P is at BUFFER[1].
+ */
+ memcpy(p, conn->read_ptr, MAX_WORD_LENGTH - 1);
+ *end = 0;
+
+ /* This will terminate at P == END because of *END == NUL. */
+ while (svn_ctype_isalnum(*p) || *p == '-')
+ ++p;
- if (++p == end)
- return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
- _("Word is too long"));
+ /* Only now do we mark data as actually read. */
+ conn->read_ptr += p - buffer;
+ }
+ else
+ {
+ /* Slow path. Byte-by-byte copying and checking for
+ * input and output buffer boundaries. */
+ for (p = buffer + 1; p != end; ++p)
+ {
+ SVN_ERR(readbuf_getchar(conn, pool, p));
+ if (!svn_ctype_isalnum(*p) && *p != '-')
+ break;
+ }
}
+ if (p == end)
+ return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
+ _("Word is too long"));
+
c = *p;
*p = '\0';
@@ -1673,13 +1784,13 @@ svn_error_t *
svn_ra_svn__write_cmd_open_root(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
svn_revnum_t rev,
- const char *token)
+ const svn_string_t *token)
{
SVN_ERR(writebuf_write_literal(conn, pool, "( open-root ( "));
SVN_ERR(write_tuple_start_list(conn, pool));
SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
SVN_ERR(write_tuple_end_list(conn, pool));
- SVN_ERR(write_tuple_cstring(conn, pool, token));
+ SVN_ERR(write_tuple_string(conn, pool, token));
SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
return SVN_NO_ERROR;
@@ -1690,14 +1801,14 @@ svn_ra_svn__write_cmd_delete_entry(svn_r
apr_pool_t *pool,
const char *path,
svn_revnum_t rev,
- const char *token)
+ const svn_string_t *token)
{
SVN_ERR(writebuf_write_literal(conn, pool, "( delete-entry ( "));
SVN_ERR(write_tuple_cstring(conn, pool, path));
SVN_ERR(write_tuple_start_list(conn, pool));
SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
SVN_ERR(write_tuple_end_list(conn, pool));
- SVN_ERR(write_tuple_cstring(conn, pool, token));
+ SVN_ERR(write_tuple_string(conn, pool, token));
SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
return SVN_NO_ERROR;
@@ -1707,8 +1818,8 @@ svn_error_t *
svn_ra_svn__write_cmd_add_dir(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *path,
- const char *parent_token,
- const char *token,
+ const svn_string_t *parent_token,
+ const svn_string_t *token,
const char *copy_path,
svn_revnum_t copy_rev)
{
@@ -1724,8 +1835,8 @@ svn_error_t *
svn_ra_svn__write_cmd_open_dir(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *path,
- const char *parent_token,
- const char *token,
+ const svn_string_t *parent_token,
+ const svn_string_t *token,
svn_revnum_t rev)
{
SVN_ERR(writebuf_write_literal(conn, pool, "( open-dir ( "));
@@ -1738,7 +1849,7 @@ svn_ra_svn__write_cmd_open_dir(svn_ra_sv
svn_error_t *
svn_ra_svn__write_cmd_change_dir_prop(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
- const char *token,
+ const svn_string_t *token,
const char *name,
const svn_string_t *value)
{
@@ -1752,10 +1863,10 @@ svn_ra_svn__write_cmd_change_dir_prop(sv
svn_error_t *
svn_ra_svn__write_cmd_close_dir(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
- const char *token)
+ const svn_string_t *token)
{
SVN_ERR(writebuf_write_literal(conn, pool, "( close-dir ( "));
- SVN_ERR(write_tuple_cstring(conn, pool, token));
+ SVN_ERR(write_tuple_string(conn, pool, token));
SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
return SVN_NO_ERROR;
@@ -1765,7 +1876,7 @@ svn_error_t *
svn_ra_svn__write_cmd_absent_dir(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *path,
- const char *parent_token)
+ const svn_string_t *parent_token)
{
SVN_ERR(writebuf_write_literal(conn, pool, "( absent-dir ( "));
SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token));
@@ -1778,8 +1889,8 @@ svn_error_t *
svn_ra_svn__write_cmd_add_file(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *path,
- const char *parent_token,
- const char *token,
+ const svn_string_t *parent_token,
+ const svn_string_t *token,
const char *copy_path,
svn_revnum_t copy_rev)
{
@@ -1795,8 +1906,8 @@ svn_error_t *
svn_ra_svn__write_cmd_open_file(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *path,
- const char *parent_token,
- const char *token,
+ const svn_string_t *parent_token,
+ const svn_string_t *token,
svn_revnum_t rev)
{
SVN_ERR(writebuf_write_literal(conn, pool, "( open-file ( "));
@@ -1809,7 +1920,7 @@ svn_ra_svn__write_cmd_open_file(svn_ra_s
svn_error_t *
svn_ra_svn__write_cmd_change_file_prop(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
- const char *token,
+ const svn_string_t *token,
const char *name,
const svn_string_t *value)
{
@@ -1823,11 +1934,11 @@ svn_ra_svn__write_cmd_change_file_prop(s
svn_error_t *
svn_ra_svn__write_cmd_close_file(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
- const char *token,
+ const svn_string_t *token,
const char *text_checksum)
{
SVN_ERR(writebuf_write_literal(conn, pool, "( close-file ( "));
- SVN_ERR(write_tuple_cstring(conn, pool, token));
+ SVN_ERR(write_tuple_string(conn, pool, token));
SVN_ERR(write_tuple_start_list(conn, pool));
SVN_ERR(write_tuple_cstring_opt(conn, pool, text_checksum));
SVN_ERR(write_tuple_end_list(conn, pool));
@@ -1840,7 +1951,7 @@ svn_error_t *
svn_ra_svn__write_cmd_absent_file(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *path,
- const char *parent_token)
+ const svn_string_t *parent_token)
{
SVN_ERR(writebuf_write_literal(conn, pool, "( absent-file ( "));
SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token));
@@ -1852,11 +1963,11 @@ svn_ra_svn__write_cmd_absent_file(svn_ra
svn_error_t *
svn_ra_svn__write_cmd_textdelta_chunk(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
- const char *token,
+ const svn_string_t *token,
const svn_string_t *chunk)
{
SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-chunk ( "));
- SVN_ERR(write_tuple_cstring(conn, pool, token));
+ SVN_ERR(write_tuple_string(conn, pool, token));
SVN_ERR(write_tuple_string(conn, pool, chunk));
SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
@@ -1866,10 +1977,10 @@ svn_ra_svn__write_cmd_textdelta_chunk(sv
svn_error_t *
svn_ra_svn__write_cmd_textdelta_end(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
- const char *token)
+ const svn_string_t *token)
{
SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-end ( "));
- SVN_ERR(write_tuple_cstring(conn, pool, token));
+ SVN_ERR(write_tuple_string(conn, pool, token));
SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
return SVN_NO_ERROR;
@@ -1878,11 +1989,11 @@ svn_ra_svn__write_cmd_textdelta_end(svn_
svn_error_t *
svn_ra_svn__write_cmd_apply_textdelta(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
- const char *token,
+ const svn_string_t *token,
const char *base_checksum)
{
SVN_ERR(writebuf_write_literal(conn, pool, "( apply-textdelta ( "));
- SVN_ERR(write_tuple_cstring(conn, pool, token));
+ SVN_ERR(write_tuple_string(conn, pool, token));
SVN_ERR(write_tuple_start_list(conn, pool));
SVN_ERR(write_tuple_cstring_opt(conn, pool, base_checksum));
SVN_ERR(write_tuple_end_list(conn, pool));
@@ -2020,9 +2131,7 @@ svn_ra_svn__write_cmd_change_rev_prop2(s
SVN_ERR(writebuf_write_literal(conn, pool, "( change-rev-prop2 ( "));
SVN_ERR(write_tuple_revision(conn, pool, rev));
SVN_ERR(write_tuple_cstring(conn, pool, name));
- SVN_ERR(write_tuple_start_list(conn, pool));
- SVN_ERR(write_tuple_string_opt(conn, pool, value));
- SVN_ERR(write_tuple_end_list(conn, pool));
+ SVN_ERR(write_tuple_string_opt_list(conn, pool, value));
SVN_ERR(write_tuple_start_list(conn, pool));
SVN_ERR(write_tuple_boolean(conn, pool, dont_care));
SVN_ERR(write_tuple_string_opt(conn, pool, old_value));
@@ -2274,14 +2383,12 @@ svn_error_t *
svn_ra_svn__write_cmd_unlock(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *path,
- const char *token,
+ const svn_string_t *token,
svn_boolean_t break_lock)
{
SVN_ERR(writebuf_write_literal(conn, pool, "( unlock ( "));
SVN_ERR(write_tuple_cstring(conn, pool, path));
- SVN_ERR(write_tuple_start_list(conn, pool));
- SVN_ERR(write_tuple_cstring_opt(conn, pool, token));
- SVN_ERR(write_tuple_end_list(conn, pool));
+ SVN_ERR(write_tuple_string_opt_list(conn, pool, token));
SVN_ERR(write_tuple_boolean(conn, pool, break_lock));
SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
@@ -2431,6 +2538,49 @@ svn_error_t *svn_ra_svn__write_cmd_failu
return writebuf_write_literal(conn, pool, ") ) ");
}
+/* Initializer for static svn_string_t . */
+#define STATIC_SVN_STRING(x) { x, sizeof(x) - 1 }
+
+/* Return a pre-cooked serialized representation for the changed path
+ flags NODE_KIND, TEXT_MODIFIED and PROPS_MODIFIED. If we don't
+ have a suitable pre-cooked string, return an empty string. */
+static const svn_string_t *
+changed_path_flags(svn_node_kind_t node_kind,
+ svn_boolean_t text_modified,
+ svn_boolean_t props_modified)
+{
+ const static svn_string_t file_flags[4]
+ = { STATIC_SVN_STRING(" ) ( 4:file false false ) ) "),
+ STATIC_SVN_STRING(" ) ( 4:file false true ) ) "),
+ STATIC_SVN_STRING(" ) ( 4:file true false ) ) "),
+ STATIC_SVN_STRING(" ) ( 4:file true true ) ) ") };
+
+ const static svn_string_t dir_flags[4]
+ = { STATIC_SVN_STRING(" ) ( 3:dir false false ) ) "),
+ STATIC_SVN_STRING(" ) ( 3:dir false true ) ) "),
+ STATIC_SVN_STRING(" ) ( 3:dir true false ) ) "),
+ STATIC_SVN_STRING(" ) ( 3:dir true true ) ) ") };
+
+ const static svn_string_t no_flags = STATIC_SVN_STRING("");
+
+ /* Select the array based on the NODE_KIND. */
+ const svn_string_t *flags;
+ if (node_kind == svn_node_file)
+ flags = file_flags;
+ else if (node_kind == svn_node_dir)
+ flags = dir_flags;
+ else
+ return &no_flags;
+
+ /* Select the correct array entry. */
+ if (text_modified)
+ flags += 2;
+ if (props_modified)
+ flags++;
+
+ return flags;
+}
+
svn_error_t *
svn_ra_svn__write_data_log_changed_path(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
@@ -2442,21 +2592,77 @@ svn_ra_svn__write_data_log_changed_path(
svn_boolean_t text_modified,
svn_boolean_t props_modified)
{
- SVN_ERR(write_tuple_start_list(conn, pool));
+ apr_size_t path_len = strlen(path);
+ apr_size_t copyfrom_len = copyfrom_path ? strlen(copyfrom_path) : 0;
+ const svn_string_t *flags_str = changed_path_flags(node_kind,
+ text_modified,
+ props_modified);
+
+ /* How much buffer space do we need (worst case)? */
+ apr_size_t needed = 2 /* list start */
+ + path_len + SVN_INT64_BUFFER_SIZE
+ /* path */
+ + 2 /* action */
+ + 2 + copyfrom_len + 2 * SVN_INT64_BUFFER_SIZE
+ /* list start + copy-from info */
+ + flags_str->len; /* flags and closing lists */
+
+ /* If the remaining buffer is big enough and we've got all parts,
+ directly copy into the buffer. */
+ if ( (conn->write_pos + needed <= sizeof(conn->write_buf))
+ && (flags_str->len > 0))
+ {
+ /* Quick path. */
+ /* Open list. */
+ char *p = conn->write_buf + conn->write_pos;
+ p[0] = '(';
+ p[1] = ' ';
+
+ /* Write path. */
+ p = write_ncstring_quick(p + 2, path, path_len);
+
+ /* Action */
+ p[0] = action;
+ p[1] = ' ';
+ p[2] = '(';
+
+ /* Copy-from info (if given) */
+ if (copyfrom_path)
+ {
+ p[3] = ' ';
+ p = write_ncstring_quick(p + 4, copyfrom_path, copyfrom_len);
+ p += svn__ui64toa(p, copyfrom_rev);
+ }
+ else
+ {
+ p += 3;
+ }
- SVN_ERR(write_tuple_cstring(conn, pool, path));
- SVN_ERR(writebuf_writechar(conn, pool, action));
- SVN_ERR(writebuf_writechar(conn, pool, ' '));
- SVN_ERR(write_tuple_start_list(conn, pool));
- SVN_ERR(write_tuple_cstring_opt(conn, pool, copyfrom_path));
- SVN_ERR(write_tuple_revision_opt(conn, pool, copyfrom_rev));
- SVN_ERR(write_tuple_end_list(conn, pool));
- SVN_ERR(write_tuple_start_list(conn, pool));
- SVN_ERR(write_tuple_cstring(conn, pool, svn_node_kind_to_word(node_kind)));
- SVN_ERR(write_tuple_boolean(conn, pool, text_modified));
- SVN_ERR(write_tuple_boolean(conn, pool, props_modified));
+ /* Close with flags. */
+ memcpy(p, flags_str->data, flags_str->len);
+ conn->write_pos = p + flags_str->len - conn->write_buf;
+ }
+ else
+ {
+ /* Standard code path (fallback). */
+ SVN_ERR(write_tuple_start_list(conn, pool));
- return writebuf_write_literal(conn, pool, ") ) ");
+ SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, path, path_len));
+ SVN_ERR(writebuf_writechar(conn, pool, action));
+ SVN_ERR(writebuf_writechar(conn, pool, ' '));
+ SVN_ERR(write_tuple_start_list(conn, pool));
+ SVN_ERR(write_tuple_cstring_opt(conn, pool, copyfrom_path));
+ SVN_ERR(write_tuple_revision_opt(conn, pool, copyfrom_rev));
+ SVN_ERR(write_tuple_end_list(conn, pool));
+ SVN_ERR(write_tuple_start_list(conn, pool));
+ SVN_ERR(write_tuple_cstring(conn, pool, svn_node_kind_to_word(node_kind)));
+ SVN_ERR(write_tuple_boolean(conn, pool, text_modified));
+ SVN_ERR(write_tuple_boolean(conn, pool, props_modified));
+
+ SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
+ }
+
+ return SVN_NO_ERROR;
}
svn_error_t *
@@ -2471,15 +2677,9 @@ svn_ra_svn__write_data_log_entry(svn_ra_
unsigned revprop_count)
{
SVN_ERR(write_tuple_revision(conn, pool, revision));
- SVN_ERR(write_tuple_start_list(conn, pool));
- SVN_ERR(write_tuple_string_opt(conn, pool, author));
- SVN_ERR(write_tuple_end_list(conn, pool));
- SVN_ERR(write_tuple_start_list(conn, pool));
- SVN_ERR(write_tuple_string_opt(conn, pool, date));
- SVN_ERR(write_tuple_end_list(conn, pool));
- SVN_ERR(write_tuple_start_list(conn, pool));
- SVN_ERR(write_tuple_string_opt(conn, pool, message));
- SVN_ERR(write_tuple_end_list(conn, pool));
+ SVN_ERR(write_tuple_string_opt_list(conn, pool, author));
+ SVN_ERR(write_tuple_string_opt_list(conn, pool, date));
+ SVN_ERR(write_tuple_string_opt_list(conn, pool, message));
SVN_ERR(write_tuple_boolean(conn, pool, has_children));
SVN_ERR(write_tuple_boolean(conn, pool, invalid_revnum));
SVN_ERR(svn_ra_svn__write_number(conn, pool, revprop_count));