Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/index.h URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/index.h?rev=1641647&r1=1641646&r2=1641647&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/index.h (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/index.h Tue Nov 25 16:26:48 2014 @@ -24,6 +24,7 @@ #define SVN_LIBSVN_FS__INDEX_H #include "fs.h" +#include "rev_file.h" /* Per-defined item index values. They are used to identify empty or * mandatory items. @@ -65,7 +66,7 @@ typedef struct svn_fs_x__p2l_entry_t apr_off_t size; /* type of the item (see SVN_FS_X__ITEM_TYPE_*) defines */ - unsigned type; + apr_uint32_t type; /* modified FNV-1a checksum. 0 if unknown checksum */ apr_uint32_t fnv1_checksum; @@ -86,20 +87,21 @@ svn_fs_x__p2l_entry_dup(const svn_fs_x__ apr_pool_t *pool); /* Open / create a log-to-phys index file with the full file path name - * FILE_NAME. Return the open file in *PROTO_INDEX and use POOL for - * allocations. + * FILE_NAME. Return the open file in *PROTO_INDEX allocated in + * RESULT_POOL. */ svn_error_t * svn_fs_x__l2p_proto_index_open(apr_file_t **proto_index, const char *file_name, - apr_pool_t *pool); + apr_pool_t *result_pool); /* Call this function before adding entries for the next revision to the - * log-to-phys index file in PROTO_INDEX. Use POOL for allocations. + * log-to-phys index file in PROTO_INDEX. Use SCRATCH_POOL for temporary + * allocations. */ svn_error_t * svn_fs_x__l2p_proto_index_add_revision(apr_file_t *proto_index, - apr_pool_t *pool); + apr_pool_t *scratch_pool); /* Add a new mapping, ITEM_INDEX to the (OFFSET, SUB_ITEM) pair, to log-to- * phys index file in PROTO_INDEX. Please note that mappings may be added @@ -109,72 +111,84 @@ svn_fs_x__l2p_proto_index_add_revision(a * that is already implied for all item indexes not explicitly given a * mapping. * - * Use POOL for allocations. + * Use SCRATCH_POOL for temporary allocations. */ svn_error_t * svn_fs_x__l2p_proto_index_add_entry(apr_file_t *proto_index, apr_off_t offset, apr_uint32_t sub_item, apr_uint64_t item_index, - apr_pool_t *pool); + apr_pool_t *scratch_pool); -/* Use the proto index file stored at PROTO_FILE_NAME and construct the - * final log-to-phys index file at FILE_NAME. The first revision will +/* Use the proto index file stored at PROTO_FILE_NAME, construct the final + * log-to-phys index and append it to INDEX_FILE. The first revision will * be REVISION, entries to the next revision will be assigned to REVISION+1 - * and so forth. Use POOL for allocations. + * and so forth. + * + * Return the MD5 checksum of the on-disk index data in *CHECKSUM, allocated + * in RESULT_POOL. Use SCRATCH_POOL for temporary allocations. */ svn_error_t * -svn_fs_x__l2p_index_create(svn_fs_t *fs, - const char *file_name, +svn_fs_x__l2p_index_append(svn_checksum_t **checksum, + svn_fs_t *fs, + apr_file_t *index_file, const char *proto_file_name, svn_revnum_t revision, - apr_pool_t *pool); + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /* Open / create a phys-to-log index file with the full file path name - * FILE_NAME. Return the open file in *PROTO_INDEX and use POOL for - * allocations. + * FILE_NAME. Return the open file in *PROTO_INDEX allocated in + * RESULT_POOL. */ svn_error_t * svn_fs_x__p2l_proto_index_open(apr_file_t **proto_index, const char *file_name, - apr_pool_t *pool); + apr_pool_t *result_pool); /* Add a new mapping ENTRY to the phys-to-log index file in PROTO_INDEX. * The entries must be added in ascending offset order and must not leave * intermittent ranges uncovered. The revision value in ENTRY may be - * SVN_INVALID_REVISION. Use POOL for allocations. + * SVN_INVALID_REVISION. Use SCRATCH_POOL for temporary allocations. */ svn_error_t * svn_fs_x__p2l_proto_index_add_entry(apr_file_t *proto_index, - svn_fs_x__p2l_entry_t *entry, - apr_pool_t *pool); + const svn_fs_x__p2l_entry_t *entry, + apr_pool_t *scratch_pool); /* Set *NEXT_OFFSET to the first offset behind the last entry in the * phys-to-log proto index file PROTO_INDEX. This will be 0 for empty - * index files. Use POOL for temporary allocations. + * index files. Use SCRATCH_POOL for temporary allocations. */ svn_error_t * svn_fs_x__p2l_proto_index_next_offset(apr_off_t *next_offset, apr_file_t *proto_index, - apr_pool_t *pool); + apr_pool_t *scratch_pool); -/* Use the proto index file stored at PROTO_FILE_NAME and construct the - * final phys-to-log index file at FILE_NAME. Entries without a valid +/* Use the proto index file stored at PROTO_FILE_NAME, construct the final + * phys-to-log index and append it to INDEX_FILE. Entries without a valid * revision will be assigned to the REVISION given here. - * Use POOL for allocations. + * + * Return the MD5 checksum of the on-disk index data in *CHECKSUM, allocated + * in RESULT_POOL. Use SCRATCH_POOL for temporary allocations. */ svn_error_t * -svn_fs_x__p2l_index_create(svn_fs_t *fs, - const char *file_name, +svn_fs_x__p2l_index_append(svn_checksum_t **checksum, + svn_fs_t *fs, + apr_file_t *index_file, const char *proto_file_name, svn_revnum_t revision, - apr_pool_t *pool); + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /* Use the phys-to-log mapping files in FS to build a list of entries * that (at least partly) overlap with the range given by BLOCK_START * offset and BLOCK_SIZE in the rep / pack file containing REVISION. - * Return the array in *ENTRIES with svn_fs_fs__p2l_entry_t as elements. - * Use POOL for allocations. + * Return the array in *ENTRIES with svn_fs_fs__p2l_entry_t as elements, + * allocated in RESULT_POOL. REV_FILE determines whether to access single + * rev or pack file data. If that is not available anymore (neither in + * cache nor on disk), return an error. Use SCRATCH_POOL for temporary + * allocations. * * Note that (only) the first and the last mapping may cross a cluster * boundary. @@ -182,69 +196,122 @@ svn_fs_x__p2l_index_create(svn_fs_t *fs, svn_error_t * svn_fs_x__p2l_index_lookup(apr_array_header_t **entries, svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, svn_revnum_t revision, apr_off_t block_start, apr_off_t block_size, - apr_pool_t *pool); + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /* Use the phys-to-log mapping files in FS to return the entry for the * container or single item starting at global OFFSET in the rep file - * containing REVISION in *ENTRY. Sets *ENTRY to NULL if no item starts - * at exactly that offset. Use POOL for allocations. + * containing REVISION in*ENTRY, allocated in RESULT_POOL. Sets *ENTRY + * to NULL if no item starts at exactly that offset. REV_FILE determines + * whether to access single rev or pack file data. If that is not available + * anymore (neither in cache nor on disk), return an error. + * Use SCRATCH_POOL for temporary allocations. */ svn_error_t * svn_fs_x__p2l_entry_lookup(svn_fs_x__p2l_entry_t **entry, svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, svn_revnum_t revision, apr_off_t offset, - apr_pool_t *pool); + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /* Use the phys-to-log mapping files in FS to return the svn_fs_x__id_part_t * for the SUB_ITEM of the container starting at global OFFSET in the rep / - * pack file containing REVISION in *ITEM. Sets *ITEM to NULL if no element - * starts at exactly that offset or if it contains no more than SUB_ITEM - * sub-items. Use POOL for allocations. + * pack file containing REVISION in *ITEM, allocated in RESULT_POOL. Sets + * *ITEM to NULL if no element starts at exactly that offset or if it + * contains no more than SUB_ITEM sub-items. + * + * Use SCRATCH_POOL for temporary allocations. */ svn_error_t * svn_fs_x__p2l_item_lookup(svn_fs_x__id_part_t **item, svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, svn_revnum_t revision, apr_off_t offset, apr_uint32_t sub_item, - apr_pool_t *pool); + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); -/* Use the log-to-phys mapping files in FS to find the packed / non-packed / - * proto-rev file offset and container sub-item of ITEM_ID. *SUB_ITEM will - * be 0 for non-container items. Use POOL for allocations. +/* For ITEM_ID in FS, return the position in the respective rev or pack file + * in *ABSOLUTE_POSITION and the *SUB_ITEM number within the object at that + * location. *SUB_ITEM will be 0 for non-container items. + * + * REV_FILE determines whether to access single rev or pack file data. + * If that is not available anymore (neither in cache nor on disk), re-open + * the rev / pack file and retry to open the index file. For transaction + * content, REV_FILE may be NULL. + * + * Use SCRATCH_POOL for temporary allocations. */ svn_error_t * -svn_fs_x__item_offset(apr_off_t *offset, +svn_fs_x__item_offset(apr_off_t *absolute_position, apr_uint32_t *sub_item, svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, const svn_fs_x__id_part_t *item_id, - apr_pool_t *pool); + apr_pool_t *scratch_pool); /* Use the log-to-phys indexes in FS to determine the maximum item indexes * assigned to revision START_REV to START_REV + COUNT - 1. That is a * close upper limit to the actual number of items in the respective revs. - * Return the results in *MAX_IDS, allocated in POOL. + * Return the results in *MAX_IDS, allocated in RESULT_POOL. + * Use SCRATCH_POOL for temporary allocations. */ svn_error_t * svn_fs_x__l2p_get_max_ids(apr_array_header_t **max_ids, svn_fs_t *fs, svn_revnum_t start_rev, apr_size_t count, - apr_pool_t *pool); + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /* In *OFFSET, return the first OFFSET in the pack / rev file containing * REVISION in FS not covered by the log-to-phys index. - * Use POOL for allocations. + * Use SCRATCH_POOL for temporary allocations. */ svn_error_t * svn_fs_x__p2l_get_max_offset(apr_off_t *offset, svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, svn_revnum_t revision, - apr_pool_t *pool); + apr_pool_t *scratch_pool); + +/* Index (re-)creation utilities. + */ + +/* For FS, create a new L2P auto-deleting proto index file in POOL and return + * its name in *PROTONAME. All entries to write are given in ENTRIES and + * entries are of type svn_fs_fs__p2l_entry_t* (sic!). The ENTRIES array + * will be reordered. Give the proto index file the lifetime of RESULT_POOL + * and use SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__l2p_index_from_p2l_entries(const char **protoname, + svn_fs_t *fs, + apr_array_header_t *entries, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* For FS, create a new P2L auto-deleting proto index file in POOL and return + * its name in *PROTONAME. All entries to write are given in ENTRIES and + * of type svn_fs_fs__p2l_entry_t*. The FVN1 checksums are not taken from + * ENTRIES but are begin calculated from the current contents of REV_FILE + * as we go. Give the proto index file the lifetime of RESULT_POOL and use + * SCRATCH_POOL for temporary allocations. + */ +svn_error_t * +svn_fs_x__p2l_index_from_p2l_entries(const char **protoname, + svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + apr_array_header_t *entries, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /* Serialization and caching interface */
Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/lock.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/lock.c?rev=1641647&r1=1641646&r2=1641647&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/lock.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/lock.c Tue Nov 25 16:26:48 2014 @@ -386,7 +386,8 @@ add_to_digest(const char *fs_path, const char *index_digest_path; apr_hash_t *children; svn_lock_t *lock; - int i, original_count; + int i; + unsigned int original_count; SVN_ERR(digest_path_from_path(&index_digest_path, fs_path, index_path, pool)); Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/low_level.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/low_level.c?rev=1641647&r1=1641646&r2=1641647&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/low_level.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/low_level.c Tue Nov 25 16:26:48 2014 @@ -27,6 +27,7 @@ #include "private/svn_sorts_private.h" #include "private/svn_string_private.h" #include "private/svn_subr_private.h" +#include "private/svn_fspath.h" #include "../libsvn_fs/fs-loader.h" @@ -71,115 +72,126 @@ * various flags. */ #define MAX_CHANGE_LINE_LEN FSX_MAX_PATH_LEN + 256 -svn_error_t * -svn_fs_x__parse_revision_trailer(apr_off_t *root_offset, - apr_off_t *changes_offset, - svn_stringbuf_t *trailer, - svn_revnum_t rev) +/* Convert the C string in *TEXT to a revision number and return it in *REV. + * Overflows, negative values other than -1 and terminating characters other + * than 0x20 or 0x0 will cause an error. Set *TEXT to the first char after + * the initial separator or to EOS. + */ +static svn_error_t * +parse_revnum(svn_revnum_t *rev, + const char **text) { - int i, num_bytes; - const char *str; - - /* This cast should be safe since the maximum amount read, 64, will - never be bigger than the size of an int. */ - num_bytes = (int) trailer->len; - - /* The last byte should be a newline. */ - if (trailer->len == 0 || trailer->data[trailer->len - 1] != '\n') + const char *string = *text; + if ((string[0] == '-') && (string[1] == '1')) { - return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, - _("Revision file (r%ld) lacks trailing newline"), - rev); + *rev = SVN_INVALID_REVNUM; + string += 2; } - - /* Look for the next previous newline. */ - for (i = num_bytes - 2; i >= 0; i--) + else { - if (trailer->data[i] == '\n') - break; + SVN_ERR(svn_revnum_parse(rev, string, &string)); } - if (i < 0) - { - return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, - _("Final line in revision file (r%ld) longer " - "than 64 characters"), - rev); - } + if (*string == ' ') + ++string; + else if (*string != '\0') + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Invalid character in revision number")); - i++; - str = &trailer->data[i]; + *text = string; + return SVN_NO_ERROR; +} - /* find the next space */ - for ( ; i < (num_bytes - 2) ; i++) - if (trailer->data[i] == ' ') - break; +svn_error_t * +svn_fs_x__parse_footer(apr_off_t *l2p_offset, + svn_checksum_t **l2p_checksum, + apr_off_t *p2l_offset, + svn_checksum_t **p2l_checksum, + svn_stringbuf_t *footer, + svn_revnum_t rev, + apr_pool_t *result_pool) +{ + apr_int64_t val; + char *last_str = footer->data; - if (i == (num_bytes - 2)) - return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, - _("Final line in revision file r%ld missing space"), - rev); + /* Get the L2P offset. */ + const char *str = svn_cstring_tokenize(" ", &last_str); + if (str == NULL) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Invalid revision footer")); - if (root_offset) - { - apr_int64_t val; + SVN_ERR(svn_cstring_atoi64(&val, str)); + *l2p_offset = (apr_off_t)val; - trailer->data[i] = '\0'; - SVN_ERR(svn_cstring_atoi64(&val, str)); - *root_offset = (apr_off_t)val; - } + /* Get the L2P checksum. */ + str = svn_cstring_tokenize(" ", &last_str); + if (str == NULL) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Invalid revision footer")); - i++; - str = &trailer->data[i]; + SVN_ERR(svn_checksum_parse_hex(l2p_checksum, svn_checksum_md5, str, + result_pool)); - /* find the next newline */ - for ( ; i < num_bytes; i++) - if (trailer->data[i] == '\n') - break; + /* Get the P2L offset. */ + str = svn_cstring_tokenize(" ", &last_str); + if (str == NULL) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Invalid revision footer")); - if (changes_offset) - { - apr_int64_t val; + SVN_ERR(svn_cstring_atoi64(&val, str)); + *p2l_offset = (apr_off_t)val; - trailer->data[i] = '\0'; - SVN_ERR(svn_cstring_atoi64(&val, str)); - *changes_offset = (apr_off_t)val; - } + /* Get the P2L checksum. */ + str = svn_cstring_tokenize(" ", &last_str); + if (str == NULL) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Invalid revision footer")); + + SVN_ERR(svn_checksum_parse_hex(p2l_checksum, svn_checksum_md5, str, + result_pool)); return SVN_NO_ERROR; } svn_stringbuf_t * -svn_fs_x__unparse_revision_trailer(apr_off_t root_offset, - apr_off_t changes_offset, - apr_pool_t *pool) +svn_fs_x__unparse_footer(apr_off_t l2p_offset, + svn_checksum_t *l2p_checksum, + apr_off_t p2l_offset, + svn_checksum_t *p2l_checksum, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - return svn_stringbuf_createf(pool, - "%" APR_OFF_T_FMT " %" APR_OFF_T_FMT "\n", - root_offset, - changes_offset); + return svn_stringbuf_createf(result_pool, + "%" APR_OFF_T_FMT " %s %" APR_OFF_T_FMT " %s", + l2p_offset, + svn_checksum_to_cstring(l2p_checksum, + scratch_pool), + p2l_offset, + svn_checksum_to_cstring(p2l_checksum, + scratch_pool)); } /* Given a revision file FILE that has been pre-positioned at the beginning of a Node-Rev header block, read in that header block and store it in the apr_hash_t HEADERS. All allocations will be from - POOL. */ + RESULT_POOL. */ static svn_error_t * read_header_block(apr_hash_t **headers, svn_stream_t *stream, - apr_pool_t *pool) + apr_pool_t *result_pool) { - *headers = svn_hash__make(pool); + *headers = svn_hash__make(result_pool); while (1) { svn_stringbuf_t *header_str; const char *name, *value; - apr_ssize_t i = 0; - apr_ssize_t name_len; + apr_size_t i = 0; + apr_size_t name_len; svn_boolean_t eof; - SVN_ERR(svn_stream_readline(stream, &header_str, "\n", &eof, pool)); + SVN_ERR(svn_stream_readline(stream, &header_str, "\n", &eof, + result_pool)); if (eof || header_str->len == 0) break; /* end of header block */ @@ -199,13 +211,10 @@ read_header_block(apr_hash_t **headers, name = header_str->data; name_len = i; - /* Skip over the NULL byte and the space following it. */ - i += 2; - - if (i > header_str->len) + /* Check if we have enough data to parse. */ + if (i + 2 > header_str->len) { /* Restore the original line for the error. */ - i -= 2; header_str->data[i] = ':'; return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, _("Found malformed header '%s' in " @@ -213,6 +222,9 @@ read_header_block(apr_hash_t **headers, header_str->data); } + /* Skip over the NULL byte and the space following it. */ + i += 2; + value = header_str->data + i; /* header_str is safely in our pool, so we can use bits of it as @@ -226,7 +238,8 @@ read_header_block(apr_hash_t **headers, svn_error_t * svn_fs_x__parse_representation(representation_t **rep_p, svn_stringbuf_t *text, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { representation_t *rep; char *str; @@ -234,7 +247,7 @@ svn_fs_x__parse_representation(represent char *string = text->data; svn_checksum_t *checksum; - rep = apr_pcalloc(pool, sizeof(*rep)); + rep = apr_pcalloc(result_pool, sizeof(*rep)); *rep_p = rep; str = svn_cstring_tokenize(" ", &string); @@ -283,7 +296,8 @@ svn_fs_x__parse_representation(represent return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("Malformed text representation offset line in node-rev")); - SVN_ERR(svn_checksum_parse_hex(&checksum, svn_checksum_md5, str, pool)); + SVN_ERR(svn_checksum_parse_hex(&checksum, svn_checksum_md5, str, + scratch_pool)); if (checksum) memcpy(rep->md5_digest, checksum->digest, sizeof(rep->md5_digest)); @@ -297,7 +311,8 @@ svn_fs_x__parse_representation(represent return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("Malformed text representation offset line in node-rev")); - SVN_ERR(svn_checksum_parse_hex(&checksum, svn_checksum_sha1, str, pool)); + SVN_ERR(svn_checksum_parse_hex(&checksum, svn_checksum_sha1, str, + scratch_pool)); rep->has_sha1 = checksum != NULL; if (checksum) memcpy(rep->sha1_digest, checksum->digest, sizeof(rep->sha1_digest)); @@ -311,17 +326,22 @@ static svn_error_t * read_rep_offsets(representation_t **rep_p, char *string, const svn_fs_id_t *noderev_id, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_error_t *err = svn_fs_x__parse_representation(rep_p, - svn_stringbuf_create_wrap(string, pool), - pool); + svn_stringbuf_create_wrap(string, + scratch_pool), + result_pool, + scratch_pool); if (err) { - const svn_string_t *id_unparsed = svn_fs_x__id_unparse(noderev_id, pool); + const svn_string_t *id_unparsed; const char *where; - where = apr_psprintf(pool, + + id_unparsed = svn_fs_x__id_unparse(noderev_id, scratch_pool); + where = apr_psprintf(scratch_pool, _("While reading representation offsets " "for node-revision '%s':"), noderev_id ? id_unparsed->data : "(null)"); @@ -387,27 +407,28 @@ auto_unescape_path(const char *path, svn_error_t * svn_fs_x__read_noderev(node_revision_t **noderev_p, svn_stream_t *stream, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { apr_hash_t *headers; node_revision_t *noderev; char *value; const char *noderev_id; - SVN_ERR(read_header_block(&headers, stream, pool)); + SVN_ERR(read_header_block(&headers, stream, scratch_pool)); - noderev = apr_pcalloc(pool, sizeof(*noderev)); + noderev = apr_pcalloc(result_pool, sizeof(*noderev)); /* Read the node-rev id. */ value = svn_hash_gets(headers, HEADER_ID); if (value == NULL) - /* ### More information: filename/offset coordinates */ - return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, - _("Missing id field in node-rev")); + /* ### More information: filename/offset coordinates */ + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Missing id field in node-rev")); SVN_ERR(svn_stream_close(stream)); - noderev->id = svn_fs_x__id_parse(value, strlen(value), pool); + SVN_ERR(svn_fs_x__id_parse(&noderev->id, value, result_pool)); noderev_id = value; /* for error messages later */ /* Read the type. */ @@ -437,7 +458,7 @@ svn_fs_x__read_noderev(node_revision_t * if (value) { SVN_ERR(read_rep_offsets(&noderev->prop_rep, value, - noderev->id, pool)); + noderev->id, result_pool, scratch_pool)); } /* Get the data location. */ @@ -445,7 +466,7 @@ svn_fs_x__read_noderev(node_revision_t * if (value) { SVN_ERR(read_rep_offsets(&noderev->data_rep, value, - noderev->id, pool)); + noderev->id, result_pool, scratch_pool)); } /* Get the created path. */ @@ -458,13 +479,21 @@ svn_fs_x__read_noderev(node_revision_t * } else { - noderev->created_path = auto_unescape_path(value, pool); + if (!svn_fspath__is_canonical(value)) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Non-canonical cpath field in node-rev '%s'"), + noderev_id); + + noderev->created_path = auto_unescape_path(apr_pstrdup(result_pool, + value), + result_pool); } /* Get the predecessor ID. */ value = svn_hash_gets(headers, HEADER_PRED); if (value) - noderev->predecessor_id = svn_fs_x__id_parse(value, strlen(value), pool); + SVN_ERR(svn_fs_x__id_parse(&noderev->predecessor_id, value, + result_pool)); /* Get the copyroot. */ value = svn_hash_gets(headers, HEADER_COPYROOT); @@ -475,21 +504,15 @@ svn_fs_x__read_noderev(node_revision_t * } else { - char *str; + SVN_ERR(parse_revnum(&noderev->copyroot_rev, (const char **)&value)); - str = svn_cstring_tokenize(" ", &value); - if (str == NULL) + if (!svn_fspath__is_canonical(value)) return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, _("Malformed copyroot line in node-rev '%s'"), noderev_id); - - noderev->copyroot_rev = SVN_STR_TO_REV(str); - - if (*value == '\0') - return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, - _("Malformed copyroot line in node-rev '%s'"), - noderev_id); - noderev->copyroot_path = auto_unescape_path(value, pool); + noderev->copyroot_path = auto_unescape_path(apr_pstrdup(result_pool, + value), + result_pool); } /* Get the copyfrom. */ @@ -501,19 +524,15 @@ svn_fs_x__read_noderev(node_revision_t * } else { - char *str = svn_cstring_tokenize(" ", &value); - if (str == NULL) - return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, - _("Malformed copyfrom line in node-rev '%s'"), - noderev_id); - - noderev->copyfrom_rev = SVN_STR_TO_REV(str); + SVN_ERR(parse_revnum(&noderev->copyfrom_rev, (const char **)&value)); if (*value == 0) return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, _("Malformed copyfrom line in node-rev '%s'"), noderev_id); - noderev->copyfrom_path = auto_unescape_path(value, pool); + noderev->copyfrom_path = auto_unescape_path(apr_pstrdup(result_pool, + value), + result_pool); } /* Get whether this is a fresh txn root. */ @@ -538,13 +557,13 @@ svn_fs_x__read_noderev(node_revision_t * /* Return a textual representation of the DIGEST of given KIND. * If IS_NULL is TRUE, no digest is available. - * Use POOL for allocations. + * Allocate the result in RESULT_POOL. */ static const char * format_digest(const unsigned char *digest, svn_checksum_kind_t kind, svn_boolean_t is_null, - apr_pool_t *pool) + apr_pool_t *result_pool) { svn_checksum_t checksum; checksum.digest = digest; @@ -553,92 +572,96 @@ format_digest(const unsigned char *diges if (is_null) return "(null)"; - return svn_checksum_to_cstring_display(&checksum, pool); + return svn_checksum_to_cstring_display(&checksum, result_pool); } svn_stringbuf_t * svn_fs_x__unparse_representation(representation_t *rep, - int format, svn_boolean_t mutable_rep_truncated, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { if (!rep->has_sha1) return svn_stringbuf_createf - (pool, + (result_pool, "%" APR_INT64_T_FMT " %" APR_UINT64_T_FMT " %" SVN_FILESIZE_T_FMT " %" SVN_FILESIZE_T_FMT " %s", rep->id.change_set, rep->id.number, rep->size, rep->expanded_size, - format_digest(rep->md5_digest, svn_checksum_md5, FALSE, pool)); + format_digest(rep->md5_digest, svn_checksum_md5, FALSE, + scratch_pool)); return svn_stringbuf_createf - (pool, + (result_pool, "%" APR_INT64_T_FMT " %" APR_UINT64_T_FMT " %" SVN_FILESIZE_T_FMT " %" SVN_FILESIZE_T_FMT " %s %s", rep->id.change_set, rep->id.number, rep->size, rep->expanded_size, - format_digest(rep->md5_digest, svn_checksum_md5, FALSE, pool), + format_digest(rep->md5_digest, svn_checksum_md5, + FALSE, scratch_pool), format_digest(rep->sha1_digest, svn_checksum_sha1, - !rep->has_sha1, pool)); + !rep->has_sha1, scratch_pool)); } svn_error_t * svn_fs_x__write_noderev(svn_stream_t *outfile, node_revision_t *noderev, - int format, - apr_pool_t *pool) + apr_pool_t *scratch_pool) { - SVN_ERR(svn_stream_printf(outfile, pool, HEADER_ID ": %s\n", - svn_fs_x__id_unparse(noderev->id, pool)->data)); + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_ID ": %s\n", + svn_fs_x__id_unparse(noderev->id, + scratch_pool)->data)); - SVN_ERR(svn_stream_printf(outfile, pool, HEADER_TYPE ": %s\n", + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_TYPE ": %s\n", (noderev->kind == svn_node_file) ? SVN_FS_X__KIND_FILE : SVN_FS_X__KIND_DIR)); if (noderev->predecessor_id) - SVN_ERR(svn_stream_printf(outfile, pool, HEADER_PRED ": %s\n", + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_PRED ": %s\n", svn_fs_x__id_unparse(noderev->predecessor_id, - pool)->data)); + scratch_pool)->data)); - SVN_ERR(svn_stream_printf(outfile, pool, HEADER_COUNT ": %d\n", + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_COUNT ": %d\n", noderev->predecessor_count)); if (noderev->data_rep) - SVN_ERR(svn_stream_printf(outfile, pool, HEADER_TEXT ": %s\n", + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_TEXT ": %s\n", svn_fs_x__unparse_representation (noderev->data_rep, - format, noderev->kind == svn_node_dir, - pool)->data)); + scratch_pool, scratch_pool)->data)); if (noderev->prop_rep) - SVN_ERR(svn_stream_printf(outfile, pool, HEADER_PROPS ": %s\n", + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_PROPS ": %s\n", svn_fs_x__unparse_representation - (noderev->prop_rep, format, - TRUE, pool)->data)); + (noderev->prop_rep, + TRUE, scratch_pool, scratch_pool)->data)); - SVN_ERR(svn_stream_printf(outfile, pool, HEADER_CPATH ": %s\n", - auto_escape_path(noderev->created_path, pool))); + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_CPATH ": %s\n", + auto_escape_path(noderev->created_path, + scratch_pool))); if (noderev->copyfrom_path) - SVN_ERR(svn_stream_printf(outfile, pool, HEADER_COPYFROM ": %ld" + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_COPYFROM ": %ld" " %s\n", noderev->copyfrom_rev, - auto_escape_path(noderev->copyfrom_path, pool))); + auto_escape_path(noderev->copyfrom_path, + scratch_pool))); if ((noderev->copyroot_rev != svn_fs_x__id_rev(noderev->id)) || (strcmp(noderev->copyroot_path, noderev->created_path) != 0)) - SVN_ERR(svn_stream_printf(outfile, pool, HEADER_COPYROOT ": %ld" + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_COPYROOT ": %ld" " %s\n", noderev->copyroot_rev, - auto_escape_path(noderev->copyroot_path, pool))); + auto_escape_path(noderev->copyroot_path, + scratch_pool))); if (noderev->is_fresh_txn_root) SVN_ERR(svn_stream_puts(outfile, HEADER_FRESHTXNRT ": y\n")); if (noderev->mergeinfo_count > 0) - SVN_ERR(svn_stream_printf(outfile, pool, HEADER_MINFO_CNT ": %" + SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_MINFO_CNT ": %" APR_INT64_T_FMT "\n", noderev->mergeinfo_count)); @@ -651,16 +674,17 @@ svn_fs_x__write_noderev(svn_stream_t *ou svn_error_t * svn_fs_x__read_rep_header(svn_fs_x__rep_header_t **header, svn_stream_t *stream, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_stringbuf_t *buffer; char *str, *last_str; apr_int64_t val; svn_boolean_t eol = FALSE; - SVN_ERR(svn_stream_readline(stream, &buffer, "\n", &eol, pool)); + SVN_ERR(svn_stream_readline(stream, &buffer, "\n", &eol, scratch_pool)); - *header = apr_pcalloc(pool, sizeof(**header)); + *header = apr_pcalloc(result_pool, sizeof(**header)); (*header)->header_size = buffer->len + 1; if (strcmp(buffer->data, REP_DELTA) == 0) { @@ -677,10 +701,7 @@ svn_fs_x__read_rep_header(svn_fs_x__rep_ if (! str || (strcmp(str, REP_DELTA) != 0)) goto error; - str = svn_cstring_tokenize(" ", &last_str); - if (! str) - goto error; - (*header)->base_revision = SVN_STR_TO_REV(str); + SVN_ERR(parse_revnum(&(*header)->base_revision, (const char **)&last_str)); str = svn_cstring_tokenize(" ", &last_str); if (! str) @@ -704,7 +725,7 @@ svn_fs_x__read_rep_header(svn_fs_x__rep_ svn_error_t * svn_fs_x__write_rep_header(svn_fs_x__rep_header_t *header, svn_stream_t *stream, - apr_pool_t *pool) + apr_pool_t *scratch_pool) { const char *text; @@ -715,8 +736,8 @@ svn_fs_x__write_rep_header(svn_fs_x__rep break; default: - text = apr_psprintf(pool, REP_DELTA " %ld %" APR_OFF_T_FMT " %" - SVN_FILESIZE_T_FMT "\n", + text = apr_psprintf(scratch_pool, REP_DELTA " %ld %" APR_OFF_T_FMT + " %" SVN_FILESIZE_T_FMT "\n", header->base_revision, header->base_item_index, header->base_length); } @@ -758,7 +779,7 @@ read_change(change_t **change_p, return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("Invalid changes line in rev-file")); - info->node_rev_id = svn_fs_x__id_parse(str, strlen(str), result_pool); + SVN_ERR(svn_fs_x__id_parse(&info->node_rev_id, str, result_pool)); if (info->node_rev_id == NULL) return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("Invalid changes line in rev-file")); @@ -874,6 +895,10 @@ read_change(change_t **change_p, } /* Get the changed path. */ + if (!svn_fspath__is_canonical(last_str)) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Invalid path in changes line")); + change->path.data = auto_unescape_path(apr_pstrmemdup(result_pool, last_str, strlen(last_str)), @@ -891,15 +916,11 @@ read_change(change_t **change_p, else { last_str = line->data; - str = svn_cstring_tokenize(" ", &last_str); - if (! str) - return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, - _("Invalid changes line in rev-file")); - info->copyfrom_rev = SVN_STR_TO_REV(str); + SVN_ERR(parse_revnum(&info->copyfrom_rev, (const char **)&last_str)); - if (! last_str) + if (!svn_fspath__is_canonical(last_str)) return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, - _("Invalid changes line in rev-file")); + _("Invalid copy-from path in changes line")); info->copyfrom_path = auto_unescape_path(last_str, result_pool); } @@ -912,20 +933,28 @@ read_change(change_t **change_p, svn_error_t * svn_fs_x__read_changes(apr_array_header_t **changes, svn_stream_t *stream, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { change_t *change; - apr_pool_t *iterpool = svn_pool_create(pool); + apr_pool_t *iterpool; + + /* Pre-allocate enough room for most change lists. + (will be auto-expanded as necessary). - /* pre-allocate enough room for most change lists - (will be auto-expanded as necessary) */ - *changes = apr_array_make(pool, 30, sizeof(change_t *)); + Chose the default to just below 2^N such that the doubling reallocs + will request roughly 2^M bytes from the OS without exceeding the + respective two-power by just a few bytes (leaves room array and APR + node overhead for large enough M). + */ + *changes = apr_array_make(result_pool, 63, sizeof(change_t *)); - SVN_ERR(read_change(&change, stream, pool, iterpool)); + SVN_ERR(read_change(&change, stream, result_pool, scratch_pool)); + iterpool = svn_pool_create(scratch_pool); while (change) { APR_ARRAY_PUSH(*changes, change_t*) = change; - SVN_ERR(read_change(&change, stream, pool, iterpool)); + SVN_ERR(read_change(&change, stream, result_pool, iterpool)); svn_pool_clear(iterpool); } svn_pool_destroy(iterpool); @@ -938,12 +967,12 @@ svn_fs_x__read_changes_incrementally(svn svn_fs_x__change_receiver_t change_receiver, void *change_receiver_baton, - apr_pool_t *pool) + apr_pool_t *scratch_pool) { change_t *change; apr_pool_t *iterpool; - iterpool = svn_pool_create(pool); + iterpool = svn_pool_create(scratch_pool); do { svn_pool_clear(iterpool); @@ -958,14 +987,14 @@ svn_fs_x__read_changes_incrementally(svn return SVN_NO_ERROR; } -/* Write a single change entry, path PATH, change CHANGE, and copyfrom - string COPYFROM, into the file specified by FILE. All temporary - allocations are in POOL. */ +/* Write a single change entry, path PATH, change CHANGE, to STREAM. + + All temporary allocations are in SCRATCH_POOL. */ static svn_error_t * write_change_entry(svn_stream_t *stream, const char *path, svn_fs_path_change2_t *change, - apr_pool_t *pool) + apr_pool_t *scratch_pool) { const char *idstr; const char *change_string = NULL; @@ -997,29 +1026,31 @@ write_change_entry(svn_stream_t *stream, } if (change->node_rev_id) - idstr = svn_fs_x__id_unparse(change->node_rev_id, pool)->data; + idstr = svn_fs_x__id_unparse(change->node_rev_id, scratch_pool)->data; else idstr = ACTION_RESET; SVN_ERR_ASSERT(change->node_kind == svn_node_dir - || change->node_kind == svn_node_file); - kind_string = apr_psprintf(pool, "-%s", - change->node_kind == svn_node_dir - ? SVN_FS_X__KIND_DIR - : SVN_FS_X__KIND_FILE); - buf = svn_stringbuf_createf(pool, "%s %s%s %s %s %s %s\n", + || change->node_kind == svn_node_file); + kind_string = apr_psprintf(scratch_pool, "-%s", + change->node_kind == svn_node_dir + ? SVN_FS_X__KIND_DIR + : SVN_FS_X__KIND_FILE); + + buf = svn_stringbuf_createf(scratch_pool, "%s %s%s %s %s %s %s\n", idstr, change_string, kind_string, change->text_mod ? FLAG_TRUE : FLAG_FALSE, change->prop_mod ? FLAG_TRUE : FLAG_FALSE, change->mergeinfo_mod == svn_tristate_true ? FLAG_TRUE : FLAG_FALSE, - auto_escape_path(path, pool)); + auto_escape_path(path, scratch_pool)); if (SVN_IS_VALID_REVNUM(change->copyfrom_rev)) { - svn_stringbuf_appendcstr(buf, apr_psprintf(pool, "%ld %s", - change->copyfrom_rev, - auto_escape_path(change->copyfrom_path, pool))); + svn_stringbuf_appendcstr(buf, apr_psprintf(scratch_pool, "%ld %s", + change->copyfrom_rev, + auto_escape_path(change->copyfrom_path, + scratch_pool))); } svn_stringbuf_appendbyte(buf, '\n'); @@ -1034,17 +1065,24 @@ svn_fs_x__write_changes(svn_stream_t *st svn_fs_t *fs, apr_hash_t *changes, svn_boolean_t terminate_list, - apr_pool_t *pool) + apr_pool_t *scratch_pool) { - apr_pool_t *iterpool = svn_pool_create(pool); + apr_pool_t *iterpool = svn_pool_create(scratch_pool); apr_array_header_t *sorted_changed_paths; int i; /* For the sake of the repository administrator sort the changes so that the final file is deterministic and repeatable, however the - rest of the FSX code doesn't require any particular order here. */ + rest of the FSX code doesn't require any particular order here. + + Also, this sorting is only effective in writing all entries with + a single call as write_final_changed_path_info() does. For the + list being written incrementally during transaction, we actually + *must not* change the order of entries from different calls. + */ sorted_changed_paths = svn_sort__hash(changes, - svn_sort_compare_items_lexically, pool); + svn_sort_compare_items_lexically, + scratch_pool); /* Write all items to disk in the new order. */ for (i = 0; i < sorted_changed_paths->nelts; ++i) @@ -1063,7 +1101,7 @@ svn_fs_x__write_changes(svn_stream_t *st if (terminate_list) svn_stream_puts(stream, "\n"); - + svn_pool_destroy(iterpool); return SVN_NO_ERROR; Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/low_level.h URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/low_level.h?rev=1641647&r1=1641646&r2=1641647&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/low_level.h (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/low_level.h Tue Nov 25 16:26:48 2014 @@ -36,62 +36,81 @@ extern "C" { #define SVN_FS_X__KIND_FILE "file" #define SVN_FS_X__KIND_DIR "dir" -/* Given the last "few" bytes (should be at least 40) of revision REV in - * TRAILER, parse the last line and return the offset of the root noderev - * in *ROOT_OFFSET and the offset of the changed paths list in - * *CHANGES_OFFSET. Offsets are relative to the revision's start offset. - * ROOT_OFFSET and / or CHANGES_OFFSET may be NULL. +/* The functions are grouped as follows: + * + * - revision footer + * - representation (as in "text:" and "props:" lines) + * - node revision + * - representation header ("DELTA" lines) + * - changed path list + */ + +/* Given the FSX revision / pack FOOTER, parse it destructively + * and return the start offsets of the index data in *L2P_OFFSET and + * *P2L_OFFSET, respectively. Also, return the expected checksums in + * in *L2P_CHECKSUM and *P2L_CHECKSUM. * - * Note that REV is only used to construct nicer error objects. + * Note that REV is only used to construct nicer error objects that + * mention this revision. Allocate the checksums in RESULT_POOL. */ svn_error_t * -svn_fs_x__parse_revision_trailer(apr_off_t *root_offset, - apr_off_t *changes_offset, - svn_stringbuf_t *trailer, - svn_revnum_t rev); - -/* Given the offset of the root noderev in ROOT_OFFSET and the offset of - * the changed paths list in CHANGES_OFFSET, return the corresponding - * revision's trailer. Allocate it in POOL. +svn_fs_x__parse_footer(apr_off_t *l2p_offset, + svn_checksum_t **l2p_checksum, + apr_off_t *p2l_offset, + svn_checksum_t **p2l_checksum, + svn_stringbuf_t *footer, + svn_revnum_t rev, + apr_pool_t *result_pool); + +/* Given the offset of the L2P index data in L2P_OFFSET, the content + * checksum in L2P_CHECKSUM and the offset plus checksum of the P2L + * index data in P2L_OFFSET and P2L_CHECKSUM. + * + * Return the corresponding format 7+ revision / pack file footer. + * Allocate it in RESULT_POOL and use SCRATCH_POOL for temporary. */ svn_stringbuf_t * -svn_fs_x__unparse_revision_trailer(apr_off_t root_offset, - apr_off_t changes_offset, - apr_pool_t *pool); +svn_fs_x__unparse_footer(apr_off_t l2p_offset, + svn_checksum_t *l2p_checksum, + apr_off_t p2l_offset, + svn_checksum_t *p2l_checksum, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /* Parse the description of a representation from TEXT and store it - into *REP_P. Allocate *REP_P in POOL. */ + into *REP_P. TEXT will be invalidated by this call. Allocate *REP_P in + RESULT_POOL and use SCRATCH_POOL for temporaries. */ svn_error_t * svn_fs_x__parse_representation(representation_t **rep_p, svn_stringbuf_t *text, - apr_pool_t *pool); + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); -/* Return a formatted string, compatible with filesystem format FORMAT, - that represents the location of representation REP. If - MUTABLE_REP_TRUNCATED is given, the rep is for props or dir contents, - and only a "-1" revision number will be given for a mutable rep. - If MAY_BE_CORRUPT is true, guard for NULL when constructing the string. - Perform the allocation from POOL. */ +/* Return a formatted string that represents the location of representation + * REP. If MUTABLE_REP_TRUNCATED is given, the rep is for props or dir + * contents, and only a "-1" revision number will be given for a mutable rep. + * If MAY_BE_CORRUPT is true, guard for NULL when constructing the string. + * Allocate the result in RESULT_POOL and temporaries in SCRATCH_POOL. */ svn_stringbuf_t * svn_fs_x__unparse_representation(representation_t *rep, - int format, svn_boolean_t mutable_rep_truncated, - apr_pool_t *pool); + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /* Read a node-revision from STREAM. Set *NODEREV to the new structure, - allocated in POOL. */ + allocated in RESULT_POOL. */ svn_error_t * svn_fs_x__read_noderev(node_revision_t **noderev, svn_stream_t *stream, - apr_pool_t *pool); + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); -/* Write the node-revision NODEREV into the stream OUTFILE, compatible with - filesystem format FORMAT. Temporary allocations are from POOL. */ +/* Write the node-revision NODEREV into the stream OUTFILE. + Temporary allocations are from SCRATCH_POOL. */ svn_error_t * svn_fs_x__write_noderev(svn_stream_t *outfile, node_revision_t *noderev, - int format, - apr_pool_t *pool); + apr_pool_t *scratch_pool); /* This type enumerates all forms of representations that we support. */ typedef enum svn_fs_x__rep_type_t @@ -132,53 +151,61 @@ typedef struct svn_fs_x__rep_header_t apr_size_t header_size; } svn_fs_x__rep_header_t; -/* Read the next line from file FILE and parse it as a text - representation entry. Return the parsed entry in *REP_ARGS_P. - Perform all allocations in POOL. */ +/* Read the next line from STREAM and parse it as a text + representation header. Return the parsed entry in *HEADER, allocated + in RESULT_POOL. Perform temporary allocations in SCRATCH_POOL. */ svn_error_t * svn_fs_x__read_rep_header(svn_fs_x__rep_header_t **header, svn_stream_t *stream, - apr_pool_t *pool); + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); -/* Write the representation HEADER to STREAM. Use POOL for allocations. */ +/* Write the representation HEADER to STREAM. + * Use SCRATCH_POOL for allocations. */ svn_error_t * svn_fs_x__write_rep_header(svn_fs_x__rep_header_t *header, svn_stream_t *stream, - apr_pool_t *pool); + apr_pool_t *scratch_pool); -/* Read all the changes from STREAM and store them in *CHANGES. Do all - allocations in POOL. */ +/* Read all the changes from STREAM and store them in *CHANGES, + allocated in RESULT_POOL. Do temporary allocations in SCRATCH_POOL. */ svn_error_t * svn_fs_x__read_changes(apr_array_header_t **changes, svn_stream_t *stream, - apr_pool_t *pool); + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); +/* Callback function used by svn_fs_fs__read_changes_incrementally(), + * asking the receiver to process to process CHANGE using BATON. CHANGE + * and SCRATCH_POOL will not be valid beyond the current callback invocation. + */ typedef svn_error_t *(*svn_fs_x__change_receiver_t)( void *baton, change_t *change, apr_pool_t *scratch_pool); /* Read all the changes from STREAM and invoke CHANGE_RECEIVER on each change. - Do all allocations in POOL. */ + Do all allocations in SCRATCH_POOL. */ svn_error_t * svn_fs_x__read_changes_incrementally(svn_stream_t *stream, svn_fs_x__change_receiver_t - change_receiver, + change_receiver, void *change_receiver_baton, apr_pool_t *scratch_pool); /* Write the changed path info from CHANGES in filesystem FS to the output stream STREAM. You may call this function multiple time on - the same stream but the last call should set TERMINATE_LIST to write - an extra empty line that marks the end of the changed paths list. - Perform temporary allocations in POOL. + the same stream. If you are writing to a (proto-)revision file, + the last call must set TERMINATE_LIST to write an extra empty line + that marks the end of the changed paths list. + Perform temporary allocations in SCRATCH_POOL. */ svn_error_t * svn_fs_x__write_changes(svn_stream_t *stream, svn_fs_t *fs, apr_hash_t *changes, svn_boolean_t terminate_list, - apr_pool_t *pool); + apr_pool_t *scratch_pool); #ifdef __cplusplus } Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/pack.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/pack.c?rev=1641647&r1=1641646&r2=1641647&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/pack.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/pack.c Tue Nov 25 16:26:48 2014 @@ -357,12 +357,6 @@ static svn_error_t * close_pack_context(pack_context_t *context, apr_pool_t *pool) { - const char *l2p_index_path - = apr_pstrcat(pool, context->pack_file_path, PATH_EXT_L2P_INDEX, - SVN_VA_NULL); - const char *p2l_index_path - = apr_pstrcat(pool, context->pack_file_path, PATH_EXT_P2L_INDEX, - SVN_VA_NULL); const char *proto_l2p_index_path; const char *proto_p2l_index_path; @@ -371,18 +365,17 @@ close_pack_context(pack_context_t *conte context->proto_l2p_index, pool)); SVN_ERR(svn_io_file_name_get(&proto_p2l_index_path, context->proto_p2l_index, pool)); - + /* finalize proto index files */ SVN_ERR(svn_io_file_close(context->proto_l2p_index, pool)); SVN_ERR(svn_io_file_close(context->proto_p2l_index, pool)); - /* Create the actual index files*/ - SVN_ERR(svn_fs_x__l2p_index_create(context->fs, l2p_index_path, - proto_l2p_index_path, - context->shard_rev, pool)); - SVN_ERR(svn_fs_x__p2l_index_create(context->fs, p2l_index_path, - proto_p2l_index_path, - context->shard_rev, pool)); + /* Append the actual index data to the pack file. */ + SVN_ERR(svn_fs_x__add_index_data(context->fs, context->pack_file, + proto_l2p_index_path, + proto_p2l_index_path, + context->shard_rev, + pool)); /* remove proto index files */ SVN_ERR(svn_io_remove_file2(proto_l2p_index_path, FALSE, pool)); @@ -476,7 +469,7 @@ static svn_error_t * copy_item_to_temp(pack_context_t *context, apr_array_header_t *entries, apr_file_t *temp_file, - apr_file_t *rev_file, + svn_fs_x__revision_file_t *rev_file, svn_fs_x__p2l_entry_t *entry, apr_pool_t *pool) { @@ -486,7 +479,8 @@ copy_item_to_temp(pack_context_t *contex SVN_ERR(svn_io_file_seek(temp_file, APR_CUR, &new_entry->offset, pool)); APR_ARRAY_PUSH(entries, svn_fs_x__p2l_entry_t *) = new_entry; - SVN_ERR(copy_file_data(context, temp_file, rev_file, entry->size, pool)); + SVN_ERR(copy_file_data(context, temp_file, rev_file->file, entry->size, + pool)); return SVN_NO_ERROR; } @@ -561,12 +555,11 @@ get_item(pack_context_t *context, */ static svn_error_t * copy_rep_to_temp(pack_context_t *context, - apr_file_t *rev_file, + svn_fs_x__revision_file_t *rev_file, svn_fs_x__p2l_entry_t *entry, apr_pool_t *pool) { svn_fs_x__rep_header_t *rep_header; - svn_stream_t *stream; apr_off_t source_offset = entry->offset; /* create a copy of ENTRY, make it point to the copy destination and @@ -578,9 +571,8 @@ copy_rep_to_temp(pack_context_t *context add_item_rep_mapping(context, entry); /* read & parse the representation header */ - stream = svn_stream_from_aprfile2(rev_file, TRUE, pool); - SVN_ERR(svn_fs_x__read_rep_header(&rep_header, stream, pool)); - svn_stream_close(stream); + SVN_ERR(svn_fs_x__read_rep_header(&rep_header, rev_file->stream, pool, + pool)); /* if the representation is a delta against some other rep, link the two */ if ( rep_header->type == svn_fs_x__rep_delta @@ -596,9 +588,9 @@ copy_rep_to_temp(pack_context_t *context } /* copy the whole rep (including header!) to our temp file */ - SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &source_offset, pool)); - SVN_ERR(copy_file_data(context, context->reps_file, rev_file, entry->size, - pool)); + SVN_ERR(svn_io_file_seek(rev_file->file, APR_SET, &source_offset, pool)); + SVN_ERR(copy_file_data(context, context->reps_file, rev_file->file, + entry->size, pool)); return SVN_NO_ERROR; } @@ -679,7 +671,7 @@ tweak_path_for_ordering(const char *orig */ static svn_error_t * copy_node_to_temp(pack_context_t *context, - apr_file_t *rev_file, + svn_fs_x__revision_file_t *rev_file, svn_fs_x__p2l_entry_t *entry, apr_pool_t *pool) { @@ -687,13 +679,10 @@ copy_node_to_temp(pack_context_t *contex sizeof(*path_order)); node_revision_t *noderev; const char *sort_path; - svn_stream_t *stream; apr_off_t source_offset = entry->offset; /* read & parse noderev */ - stream = svn_stream_from_aprfile2(rev_file, TRUE, pool); - SVN_ERR(svn_fs_x__read_noderev(&noderev, stream, pool)); - svn_stream_close(stream); + SVN_ERR(svn_fs_x__read_noderev(&noderev, rev_file->stream, pool, pool)); /* create a copy of ENTRY, make it point to the copy destination and * store it in CONTEXT */ @@ -704,9 +693,9 @@ copy_node_to_temp(pack_context_t *contex add_item_rep_mapping(context, entry); /* copy the noderev to our temp file */ - SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &source_offset, pool)); - SVN_ERR(copy_file_data(context, context->reps_file, rev_file, entry->size, - pool)); + SVN_ERR(svn_io_file_seek(rev_file->file, APR_SET, &source_offset, pool)); + SVN_ERR(copy_file_data(context, context->reps_file, rev_file->file, + entry->size, pool)); /* if the node has a data representation, make that the node's "base". * This will (often) cause the noderev to be placed right in front of @@ -768,58 +757,6 @@ sort_items(apr_array_header_t *entries) (int (*)(const void *, const void *))compare_p2l_info); } -/* Decorator for svn_fs_x__p2l_entry_t that associates it with a sorted - * variant of its ITEMS array. - */ -typedef struct sub_item_ordered_t -{ - /* ENTRY that got wrapped */ - svn_fs_x__p2l_entry_t *entry; - - /* Array of pointers into ENTRY->ITEMS, sorted by their revision member - * _descending_ order. May be NULL if ENTRY->ITEM_COUNT < 2. */ - svn_fs_x__id_part_t **order; -} sub_item_ordered_t; - -/* implements compare_fn_t. Place LHS before RHS, if the latter is younger. - * Used to sort sub_item_ordered_t::order - */ -static int -compare_sub_items(const svn_fs_x__id_part_t * const * lhs, - const svn_fs_x__id_part_t * const * rhs) -{ - return (*lhs)->change_set < (*rhs)->change_set - ? 1 - : ((*lhs)->change_set > (*rhs)->change_set ? -1 : 0); -} - -/* implements compare_fn_t. Place LHS before RHS, if the latter belongs to - * a newer revision. - */ -static int -compare_p2l_info_rev(const sub_item_ordered_t * lhs, - const sub_item_ordered_t * rhs) -{ - svn_fs_x__id_part_t *lhs_part; - svn_fs_x__id_part_t *rhs_part; - - assert(lhs != rhs); - if (lhs->entry->item_count == 0) - return rhs->entry->item_count == 0 ? 0 : -1; - if (rhs->entry->item_count == 0) - return 1; - - lhs_part = lhs->order ? lhs->order[lhs->entry->item_count - 1] - : &lhs->entry->items[0]; - rhs_part = rhs->order ? rhs->order[rhs->entry->item_count - 1] - : &rhs->entry->items[0]; - - if (lhs_part->change_set == rhs_part->change_set) - return 0; - - return lhs_part->change_set < rhs_part->change_set ? -1 : 1; -} - /* implements compare_fn_t. Sort descending by PATH, NODE_ID and REVISION. */ static int @@ -1207,7 +1144,7 @@ store_nodes(pack_context_t *context, /* item will fit into the block. */ SVN_ERR(svn_io_file_seek(temp_file, APR_SET, &entry->offset, iterpool)); - SVN_ERR(svn_fs_x__read_noderev(&noderev, stream, iterpool)); + SVN_ERR(svn_fs_x__read_noderev(&noderev, stream, iterpool, iterpool)); svn_fs_x__noderevs_add(*container, noderev); container_size += entry->size; @@ -1284,8 +1221,9 @@ write_reps_containers(pack_context_t *co = svn_fs_x__reps_builder_create(context->fs, container_pool); apr_array_header_t *sub_items = apr_array_make(pool, 64, sizeof(svn_fs_x__id_part_t)); - svn_stream_t *temp_stream - = svn_stream_from_aprfile2(temp_file, TRUE, pool); + svn_fs_x__revision_file_t *file; + + SVN_ERR(svn_fs_x__wrap_temp_rev_file(&file, context->fs, temp_file, pool)); /* copy all items in strict order */ for (i = entries->nelts-1; i >= 0; --i) @@ -1331,8 +1269,8 @@ write_reps_containers(pack_context_t *co iterpool)); SVN_ERR(svn_fs_x__get_representation_length(&representation.size, &representation.expanded_size, - context->fs, temp_file, - temp_stream, entry, iterpool)); + context->fs, file, + entry, iterpool)); SVN_ERR(svn_fs_x__get_contents(&stream, context->fs, &representation, FALSE, iterpool)); contents = svn_stringbuf_create_ensure(representation.expanded_size, @@ -1656,7 +1594,7 @@ write_changes_containers(pack_context_t * the container */ SVN_ERR(svn_io_file_seek(temp_file, APR_SET, &entry->offset, iterpool)); - SVN_ERR(svn_fs_x__read_changes(&changes, temp_stream, iterpool)); + SVN_ERR(svn_fs_x__read_changes(&changes, temp_stream, pool, iterpool)); SVN_ERR(svn_fs_x__changes_append_list(&list_index, container, changes)); SVN_ERR_ASSERT(list_index == sub_items->nelts); block_left -= estimated_size; @@ -1720,13 +1658,10 @@ static svn_error_t * write_l2p_index(pack_context_t *context, apr_pool_t *pool) { - apr_pool_t *iterpool = svn_pool_create(pool); - svn_revnum_t prev_rev = SVN_INVALID_REVNUM; - int i; - apr_uint32_t k; - svn_priority_queue__t *queue; - apr_size_t count = 0; - apr_array_header_t *sub_item_orders; + apr_pool_t *scratch_pool = svn_pool_create(pool); + const char *temp_name; + const char *proto_index; + apr_off_t offset = 0; /* lump all items into one bucket. As target, use the bucket that * probably has the most entries already. */ @@ -1734,88 +1669,24 @@ write_l2p_index(pack_context_t *context, append_entries(context->reps, context->file_props); append_entries(context->reps, context->dir_props); - /* wrap P2L entries such that we have access to the sub-items in revision - order. The ENTRY_COUNT member will point to the next item to read+1. */ - sub_item_orders - = apr_array_make(pool, context->reps->nelts, sizeof(sub_item_ordered_t)); - sub_item_orders->nelts = context->reps->nelts; - - for (i = 0; i < context->reps->nelts; ++i) - { - svn_fs_x__p2l_entry_t *entry - = APR_ARRAY_IDX(context->reps, i, svn_fs_x__p2l_entry_t *); - sub_item_ordered_t *ordered - = &APR_ARRAY_IDX(sub_item_orders, i, sub_item_ordered_t); - - /* skip unused regions (e.g. padding) */ - if (entry->item_count == 0) - continue; - - assert(entry); - ordered->entry = entry; - count += entry->item_count; - - if (entry->item_count > 1) - { - ordered->order - = apr_palloc(pool, sizeof(*ordered->order) * entry->item_count); - for (k = 0; k < entry->item_count; ++k) - ordered->order[k] = &entry->items[k]; - - qsort(ordered->order, entry->item_count, sizeof(*ordered->order), - (int (*)(const void *, const void *))compare_sub_items); - } - } - - /* we need to write the index in ascending revision order */ - queue = svn_priority_queue__create - (sub_item_orders, - (int (*)(const void *, const void *))compare_p2l_info_rev); - - /* write index entries */ - for (i = 0; i < count; ++i) - { - svn_fs_x__id_part_t *sub_item; - sub_item_ordered_t *ordered = svn_priority_queue__peek(queue); - - if (ordered->entry->item_count > 0) - { - /* if there is only one item, we skip the overhead of having an - extra array for the item order */ - sub_item = ordered->order - ? ordered->order[ordered->entry->item_count - 1] - : &ordered->entry->items[0]; - - /* next revision? */ - if (prev_rev != svn_fs_x__get_revnum(sub_item->change_set)) - { - prev_rev = svn_fs_x__get_revnum(sub_item->change_set); - SVN_ERR(svn_fs_x__l2p_proto_index_add_revision - (context->proto_l2p_index, iterpool)); - } - - /* add entry */ - SVN_ERR(svn_fs_x__l2p_proto_index_add_entry - (context->proto_l2p_index, ordered->entry->offset, - (apr_uint32_t)(sub_item - ordered->entry->items), - sub_item->number, iterpool)); - - /* make ITEM_COUNT point the next sub-item to use+1 */ - --ordered->entry->item_count; - } - - /* process remaining sub-items (if any) of that container later */ - if (ordered->entry->item_count) - svn_priority_queue__update(queue); - else - svn_priority_queue__pop(queue); - - /* keep memory usage in check */ - if (i % 256 == 0) - svn_pool_clear(iterpool); - } + /* Let the index code do the expensive L2P -> P2L transformation. */ + SVN_ERR(svn_fs_x__l2p_index_from_p2l_entries(&temp_name, + context->fs, + context->reps, + pool, scratch_pool)); + + /* Append newly written segment to exisiting proto index file. */ + SVN_ERR(svn_io_file_name_get(&proto_index, context->proto_l2p_index, + scratch_pool)); + + SVN_ERR(svn_io_file_flush(context->proto_l2p_index, scratch_pool)); + SVN_ERR(svn_io_append_file(temp_name, proto_index, scratch_pool)); + SVN_ERR(svn_io_remove_file2(temp_name, FALSE, scratch_pool)); + SVN_ERR(svn_io_file_seek(context->proto_l2p_index, APR_END, &offset, + scratch_pool)); - svn_pool_destroy(iterpool); + /* Done. */ + svn_pool_destroy(scratch_pool); return SVN_NO_ERROR; } @@ -1836,26 +1707,19 @@ pack_range(pack_context_t *context, for (revision = context->start_rev; revision < context->end_rev; ++revision) { apr_off_t offset = 0; - apr_finfo_t finfo; - apr_file_t *rev_file; + svn_fs_x__revision_file_t *rev_file; - /* Get the size of the file. */ - const char *path = svn_dirent_join(context->shard_dir, - apr_psprintf(revpool, "%ld", - revision), - revpool); - SVN_ERR(svn_io_stat(&finfo, path, APR_FINFO_SIZE, revpool)); - - SVN_ERR(svn_io_file_open(&rev_file, path, - APR_READ | APR_BUFFERED | APR_BINARY, - APR_OS_DEFAULT, revpool)); + /* Get the rev file dimensions (mainly index locations). */ + SVN_ERR(svn_fs_x__open_pack_or_rev_file(&rev_file, context->fs, + revision, revpool, iterpool)); + SVN_ERR(svn_fs_x__auto_read_footer(rev_file)); /* store the indirect array index */ APR_ARRAY_PUSH(context->rev_offsets, int) = context->reps->nelts; /* read the phys-to-log index file until we covered the whole rev file. * That index contains enough info to build both target indexes from it. */ - while (offset < finfo.size) + while (offset < rev_file->l2p_offset) { /* read one cluster */ int i; @@ -1863,8 +1727,9 @@ pack_range(pack_context_t *context, svn_pool_clear(iterpool); SVN_ERR(svn_fs_x__p2l_index_lookup(&entries, context->fs, - revision, offset, - ffd->p2l_page_size, iterpool)); + rev_file, revision, offset, + ffd->p2l_page_size, iterpool, + iterpool)); for (i = 0; i < entries->nelts; ++i) { @@ -1878,9 +1743,9 @@ pack_range(pack_context_t *context, /* process entry while inside the rev file */ offset = entry->offset; - if (offset < finfo.size) + if (offset < rev_file->l2p_offset) { - SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &offset, + SVN_ERR(svn_io_file_seek(rev_file->file, APR_SET, &offset, iterpool)); if (entry->type == SVN_FS_X__ITEM_TYPE_CHANGES) @@ -1962,7 +1827,7 @@ append_revision(pack_context_t *context, fs_x_data_t *ffd = context->fs->fsap_data; apr_off_t offset = 0; apr_pool_t *iterpool = svn_pool_create(pool); - apr_file_t *rev_file; + svn_fs_x__revision_file_t *rev_file; apr_finfo_t finfo; /* Get the size of the file. */ @@ -1973,11 +1838,11 @@ append_revision(pack_context_t *context, SVN_ERR(svn_io_stat(&finfo, path, APR_FINFO_SIZE, pool)); /* Copy all the bits from the rev file to the end of the pack file. */ - SVN_ERR(svn_io_file_open(&rev_file, path, - APR_READ | APR_BUFFERED | APR_BINARY, - APR_OS_DEFAULT, pool)); - SVN_ERR(copy_file_data(context, context->pack_file, rev_file, finfo.size, - iterpool)); + SVN_ERR(svn_fs_x__open_pack_or_rev_file(&rev_file, context->fs, + context->start_rev, pool, + iterpool)); + SVN_ERR(copy_file_data(context, context->pack_file, rev_file->file, + finfo.size, iterpool)); /* mark the start of a new revision */ SVN_ERR(svn_fs_x__l2p_proto_index_add_revision(context->proto_l2p_index, @@ -1990,9 +1855,10 @@ append_revision(pack_context_t *context, /* read one cluster */ int i; apr_array_header_t *entries; - SVN_ERR(svn_fs_x__p2l_index_lookup(&entries, context->fs, - context->start_rev, offset, - ffd->p2l_page_size, iterpool)); + SVN_ERR(svn_fs_x__p2l_index_lookup(&entries, context->fs, rev_file, + context->start_rev, offset, + ffd->p2l_page_size, iterpool, + iterpool)); for (i = 0; i < entries->nelts; ++i) { @@ -2075,7 +1941,7 @@ pack_log_addressed(svn_fs_t *fs, /* phase 1: determine the size of the revisions to pack */ SVN_ERR(svn_fs_x__l2p_get_max_ids(&max_ids, fs, shard_rev, context.shard_end_rev - shard_rev, - pool)); + pool, pool)); /* pack revisions in ranges that don't exceed MAX_MEM */ for (i = 0; i < max_ids->nelts; ++i) Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/recovery.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/recovery.c?rev=1641647&r1=1641646&r2=1641647&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/recovery.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/recovery.c Tue Nov 25 16:26:48 2014 @@ -55,10 +55,11 @@ recover_get_largest_revision(svn_fs_t *f while (1) { svn_error_t *err; - apr_file_t *file; + svn_fs_x__revision_file_t *file; svn_pool_clear(iterpool); - err = svn_fs_x__open_pack_or_rev_file(&file, fs, right, iterpool); + err = svn_fs_x__open_pack_or_rev_file(&file, fs, right, iterpool, + iterpool); if (err && err->apr_err == SVN_ERR_FS_NO_SUCH_REVISION) { svn_error_clear(err); @@ -78,10 +79,11 @@ recover_get_largest_revision(svn_fs_t *f { svn_revnum_t probe = left + ((right - left) / 2); svn_error_t *err; - apr_file_t *file; + svn_fs_x__revision_file_t *file; svn_pool_clear(iterpool); - err = svn_fs_x__open_pack_or_rev_file(&file, fs, probe, iterpool); + err = svn_fs_x__open_pack_or_rev_file(&file, fs, probe, iterpool, + iterpool); if (err && err->apr_err == SVN_ERR_FS_NO_SUCH_REVISION) { svn_error_clear(err); @@ -124,6 +126,11 @@ recover_body(void *baton, apr_pool_t *po /* Lose potentially corrupted data in temp files */ SVN_ERR(svn_fs_x__reset_revprop_generation_file(fs, pool)); + /* The admin may have created a plain copy of this repo before attempting + to recover it (hotcopy may or may not work with corrupted repos). + Bump the instance ID. */ + SVN_ERR(svn_fs_x__set_uuid(fs, fs->uuid, NULL, pool)); + /* We need to know the largest revision in the filesystem. */ SVN_ERR(recover_get_largest_revision(fs, &max_rev, pool)); Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/temp_serializer.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/temp_serializer.c?rev=1641647&r1=1641646&r2=1641647&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/temp_serializer.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/temp_serializer.c Tue Nov 25 16:26:48 2014 @@ -242,7 +242,7 @@ static svn_temp_serializer__context_t * serialize_dir(apr_array_header_t *entries, apr_pool_t *pool) { dir_data_t dir_data; - apr_size_t i = 0; + int i = 0; svn_temp_serializer__context_t *context; /* calculate sizes */ @@ -1144,18 +1144,15 @@ svn_fs_x__serialize_changes(void **data, svn_stringbuf_t *serialized; int i; - /* initialize our auxiliary data structure */ + /* initialize our auxiliary data structure and link it to the + * array elements */ changes.count = array->nelts; - changes.changes = apr_palloc(pool, sizeof(change_t*) * changes.count); - - /* populate it with the array elements */ - for (i = 0; i < changes.count; ++i) - changes.changes[i] = APR_ARRAY_IDX(array, i, change_t*); + changes.changes = (change_t **)array->elts; /* serialize it and all its elements */ context = svn_temp_serializer__init(&changes, sizeof(changes), - changes.count * 100, + changes.count * 250, pool); svn_temp_serializer__push(context, @@ -1184,20 +1181,22 @@ svn_fs_x__deserialize_changes(void **out { int i; changes_data_t *changes = (changes_data_t *)data; - apr_array_header_t *array = apr_array_make(pool, changes->count, - sizeof(change_t *)); + apr_array_header_t *array = apr_array_make(pool, 0, sizeof(change_t *)); /* de-serialize our auxiliary data structure */ svn_temp_deserializer__resolve(changes, (void**)&changes->changes); /* de-serialize each entry and add it to the array */ for (i = 0; i < changes->count; ++i) - { - deserialize_change(changes->changes, - (change_t **)&changes->changes[i], - pool); - APR_ARRAY_PUSH(array, change_t *) = changes->changes[i]; - } + deserialize_change(changes->changes, + (change_t **)&changes->changes[i], + pool); + + /* Use the changes buffer as the array's data buffer + * (DATA remains valid for at least as long as POOL). */ + array->elts = (char *)changes->changes; + array->nelts = changes->count; + array->nalloc = changes->count; /* done */ *out = array;
