1/* noderevs.h --- FSX node revision container
2 *
3 * ====================================================================
4 *    Licensed to the Apache Software Foundation (ASF) under one
5 *    or more contributor license agreements.  See the NOTICE file
6 *    distributed with this work for additional information
7 *    regarding copyright ownership.  The ASF licenses this file
8 *    to you under the Apache License, Version 2.0 (the
9 *    "License"); you may not use this file except in compliance
10 *    with the License.  You may obtain a copy of the License at
11 *
12 *      http://www.apache.org/licenses/LICENSE-2.0
13 *
14 *    Unless required by applicable law or agreed to in writing,
15 *    software distributed under the License is distributed on an
16 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 *    KIND, either express or implied.  See the License for the
18 *    specific language governing permissions and limitations
19 *    under the License.
20 * ====================================================================
21 */
22
23#include "svn_private_config.h"
24
25#include "private/svn_dep_compat.h"
26#include "private/svn_packed_data.h"
27#include "private/svn_subr_private.h"
28#include "private/svn_temp_serializer.h"
29
30#include "noderevs.h"
31#include "string_table.h"
32#include "temp_serializer.h"
33
34/* These flags will be used with the FLAGS field in binary_noderev_t.
35 */
36
37/* (flags & NODEREV_KIND_MASK) extracts the noderev type */
38#define NODEREV_KIND_MASK    0x00007
39
40/* the noderev has merge info */
41#define NODEREV_HAS_MINFO    0x00008
42
43/* the noderev has copy-from-path and revision */
44#define NODEREV_HAS_COPYFROM 0x00010
45
46/* the noderev has copy-root path and revision */
47#define NODEREV_HAS_COPYROOT 0x00020
48
49/* the noderev has copy-root path and revision */
50#define NODEREV_HAS_CPATH    0x00040
51
52/* Our internal representation of a svn_fs_x__noderev_t.
53 *
54 * We will store path strings in a string container and reference them
55 * from here.  Similarly, IDs and representations are being stored in
56 * separate containers and then also referenced here.  This eliminates
57 * the need to store the same IDs and representations more than once.
58 */
59typedef struct binary_noderev_t
60{
61  /* node type and presence indicators */
62  apr_uint32_t flags;
63
64  /* Index+1 of the noderev-id for this node-rev. */
65  int id;
66
67  /* Index+1 of the node-id for this node-rev. */
68  int node_id;
69
70  /* Index+1 of the copy-id for this node-rev. */
71  int copy_id;
72
73  /* Index+1 of the predecessor node revision id, or 0 if there is no
74     predecessor for this node revision */
75  int predecessor_id;
76
77  /* number of predecessors this node revision has (recursively), or
78     -1 if not known (for backward compatibility). */
79  int predecessor_count;
80
81  /* If this node-rev is a copy, what revision was it copied from? */
82  svn_revnum_t copyfrom_rev;
83
84  /* Helper for history tracing, root revision of the parent tree from
85     whence this node-rev was copied. */
86  svn_revnum_t copyroot_rev;
87
88  /* If this node-rev is a copy, this is the string index+1 of the path
89     from which that copy way made. 0, otherwise. */
90  apr_size_t copyfrom_path;
91
92  /* String index+1 of the root of the parent tree from whence this node-
93   * rev was copied. */
94  apr_size_t copyroot_path;
95
96  /* Index+1 of the representation key for this node's properties.
97     May be 0 if there are no properties.  */
98  int prop_rep;
99
100  /* Index+1 of the representation for this node's data.
101     May be 0 if there is no data. */
102  int data_rep;
103
104  /* String index+1 of the path at which this node first came into
105     existence.  */
106  apr_size_t created_path;
107
108  /* Number of nodes with svn:mergeinfo properties that are
109     descendants of this node (including it itself) */
110  apr_int64_t mergeinfo_count;
111
112} binary_noderev_t;
113
114/* The actual container object.  Node revisions are concatenated into
115 * NODEREVS, referenced representations are stored in DATA_REPS / PROP_REPS
116 * and the ids in IDs.  PATHS is the string table for all paths.
117 *
118 * During construction, BUILDER will be used instead of PATHS. IDS_DICT,
119 * DATA_REPS_DICT and PROP_REPS_DICT are also only used during construction
120 * and are NULL otherwise.
121 */
122struct svn_fs_x__noderevs_t
123{
124  /* The paths - either in 'builder' mode or finalized mode.
125   * The respective other pointer will be NULL. */
126  string_table_builder_t *builder;
127  string_table_t *paths;
128
129  /* During construction, maps a full binary_id_t to an index into IDS */
130  apr_hash_t *ids_dict;
131
132  /* During construction, maps a full binary_representation_t to an index
133   * into REPS. */
134  apr_hash_t *reps_dict;
135
136  /* array of binary_id_t */
137  apr_array_header_t *ids;
138
139  /* array of binary_representation_t */
140  apr_array_header_t *reps;
141
142  /* array of binary_noderev_t. */
143  apr_array_header_t *noderevs;
144};
145
146svn_fs_x__noderevs_t *
147svn_fs_x__noderevs_create(int initial_count,
148                          apr_pool_t* result_pool)
149{
150  svn_fs_x__noderevs_t *noderevs
151    = apr_palloc(result_pool, sizeof(*noderevs));
152
153  noderevs->builder = svn_fs_x__string_table_builder_create(result_pool);
154  noderevs->ids_dict = svn_hash__make(result_pool);
155  noderevs->reps_dict = svn_hash__make(result_pool);
156  noderevs->paths = NULL;
157
158  noderevs->ids
159    = apr_array_make(result_pool, 2 * initial_count, sizeof(svn_fs_x__id_t));
160  noderevs->reps
161    = apr_array_make(result_pool, 2 * initial_count,
162                     sizeof(svn_fs_x__representation_t));
163  noderevs->noderevs
164    = apr_array_make(result_pool, initial_count, sizeof(binary_noderev_t));
165
166  return noderevs;
167}
168
169/* Given the ID, return the index+1 into IDS that contains a binary_id
170 * for it.  Returns 0 for NULL IDs.  We use DICT to detect duplicates.
171 */
172static int
173store_id(apr_array_header_t *ids,
174         apr_hash_t *dict,
175         const svn_fs_x__id_t *id)
176{
177  int idx;
178  void *idx_void;
179
180  if (!svn_fs_x__id_used(id))
181    return 0;
182
183  idx_void = apr_hash_get(dict, &id, sizeof(id));
184  idx = (int)(apr_uintptr_t)idx_void;
185  if (idx == 0)
186    {
187      APR_ARRAY_PUSH(ids, svn_fs_x__id_t) = *id;
188      idx = ids->nelts;
189      apr_hash_set(dict, ids->elts + (idx-1) * ids->elt_size,
190                   ids->elt_size, (void*)(apr_uintptr_t)idx);
191    }
192
193  return idx;
194}
195
196/* Given the REP, return the index+1 into REPS that contains a copy of it.
197 * Returns 0 for NULL IDs.  We use DICT to detect duplicates.
198 */
199static int
200store_representation(apr_array_header_t *reps,
201                     apr_hash_t *dict,
202                     const svn_fs_x__representation_t *rep)
203{
204  int idx;
205  void *idx_void;
206
207  if (rep == NULL)
208    return 0;
209
210  idx_void = apr_hash_get(dict, rep, sizeof(*rep));
211  idx = (int)(apr_uintptr_t)idx_void;
212  if (idx == 0)
213    {
214      APR_ARRAY_PUSH(reps, svn_fs_x__representation_t) = *rep;
215      idx = reps->nelts;
216      apr_hash_set(dict, reps->elts + (idx-1) * reps->elt_size,
217                   reps->elt_size, (void*)(apr_uintptr_t)idx);
218    }
219
220  return idx;
221}
222
223apr_size_t
224svn_fs_x__noderevs_add(svn_fs_x__noderevs_t *container,
225                       svn_fs_x__noderev_t *noderev)
226{
227  binary_noderev_t binary_noderev = { 0 };
228
229  binary_noderev.flags = (noderev->has_mergeinfo ? NODEREV_HAS_MINFO : 0)
230                       | (noderev->copyfrom_path ? NODEREV_HAS_COPYFROM : 0)
231                       | (noderev->copyroot_path  ? NODEREV_HAS_COPYROOT : 0)
232                       | (noderev->created_path  ? NODEREV_HAS_CPATH : 0)
233                       | (int)noderev->kind;
234
235  binary_noderev.id
236    = store_id(container->ids, container->ids_dict, &noderev->noderev_id);
237  binary_noderev.node_id
238    = store_id(container->ids, container->ids_dict, &noderev->node_id);
239  binary_noderev.copy_id
240    = store_id(container->ids, container->ids_dict, &noderev->copy_id);
241  binary_noderev.predecessor_id
242    = store_id(container->ids, container->ids_dict, &noderev->predecessor_id);
243
244  if (noderev->copyfrom_path)
245    {
246      binary_noderev.copyfrom_path
247        = svn_fs_x__string_table_builder_add(container->builder,
248                                             noderev->copyfrom_path,
249                                             0);
250      binary_noderev.copyfrom_rev = noderev->copyfrom_rev;
251    }
252
253  if (noderev->copyroot_path)
254    {
255      binary_noderev.copyroot_path
256        = svn_fs_x__string_table_builder_add(container->builder,
257                                             noderev->copyroot_path,
258                                             0);
259      binary_noderev.copyroot_rev = noderev->copyroot_rev;
260    }
261
262  binary_noderev.predecessor_count = noderev->predecessor_count;
263  binary_noderev.prop_rep = store_representation(container->reps,
264                                                 container->reps_dict,
265                                                 noderev->prop_rep);
266  binary_noderev.data_rep = store_representation(container->reps,
267                                                 container->reps_dict,
268                                                 noderev->data_rep);
269
270  if (noderev->created_path)
271    binary_noderev.created_path
272      = svn_fs_x__string_table_builder_add(container->builder,
273                                           noderev->created_path,
274                                           0);
275
276  binary_noderev.mergeinfo_count = noderev->mergeinfo_count;
277
278  APR_ARRAY_PUSH(container->noderevs, binary_noderev_t) = binary_noderev;
279
280  return container->noderevs->nelts - 1;
281}
282
283apr_size_t
284svn_fs_x__noderevs_estimate_size(const svn_fs_x__noderevs_t *container)
285{
286  /* CONTAINER must be in 'builder' mode */
287  if (container->builder == NULL)
288    return 0;
289
290  /* string table code makes its own prediction,
291   * noderevs should be < 16 bytes each,
292   * id parts < 4 bytes each,
293   * data representations < 40 bytes each,
294   * property representations < 30 bytes each,
295   * some static overhead should be assumed */
296  return svn_fs_x__string_table_builder_estimate_size(container->builder)
297       + container->noderevs->nelts * 16
298       + container->ids->nelts * 4
299       + container->reps->nelts * 40
300       + 100;
301}
302
303/* Set *ID to the ID part stored at index IDX in IDS.
304 */
305static svn_error_t *
306get_id(svn_fs_x__id_t *id,
307       const apr_array_header_t *ids,
308       int idx)
309{
310  /* handle NULL IDs  */
311  if (idx == 0)
312    {
313      svn_fs_x__id_reset(id);
314      return SVN_NO_ERROR;
315    }
316
317  /* check for corrupted data */
318  if (idx < 0 || idx > ids->nelts)
319    return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL,
320                             _("ID part index %d exceeds container size %d"),
321                             idx, ids->nelts);
322
323  /* Return the requested ID. */
324  *id = APR_ARRAY_IDX(ids, idx - 1, svn_fs_x__id_t);
325
326  return SVN_NO_ERROR;
327}
328
329/* Create a svn_fs_x__representation_t in *REP, allocated in POOL based on the
330 * representation stored at index IDX in REPS.
331 */
332static svn_error_t *
333get_representation(svn_fs_x__representation_t **rep,
334                   const apr_array_header_t *reps,
335                   int idx,
336                   apr_pool_t *pool)
337{
338  /* handle NULL representations  */
339  if (idx == 0)
340    {
341      *rep = NULL;
342      return SVN_NO_ERROR;
343    }
344
345  /* check for corrupted data */
346  if (idx < 0 || idx > reps->nelts)
347    return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL,
348                             _("Node revision ID index %d"
349                               " exceeds container size %d"),
350                             idx, reps->nelts);
351
352  /* no translation required. Just duplicate the info */
353  *rep = apr_pmemdup(pool,
354                     &APR_ARRAY_IDX(reps, idx - 1, svn_fs_x__representation_t),
355                     sizeof(**rep));
356
357  return SVN_NO_ERROR;
358}
359
360svn_error_t *
361svn_fs_x__noderevs_get(svn_fs_x__noderev_t **noderev_p,
362                       const svn_fs_x__noderevs_t *container,
363                       apr_size_t idx,
364                       apr_pool_t *pool)
365{
366  svn_fs_x__noderev_t *noderev;
367  binary_noderev_t *binary_noderev;
368
369  /* CONTAINER must be in 'finalized' mode */
370  SVN_ERR_ASSERT(container->builder == NULL);
371  SVN_ERR_ASSERT(container->paths);
372
373  /* validate index */
374  if (idx >= (apr_size_t)container->noderevs->nelts)
375    return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL,
376                             apr_psprintf(pool,
377                                          _("Node revision index %%%s"
378                                            " exceeds container size %%d"),
379                                          APR_SIZE_T_FMT),
380                             idx, container->noderevs->nelts);
381
382  /* allocate result struct and fill it field by field */
383  noderev = apr_pcalloc(pool, sizeof(*noderev));
384  binary_noderev = &APR_ARRAY_IDX(container->noderevs, idx, binary_noderev_t);
385
386  noderev->kind = (svn_node_kind_t)(binary_noderev->flags & NODEREV_KIND_MASK);
387  SVN_ERR(get_id(&noderev->noderev_id, container->ids, binary_noderev->id));
388  SVN_ERR(get_id(&noderev->node_id, container->ids,
389                 binary_noderev->node_id));
390  SVN_ERR(get_id(&noderev->copy_id, container->ids,
391                 binary_noderev->copy_id));
392  SVN_ERR(get_id(&noderev->predecessor_id, container->ids,
393                 binary_noderev->predecessor_id));
394
395  if (binary_noderev->flags & NODEREV_HAS_COPYFROM)
396    {
397      noderev->copyfrom_path
398        = svn_fs_x__string_table_get(container->paths,
399                                     binary_noderev->copyfrom_path,
400                                     NULL,
401                                     pool);
402      noderev->copyfrom_rev = binary_noderev->copyfrom_rev;
403    }
404  else
405    {
406      noderev->copyfrom_path = NULL;
407      noderev->copyfrom_rev = SVN_INVALID_REVNUM;
408    }
409
410  if (binary_noderev->flags & NODEREV_HAS_COPYROOT)
411    {
412      noderev->copyroot_path
413        = svn_fs_x__string_table_get(container->paths,
414                                     binary_noderev->copyroot_path,
415                                     NULL,
416                                     pool);
417      noderev->copyroot_rev = binary_noderev->copyroot_rev;
418    }
419  else
420    {
421      noderev->copyroot_path = NULL;
422      noderev->copyroot_rev = 0;
423    }
424
425  noderev->predecessor_count = binary_noderev->predecessor_count;
426
427  SVN_ERR(get_representation(&noderev->prop_rep, container->reps,
428                             binary_noderev->prop_rep, pool));
429  SVN_ERR(get_representation(&noderev->data_rep, container->reps,
430                             binary_noderev->data_rep, pool));
431
432  if (binary_noderev->flags & NODEREV_HAS_CPATH)
433    noderev->created_path
434      = svn_fs_x__string_table_get(container->paths,
435                                   binary_noderev->created_path,
436                                   NULL,
437                                   pool);
438
439  noderev->mergeinfo_count = binary_noderev->mergeinfo_count;
440
441  noderev->has_mergeinfo = (binary_noderev->flags & NODEREV_HAS_MINFO) ? 1 : 0;
442  *noderev_p = noderev;
443
444  return SVN_NO_ERROR;
445}
446
447/* Create and return a stream for representations in PARENT.
448 * Initialize the sub-streams for all fields, except checksums.
449 */
450static svn_packed__int_stream_t *
451create_rep_stream(svn_packed__int_stream_t *parent)
452{
453  svn_packed__int_stream_t *stream
454    = svn_packed__create_int_substream(parent, FALSE, FALSE);
455
456  /* sub-streams for members - except for checksums */
457  /* has_sha1 */
458  svn_packed__create_int_substream(stream, FALSE, FALSE);
459
460  /* rev, item_index, size, expanded_size */
461  svn_packed__create_int_substream(stream, TRUE, FALSE);
462  svn_packed__create_int_substream(stream, FALSE, FALSE);
463  svn_packed__create_int_substream(stream, FALSE, FALSE);
464  svn_packed__create_int_substream(stream, FALSE, FALSE);
465
466  return stream;
467}
468
469/* Serialize all representations in REP.  Store checksums in DIGEST_STREAM,
470 * put all other fields into REP_STREAM.
471 */
472static void
473write_reps(svn_packed__int_stream_t *rep_stream,
474           svn_packed__byte_stream_t *digest_stream,
475           apr_array_header_t *reps)
476{
477  int i;
478  for (i = 0; i < reps->nelts; ++i)
479    {
480      svn_fs_x__representation_t *rep
481        = &APR_ARRAY_IDX(reps, i, svn_fs_x__representation_t);
482
483      svn_packed__add_uint(rep_stream, rep->has_sha1);
484
485      svn_packed__add_uint(rep_stream, rep->id.change_set);
486      svn_packed__add_uint(rep_stream, rep->id.number);
487      svn_packed__add_uint(rep_stream, rep->size);
488      svn_packed__add_uint(rep_stream, rep->expanded_size);
489
490      svn_packed__add_bytes(digest_stream,
491                            (const char *)rep->md5_digest,
492                            sizeof(rep->md5_digest));
493      if (rep->has_sha1)
494        svn_packed__add_bytes(digest_stream,
495                              (const char *)rep->sha1_digest,
496                              sizeof(rep->sha1_digest));
497    }
498}
499
500svn_error_t *
501svn_fs_x__write_noderevs_container(svn_stream_t *stream,
502                                   const svn_fs_x__noderevs_t *container,
503                                   apr_pool_t *scratch_pool)
504{
505  int i;
506
507  string_table_t *paths = container->paths
508                        ? container->paths
509                        : svn_fs_x__string_table_create(container->builder,
510                                                        scratch_pool);
511
512  svn_packed__data_root_t *root = svn_packed__data_create_root(scratch_pool);
513
514  /* one common top-level stream for all arrays. One sub-stream */
515  svn_packed__int_stream_t *structs_stream
516    = svn_packed__create_int_stream(root, FALSE, FALSE);
517  svn_packed__int_stream_t *ids_stream
518    = svn_packed__create_int_substream(structs_stream, FALSE, FALSE);
519  svn_packed__int_stream_t *reps_stream
520    = create_rep_stream(structs_stream);
521  svn_packed__int_stream_t *noderevs_stream
522    = svn_packed__create_int_substream(structs_stream, FALSE, FALSE);
523  svn_packed__byte_stream_t *digests_stream
524    = svn_packed__create_bytes_stream(root);
525
526  /* structure the IDS_STREAM such we can extract much of the redundancy
527   * from the svn_fs_x__ip_part_t structs */
528  for (i = 0; i < 2; ++i)
529    svn_packed__create_int_substream(ids_stream, TRUE, FALSE);
530
531  /* Same storing binary_noderev_t in the NODEREVS_STREAM */
532  svn_packed__create_int_substream(noderevs_stream, FALSE, FALSE);
533  for (i = 0; i < 13; ++i)
534    svn_packed__create_int_substream(noderevs_stream, TRUE, FALSE);
535
536  /* serialize ids array */
537  for (i = 0; i < container->ids->nelts; ++i)
538    {
539      svn_fs_x__id_t *id = &APR_ARRAY_IDX(container->ids, i, svn_fs_x__id_t);
540
541      svn_packed__add_int(ids_stream, id->change_set);
542      svn_packed__add_uint(ids_stream, id->number);
543    }
544
545  /* serialize rep arrays */
546  write_reps(reps_stream, digests_stream, container->reps);
547
548  /* serialize noderevs array */
549  for (i = 0; i < container->noderevs->nelts; ++i)
550    {
551      const binary_noderev_t *noderev
552        = &APR_ARRAY_IDX(container->noderevs, i, binary_noderev_t);
553
554      svn_packed__add_uint(noderevs_stream, noderev->flags);
555
556      svn_packed__add_uint(noderevs_stream, noderev->id);
557      svn_packed__add_uint(noderevs_stream, noderev->node_id);
558      svn_packed__add_uint(noderevs_stream, noderev->copy_id);
559      svn_packed__add_uint(noderevs_stream, noderev->predecessor_id);
560      svn_packed__add_uint(noderevs_stream, noderev->predecessor_count);
561
562      svn_packed__add_uint(noderevs_stream, noderev->copyfrom_path);
563      svn_packed__add_int(noderevs_stream, noderev->copyfrom_rev);
564      svn_packed__add_uint(noderevs_stream, noderev->copyroot_path);
565      svn_packed__add_int(noderevs_stream, noderev->copyroot_rev);
566
567      svn_packed__add_uint(noderevs_stream, noderev->prop_rep);
568      svn_packed__add_uint(noderevs_stream, noderev->data_rep);
569
570      svn_packed__add_uint(noderevs_stream, noderev->created_path);
571      svn_packed__add_uint(noderevs_stream, noderev->mergeinfo_count);
572    }
573
574  /* write to disk */
575  SVN_ERR(svn_fs_x__write_string_table(stream, paths, scratch_pool));
576  SVN_ERR(svn_packed__data_write(stream, root, scratch_pool));
577
578  return SVN_NO_ERROR;
579}
580
581/* Allocate a svn_fs_x__representation_t array in POOL and return it in
582 * REPS_P.  Deserialize the data in REP_STREAM and DIGEST_STREAM and store
583 * the resulting representations into the *REPS_P.
584 */
585static svn_error_t *
586read_reps(apr_array_header_t **reps_p,
587          svn_packed__int_stream_t *rep_stream,
588          svn_packed__byte_stream_t *digest_stream,
589          apr_pool_t *pool)
590{
591  apr_size_t i;
592  apr_size_t len;
593  const char *bytes;
594
595  apr_size_t count
596    = svn_packed__int_count(svn_packed__first_int_substream(rep_stream));
597  apr_array_header_t *reps
598    = apr_array_make(pool, (int)count, sizeof(svn_fs_x__representation_t));
599
600  for (i = 0; i < count; ++i)
601    {
602      svn_fs_x__representation_t rep;
603
604      rep.has_sha1 = (svn_boolean_t)svn_packed__get_uint(rep_stream);
605
606      rep.id.change_set = (svn_revnum_t)svn_packed__get_uint(rep_stream);
607      rep.id.number = svn_packed__get_uint(rep_stream);
608      rep.size = svn_packed__get_uint(rep_stream);
609      rep.expanded_size = svn_packed__get_uint(rep_stream);
610
611      /* when extracting the checksums, beware of buffer under/overflows
612         caused by disk data corruption. */
613      bytes = svn_packed__get_bytes(digest_stream, &len);
614      if (len != sizeof(rep.md5_digest))
615        return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL,
616                                 apr_psprintf(pool,
617                                              _("Unexpected MD5"
618                                                " digest size %%%s"),
619                                              APR_SIZE_T_FMT),
620                                 len);
621
622      memcpy(rep.md5_digest, bytes, sizeof(rep.md5_digest));
623      if (rep.has_sha1)
624        {
625          bytes = svn_packed__get_bytes(digest_stream, &len);
626          if (len != sizeof(rep.sha1_digest))
627            return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL,
628                                     apr_psprintf(pool,
629                                                  _("Unexpected SHA1"
630                                                    " digest size %%%s"),
631                                                  APR_SIZE_T_FMT),
632                                     len);
633
634          memcpy(rep.sha1_digest, bytes, sizeof(rep.sha1_digest));
635        }
636
637      APR_ARRAY_PUSH(reps, svn_fs_x__representation_t) = rep;
638    }
639
640  *reps_p = reps;
641
642  return SVN_NO_ERROR;
643}
644
645svn_error_t *
646svn_fs_x__read_noderevs_container(svn_fs_x__noderevs_t **container,
647                                  svn_stream_t *stream,
648                                  apr_pool_t *result_pool,
649                                  apr_pool_t *scratch_pool)
650{
651  apr_size_t i;
652  apr_size_t count;
653
654  svn_fs_x__noderevs_t *noderevs
655    = apr_pcalloc(result_pool, sizeof(*noderevs));
656
657  svn_packed__data_root_t *root;
658  svn_packed__int_stream_t *structs_stream;
659  svn_packed__int_stream_t *ids_stream;
660  svn_packed__int_stream_t *reps_stream;
661  svn_packed__int_stream_t *noderevs_stream;
662  svn_packed__byte_stream_t *digests_stream;
663
664  /* read everything from disk */
665  SVN_ERR(svn_fs_x__read_string_table(&noderevs->paths, stream,
666                                      result_pool, scratch_pool));
667  SVN_ERR(svn_packed__data_read(&root, stream, result_pool, scratch_pool));
668
669  /* get streams */
670  structs_stream = svn_packed__first_int_stream(root);
671  ids_stream = svn_packed__first_int_substream(structs_stream);
672  reps_stream = svn_packed__next_int_stream(ids_stream);
673  noderevs_stream = svn_packed__next_int_stream(reps_stream);
674  digests_stream = svn_packed__first_byte_stream(root);
675
676  /* read ids array */
677  count
678    = svn_packed__int_count(svn_packed__first_int_substream(ids_stream));
679  noderevs->ids
680    = apr_array_make(result_pool, (int)count, sizeof(svn_fs_x__id_t));
681  for (i = 0; i < count; ++i)
682    {
683      svn_fs_x__id_t id;
684
685      id.change_set = (svn_revnum_t)svn_packed__get_int(ids_stream);
686      id.number = svn_packed__get_uint(ids_stream);
687
688      APR_ARRAY_PUSH(noderevs->ids, svn_fs_x__id_t) = id;
689    }
690
691  /* read rep arrays */
692  SVN_ERR(read_reps(&noderevs->reps, reps_stream, digests_stream,
693                    result_pool));
694
695  /* read noderevs array */
696  count
697    = svn_packed__int_count(svn_packed__first_int_substream(noderevs_stream));
698  noderevs->noderevs
699    = apr_array_make(result_pool, (int)count, sizeof(binary_noderev_t));
700  for (i = 0; i < count; ++i)
701    {
702      binary_noderev_t noderev;
703
704      noderev.flags = (apr_uint32_t)svn_packed__get_uint(noderevs_stream);
705
706      noderev.id = (int)svn_packed__get_uint(noderevs_stream);
707      noderev.node_id = (int)svn_packed__get_uint(noderevs_stream);
708      noderev.copy_id = (int)svn_packed__get_uint(noderevs_stream);
709      noderev.predecessor_id = (int)svn_packed__get_uint(noderevs_stream);
710      noderev.predecessor_count = (int)svn_packed__get_uint(noderevs_stream);
711
712      noderev.copyfrom_path = (apr_size_t)svn_packed__get_uint(noderevs_stream);
713      noderev.copyfrom_rev = (svn_revnum_t)svn_packed__get_int(noderevs_stream);
714      noderev.copyroot_path = (apr_size_t)svn_packed__get_uint(noderevs_stream);
715      noderev.copyroot_rev = (svn_revnum_t)svn_packed__get_int(noderevs_stream);
716
717      noderev.prop_rep = (int)svn_packed__get_uint(noderevs_stream);
718      noderev.data_rep = (int)svn_packed__get_uint(noderevs_stream);
719
720      noderev.created_path = (apr_size_t)svn_packed__get_uint(noderevs_stream);
721      noderev.mergeinfo_count = svn_packed__get_uint(noderevs_stream);
722
723      APR_ARRAY_PUSH(noderevs->noderevs, binary_noderev_t) = noderev;
724    }
725
726  *container = noderevs;
727
728  return SVN_NO_ERROR;
729}
730
731svn_error_t *
732svn_fs_x__serialize_noderevs_container(void **data,
733                                       apr_size_t *data_len,
734                                       void *in,
735                                       apr_pool_t *pool)
736{
737  svn_fs_x__noderevs_t *noderevs = in;
738  svn_stringbuf_t *serialized;
739  apr_size_t size
740    = noderevs->ids->elt_size * noderevs->ids->nelts
741    + noderevs->reps->elt_size * noderevs->reps->nelts
742    + noderevs->noderevs->elt_size * noderevs->noderevs->nelts
743    + 10 * noderevs->noderevs->elt_size
744    + 100;
745
746  /* serialize array header and all its elements */
747  svn_temp_serializer__context_t *context
748    = svn_temp_serializer__init(noderevs, sizeof(*noderevs), size, pool);
749
750  /* serialize sub-structures */
751  svn_fs_x__serialize_string_table(context, &noderevs->paths);
752  svn_fs_x__serialize_apr_array(context, &noderevs->ids);
753  svn_fs_x__serialize_apr_array(context, &noderevs->reps);
754  svn_fs_x__serialize_apr_array(context, &noderevs->noderevs);
755
756  /* return the serialized result */
757  serialized = svn_temp_serializer__get(context);
758
759  *data = serialized->data;
760  *data_len = serialized->len;
761
762  return SVN_NO_ERROR;
763}
764
765svn_error_t *
766svn_fs_x__deserialize_noderevs_container(void **out,
767                                         void *data,
768                                         apr_size_t data_len,
769                                         apr_pool_t *pool)
770{
771  svn_fs_x__noderevs_t *noderevs = (svn_fs_x__noderevs_t *)data;
772
773  /* de-serialize sub-structures */
774  svn_fs_x__deserialize_string_table(noderevs, &noderevs->paths);
775  svn_fs_x__deserialize_apr_array(noderevs, &noderevs->ids, pool);
776  svn_fs_x__deserialize_apr_array(noderevs, &noderevs->reps, pool);
777  svn_fs_x__deserialize_apr_array(noderevs, &noderevs->noderevs, pool);
778
779  /* done */
780  *out = noderevs;
781
782  return SVN_NO_ERROR;
783}
784
785/* Deserialize the cache serialized APR struct at *IN in BUFFER and write
786 * the result to OUT.  Note that this will only resolve the pointers and
787 * not the array elements themselves. */
788static void
789resolve_apr_array_header(apr_array_header_t *out,
790                         const void *buffer,
791                         apr_array_header_t * const *in)
792{
793  const apr_array_header_t *array
794    = svn_temp_deserializer__ptr(buffer, (const void *const *)in);
795  const char *elements
796    = svn_temp_deserializer__ptr(array, (const void *const *)&array->elts);
797
798  *out = *array;
799  out->elts = (char *)elements;
800  out->pool = NULL;
801}
802
803svn_error_t *
804svn_fs_x__noderevs_get_func(void **out,
805                            const void *data,
806                            apr_size_t data_len,
807                            void *baton,
808                            apr_pool_t *pool)
809{
810  svn_fs_x__noderev_t *noderev;
811  binary_noderev_t *binary_noderev;
812
813  apr_array_header_t ids;
814  apr_array_header_t reps;
815  apr_array_header_t noderevs;
816
817  apr_uint32_t idx = *(apr_uint32_t *)baton;
818  const svn_fs_x__noderevs_t *container = data;
819
820  /* Resolve all container pointers */
821  const string_table_t *paths
822    = svn_temp_deserializer__ptr(container,
823                         (const void *const *)&container->paths);
824
825  resolve_apr_array_header(&ids, container, &container->ids);
826  resolve_apr_array_header(&reps, container, &container->reps);
827  resolve_apr_array_header(&noderevs, container, &container->noderevs);
828
829  /* allocate result struct and fill it field by field */
830  noderev = apr_pcalloc(pool, sizeof(*noderev));
831  binary_noderev = &APR_ARRAY_IDX(&noderevs, idx, binary_noderev_t);
832
833  noderev->kind = (svn_node_kind_t)(binary_noderev->flags & NODEREV_KIND_MASK);
834  SVN_ERR(get_id(&noderev->noderev_id, &ids, binary_noderev->id));
835  SVN_ERR(get_id(&noderev->node_id, &ids, binary_noderev->node_id));
836  SVN_ERR(get_id(&noderev->copy_id, &ids, binary_noderev->copy_id));
837  SVN_ERR(get_id(&noderev->predecessor_id, &ids,
838                 binary_noderev->predecessor_id));
839
840  if (binary_noderev->flags & NODEREV_HAS_COPYFROM)
841    {
842      noderev->copyfrom_path
843        = svn_fs_x__string_table_get_func(paths,
844                                          binary_noderev->copyfrom_path,
845                                          NULL,
846                                          pool);
847      noderev->copyfrom_rev = binary_noderev->copyfrom_rev;
848    }
849  else
850    {
851      noderev->copyfrom_path = NULL;
852      noderev->copyfrom_rev = SVN_INVALID_REVNUM;
853    }
854
855  if (binary_noderev->flags & NODEREV_HAS_COPYROOT)
856    {
857      noderev->copyroot_path
858        = svn_fs_x__string_table_get_func(paths,
859                                          binary_noderev->copyroot_path,
860                                          NULL,
861                                          pool);
862      noderev->copyroot_rev = binary_noderev->copyroot_rev;
863    }
864  else
865    {
866      noderev->copyroot_path = NULL;
867      noderev->copyroot_rev = 0;
868    }
869
870  noderev->predecessor_count = binary_noderev->predecessor_count;
871
872  SVN_ERR(get_representation(&noderev->prop_rep, &reps,
873                             binary_noderev->prop_rep, pool));
874  SVN_ERR(get_representation(&noderev->data_rep, &reps,
875                             binary_noderev->data_rep, pool));
876
877  if (binary_noderev->flags & NODEREV_HAS_CPATH)
878    noderev->created_path
879      = svn_fs_x__string_table_get_func(paths,
880                                        binary_noderev->created_path,
881                                        NULL,
882                                        pool);
883
884  noderev->mergeinfo_count = binary_noderev->mergeinfo_count;
885
886  noderev->has_mergeinfo = (binary_noderev->flags & NODEREV_HAS_MINFO) ? 1 : 0;
887  *out = noderev;
888
889  return SVN_NO_ERROR;
890}
891
892svn_error_t *
893svn_fs_x__mergeinfo_count_get_func(void **out,
894                                   const void *data,
895                                   apr_size_t data_len,
896                                   void *baton,
897                                   apr_pool_t *pool)
898{
899  binary_noderev_t *binary_noderev;
900  apr_array_header_t noderevs;
901
902  apr_uint32_t idx = *(apr_uint32_t *)baton;
903  const svn_fs_x__noderevs_t *container = data;
904
905  /* Resolve all container pointers */
906  resolve_apr_array_header(&noderevs, container, &container->noderevs);
907  binary_noderev = &APR_ARRAY_IDX(&noderevs, idx, binary_noderev_t);
908
909  *(apr_int64_t *)out = binary_noderev->mergeinfo_count;
910
911  return SVN_NO_ERROR;
912}
913