1
/* reps.c --- FSX representation container
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
12
* http://www.apache.org/licenses/LICENSE-2.0
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
20
* ====================================================================
25
#include "svn_sorts.h"
26
#include "private/svn_string_private.h"
27
#include "private/svn_packed_data.h"
28
#include "private/svn_temp_serializer.h"
30
#include "svn_private_config.h"
32
#include "cached_data.h"
34
/* Length of the text chunks we hash and match. The algorithm will find
35
* most matches with a length of 2 * MATCH_BLOCKSIZE and only specific
36
* ones that are shorter than MATCH_BLOCKSIZE.
38
* This should be a power of two and must be a multiple of 8.
39
* Good choices are 32, 64 and 128.
41
#define MATCH_BLOCKSIZE 64
43
/* Limit the total text body within a container to 16MB. Larger values
44
* of up to 2GB are possible but become increasingly impractical as the
45
* container has to be loaded in its entirety before any of it can be read.
47
#define MAX_TEXT_BODY 0x1000000
49
/* Limit the size of the instructions stream. This should not exceed the
50
* text body size limit. */
51
#define MAX_INSTRUCTIONS (MAX_TEXT_BODY / 8)
53
/* value of unused hash buckets */
54
#define NO_OFFSET ((apr_uint32_t)(-1))
56
/* Byte strings are described by a series of copy instructions that each
57
* do one of the following
59
* - copy a given number of bytes from the text corpus starting at a
61
* - reference other instruction and specify how many of instructions of
62
* that sequence shall be executed (i.e. a sub-sequence)
63
* - copy a number of bytes from the base representation buffer starting
67
/* The contents of a fulltext / representation is defined by its first
68
* instruction and the number of instructions to execute.
72
apr_uint32_t first_instruction;
73
apr_uint32_t instruction_count;
76
/* A single instruction. The instruction type is being encoded in OFFSET.
78
typedef struct instruction_t
80
/* Instruction type and offset.
82
* reference to instruction sub-sequence starting with
83
* container->instructions[-offset].
84
* - 0 <= offset < container->base_text_len
85
* reference to the base text corpus;
86
* start copy at offset
87
* - offset >= container->base_text_len
88
* reference to the text corpus;
89
* start copy at offset-container->base_text_len
93
/* Number of bytes to copy / instructions to execute
98
/* Describe a base fulltext.
100
typedef struct base_t
103
svn_revnum_t revision;
105
/* Item within that revision */
106
apr_uint64_t item_index;
108
/* Priority with which to use this base over others */
111
/* Index into builder->representations that identifies the copy
112
* instructions for this base. */
116
/* Yet another hash data structure. This one tries to be more cache
117
* friendly by putting the first byte of each hashed sequence in a
118
* common array. This array will often fit into L1 or L2 at least and
119
* give a 99% accurate test for a match without giving false negatives.
121
typedef struct hash_t
123
/* for used entries i, prefixes[i] == text[offsets[i]]; 0 otherwise.
124
* This allows for a quick check without resolving the double
128
/* for used entries i, offsets[i] is start offset in the text corpus;
129
* NO_OFFSET otherwise.
131
apr_uint32_t *offsets;
133
/* to be used later for optimizations. */
134
apr_uint32_t *last_matches;
136
/* number of buckets in this hash, i.e. elements in each array above.
137
* Must be 1 << (8 * sizeof(hash_key_t) - shift) */
140
/* number of buckets actually in use. Must be <= size. */
143
/* number of bits to shift right to map a hash_key_t to a bucket index */
146
/* pool to use when growing the hash */
150
/* Hash key type. 32 bits for pseudo-Adler32 hash sums.
152
typedef apr_uint32_t hash_key_t;
154
/* Constructor data structure.
156
struct svn_fs_x__reps_builder_t
158
/* file system to read base representations from */
162
svn_stringbuf_t *text;
164
/* text block hash */
167
/* array of base_t objects describing all bases defined so far */
168
apr_array_header_t *bases;
170
/* array of rep_t objects describing all fulltexts (including bases)
172
apr_array_header_t *reps;
174
/* array of instruction_t objects describing all instructions */
175
apr_array_header_t *instructions;
177
/* number of bytes in the text corpus that belongs to bases */
178
apr_size_t base_text_len;
183
struct svn_fs_x__reps_t
188
/* length of the text corpus in bytes */
194
/* number of bases used */
195
apr_size_t base_count;
197
/* fulltext i can be reconstructed by executing instructions
198
* first_instructions[i] .. first_instructions[i+1]-1
199
* (this array has one extra element at the end)
201
const apr_uint32_t *first_instructions;
203
/* number of fulltexts (no bases) */
204
apr_size_t rep_count;
207
const instruction_t *instructions;
209
/* total number of instructions */
210
apr_size_t instruction_count;
212
/* offsets > 0 but smaller that this are considered base references */
213
apr_size_t base_text_len;
216
/* describe a section in the extractor's result string that is not filled
217
* yet (but already exists).
219
typedef struct missing_t
221
/* start offset within the result string */
224
/* number of bytes to write */
227
/* index into extractor->bases selecting the base representation to
231
/* copy source offset within that base representation */
235
/* Fulltext extractor data structure.
237
struct svn_fs_x__rep_extractor_t
239
/* filesystem to read the bases from */
242
/* fulltext being constructed */
243
svn_stringbuf_t *result;
245
/* bases (base_t) yet to process (not used ATM) */
246
apr_array_header_t *bases;
248
/* missing sections (missing_t) in result->data that need to be filled,
250
apr_array_header_t *missing;
252
/* pool to use for allocating the above arrays */
256
/* Given the ADLER32 checksum for a certain range of MATCH_BLOCKSIZE
257
* bytes, return the checksum for the range excluding the first byte
258
* C_OUT and appending C_IN.
261
hash_key_replace(hash_key_t adler32, const char c_out, const char c_in)
263
adler32 -= (MATCH_BLOCKSIZE * 0x10000u * ((unsigned char) c_out));
265
adler32 -= (unsigned char)c_out;
266
adler32 += (unsigned char)c_in;
268
return adler32 + adler32 * 0x10000;
271
/* Calculate an pseudo-adler32 checksum for MATCH_BLOCKSIZE bytes starting
272
at DATA. Return the checksum value. */
274
hash_key(const char *data)
276
const unsigned char *input = (const unsigned char *)data;
277
const unsigned char *last = input + MATCH_BLOCKSIZE;
282
for (; input < last; input += 8)
284
s1 += input[0]; s2 += s1;
285
s1 += input[1]; s2 += s1;
286
s1 += input[2]; s2 += s1;
287
s1 += input[3]; s2 += s1;
288
s1 += input[4]; s2 += s1;
289
s1 += input[5]; s2 += s1;
290
s1 += input[6]; s2 += s1;
291
s1 += input[7]; s2 += s1;
294
return s2 * 0x10000 + s1;
297
/* Map the ADLER32 key to a bucket index in HASH and return that index.
300
hash_to_index(hash_t *hash, hash_key_t adler32)
302
return (adler32 * 0xd1f3da69) >> hash->shift;
305
/* Allocate and initialized SIZE buckets in RESULT_POOL.
306
* Assign them to HASH.
309
allocate_hash_members(hash_t *hash,
311
apr_pool_t *result_pool)
315
hash->pool = result_pool;
318
hash->prefixes = apr_pcalloc(result_pool, size);
319
hash->last_matches = apr_pcalloc(result_pool,
320
sizeof(*hash->last_matches) * size);
321
hash->offsets = apr_palloc(result_pool, sizeof(*hash->offsets) * size);
323
for (i = 0; i < size; ++i)
324
hash->offsets[i] = NO_OFFSET;
327
/* Initialize the HASH data structure with 2**TWOPOWER buckets allocated
331
init_hash(hash_t *hash,
333
apr_pool_t *result_pool)
336
hash->shift = sizeof(hash_key_t) * 8 - twoPower;
338
allocate_hash_members(hash, 1 << twoPower, result_pool);
341
/* Make HASH have at least MIN_SIZE buckets but at least double the number
342
* of buckets in HASH by rehashing it based TEXT.
345
grow_hash(hash_t *hash,
346
svn_stringbuf_t *text,
352
/* determine the new hash size */
353
apr_size_t new_size = hash->size * 2;
354
apr_size_t new_shift = hash->shift - 1;
355
while (new_size < min_size)
361
/* allocate new hash */
362
allocate_hash_members(©, new_size, hash->pool);
364
copy.shift = new_shift;
366
/* copy / translate data */
367
for (i = 0; i < hash->size; ++i)
369
apr_uint32_t offset = hash->offsets[i];
370
if (offset != NO_OFFSET)
372
hash_key_t key = hash_key(text->data + offset);
373
size_t idx = hash_to_index(©, key);
375
if (copy.offsets[idx] == NO_OFFSET)
378
copy.prefixes[idx] = hash->prefixes[i];
379
copy.offsets[idx] = offset;
380
copy.last_matches[idx] = hash->last_matches[i];
387
svn_fs_x__reps_builder_t *
388
svn_fs_x__reps_builder_create(svn_fs_t *fs,
389
apr_pool_t *result_pool)
391
svn_fs_x__reps_builder_t *result = apr_pcalloc(result_pool,
395
result->text = svn_stringbuf_create_empty(result_pool);
396
init_hash(&result->hash, 4, result_pool);
398
result->bases = apr_array_make(result_pool, 0, sizeof(base_t));
399
result->reps = apr_array_make(result_pool, 0, sizeof(rep_t));
400
result->instructions = apr_array_make(result_pool, 0,
401
sizeof(instruction_t));
407
svn_fs_x__reps_add_base(svn_fs_x__reps_builder_t *builder,
408
svn_fs_x__representation_t *rep,
410
apr_pool_t *scratch_pool)
413
apr_size_t text_start_offset = builder->text->len;
415
svn_stream_t *stream;
416
svn_string_t *contents;
418
SVN_ERR(svn_fs_x__get_contents(&stream, builder->fs, rep, FALSE,
420
SVN_ERR(svn_string_from_stream(&contents, stream, scratch_pool,
422
SVN_ERR(svn_fs_x__reps_add(&idx, builder, contents));
424
base.revision = svn_fs_x__get_revnum(rep->id.change_set);
425
base.item_index = rep->id.number;
426
base.priority = priority;
427
base.rep = (apr_uint32_t)idx;
429
APR_ARRAY_PUSH(builder->bases, base_t) = base;
430
builder->base_text_len += builder->text->len - text_start_offset;
435
/* Add LEN bytes from DATA to BUILDER's text corpus. Also, add a copy
436
* operation for that text fragment.
439
add_new_text(svn_fs_x__reps_builder_t *builder,
443
instruction_t instruction;
445
apr_size_t buckets_required;
450
/* new instruction */
451
instruction.offset = (apr_int32_t)builder->text->len;
452
instruction.count = (apr_uint32_t)len;
453
APR_ARRAY_PUSH(builder->instructions, instruction_t) = instruction;
455
/* add to text corpus */
456
svn_stringbuf_appendbytes(builder->text, data, len);
458
/* expand the hash upfront to minimize the chances of collisions */
459
buckets_required = builder->hash.used + len / MATCH_BLOCKSIZE;
460
if (buckets_required * 3 >= builder->hash.size * 2)
461
grow_hash(&builder->hash, builder->text, 2 * buckets_required);
463
/* add hash entries for the new sequence */
464
for (offset = instruction.offset;
465
offset + MATCH_BLOCKSIZE <= builder->text->len;
466
offset += MATCH_BLOCKSIZE)
468
hash_key_t key = hash_key(builder->text->data + offset);
469
size_t idx = hash_to_index(&builder->hash, key);
471
/* Don't replace hash entries that stem from the current text.
472
* This makes early matches more likely. */
473
if (builder->hash.offsets[idx] == NO_OFFSET)
474
++builder->hash.used;
475
else if (builder->hash.offsets[idx] >= instruction.offset)
478
builder->hash.offsets[idx] = (apr_uint32_t)offset;
479
builder->hash.prefixes[idx] = builder->text->data[offset];
484
svn_fs_x__reps_add(apr_size_t *rep_idx,
485
svn_fs_x__reps_builder_t *builder,
486
const svn_string_t *contents)
489
const char *current = contents->data;
490
const char *processed = current;
491
const char *end = current + contents->len;
492
const char *last_to_test = end - MATCH_BLOCKSIZE - 1;
494
if (builder->text->len + contents->len > MAX_TEXT_BODY)
495
return svn_error_create(SVN_ERR_FS_CONTAINER_SIZE, NULL,
496
_("Text body exceeds star delta container capacity"));
498
if ( builder->instructions->nelts + 2 * contents->len / MATCH_BLOCKSIZE
500
return svn_error_create(SVN_ERR_FS_CONTAINER_SIZE, NULL,
501
_("Instruction count exceeds star delta container capacity"));
503
rep.first_instruction = (apr_uint32_t)builder->instructions->nelts;
504
while (current < last_to_test)
506
hash_key_t key = hash_key(current);
510
/* search for the next matching sequence */
512
for (; current < last_to_test; ++current)
514
idx = hash_to_index(&builder->hash, key);
515
if (builder->hash.prefixes[idx] == current[0])
517
offset = builder->hash.offsets[idx];
518
if ( (offset != NO_OFFSET)
519
&& (memcmp(&builder->text->data[offset], current,
520
MATCH_BLOCKSIZE) == 0))
523
key = hash_key_replace(key, current[0], current[MATCH_BLOCKSIZE]);
528
if (current < last_to_test)
530
instruction_t instruction;
532
/* extend the match */
535
= svn_cstring__reverse_match_length(current,
536
builder->text->data + offset,
537
MIN(offset, current - processed));
539
= svn_cstring__match_length(current + MATCH_BLOCKSIZE,
540
builder->text->data + offset + MATCH_BLOCKSIZE,
541
MIN(builder->text->len - offset - MATCH_BLOCKSIZE,
542
end - current - MATCH_BLOCKSIZE));
544
/* non-matched section */
546
size_t new_copy = (current - processed) - prefix_match;
548
add_new_text(builder, processed, new_copy);
550
/* add instruction for matching section */
552
instruction.offset = (apr_int32_t)(offset - prefix_match);
553
instruction.count = (apr_uint32_t)(prefix_match + postfix_match +
555
APR_ARRAY_PUSH(builder->instructions, instruction_t) = instruction;
557
processed = current + MATCH_BLOCKSIZE + postfix_match;
562
add_new_text(builder, processed, end - processed);
563
rep.instruction_count = (apr_uint32_t)builder->instructions->nelts
564
- rep.first_instruction;
565
APR_ARRAY_PUSH(builder->reps, rep_t) = rep;
567
*rep_idx = (apr_size_t)(builder->reps->nelts - 1);
572
svn_fs_x__reps_estimate_size(const svn_fs_x__reps_builder_t *builder)
574
/* approx: size of the text exclusive to us @ 50% compression rate
575
* + 2 bytes per instruction
576
* + 2 bytes per representation
577
* + 8 bytes per base representation
578
* + 1:8 inefficiency in using the base representations
579
* + 100 bytes static overhead
581
return (builder->text->len - builder->base_text_len) / 2
582
+ builder->instructions->nelts * 2
583
+ builder->reps->nelts * 2
584
+ builder->bases->nelts * 8
585
+ builder->base_text_len / 8
589
/* Execute COUNT instructions starting at INSTRUCTION_IDX in CONTAINER
590
* and fill the parts of EXTRACTOR->RESULT that we can from this container.
591
* Record the remainder in EXTRACTOR->MISSING.
593
* This function will recurse for instructions that reference other
594
* instruction sequences. COUNT refers to the top-level instructions only.
597
get_text(svn_fs_x__rep_extractor_t *extractor,
598
const svn_fs_x__reps_t *container,
599
apr_size_t instruction_idx,
602
const instruction_t *instruction;
603
const char *offset_0 = container->text - container->base_text_len;
605
for (instruction = container->instructions + instruction_idx;
606
instruction < container->instructions + instruction_idx + count;
608
if (instruction->offset < 0)
610
/* instruction sub-sequence */
611
get_text(extractor, container, -instruction->offset,
614
else if (instruction->offset >= container->base_text_len)
616
/* direct copy instruction */
617
svn_stringbuf_appendbytes(extractor->result,
618
offset_0 + instruction->offset,
623
/* a section that we need to fill from some external base rep. */
626
missing.start = (apr_uint32_t)extractor->result->len;
627
missing.count = instruction->count;
628
missing.offset = instruction->offset;
629
svn_stringbuf_appendfill(extractor->result, 0, instruction->count);
631
if (extractor->missing == NULL)
632
extractor->missing = apr_array_make(extractor->pool, 1,
635
APR_ARRAY_PUSH(extractor->missing, missing_t) = missing;
640
svn_fs_x__reps_get(svn_fs_x__rep_extractor_t **extractor,
642
const svn_fs_x__reps_t *container,
646
apr_uint32_t first = container->first_instructions[idx];
647
apr_uint32_t last = container->first_instructions[idx + 1];
649
/* create the extractor object */
650
svn_fs_x__rep_extractor_t *result = apr_pcalloc(pool, sizeof(*result));
652
result->result = svn_stringbuf_create_empty(pool);
655
/* fill all the bits of the result that we can, i.e. all but bits coming
656
* from base representations */
657
get_text(result, container, first, last - first);
663
svn_fs_x__extractor_drive(svn_stringbuf_t **contents,
664
svn_fs_x__rep_extractor_t *extractor,
665
apr_size_t start_offset,
667
apr_pool_t *result_pool,
668
apr_pool_t *scratch_pool)
670
/* we don't support base reps right now */
671
SVN_ERR_ASSERT(extractor->missing == NULL);
675
*contents = svn_stringbuf_dup(extractor->result, result_pool);
679
/* clip the selected range */
680
if (start_offset > extractor->result->len)
681
start_offset = extractor->result->len;
683
if (size > extractor->result->len - start_offset)
684
size = extractor->result->len - start_offset;
686
*contents = svn_stringbuf_ncreate(extractor->result->data + start_offset,
694
svn_fs_x__write_reps_container(svn_stream_t *stream,
695
const svn_fs_x__reps_builder_t *builder,
696
apr_pool_t *scratch_pool)
699
svn_packed__data_root_t *root = svn_packed__data_create_root(scratch_pool);
701
/* one top-level stream for each array */
702
svn_packed__int_stream_t *bases_stream
703
= svn_packed__create_int_stream(root, FALSE, FALSE);
704
svn_packed__int_stream_t *reps_stream
705
= svn_packed__create_int_stream(root, TRUE, FALSE);
706
svn_packed__int_stream_t *instructions_stream
707
= svn_packed__create_int_stream(root, FALSE, FALSE);
710
svn_packed__int_stream_t *misc_stream
711
= svn_packed__create_int_stream(root, FALSE, FALSE);
713
/* TEXT will be just a single string */
714
svn_packed__byte_stream_t *text_stream
715
= svn_packed__create_bytes_stream(root);
717
/* structure the struct streams such we can extract much of the redundancy
719
svn_packed__create_int_substream(bases_stream, TRUE, TRUE);
720
svn_packed__create_int_substream(bases_stream, TRUE, FALSE);
721
svn_packed__create_int_substream(bases_stream, TRUE, FALSE);
722
svn_packed__create_int_substream(bases_stream, TRUE, FALSE);
724
svn_packed__create_int_substream(instructions_stream, TRUE, TRUE);
725
svn_packed__create_int_substream(instructions_stream, FALSE, FALSE);
728
svn_packed__add_bytes(text_stream, builder->text->data, builder->text->len);
730
/* serialize bases */
731
for (i = 0; i < builder->bases->nelts; ++i)
733
const base_t *base = &APR_ARRAY_IDX(builder->bases, i, base_t);
734
svn_packed__add_int(bases_stream, base->revision);
735
svn_packed__add_uint(bases_stream, base->item_index);
736
svn_packed__add_uint(bases_stream, base->priority);
737
svn_packed__add_uint(bases_stream, base->rep);
741
for (i = 0; i < builder->reps->nelts; ++i)
743
const rep_t *rep = &APR_ARRAY_IDX(builder->reps, i, rep_t);
744
svn_packed__add_uint(reps_stream, rep->first_instruction);
747
svn_packed__add_uint(reps_stream, builder->instructions->nelts);
749
/* serialize instructions */
750
for (i = 0; i < builder->instructions->nelts; ++i)
752
const instruction_t *instruction
753
= &APR_ARRAY_IDX(builder->instructions, i, instruction_t);
754
svn_packed__add_int(instructions_stream, instruction->offset);
755
svn_packed__add_uint(instructions_stream, instruction->count);
759
svn_packed__add_uint(misc_stream, 0);
761
/* write to stream */
762
SVN_ERR(svn_packed__data_write(stream, root, scratch_pool));
768
svn_fs_x__read_reps_container(svn_fs_x__reps_t **container,
769
svn_stream_t *stream,
770
apr_pool_t *result_pool,
771
apr_pool_t *scratch_pool)
776
apr_uint32_t *first_instructions;
777
instruction_t *instructions;
779
svn_fs_x__reps_t *reps = apr_pcalloc(result_pool, sizeof(*reps));
781
svn_packed__data_root_t *root;
782
svn_packed__int_stream_t *bases_stream;
783
svn_packed__int_stream_t *reps_stream;
784
svn_packed__int_stream_t *instructions_stream;
785
svn_packed__int_stream_t *misc_stream;
786
svn_packed__byte_stream_t *text_stream;
789
SVN_ERR(svn_packed__data_read(&root, stream, result_pool, scratch_pool));
791
bases_stream = svn_packed__first_int_stream(root);
792
reps_stream = svn_packed__next_int_stream(bases_stream);
793
instructions_stream = svn_packed__next_int_stream(reps_stream);
794
misc_stream = svn_packed__next_int_stream(instructions_stream);
795
text_stream = svn_packed__first_byte_stream(root);
798
reps->text = svn_packed__get_bytes(text_stream, &reps->text_len);
799
reps->text = apr_pmemdup(result_pool, reps->text, reps->text_len);
801
/* de-serialize bases */
803
= svn_packed__int_count(svn_packed__first_int_substream(bases_stream));
804
bases = apr_palloc(result_pool, reps->base_count * sizeof(*bases));
807
for (i = 0; i < reps->base_count; ++i)
809
base_t *base = bases + i;
810
base->revision = (svn_revnum_t)svn_packed__get_int(bases_stream);
811
base->item_index = svn_packed__get_uint(bases_stream);
812
base->priority = (int)svn_packed__get_uint(bases_stream);
813
base->rep = (apr_uint32_t)svn_packed__get_uint(bases_stream);
816
/* de-serialize instructions */
817
reps->instruction_count
818
= svn_packed__int_count
819
(svn_packed__first_int_substream(instructions_stream));
821
= apr_palloc(result_pool,
822
reps->instruction_count * sizeof(*instructions));
823
reps->instructions = instructions;
825
for (i = 0; i < reps->instruction_count; ++i)
827
instruction_t *instruction = instructions + i;
829
= (apr_int32_t)svn_packed__get_int(instructions_stream);
831
= (apr_uint32_t)svn_packed__get_uint(instructions_stream);
834
/* de-serialize reps */
835
reps->rep_count = svn_packed__int_count(reps_stream);
837
= apr_palloc(result_pool,
838
(reps->rep_count + 1) * sizeof(*first_instructions));
839
reps->first_instructions = first_instructions;
841
for (i = 0; i < reps->rep_count; ++i)
842
first_instructions[i]
843
= (apr_uint32_t)svn_packed__get_uint(reps_stream);
844
first_instructions[reps->rep_count] = (apr_uint32_t)reps->instruction_count;
847
reps->base_text_len = (apr_size_t)svn_packed__get_uint(misc_stream);
856
svn_fs_x__serialize_reps_container(void **data,
857
apr_size_t *data_len,
861
svn_fs_x__reps_t *reps = in;
862
svn_stringbuf_t *serialized;
864
/* make a guesstimate on the size of the serialized data. Erring on the
865
* low side will cause the serializer to re-alloc its buffer. */
868
+ reps->base_count * sizeof(*reps->bases)
869
+ reps->rep_count * sizeof(*reps->first_instructions)
870
+ reps->instruction_count * sizeof(*reps->instructions)
873
/* serialize array header and all its elements */
874
svn_temp_serializer__context_t *context
875
= svn_temp_serializer__init(reps, sizeof(*reps), size, pool);
877
/* serialize sub-structures */
878
svn_temp_serializer__add_leaf(context, (const void **)&reps->text,
880
svn_temp_serializer__add_leaf(context, (const void **)&reps->bases,
881
reps->base_count * sizeof(*reps->bases));
882
svn_temp_serializer__add_leaf(context,
883
(const void **)&reps->first_instructions,
885
sizeof(*reps->first_instructions));
886
svn_temp_serializer__add_leaf(context, (const void **)&reps->instructions,
887
reps->instruction_count *
888
sizeof(*reps->instructions));
890
/* return the serialized result */
891
serialized = svn_temp_serializer__get(context);
893
*data = serialized->data;
894
*data_len = serialized->len;
900
svn_fs_x__deserialize_reps_container(void **out,
905
svn_fs_x__reps_t *reps = (svn_fs_x__reps_t *)data;
907
/* de-serialize sub-structures */
908
svn_temp_deserializer__resolve(reps, (void **)&reps->text);
909
svn_temp_deserializer__resolve(reps, (void **)&reps->bases);
910
svn_temp_deserializer__resolve(reps, (void **)&reps->first_instructions);
911
svn_temp_deserializer__resolve(reps, (void **)&reps->instructions);
920
svn_fs_x__reps_get_func(void **out,
926
svn_fs_x__reps_baton_t *reps_baton = baton;
928
/* get a usable reps structure */
929
const svn_fs_x__reps_t *cached = data;
930
svn_fs_x__reps_t *reps = apr_pmemdup(pool, cached, sizeof(*reps));
933
= svn_temp_deserializer__ptr(cached, (const void **)&cached->text);
935
= svn_temp_deserializer__ptr(cached, (const void **)&cached->bases);
936
reps->first_instructions
937
= svn_temp_deserializer__ptr(cached,
938
(const void **)&cached->first_instructions);
940
= svn_temp_deserializer__ptr(cached,
941
(const void **)&cached->instructions);
943
/* return an extractor for the selected item */
944
SVN_ERR(svn_fs_x__reps_get((svn_fs_x__rep_extractor_t **)out,
945
reps_baton->fs, reps, reps_baton->idx, pool));