diff options
Diffstat (limited to 'subversion/libsvn_fs_x/noderevs.c')
-rw-r--r-- | subversion/libsvn_fs_x/noderevs.c | 912 |
1 files changed, 912 insertions, 0 deletions
diff --git a/subversion/libsvn_fs_x/noderevs.c b/subversion/libsvn_fs_x/noderevs.c new file mode 100644 index 0000000..60c6029 --- /dev/null +++ b/subversion/libsvn_fs_x/noderevs.c @@ -0,0 +1,912 @@ +/* noderevs.h --- FSX node revision container + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ + +#include "svn_private_config.h" + +#include "private/svn_dep_compat.h" +#include "private/svn_packed_data.h" +#include "private/svn_subr_private.h" +#include "private/svn_temp_serializer.h" + +#include "noderevs.h" +#include "string_table.h" +#include "temp_serializer.h" + +/* These flags will be used with the FLAGS field in binary_noderev_t. + */ + +/* (flags & NODEREV_KIND_MASK) extracts the noderev type */ +#define NODEREV_KIND_MASK 0x00007 + +/* the noderev has merge info */ +#define NODEREV_HAS_MINFO 0x00008 + +/* the noderev has copy-from-path and revision */ +#define NODEREV_HAS_COPYFROM 0x00010 + +/* the noderev has copy-root path and revision */ +#define NODEREV_HAS_COPYROOT 0x00020 + +/* the noderev has copy-root path and revision */ +#define NODEREV_HAS_CPATH 0x00040 + +/* Our internal representation of a svn_fs_x__noderev_t. + * + * We will store path strings in a string container and reference them + * from here. Similarly, IDs and representations are being stored in + * separate containers and then also referenced here. This eliminates + * the need to store the same IDs and representations more than once. + */ +typedef struct binary_noderev_t +{ + /* node type and presence indicators */ + apr_uint32_t flags; + + /* Index+1 of the noderev-id for this node-rev. */ + int id; + + /* Index+1 of the node-id for this node-rev. */ + int node_id; + + /* Index+1 of the copy-id for this node-rev. */ + int copy_id; + + /* Index+1 of the predecessor node revision id, or 0 if there is no + predecessor for this node revision */ + int predecessor_id; + + /* number of predecessors this node revision has (recursively), or + -1 if not known (for backward compatibility). */ + int predecessor_count; + + /* If this node-rev is a copy, what revision was it copied from? */ + svn_revnum_t copyfrom_rev; + + /* Helper for history tracing, root revision of the parent tree from + whence this node-rev was copied. */ + svn_revnum_t copyroot_rev; + + /* If this node-rev is a copy, this is the string index+1 of the path + from which that copy way made. 0, otherwise. */ + apr_size_t copyfrom_path; + + /* String index+1 of the root of the parent tree from whence this node- + * rev was copied. */ + apr_size_t copyroot_path; + + /* Index+1 of the representation key for this node's properties. + May be 0 if there are no properties. */ + int prop_rep; + + /* Index+1 of the representation for this node's data. + May be 0 if there is no data. */ + int data_rep; + + /* String index+1 of the path at which this node first came into + existence. */ + apr_size_t created_path; + + /* Number of nodes with svn:mergeinfo properties that are + descendants of this node (including it itself) */ + apr_int64_t mergeinfo_count; + +} binary_noderev_t; + +/* The actual container object. Node revisions are concatenated into + * NODEREVS, referenced representations are stored in DATA_REPS / PROP_REPS + * and the ids in IDs. PATHS is the string table for all paths. + * + * During construction, BUILDER will be used instead of PATHS. IDS_DICT, + * DATA_REPS_DICT and PROP_REPS_DICT are also only used during construction + * and are NULL otherwise. + */ +struct svn_fs_x__noderevs_t +{ + /* The paths - either in 'builder' mode or finalized mode. + * The respective other pointer will be NULL. */ + string_table_builder_t *builder; + string_table_t *paths; + + /* During construction, maps a full binary_id_t to an index into IDS */ + apr_hash_t *ids_dict; + + /* During construction, maps a full binary_representation_t to an index + * into REPS. */ + apr_hash_t *reps_dict; + + /* array of binary_id_t */ + apr_array_header_t *ids; + + /* array of binary_representation_t */ + apr_array_header_t *reps; + + /* array of binary_noderev_t. */ + apr_array_header_t *noderevs; +}; + +svn_fs_x__noderevs_t * +svn_fs_x__noderevs_create(int initial_count, + apr_pool_t* result_pool) +{ + svn_fs_x__noderevs_t *noderevs + = apr_palloc(result_pool, sizeof(*noderevs)); + + noderevs->builder = svn_fs_x__string_table_builder_create(result_pool); + noderevs->ids_dict = svn_hash__make(result_pool); + noderevs->reps_dict = svn_hash__make(result_pool); + noderevs->paths = NULL; + + noderevs->ids + = apr_array_make(result_pool, 2 * initial_count, sizeof(svn_fs_x__id_t)); + noderevs->reps + = apr_array_make(result_pool, 2 * initial_count, + sizeof(svn_fs_x__representation_t)); + noderevs->noderevs + = apr_array_make(result_pool, initial_count, sizeof(binary_noderev_t)); + + return noderevs; +} + +/* Given the ID, return the index+1 into IDS that contains a binary_id + * for it. Returns 0 for NULL IDs. We use DICT to detect duplicates. + */ +static int +store_id(apr_array_header_t *ids, + apr_hash_t *dict, + const svn_fs_x__id_t *id) +{ + int idx; + void *idx_void; + + if (!svn_fs_x__id_used(id)) + return 0; + + idx_void = apr_hash_get(dict, &id, sizeof(id)); + idx = (int)(apr_uintptr_t)idx_void; + if (idx == 0) + { + APR_ARRAY_PUSH(ids, svn_fs_x__id_t) = *id; + idx = ids->nelts; + apr_hash_set(dict, ids->elts + (idx-1) * ids->elt_size, + ids->elt_size, (void*)(apr_uintptr_t)idx); + } + + return idx; +} + +/* Given the REP, return the index+1 into REPS that contains a copy of it. + * Returns 0 for NULL IDs. We use DICT to detect duplicates. + */ +static int +store_representation(apr_array_header_t *reps, + apr_hash_t *dict, + const svn_fs_x__representation_t *rep) +{ + int idx; + void *idx_void; + + if (rep == NULL) + return 0; + + idx_void = apr_hash_get(dict, rep, sizeof(*rep)); + idx = (int)(apr_uintptr_t)idx_void; + if (idx == 0) + { + APR_ARRAY_PUSH(reps, svn_fs_x__representation_t) = *rep; + idx = reps->nelts; + apr_hash_set(dict, reps->elts + (idx-1) * reps->elt_size, + reps->elt_size, (void*)(apr_uintptr_t)idx); + } + + return idx; +} + +apr_size_t +svn_fs_x__noderevs_add(svn_fs_x__noderevs_t *container, + svn_fs_x__noderev_t *noderev) +{ + binary_noderev_t binary_noderev = { 0 }; + + binary_noderev.flags = (noderev->has_mergeinfo ? NODEREV_HAS_MINFO : 0) + | (noderev->copyfrom_path ? NODEREV_HAS_COPYFROM : 0) + | (noderev->copyroot_path ? NODEREV_HAS_COPYROOT : 0) + | (noderev->created_path ? NODEREV_HAS_CPATH : 0) + | (int)noderev->kind; + + binary_noderev.id + = store_id(container->ids, container->ids_dict, &noderev->noderev_id); + binary_noderev.node_id + = store_id(container->ids, container->ids_dict, &noderev->node_id); + binary_noderev.copy_id + = store_id(container->ids, container->ids_dict, &noderev->copy_id); + binary_noderev.predecessor_id + = store_id(container->ids, container->ids_dict, &noderev->predecessor_id); + + if (noderev->copyfrom_path) + { + binary_noderev.copyfrom_path + = svn_fs_x__string_table_builder_add(container->builder, + noderev->copyfrom_path, + 0); + binary_noderev.copyfrom_rev = noderev->copyfrom_rev; + } + + if (noderev->copyroot_path) + { + binary_noderev.copyroot_path + = svn_fs_x__string_table_builder_add(container->builder, + noderev->copyroot_path, + 0); + binary_noderev.copyroot_rev = noderev->copyroot_rev; + } + + binary_noderev.predecessor_count = noderev->predecessor_count; + binary_noderev.prop_rep = store_representation(container->reps, + container->reps_dict, + noderev->prop_rep); + binary_noderev.data_rep = store_representation(container->reps, + container->reps_dict, + noderev->data_rep); + + if (noderev->created_path) + binary_noderev.created_path + = svn_fs_x__string_table_builder_add(container->builder, + noderev->created_path, + 0); + + binary_noderev.mergeinfo_count = noderev->mergeinfo_count; + + APR_ARRAY_PUSH(container->noderevs, binary_noderev_t) = binary_noderev; + + return container->noderevs->nelts - 1; +} + +apr_size_t +svn_fs_x__noderevs_estimate_size(const svn_fs_x__noderevs_t *container) +{ + /* CONTAINER must be in 'builder' mode */ + if (container->builder == NULL) + return 0; + + /* string table code makes its own prediction, + * noderevs should be < 16 bytes each, + * id parts < 4 bytes each, + * data representations < 40 bytes each, + * property representations < 30 bytes each, + * some static overhead should be assumed */ + return svn_fs_x__string_table_builder_estimate_size(container->builder) + + container->noderevs->nelts * 16 + + container->ids->nelts * 4 + + container->reps->nelts * 40 + + 100; +} + +/* Set *ID to the ID part stored at index IDX in IDS. + */ +static svn_error_t * +get_id(svn_fs_x__id_t *id, + const apr_array_header_t *ids, + int idx) +{ + /* handle NULL IDs */ + if (idx == 0) + { + svn_fs_x__id_reset(id); + return SVN_NO_ERROR; + } + + /* check for corrupted data */ + if (idx < 0 || idx > ids->nelts) + return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL, + _("ID part index %d exceeds container size %d"), + idx, ids->nelts); + + /* Return the requested ID. */ + *id = APR_ARRAY_IDX(ids, idx - 1, svn_fs_x__id_t); + + return SVN_NO_ERROR; +} + +/* Create a svn_fs_x__representation_t in *REP, allocated in POOL based on the + * representation stored at index IDX in REPS. + */ +static svn_error_t * +get_representation(svn_fs_x__representation_t **rep, + const apr_array_header_t *reps, + int idx, + apr_pool_t *pool) +{ + /* handle NULL representations */ + if (idx == 0) + { + *rep = NULL; + return SVN_NO_ERROR; + } + + /* check for corrupted data */ + if (idx < 0 || idx > reps->nelts) + return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL, + _("Node revision ID index %d" + " exceeds container size %d"), + idx, reps->nelts); + + /* no translation required. Just duplicate the info */ + *rep = apr_pmemdup(pool, + &APR_ARRAY_IDX(reps, idx - 1, svn_fs_x__representation_t), + sizeof(**rep)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__noderevs_get(svn_fs_x__noderev_t **noderev_p, + const svn_fs_x__noderevs_t *container, + apr_size_t idx, + apr_pool_t *pool) +{ + svn_fs_x__noderev_t *noderev; + binary_noderev_t *binary_noderev; + + /* CONTAINER must be in 'finalized' mode */ + SVN_ERR_ASSERT(container->builder == NULL); + SVN_ERR_ASSERT(container->paths); + + /* validate index */ + if (idx >= (apr_size_t)container->noderevs->nelts) + return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL, + apr_psprintf(pool, + _("Node revision index %%%s" + " exceeds container size %%d"), + APR_SIZE_T_FMT), + idx, container->noderevs->nelts); + + /* allocate result struct and fill it field by field */ + noderev = apr_pcalloc(pool, sizeof(*noderev)); + binary_noderev = &APR_ARRAY_IDX(container->noderevs, idx, binary_noderev_t); + + noderev->kind = (svn_node_kind_t)(binary_noderev->flags & NODEREV_KIND_MASK); + SVN_ERR(get_id(&noderev->noderev_id, container->ids, binary_noderev->id)); + SVN_ERR(get_id(&noderev->node_id, container->ids, + binary_noderev->node_id)); + SVN_ERR(get_id(&noderev->copy_id, container->ids, + binary_noderev->copy_id)); + SVN_ERR(get_id(&noderev->predecessor_id, container->ids, + binary_noderev->predecessor_id)); + + if (binary_noderev->flags & NODEREV_HAS_COPYFROM) + { + noderev->copyfrom_path + = svn_fs_x__string_table_get(container->paths, + binary_noderev->copyfrom_path, + NULL, + pool); + noderev->copyfrom_rev = binary_noderev->copyfrom_rev; + } + else + { + noderev->copyfrom_path = NULL; + noderev->copyfrom_rev = SVN_INVALID_REVNUM; + } + + if (binary_noderev->flags & NODEREV_HAS_COPYROOT) + { + noderev->copyroot_path + = svn_fs_x__string_table_get(container->paths, + binary_noderev->copyroot_path, + NULL, + pool); + noderev->copyroot_rev = binary_noderev->copyroot_rev; + } + else + { + noderev->copyroot_path = NULL; + noderev->copyroot_rev = 0; + } + + noderev->predecessor_count = binary_noderev->predecessor_count; + + SVN_ERR(get_representation(&noderev->prop_rep, container->reps, + binary_noderev->prop_rep, pool)); + SVN_ERR(get_representation(&noderev->data_rep, container->reps, + binary_noderev->data_rep, pool)); + + if (binary_noderev->flags & NODEREV_HAS_CPATH) + noderev->created_path + = svn_fs_x__string_table_get(container->paths, + binary_noderev->created_path, + NULL, + pool); + + noderev->mergeinfo_count = binary_noderev->mergeinfo_count; + + noderev->has_mergeinfo = (binary_noderev->flags & NODEREV_HAS_MINFO) ? 1 : 0; + *noderev_p = noderev; + + return SVN_NO_ERROR; +} + +/* Create and return a stream for representations in PARENT. + * Initialize the sub-streams for all fields, except checksums. + */ +static svn_packed__int_stream_t * +create_rep_stream(svn_packed__int_stream_t *parent) +{ + svn_packed__int_stream_t *stream + = svn_packed__create_int_substream(parent, FALSE, FALSE); + + /* sub-streams for members - except for checksums */ + /* has_sha1 */ + svn_packed__create_int_substream(stream, FALSE, FALSE); + + /* rev, item_index, size, expanded_size */ + svn_packed__create_int_substream(stream, TRUE, FALSE); + svn_packed__create_int_substream(stream, FALSE, FALSE); + svn_packed__create_int_substream(stream, FALSE, FALSE); + svn_packed__create_int_substream(stream, FALSE, FALSE); + + return stream; +} + +/* Serialize all representations in REP. Store checksums in DIGEST_STREAM, + * put all other fields into REP_STREAM. + */ +static void +write_reps(svn_packed__int_stream_t *rep_stream, + svn_packed__byte_stream_t *digest_stream, + apr_array_header_t *reps) +{ + int i; + for (i = 0; i < reps->nelts; ++i) + { + svn_fs_x__representation_t *rep + = &APR_ARRAY_IDX(reps, i, svn_fs_x__representation_t); + + svn_packed__add_uint(rep_stream, rep->has_sha1); + + svn_packed__add_uint(rep_stream, rep->id.change_set); + svn_packed__add_uint(rep_stream, rep->id.number); + svn_packed__add_uint(rep_stream, rep->size); + svn_packed__add_uint(rep_stream, rep->expanded_size); + + svn_packed__add_bytes(digest_stream, + (const char *)rep->md5_digest, + sizeof(rep->md5_digest)); + if (rep->has_sha1) + svn_packed__add_bytes(digest_stream, + (const char *)rep->sha1_digest, + sizeof(rep->sha1_digest)); + } +} + +svn_error_t * +svn_fs_x__write_noderevs_container(svn_stream_t *stream, + const svn_fs_x__noderevs_t *container, + apr_pool_t *scratch_pool) +{ + int i; + + string_table_t *paths = container->paths + ? container->paths + : svn_fs_x__string_table_create(container->builder, + scratch_pool); + + svn_packed__data_root_t *root = svn_packed__data_create_root(scratch_pool); + + /* one common top-level stream for all arrays. One sub-stream */ + svn_packed__int_stream_t *structs_stream + = svn_packed__create_int_stream(root, FALSE, FALSE); + svn_packed__int_stream_t *ids_stream + = svn_packed__create_int_substream(structs_stream, FALSE, FALSE); + svn_packed__int_stream_t *reps_stream + = create_rep_stream(structs_stream); + svn_packed__int_stream_t *noderevs_stream + = svn_packed__create_int_substream(structs_stream, FALSE, FALSE); + svn_packed__byte_stream_t *digests_stream + = svn_packed__create_bytes_stream(root); + + /* structure the IDS_STREAM such we can extract much of the redundancy + * from the svn_fs_x__ip_part_t structs */ + for (i = 0; i < 2; ++i) + svn_packed__create_int_substream(ids_stream, TRUE, FALSE); + + /* Same storing binary_noderev_t in the NODEREVS_STREAM */ + svn_packed__create_int_substream(noderevs_stream, FALSE, FALSE); + for (i = 0; i < 13; ++i) + svn_packed__create_int_substream(noderevs_stream, TRUE, FALSE); + + /* serialize ids array */ + for (i = 0; i < container->ids->nelts; ++i) + { + svn_fs_x__id_t *id = &APR_ARRAY_IDX(container->ids, i, svn_fs_x__id_t); + + svn_packed__add_int(ids_stream, id->change_set); + svn_packed__add_uint(ids_stream, id->number); + } + + /* serialize rep arrays */ + write_reps(reps_stream, digests_stream, container->reps); + + /* serialize noderevs array */ + for (i = 0; i < container->noderevs->nelts; ++i) + { + const binary_noderev_t *noderev + = &APR_ARRAY_IDX(container->noderevs, i, binary_noderev_t); + + svn_packed__add_uint(noderevs_stream, noderev->flags); + + svn_packed__add_uint(noderevs_stream, noderev->id); + svn_packed__add_uint(noderevs_stream, noderev->node_id); + svn_packed__add_uint(noderevs_stream, noderev->copy_id); + svn_packed__add_uint(noderevs_stream, noderev->predecessor_id); + svn_packed__add_uint(noderevs_stream, noderev->predecessor_count); + + svn_packed__add_uint(noderevs_stream, noderev->copyfrom_path); + svn_packed__add_int(noderevs_stream, noderev->copyfrom_rev); + svn_packed__add_uint(noderevs_stream, noderev->copyroot_path); + svn_packed__add_int(noderevs_stream, noderev->copyroot_rev); + + svn_packed__add_uint(noderevs_stream, noderev->prop_rep); + svn_packed__add_uint(noderevs_stream, noderev->data_rep); + + svn_packed__add_uint(noderevs_stream, noderev->created_path); + svn_packed__add_uint(noderevs_stream, noderev->mergeinfo_count); + } + + /* write to disk */ + SVN_ERR(svn_fs_x__write_string_table(stream, paths, scratch_pool)); + SVN_ERR(svn_packed__data_write(stream, root, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Allocate a svn_fs_x__representation_t array in POOL and return it in + * REPS_P. Deserialize the data in REP_STREAM and DIGEST_STREAM and store + * the resulting representations into the *REPS_P. + */ +static svn_error_t * +read_reps(apr_array_header_t **reps_p, + svn_packed__int_stream_t *rep_stream, + svn_packed__byte_stream_t *digest_stream, + apr_pool_t *pool) +{ + apr_size_t i; + apr_size_t len; + const char *bytes; + + apr_size_t count + = svn_packed__int_count(svn_packed__first_int_substream(rep_stream)); + apr_array_header_t *reps + = apr_array_make(pool, (int)count, sizeof(svn_fs_x__representation_t)); + + for (i = 0; i < count; ++i) + { + svn_fs_x__representation_t rep; + + rep.has_sha1 = (svn_boolean_t)svn_packed__get_uint(rep_stream); + + rep.id.change_set = (svn_revnum_t)svn_packed__get_uint(rep_stream); + rep.id.number = svn_packed__get_uint(rep_stream); + rep.size = svn_packed__get_uint(rep_stream); + rep.expanded_size = svn_packed__get_uint(rep_stream); + + /* when extracting the checksums, beware of buffer under/overflows + caused by disk data corruption. */ + bytes = svn_packed__get_bytes(digest_stream, &len); + if (len != sizeof(rep.md5_digest)) + return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL, + apr_psprintf(pool, + _("Unexpected MD5" + " digest size %%%s"), + APR_SIZE_T_FMT), + len); + + memcpy(rep.md5_digest, bytes, sizeof(rep.md5_digest)); + if (rep.has_sha1) + { + bytes = svn_packed__get_bytes(digest_stream, &len); + if (len != sizeof(rep.sha1_digest)) + return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL, + apr_psprintf(pool, + _("Unexpected SHA1" + " digest size %%%s"), + APR_SIZE_T_FMT), + len); + + memcpy(rep.sha1_digest, bytes, sizeof(rep.sha1_digest)); + } + + APR_ARRAY_PUSH(reps, svn_fs_x__representation_t) = rep; + } + + *reps_p = reps; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__read_noderevs_container(svn_fs_x__noderevs_t **container, + svn_stream_t *stream, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_size_t i; + apr_size_t count; + + svn_fs_x__noderevs_t *noderevs + = apr_pcalloc(result_pool, sizeof(*noderevs)); + + svn_packed__data_root_t *root; + svn_packed__int_stream_t *structs_stream; + svn_packed__int_stream_t *ids_stream; + svn_packed__int_stream_t *reps_stream; + svn_packed__int_stream_t *noderevs_stream; + svn_packed__byte_stream_t *digests_stream; + + /* read everything from disk */ + SVN_ERR(svn_fs_x__read_string_table(&noderevs->paths, stream, + result_pool, scratch_pool)); + SVN_ERR(svn_packed__data_read(&root, stream, result_pool, scratch_pool)); + + /* get streams */ + structs_stream = svn_packed__first_int_stream(root); + ids_stream = svn_packed__first_int_substream(structs_stream); + reps_stream = svn_packed__next_int_stream(ids_stream); + noderevs_stream = svn_packed__next_int_stream(reps_stream); + digests_stream = svn_packed__first_byte_stream(root); + + /* read ids array */ + count + = svn_packed__int_count(svn_packed__first_int_substream(ids_stream)); + noderevs->ids + = apr_array_make(result_pool, (int)count, sizeof(svn_fs_x__id_t)); + for (i = 0; i < count; ++i) + { + svn_fs_x__id_t id; + + id.change_set = (svn_revnum_t)svn_packed__get_int(ids_stream); + id.number = svn_packed__get_uint(ids_stream); + + APR_ARRAY_PUSH(noderevs->ids, svn_fs_x__id_t) = id; + } + + /* read rep arrays */ + SVN_ERR(read_reps(&noderevs->reps, reps_stream, digests_stream, + result_pool)); + + /* read noderevs array */ + count + = svn_packed__int_count(svn_packed__first_int_substream(noderevs_stream)); + noderevs->noderevs + = apr_array_make(result_pool, (int)count, sizeof(binary_noderev_t)); + for (i = 0; i < count; ++i) + { + binary_noderev_t noderev; + + noderev.flags = (apr_uint32_t)svn_packed__get_uint(noderevs_stream); + + noderev.id = (int)svn_packed__get_uint(noderevs_stream); + noderev.node_id = (int)svn_packed__get_uint(noderevs_stream); + noderev.copy_id = (int)svn_packed__get_uint(noderevs_stream); + noderev.predecessor_id = (int)svn_packed__get_uint(noderevs_stream); + noderev.predecessor_count = (int)svn_packed__get_uint(noderevs_stream); + + noderev.copyfrom_path = (apr_size_t)svn_packed__get_uint(noderevs_stream); + noderev.copyfrom_rev = (svn_revnum_t)svn_packed__get_int(noderevs_stream); + noderev.copyroot_path = (apr_size_t)svn_packed__get_uint(noderevs_stream); + noderev.copyroot_rev = (svn_revnum_t)svn_packed__get_int(noderevs_stream); + + noderev.prop_rep = (int)svn_packed__get_uint(noderevs_stream); + noderev.data_rep = (int)svn_packed__get_uint(noderevs_stream); + + noderev.created_path = (apr_size_t)svn_packed__get_uint(noderevs_stream); + noderev.mergeinfo_count = svn_packed__get_uint(noderevs_stream); + + APR_ARRAY_PUSH(noderevs->noderevs, binary_noderev_t) = noderev; + } + + *container = noderevs; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__serialize_noderevs_container(void **data, + apr_size_t *data_len, + void *in, + apr_pool_t *pool) +{ + svn_fs_x__noderevs_t *noderevs = in; + svn_stringbuf_t *serialized; + apr_size_t size + = noderevs->ids->elt_size * noderevs->ids->nelts + + noderevs->reps->elt_size * noderevs->reps->nelts + + noderevs->noderevs->elt_size * noderevs->noderevs->nelts + + 10 * noderevs->noderevs->elt_size + + 100; + + /* serialize array header and all its elements */ + svn_temp_serializer__context_t *context + = svn_temp_serializer__init(noderevs, sizeof(*noderevs), size, pool); + + /* serialize sub-structures */ + svn_fs_x__serialize_string_table(context, &noderevs->paths); + svn_fs_x__serialize_apr_array(context, &noderevs->ids); + svn_fs_x__serialize_apr_array(context, &noderevs->reps); + svn_fs_x__serialize_apr_array(context, &noderevs->noderevs); + + /* return the serialized result */ + serialized = svn_temp_serializer__get(context); + + *data = serialized->data; + *data_len = serialized->len; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__deserialize_noderevs_container(void **out, + void *data, + apr_size_t data_len, + apr_pool_t *pool) +{ + svn_fs_x__noderevs_t *noderevs = (svn_fs_x__noderevs_t *)data; + + /* de-serialize sub-structures */ + svn_fs_x__deserialize_string_table(noderevs, &noderevs->paths); + svn_fs_x__deserialize_apr_array(noderevs, &noderevs->ids, pool); + svn_fs_x__deserialize_apr_array(noderevs, &noderevs->reps, pool); + svn_fs_x__deserialize_apr_array(noderevs, &noderevs->noderevs, pool); + + /* done */ + *out = noderevs; + + return SVN_NO_ERROR; +} + +/* Deserialize the cache serialized APR struct at *IN in BUFFER and write + * the result to OUT. Note that this will only resolve the pointers and + * not the array elements themselves. */ +static void +resolve_apr_array_header(apr_array_header_t *out, + const void *buffer, + apr_array_header_t * const *in) +{ + const apr_array_header_t *array + = svn_temp_deserializer__ptr(buffer, (const void *const *)in); + const char *elements + = svn_temp_deserializer__ptr(array, (const void *const *)&array->elts); + + *out = *array; + out->elts = (char *)elements; + out->pool = NULL; +} + +svn_error_t * +svn_fs_x__noderevs_get_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *pool) +{ + svn_fs_x__noderev_t *noderev; + binary_noderev_t *binary_noderev; + + apr_array_header_t ids; + apr_array_header_t reps; + apr_array_header_t noderevs; + + apr_uint32_t idx = *(apr_uint32_t *)baton; + const svn_fs_x__noderevs_t *container = data; + + /* Resolve all container pointers */ + const string_table_t *paths + = svn_temp_deserializer__ptr(container, + (const void *const *)&container->paths); + + resolve_apr_array_header(&ids, container, &container->ids); + resolve_apr_array_header(&reps, container, &container->reps); + resolve_apr_array_header(&noderevs, container, &container->noderevs); + + /* allocate result struct and fill it field by field */ + noderev = apr_pcalloc(pool, sizeof(*noderev)); + binary_noderev = &APR_ARRAY_IDX(&noderevs, idx, binary_noderev_t); + + noderev->kind = (svn_node_kind_t)(binary_noderev->flags & NODEREV_KIND_MASK); + SVN_ERR(get_id(&noderev->noderev_id, &ids, binary_noderev->id)); + SVN_ERR(get_id(&noderev->node_id, &ids, binary_noderev->node_id)); + SVN_ERR(get_id(&noderev->copy_id, &ids, binary_noderev->copy_id)); + SVN_ERR(get_id(&noderev->predecessor_id, &ids, + binary_noderev->predecessor_id)); + + if (binary_noderev->flags & NODEREV_HAS_COPYFROM) + { + noderev->copyfrom_path + = svn_fs_x__string_table_get_func(paths, + binary_noderev->copyfrom_path, + NULL, + pool); + noderev->copyfrom_rev = binary_noderev->copyfrom_rev; + } + else + { + noderev->copyfrom_path = NULL; + noderev->copyfrom_rev = SVN_INVALID_REVNUM; + } + + if (binary_noderev->flags & NODEREV_HAS_COPYROOT) + { + noderev->copyroot_path + = svn_fs_x__string_table_get_func(paths, + binary_noderev->copyroot_path, + NULL, + pool); + noderev->copyroot_rev = binary_noderev->copyroot_rev; + } + else + { + noderev->copyroot_path = NULL; + noderev->copyroot_rev = 0; + } + + noderev->predecessor_count = binary_noderev->predecessor_count; + + SVN_ERR(get_representation(&noderev->prop_rep, &reps, + binary_noderev->prop_rep, pool)); + SVN_ERR(get_representation(&noderev->data_rep, &reps, + binary_noderev->data_rep, pool)); + + if (binary_noderev->flags & NODEREV_HAS_CPATH) + noderev->created_path + = svn_fs_x__string_table_get_func(paths, + binary_noderev->created_path, + NULL, + pool); + + noderev->mergeinfo_count = binary_noderev->mergeinfo_count; + + noderev->has_mergeinfo = (binary_noderev->flags & NODEREV_HAS_MINFO) ? 1 : 0; + *out = noderev; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__mergeinfo_count_get_func(void **out, + const void *data, + apr_size_t data_len, + void *baton, + apr_pool_t *pool) +{ + binary_noderev_t *binary_noderev; + apr_array_header_t noderevs; + + apr_uint32_t idx = *(apr_uint32_t *)baton; + const svn_fs_x__noderevs_t *container = data; + + /* Resolve all container pointers */ + resolve_apr_array_header(&noderevs, container, &container->noderevs); + binary_noderev = &APR_ARRAY_IDX(&noderevs, idx, binary_noderev_t); + + *(apr_int64_t *)out = binary_noderev->mergeinfo_count; + + return SVN_NO_ERROR; +} |