]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_fs_x/noderevs.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / libsvn_fs_x / noderevs.c
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  */
59 typedef 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  */
122 struct 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
146 svn_fs_x__noderevs_t *
147 svn_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  */
172 static int
173 store_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  */
199 static int
200 store_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
223 apr_size_t
224 svn_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
283 apr_size_t
284 svn_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  */
305 static svn_error_t *
306 get_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  */
332 static svn_error_t *
333 get_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
360 svn_error_t *
361 svn_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  */
450 static svn_packed__int_stream_t *
451 create_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  */
472 static void
473 write_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
500 svn_error_t *
501 svn_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  */
585 static svn_error_t *
586 read_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
645 svn_error_t *
646 svn_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
731 svn_error_t *
732 svn_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
765 svn_error_t *
766 svn_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. */
788 static void
789 resolve_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
803 svn_error_t *
804 svn_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
892 svn_error_t *
893 svn_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 }