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 *
svn_fs_x__noderevs_create(int initial_count,apr_pool_t * result_pool)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
store_id(apr_array_header_t * ids,apr_hash_t * dict,const svn_fs_x__id_t * id)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
store_representation(apr_array_header_t * reps,apr_hash_t * dict,const svn_fs_x__representation_t * rep)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
svn_fs_x__noderevs_add(svn_fs_x__noderevs_t * container,svn_fs_x__noderev_t * noderev)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
svn_fs_x__noderevs_estimate_size(const svn_fs_x__noderevs_t * container)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 *
get_id(svn_fs_x__id_t * id,const apr_array_header_t * ids,int idx)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 *
get_representation(svn_fs_x__representation_t ** rep,const apr_array_header_t * reps,int idx,apr_pool_t * pool)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 *
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 * result_pool)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 *result_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(result_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(result_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 result_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 result_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, result_pool));
429 SVN_ERR(get_representation(&noderev->data_rep, container->reps,
430 binary_noderev->data_rep, result_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 result_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 *
create_rep_stream(svn_packed__int_stream_t * parent)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
write_reps(svn_packed__int_stream_t * rep_stream,svn_packed__byte_stream_t * digest_stream,apr_array_header_t * reps)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 *
svn_fs_x__write_noderevs_container(svn_stream_t * stream,const svn_fs_x__noderevs_t * container,apr_pool_t * scratch_pool)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 RESULT_POOL and return it
582 * in 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 *
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 * result_pool)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 *result_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(result_pool, (int)count,
599 sizeof(svn_fs_x__representation_t));
600
601 for (i = 0; i < count; ++i)
602 {
603 svn_fs_x__representation_t rep;
604
605 rep.has_sha1 = (svn_boolean_t)svn_packed__get_uint(rep_stream);
606
607 rep.id.change_set = (svn_revnum_t)svn_packed__get_uint(rep_stream);
608 rep.id.number = svn_packed__get_uint(rep_stream);
609 rep.size = svn_packed__get_uint(rep_stream);
610 rep.expanded_size = svn_packed__get_uint(rep_stream);
611
612 /* when extracting the checksums, beware of buffer under/overflows
613 caused by disk data corruption. */
614 bytes = svn_packed__get_bytes(digest_stream, &len);
615 if (len != sizeof(rep.md5_digest))
616 return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL,
617 apr_psprintf(result_pool,
618 _("Unexpected MD5"
619 " digest size %%%s"),
620 APR_SIZE_T_FMT),
621 len);
622
623 memcpy(rep.md5_digest, bytes, sizeof(rep.md5_digest));
624 if (rep.has_sha1)
625 {
626 bytes = svn_packed__get_bytes(digest_stream, &len);
627 if (len != sizeof(rep.sha1_digest))
628 return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL,
629 apr_psprintf(result_pool,
630 _("Unexpected SHA1"
631 " digest size %%%s"),
632 APR_SIZE_T_FMT),
633 len);
634
635 memcpy(rep.sha1_digest, bytes, sizeof(rep.sha1_digest));
636 }
637
638 APR_ARRAY_PUSH(reps, svn_fs_x__representation_t) = rep;
639 }
640
641 *reps_p = reps;
642
643 return SVN_NO_ERROR;
644 }
645
646 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)647 svn_fs_x__read_noderevs_container(svn_fs_x__noderevs_t **container,
648 svn_stream_t *stream,
649 apr_pool_t *result_pool,
650 apr_pool_t *scratch_pool)
651 {
652 apr_size_t i;
653 apr_size_t count;
654
655 svn_fs_x__noderevs_t *noderevs
656 = apr_pcalloc(result_pool, sizeof(*noderevs));
657
658 svn_packed__data_root_t *root;
659 svn_packed__int_stream_t *structs_stream;
660 svn_packed__int_stream_t *ids_stream;
661 svn_packed__int_stream_t *reps_stream;
662 svn_packed__int_stream_t *noderevs_stream;
663 svn_packed__byte_stream_t *digests_stream;
664
665 /* read everything from disk */
666 SVN_ERR(svn_fs_x__read_string_table(&noderevs->paths, stream,
667 result_pool, scratch_pool));
668 SVN_ERR(svn_packed__data_read(&root, stream, result_pool, scratch_pool));
669
670 /* get streams */
671 structs_stream = svn_packed__first_int_stream(root);
672 ids_stream = svn_packed__first_int_substream(structs_stream);
673 reps_stream = svn_packed__next_int_stream(ids_stream);
674 noderevs_stream = svn_packed__next_int_stream(reps_stream);
675 digests_stream = svn_packed__first_byte_stream(root);
676
677 /* read ids array */
678 count
679 = svn_packed__int_count(svn_packed__first_int_substream(ids_stream));
680 noderevs->ids
681 = apr_array_make(result_pool, (int)count, sizeof(svn_fs_x__id_t));
682 for (i = 0; i < count; ++i)
683 {
684 svn_fs_x__id_t id;
685
686 id.change_set = (svn_revnum_t)svn_packed__get_int(ids_stream);
687 id.number = svn_packed__get_uint(ids_stream);
688
689 APR_ARRAY_PUSH(noderevs->ids, svn_fs_x__id_t) = id;
690 }
691
692 /* read rep arrays */
693 SVN_ERR(read_reps(&noderevs->reps, reps_stream, digests_stream,
694 result_pool));
695
696 /* read noderevs array */
697 count
698 = svn_packed__int_count(svn_packed__first_int_substream(noderevs_stream));
699 noderevs->noderevs
700 = apr_array_make(result_pool, (int)count, sizeof(binary_noderev_t));
701 for (i = 0; i < count; ++i)
702 {
703 binary_noderev_t noderev;
704
705 noderev.flags = (apr_uint32_t)svn_packed__get_uint(noderevs_stream);
706
707 noderev.id = (int)svn_packed__get_uint(noderevs_stream);
708 noderev.node_id = (int)svn_packed__get_uint(noderevs_stream);
709 noderev.copy_id = (int)svn_packed__get_uint(noderevs_stream);
710 noderev.predecessor_id = (int)svn_packed__get_uint(noderevs_stream);
711 noderev.predecessor_count = (int)svn_packed__get_uint(noderevs_stream);
712
713 noderev.copyfrom_path = (apr_size_t)svn_packed__get_uint(noderevs_stream);
714 noderev.copyfrom_rev = (svn_revnum_t)svn_packed__get_int(noderevs_stream);
715 noderev.copyroot_path = (apr_size_t)svn_packed__get_uint(noderevs_stream);
716 noderev.copyroot_rev = (svn_revnum_t)svn_packed__get_int(noderevs_stream);
717
718 noderev.prop_rep = (int)svn_packed__get_uint(noderevs_stream);
719 noderev.data_rep = (int)svn_packed__get_uint(noderevs_stream);
720
721 noderev.created_path = (apr_size_t)svn_packed__get_uint(noderevs_stream);
722 noderev.mergeinfo_count = svn_packed__get_uint(noderevs_stream);
723
724 APR_ARRAY_PUSH(noderevs->noderevs, binary_noderev_t) = noderev;
725 }
726
727 *container = noderevs;
728
729 return SVN_NO_ERROR;
730 }
731
732 svn_error_t *
svn_fs_x__serialize_noderevs_container(void ** data,apr_size_t * data_len,void * in,apr_pool_t * pool)733 svn_fs_x__serialize_noderevs_container(void **data,
734 apr_size_t *data_len,
735 void *in,
736 apr_pool_t *pool)
737 {
738 svn_fs_x__noderevs_t *noderevs = in;
739 svn_stringbuf_t *serialized;
740 apr_size_t size
741 = noderevs->ids->elt_size * noderevs->ids->nelts
742 + noderevs->reps->elt_size * noderevs->reps->nelts
743 + noderevs->noderevs->elt_size * noderevs->noderevs->nelts
744 + 10 * noderevs->noderevs->elt_size
745 + 100;
746
747 /* serialize array header and all its elements */
748 svn_temp_serializer__context_t *context
749 = svn_temp_serializer__init(noderevs, sizeof(*noderevs), size, pool);
750
751 /* serialize sub-structures */
752 svn_fs_x__serialize_string_table(context, &noderevs->paths);
753 svn_fs_x__serialize_apr_array(context, &noderevs->ids);
754 svn_fs_x__serialize_apr_array(context, &noderevs->reps);
755 svn_fs_x__serialize_apr_array(context, &noderevs->noderevs);
756
757 /* return the serialized result */
758 serialized = svn_temp_serializer__get(context);
759
760 *data = serialized->data;
761 *data_len = serialized->len;
762
763 return SVN_NO_ERROR;
764 }
765
766 svn_error_t *
svn_fs_x__deserialize_noderevs_container(void ** out,void * data,apr_size_t data_len,apr_pool_t * result_pool)767 svn_fs_x__deserialize_noderevs_container(void **out,
768 void *data,
769 apr_size_t data_len,
770 apr_pool_t *result_pool)
771 {
772 svn_fs_x__noderevs_t *noderevs = (svn_fs_x__noderevs_t *)data;
773
774 /* de-serialize sub-structures */
775 svn_fs_x__deserialize_string_table(noderevs, &noderevs->paths);
776 svn_fs_x__deserialize_apr_array(noderevs, &noderevs->ids, result_pool);
777 svn_fs_x__deserialize_apr_array(noderevs, &noderevs->reps, result_pool);
778 svn_fs_x__deserialize_apr_array(noderevs, &noderevs->noderevs, result_pool);
779
780 /* done */
781 *out = noderevs;
782
783 return SVN_NO_ERROR;
784 }
785
786 /* Deserialize the cache serialized APR struct at *IN in BUFFER and write
787 * the result to OUT. Note that this will only resolve the pointers and
788 * not the array elements themselves. */
789 static void
resolve_apr_array_header(apr_array_header_t * out,const void * buffer,apr_array_header_t * const * in)790 resolve_apr_array_header(apr_array_header_t *out,
791 const void *buffer,
792 apr_array_header_t * const *in)
793 {
794 const apr_array_header_t *array
795 = svn_temp_deserializer__ptr(buffer, (const void *const *)in);
796 const char *elements
797 = svn_temp_deserializer__ptr(array, (const void *const *)&array->elts);
798
799 *out = *array;
800 out->elts = (char *)elements;
801 out->pool = NULL;
802 }
803
804 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)805 svn_fs_x__noderevs_get_func(void **out,
806 const void *data,
807 apr_size_t data_len,
808 void *baton,
809 apr_pool_t *pool)
810 {
811 svn_fs_x__noderev_t *noderev;
812 binary_noderev_t *binary_noderev;
813
814 apr_array_header_t ids;
815 apr_array_header_t reps;
816 apr_array_header_t noderevs;
817
818 apr_uint32_t idx = *(apr_uint32_t *)baton;
819 const svn_fs_x__noderevs_t *container = data;
820
821 /* Resolve all container pointers */
822 const string_table_t *paths
823 = svn_temp_deserializer__ptr(container,
824 (const void *const *)&container->paths);
825
826 resolve_apr_array_header(&ids, container, &container->ids);
827 resolve_apr_array_header(&reps, container, &container->reps);
828 resolve_apr_array_header(&noderevs, container, &container->noderevs);
829
830 /* allocate result struct and fill it field by field */
831 noderev = apr_pcalloc(pool, sizeof(*noderev));
832 binary_noderev = &APR_ARRAY_IDX(&noderevs, idx, binary_noderev_t);
833
834 noderev->kind = (svn_node_kind_t)(binary_noderev->flags & NODEREV_KIND_MASK);
835 SVN_ERR(get_id(&noderev->noderev_id, &ids, binary_noderev->id));
836 SVN_ERR(get_id(&noderev->node_id, &ids, binary_noderev->node_id));
837 SVN_ERR(get_id(&noderev->copy_id, &ids, binary_noderev->copy_id));
838 SVN_ERR(get_id(&noderev->predecessor_id, &ids,
839 binary_noderev->predecessor_id));
840
841 if (binary_noderev->flags & NODEREV_HAS_COPYFROM)
842 {
843 noderev->copyfrom_path
844 = svn_fs_x__string_table_get_func(paths,
845 binary_noderev->copyfrom_path,
846 NULL,
847 pool);
848 noderev->copyfrom_rev = binary_noderev->copyfrom_rev;
849 }
850 else
851 {
852 noderev->copyfrom_path = NULL;
853 noderev->copyfrom_rev = SVN_INVALID_REVNUM;
854 }
855
856 if (binary_noderev->flags & NODEREV_HAS_COPYROOT)
857 {
858 noderev->copyroot_path
859 = svn_fs_x__string_table_get_func(paths,
860 binary_noderev->copyroot_path,
861 NULL,
862 pool);
863 noderev->copyroot_rev = binary_noderev->copyroot_rev;
864 }
865 else
866 {
867 noderev->copyroot_path = NULL;
868 noderev->copyroot_rev = 0;
869 }
870
871 noderev->predecessor_count = binary_noderev->predecessor_count;
872
873 SVN_ERR(get_representation(&noderev->prop_rep, &reps,
874 binary_noderev->prop_rep, pool));
875 SVN_ERR(get_representation(&noderev->data_rep, &reps,
876 binary_noderev->data_rep, pool));
877
878 if (binary_noderev->flags & NODEREV_HAS_CPATH)
879 noderev->created_path
880 = svn_fs_x__string_table_get_func(paths,
881 binary_noderev->created_path,
882 NULL,
883 pool);
884
885 noderev->mergeinfo_count = binary_noderev->mergeinfo_count;
886
887 noderev->has_mergeinfo = (binary_noderev->flags & NODEREV_HAS_MINFO) ? 1 : 0;
888 *out = noderev;
889
890 return SVN_NO_ERROR;
891 }
892
893 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)894 svn_fs_x__mergeinfo_count_get_func(void **out,
895 const void *data,
896 apr_size_t data_len,
897 void *baton,
898 apr_pool_t *pool)
899 {
900 binary_noderev_t *binary_noderev;
901 apr_array_header_t noderevs;
902
903 apr_uint32_t idx = *(apr_uint32_t *)baton;
904 const svn_fs_x__noderevs_t *container = data;
905
906 /* Resolve all container pointers */
907 resolve_apr_array_header(&noderevs, container, &container->noderevs);
908 binary_noderev = &APR_ARRAY_IDX(&noderevs, idx, binary_noderev_t);
909
910 *(apr_int64_t *)out = binary_noderev->mergeinfo_count;
911
912 return SVN_NO_ERROR;
913 }
914