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