summaryrefslogtreecommitdiff
path: root/subversion/libsvn_fs_x/noderevs.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_fs_x/noderevs.c')
-rw-r--r--subversion/libsvn_fs_x/noderevs.c912
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;
+}