1 /* temp_serializer.c: serialization functions for caching of FSX structures
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 <apr_pools.h>
24
25 #include "svn_pools.h"
26 #include "svn_hash.h"
27 #include "svn_sorts.h"
28 #include "svn_fs.h"
29
30 #include "private/svn_fs_util.h"
31 #include "private/svn_sorts_private.h"
32 #include "private/svn_temp_serializer.h"
33 #include "private/svn_subr_private.h"
34
35 #include "id.h"
36 #include "temp_serializer.h"
37 #include "low_level.h"
38 #include "cached_data.h"
39
40 /* Utility to encode a signed NUMBER into a variable-length sequence of
41 * 8-bit chars in KEY_BUFFER and return the last writen position.
42 *
43 * Numbers will be stored in 7 bits / byte and using byte values above
44 * 32 (' ') to make them combinable with other string by simply separating
45 * individual parts with spaces.
46 */
47 static char*
encode_number(apr_int64_t number,char * key_buffer)48 encode_number(apr_int64_t number, char *key_buffer)
49 {
50 /* encode the sign in the first byte */
51 if (number < 0)
52 {
53 number = -number;
54 *key_buffer = (char)((number & 63) + ' ' + 65);
55 }
56 else
57 *key_buffer = (char)((number & 63) + ' ' + 1);
58 number /= 64;
59
60 /* write 7 bits / byte until no significant bits are left */
61 while (number)
62 {
63 *++key_buffer = (char)((number & 127) + ' ' + 1);
64 number /= 128;
65 }
66
67 /* return the last written position */
68 return key_buffer;
69 }
70
71 const char*
svn_fs_x__combine_number_and_string(apr_int64_t number,const char * string,apr_pool_t * result_pool)72 svn_fs_x__combine_number_and_string(apr_int64_t number,
73 const char *string,
74 apr_pool_t *result_pool)
75 {
76 apr_size_t len = strlen(string);
77
78 /* number part requires max. 10x7 bits + 1 space.
79 * Add another 1 for the terminal 0 */
80 char *key_buffer = apr_palloc(result_pool, len + 12);
81 const char *key = key_buffer;
82
83 /* Prepend the number to the string and separate them by space. No other
84 * number can result in the same prefix, no other string in the same
85 * postfix nor can the boundary between them be ambiguous. */
86 key_buffer = encode_number(number, key_buffer);
87 *++key_buffer = ' ';
88 memcpy(++key_buffer, string, len+1);
89
90 /* return the start of the key */
91 return key;
92 }
93
94 /* Utility function to serialize string S in the given serialization CONTEXT.
95 */
96 static void
serialize_svn_string(svn_temp_serializer__context_t * context,const svn_string_t * const * s)97 serialize_svn_string(svn_temp_serializer__context_t *context,
98 const svn_string_t * const *s)
99 {
100 const svn_string_t *string = *s;
101
102 /* Nothing to do for NULL string references. */
103 if (string == NULL)
104 return;
105
106 svn_temp_serializer__push(context,
107 (const void * const *)s,
108 sizeof(*string));
109
110 /* the "string" content may actually be arbitrary binary data.
111 * Thus, we cannot use svn_temp_serializer__add_string. */
112 svn_temp_serializer__add_leaf(context,
113 (const void * const *)&string->data,
114 string->len + 1);
115
116 /* back to the caller's nesting level */
117 svn_temp_serializer__pop(context);
118 }
119
120 /* Utility function to deserialize the STRING inside the BUFFER.
121 */
122 static void
deserialize_svn_string(const void * buffer,svn_string_t ** string)123 deserialize_svn_string(const void *buffer, svn_string_t **string)
124 {
125 svn_temp_deserializer__resolve(buffer, (void **)string);
126 if (*string == NULL)
127 return;
128
129 svn_temp_deserializer__resolve(*string, (void **)&(*string)->data);
130 }
131
132 /* Utility function to serialize the REPRESENTATION within the given
133 * serialization CONTEXT.
134 */
135 static void
serialize_representation(svn_temp_serializer__context_t * context,svn_fs_x__representation_t * const * representation)136 serialize_representation(svn_temp_serializer__context_t *context,
137 svn_fs_x__representation_t * const *representation)
138 {
139 const svn_fs_x__representation_t * rep = *representation;
140 if (rep == NULL)
141 return;
142
143 /* serialize the representation struct itself */
144 svn_temp_serializer__add_leaf(context,
145 (const void * const *)representation,
146 sizeof(*rep));
147 }
148
149 void
svn_fs_x__serialize_apr_array(svn_temp_serializer__context_t * context,apr_array_header_t ** a)150 svn_fs_x__serialize_apr_array(svn_temp_serializer__context_t *context,
151 apr_array_header_t **a)
152 {
153 const apr_array_header_t *array = *a;
154
155 /* Nothing to do for NULL string references. */
156 if (array == NULL)
157 return;
158
159 /* array header struct */
160 svn_temp_serializer__push(context,
161 (const void * const *)a,
162 sizeof(*array));
163
164 /* contents */
165 svn_temp_serializer__add_leaf(context,
166 (const void * const *)&array->elts,
167 (apr_size_t)array->nelts * array->elt_size);
168
169 /* back to the caller's nesting level */
170 svn_temp_serializer__pop(context);
171 }
172
173 void
svn_fs_x__deserialize_apr_array(void * buffer,apr_array_header_t ** array,apr_pool_t * result_pool)174 svn_fs_x__deserialize_apr_array(void *buffer,
175 apr_array_header_t **array,
176 apr_pool_t *result_pool)
177 {
178 svn_temp_deserializer__resolve(buffer, (void **)array);
179 if (*array == NULL)
180 return;
181
182 svn_temp_deserializer__resolve(*array, (void **)&(*array)->elts);
183 (*array)->pool = result_pool;
184 }
185
186 /* auxilliary structure representing the content of a directory array */
187 typedef struct dir_data_t
188 {
189 /* number of entries in the directory
190 * (it's int because the directory is an APR array) */
191 int count;
192
193 /** Current length of the in-txn in-disk representation of the directory.
194 * SVN_INVALID_FILESIZE if unknown (i.e. committed data). */
195 svn_filesize_t txn_filesize;
196
197 /* number of unused dir entry buckets in the index */
198 apr_size_t over_provision;
199
200 /* internal modifying operations counter
201 * (used to repack data once in a while) */
202 apr_size_t operations;
203
204 /* size of the serialization buffer actually used.
205 * (we will allocate more than we actually need such that we may
206 * append more data in situ later) */
207 apr_size_t len;
208
209 /* reference to the entries */
210 svn_fs_x__dirent_t **entries;
211
212 /* size of the serialized entries and don't be too wasteful
213 * (needed since the entries are no longer in sequence) */
214 apr_uint32_t *lengths;
215 } dir_data_t;
216
217 /* Utility function to serialize the *ENTRY_P into a the given
218 * serialization CONTEXT. Return the serialized size of the
219 * dir entry in *LENGTH.
220 */
221 static void
serialize_dir_entry(svn_temp_serializer__context_t * context,svn_fs_x__dirent_t ** entry_p,apr_uint32_t * length)222 serialize_dir_entry(svn_temp_serializer__context_t *context,
223 svn_fs_x__dirent_t **entry_p,
224 apr_uint32_t *length)
225 {
226 svn_fs_x__dirent_t *entry = *entry_p;
227 apr_size_t initial_length = svn_temp_serializer__get_length(context);
228
229 svn_temp_serializer__push(context,
230 (const void * const *)entry_p,
231 sizeof(**entry_p));
232
233 svn_temp_serializer__add_string(context, &entry->name);
234
235 *length = (apr_uint32_t)( svn_temp_serializer__get_length(context)
236 - APR_ALIGN_DEFAULT(initial_length));
237
238 svn_temp_serializer__pop(context);
239 }
240
241 /* Utility function to serialize the DIR into a new serialization
242 * context to be returned.
243 *
244 * Temporary allocation will be made form SCRATCH_POOL.
245 */
246 static svn_temp_serializer__context_t *
serialize_dir(svn_fs_x__dir_data_t * dir,apr_pool_t * scratch_pool)247 serialize_dir(svn_fs_x__dir_data_t *dir,
248 apr_pool_t *scratch_pool)
249 {
250 dir_data_t dir_data;
251 int i = 0;
252 svn_temp_serializer__context_t *context;
253 apr_array_header_t *entries = dir->entries;
254
255 /* calculate sizes */
256 int count = entries->nelts;
257 apr_size_t over_provision = 2 + count / 4;
258 apr_size_t entries_len = (count + over_provision)
259 * sizeof(svn_fs_x__dirent_t*);
260 apr_size_t lengths_len = (count + over_provision) * sizeof(apr_uint32_t);
261
262 /* Estimate the size of a directory entry + its name. */
263 enum { ENTRY_SIZE = sizeof(svn_fs_x__dirent_t) + 32 };
264
265 /* copy the hash entries to an auxiliary struct of known layout */
266 dir_data.count = count;
267 dir_data.txn_filesize = dir->txn_filesize;
268 dir_data.over_provision = over_provision;
269 dir_data.operations = 0;
270 dir_data.entries = apr_palloc(scratch_pool, entries_len);
271 dir_data.lengths = apr_palloc(scratch_pool, lengths_len);
272
273 for (i = 0; i < count; ++i)
274 dir_data.entries[i] = APR_ARRAY_IDX(entries, i, svn_fs_x__dirent_t *);
275
276 /* Serialize that aux. structure into a new one. Also, provide a good
277 * estimate for the size of the buffer that we will need. */
278 context = svn_temp_serializer__init(&dir_data,
279 sizeof(dir_data),
280 50 + count * ENTRY_SIZE
281 + entries_len + lengths_len,
282 scratch_pool);
283
284 /* serialize entries references */
285 svn_temp_serializer__push(context,
286 (const void * const *)&dir_data.entries,
287 entries_len);
288
289 /* serialize the individual entries and their sub-structures */
290 for (i = 0; i < count; ++i)
291 serialize_dir_entry(context,
292 &dir_data.entries[i],
293 &dir_data.lengths[i]);
294
295 svn_temp_serializer__pop(context);
296
297 /* serialize entries references */
298 svn_temp_serializer__push(context,
299 (const void * const *)&dir_data.lengths,
300 lengths_len);
301
302 return context;
303 }
304
305 /* Utility function to reconstruct a dir entries struct from serialized data
306 * in BUFFER and DIR_DATA. Allocation will be made form RESULT_POOL.
307 */
308 static svn_fs_x__dir_data_t *
deserialize_dir(void * buffer,dir_data_t * dir_data,apr_pool_t * result_pool)309 deserialize_dir(void *buffer,
310 dir_data_t *dir_data,
311 apr_pool_t *result_pool)
312 {
313 svn_fs_x__dir_data_t *result;
314 apr_size_t i;
315 apr_size_t count;
316 svn_fs_x__dirent_t *entry;
317 svn_fs_x__dirent_t **entries;
318
319 /* Construct empty directory object. */
320 result = apr_pcalloc(result_pool, sizeof(*result));
321 result->entries
322 = apr_array_make(result_pool, dir_data->count,
323 sizeof(svn_fs_x__dirent_t *));
324 result->txn_filesize = dir_data->txn_filesize;
325
326 /* resolve the reference to the entries array */
327 svn_temp_deserializer__resolve(buffer, (void **)&dir_data->entries);
328 entries = dir_data->entries;
329
330 /* fixup the references within each entry and add it to the RESULT */
331 for (i = 0, count = dir_data->count; i < count; ++i)
332 {
333 svn_temp_deserializer__resolve(entries, (void **)&entries[i]);
334 entry = dir_data->entries[i];
335
336 /* pointer fixup */
337 svn_temp_deserializer__resolve(entry, (void **)&entry->name);
338
339 /* add the entry to the hash */
340 APR_ARRAY_PUSH(result->entries, svn_fs_x__dirent_t *) = entry;
341 }
342
343 /* return the now complete hash */
344 return result;
345 }
346
347 /**
348 * Serialize a NODEREV_P within the serialization CONTEXT.
349 */
350 static void
noderev_serialize(svn_temp_serializer__context_t * context,svn_fs_x__noderev_t * const * noderev_p)351 noderev_serialize(svn_temp_serializer__context_t *context,
352 svn_fs_x__noderev_t * const *noderev_p)
353 {
354 const svn_fs_x__noderev_t *noderev = *noderev_p;
355 if (noderev == NULL)
356 return;
357
358 /* serialize the representation struct itself */
359 svn_temp_serializer__push(context,
360 (const void * const *)noderev_p,
361 sizeof(*noderev));
362
363 /* serialize sub-structures */
364 serialize_representation(context, &noderev->prop_rep);
365 serialize_representation(context, &noderev->data_rep);
366
367 svn_temp_serializer__add_string(context, &noderev->copyfrom_path);
368 svn_temp_serializer__add_string(context, &noderev->copyroot_path);
369 svn_temp_serializer__add_string(context, &noderev->created_path);
370
371 /* return to the caller's nesting level */
372 svn_temp_serializer__pop(context);
373 }
374
375 /**
376 * Deserialize a NODEREV_P within the BUFFER and associate it with.
377 */
378 static void
noderev_deserialize(void * buffer,svn_fs_x__noderev_t ** noderev_p)379 noderev_deserialize(void *buffer,
380 svn_fs_x__noderev_t **noderev_p)
381 {
382 svn_fs_x__noderev_t *noderev;
383
384 /* fixup the reference to the representation itself,
385 * if this is part of a parent structure. */
386 if (buffer != *noderev_p)
387 svn_temp_deserializer__resolve(buffer, (void **)noderev_p);
388
389 noderev = *noderev_p;
390 if (noderev == NULL)
391 return;
392
393 /* fixup of sub-structures */
394 svn_temp_deserializer__resolve(noderev, (void **)&noderev->prop_rep);
395 svn_temp_deserializer__resolve(noderev, (void **)&noderev->data_rep);
396
397 svn_temp_deserializer__resolve(noderev, (void **)&noderev->copyfrom_path);
398 svn_temp_deserializer__resolve(noderev, (void **)&noderev->copyroot_path);
399 svn_temp_deserializer__resolve(noderev, (void **)&noderev->created_path);
400 }
401
402
403 /* Utility function to serialize COUNT svn_txdelta_op_t objects
404 * at OPS in the given serialization CONTEXT.
405 */
406 static void
serialize_txdelta_ops(svn_temp_serializer__context_t * context,const svn_txdelta_op_t * const * ops,apr_size_t count)407 serialize_txdelta_ops(svn_temp_serializer__context_t *context,
408 const svn_txdelta_op_t * const * ops,
409 apr_size_t count)
410 {
411 if (*ops == NULL)
412 return;
413
414 /* the ops form a contiguous chunk of memory with no further references */
415 svn_temp_serializer__add_leaf(context,
416 (const void * const *)ops,
417 count * sizeof(svn_txdelta_op_t));
418 }
419
420 /* Utility function to serialize W in the given serialization CONTEXT.
421 */
422 static void
serialize_txdeltawindow(svn_temp_serializer__context_t * context,svn_txdelta_window_t * const * w)423 serialize_txdeltawindow(svn_temp_serializer__context_t *context,
424 svn_txdelta_window_t * const * w)
425 {
426 svn_txdelta_window_t *window = *w;
427
428 /* serialize the window struct itself */
429 svn_temp_serializer__push(context,
430 (const void * const *)w,
431 sizeof(svn_txdelta_window_t));
432
433 /* serialize its sub-structures */
434 serialize_txdelta_ops(context, &window->ops, window->num_ops);
435 serialize_svn_string(context, &window->new_data);
436
437 svn_temp_serializer__pop(context);
438 }
439
440 svn_error_t *
svn_fs_x__serialize_txdelta_window(void ** buffer,apr_size_t * buffer_size,void * item,apr_pool_t * pool)441 svn_fs_x__serialize_txdelta_window(void **buffer,
442 apr_size_t *buffer_size,
443 void *item,
444 apr_pool_t *pool)
445 {
446 svn_fs_x__txdelta_cached_window_t *window_info = item;
447 svn_stringbuf_t *serialized;
448
449 /* initialize the serialization process and allocate a buffer large
450 * enough to do without the need of re-allocations in most cases. */
451 apr_size_t text_len = window_info->window->new_data
452 ? window_info->window->new_data->len
453 : 0;
454 svn_temp_serializer__context_t *context =
455 svn_temp_serializer__init(window_info,
456 sizeof(*window_info),
457 500 + text_len,
458 pool);
459
460 /* serialize the sub-structure(s) */
461 serialize_txdeltawindow(context, &window_info->window);
462
463 /* return the serialized result */
464 serialized = svn_temp_serializer__get(context);
465
466 *buffer = serialized->data;
467 *buffer_size = serialized->len;
468
469 return SVN_NO_ERROR;
470 }
471
472 svn_error_t *
svn_fs_x__deserialize_txdelta_window(void ** item,void * buffer,apr_size_t buffer_size,apr_pool_t * result_pool)473 svn_fs_x__deserialize_txdelta_window(void **item,
474 void *buffer,
475 apr_size_t buffer_size,
476 apr_pool_t *result_pool)
477 {
478 svn_txdelta_window_t *window;
479
480 /* Copy the _full_ buffer as it also contains the sub-structures. */
481 svn_fs_x__txdelta_cached_window_t *window_info =
482 (svn_fs_x__txdelta_cached_window_t *)buffer;
483
484 /* pointer reference fixup */
485 svn_temp_deserializer__resolve(window_info,
486 (void **)&window_info->window);
487 window = window_info->window;
488
489 svn_temp_deserializer__resolve(window, (void **)&window->ops);
490
491 deserialize_svn_string(window, (svn_string_t**)&window->new_data);
492
493 /* done */
494 *item = window_info;
495
496 return SVN_NO_ERROR;
497 }
498
499 /* Auxiliary structure representing the content of a properties hash.
500 This structure is much easier to (de-)serialize than an apr_hash.
501 */
502 typedef struct properties_data_t
503 {
504 /* number of entries in the hash */
505 apr_size_t count;
506
507 /* reference to the keys */
508 const char **keys;
509
510 /* reference to the values */
511 const svn_string_t **values;
512 } properties_data_t;
513
514 /* Serialize COUNT C-style strings from *STRINGS into CONTEXT. */
515 static void
serialize_cstring_array(svn_temp_serializer__context_t * context,const char *** strings,apr_size_t count)516 serialize_cstring_array(svn_temp_serializer__context_t *context,
517 const char ***strings,
518 apr_size_t count)
519 {
520 apr_size_t i;
521 const char **entries = *strings;
522
523 /* serialize COUNT entries pointers (the array) */
524 svn_temp_serializer__push(context,
525 (const void * const *)strings,
526 count * sizeof(const char*));
527
528 /* serialize array elements */
529 for (i = 0; i < count; ++i)
530 svn_temp_serializer__add_string(context, &entries[i]);
531
532 svn_temp_serializer__pop(context);
533 }
534
535 /* Serialize COUNT svn_string_t* items from *STRINGS into CONTEXT. */
536 static void
serialize_svn_string_array(svn_temp_serializer__context_t * context,const svn_string_t *** strings,apr_size_t count)537 serialize_svn_string_array(svn_temp_serializer__context_t *context,
538 const svn_string_t ***strings,
539 apr_size_t count)
540 {
541 apr_size_t i;
542 const svn_string_t **entries = *strings;
543
544 /* serialize COUNT entries pointers (the array) */
545 svn_temp_serializer__push(context,
546 (const void * const *)strings,
547 count * sizeof(const char*));
548
549 /* serialize array elements */
550 for (i = 0; i < count; ++i)
551 serialize_svn_string(context, &entries[i]);
552
553 svn_temp_serializer__pop(context);
554 }
555
556 svn_error_t *
svn_fs_x__serialize_properties(void ** data,apr_size_t * data_len,void * in,apr_pool_t * pool)557 svn_fs_x__serialize_properties(void **data,
558 apr_size_t *data_len,
559 void *in,
560 apr_pool_t *pool)
561 {
562 apr_hash_t *hash = in;
563 properties_data_t properties;
564 svn_temp_serializer__context_t *context;
565 apr_hash_index_t *hi;
566 svn_stringbuf_t *serialized;
567 apr_size_t i;
568
569 /* create our auxiliary data structure */
570 properties.count = apr_hash_count(hash);
571 properties.keys = apr_palloc(pool, sizeof(const char*) * (properties.count + 1));
572 properties.values = apr_palloc(pool, sizeof(const svn_string_t *) * properties.count);
573
574 /* populate it with the hash entries */
575 for (hi = apr_hash_first(pool, hash), i=0; hi; hi = apr_hash_next(hi), ++i)
576 {
577 properties.keys[i] = apr_hash_this_key(hi);
578 properties.values[i] = apr_hash_this_val(hi);
579 }
580
581 /* serialize it */
582 context = svn_temp_serializer__init(&properties,
583 sizeof(properties),
584 properties.count * 100,
585 pool);
586
587 properties.keys[i] = "";
588 serialize_cstring_array(context, &properties.keys, properties.count + 1);
589 serialize_svn_string_array(context, &properties.values, properties.count);
590
591 /* return the serialized result */
592 serialized = svn_temp_serializer__get(context);
593
594 *data = serialized->data;
595 *data_len = serialized->len;
596
597 return SVN_NO_ERROR;
598 }
599
600 svn_error_t *
svn_fs_x__deserialize_properties(void ** out,void * data,apr_size_t data_len,apr_pool_t * result_pool)601 svn_fs_x__deserialize_properties(void **out,
602 void *data,
603 apr_size_t data_len,
604 apr_pool_t *result_pool)
605 {
606 apr_hash_t *hash = svn_hash__make(result_pool);
607 properties_data_t *properties = (properties_data_t *)data;
608 size_t i;
609
610 /* de-serialize our auxiliary data structure */
611 svn_temp_deserializer__resolve(properties, (void**)&properties->keys);
612 svn_temp_deserializer__resolve(properties, (void**)&properties->values);
613
614 /* de-serialize each entry and put it into the hash */
615 for (i = 0; i < properties->count; ++i)
616 {
617 apr_size_t len = properties->keys[i+1] - properties->keys[i] - 1;
618 svn_temp_deserializer__resolve(properties->keys,
619 (void**)&properties->keys[i]);
620
621 deserialize_svn_string(properties->values,
622 (svn_string_t **)&properties->values[i]);
623
624 apr_hash_set(hash,
625 properties->keys[i], len,
626 properties->values[i]);
627 }
628
629 /* done */
630 *out = hash;
631
632 return SVN_NO_ERROR;
633 }
634
635 /** Caching svn_fs_x__noderev_t objects. **/
636
637 svn_error_t *
svn_fs_x__serialize_node_revision(void ** buffer,apr_size_t * buffer_size,void * item,apr_pool_t * pool)638 svn_fs_x__serialize_node_revision(void **buffer,
639 apr_size_t *buffer_size,
640 void *item,
641 apr_pool_t *pool)
642 {
643 svn_stringbuf_t *serialized;
644 svn_fs_x__noderev_t *noderev = item;
645
646 /* create an (empty) serialization context with plenty of (initial)
647 * buffer space. */
648 svn_temp_serializer__context_t *context =
649 svn_temp_serializer__init(NULL, 0,
650 1024 - SVN_TEMP_SERIALIZER__OVERHEAD,
651 pool);
652
653 /* serialize the noderev */
654 noderev_serialize(context, &noderev);
655
656 /* return serialized data */
657 serialized = svn_temp_serializer__get(context);
658 *buffer = serialized->data;
659 *buffer_size = serialized->len;
660
661 return SVN_NO_ERROR;
662 }
663
664 svn_error_t *
svn_fs_x__deserialize_node_revision(void ** item,void * buffer,apr_size_t buffer_size,apr_pool_t * result_pool)665 svn_fs_x__deserialize_node_revision(void **item,
666 void *buffer,
667 apr_size_t buffer_size,
668 apr_pool_t *result_pool)
669 {
670 /* Copy the _full_ buffer as it also contains the sub-structures. */
671 svn_fs_x__noderev_t *noderev = (svn_fs_x__noderev_t *)buffer;
672
673 /* fixup of all pointers etc. */
674 noderev_deserialize(noderev, &noderev);
675
676 /* done */
677 *item = noderev;
678 return SVN_NO_ERROR;
679 }
680
681 /* Utility function that returns the directory serialized inside CONTEXT
682 * to DATA and DATA_LEN. If OVERPROVISION is set, allocate some extra
683 * room for future in-place changes by svn_fs_x__replace_dir_entry. */
684 static svn_error_t *
return_serialized_dir_context(svn_temp_serializer__context_t * context,void ** data,apr_size_t * data_len,svn_boolean_t overprovision)685 return_serialized_dir_context(svn_temp_serializer__context_t *context,
686 void **data,
687 apr_size_t *data_len,
688 svn_boolean_t overprovision)
689 {
690 svn_stringbuf_t *serialized = svn_temp_serializer__get(context);
691
692 *data = serialized->data;
693 *data_len = overprovision ? serialized->blocksize : serialized->len;
694 ((dir_data_t *)serialized->data)->len = serialized->len;
695
696 return SVN_NO_ERROR;
697 }
698
699 svn_error_t *
svn_fs_x__serialize_dir_entries(void ** data,apr_size_t * data_len,void * in,apr_pool_t * pool)700 svn_fs_x__serialize_dir_entries(void **data,
701 apr_size_t *data_len,
702 void *in,
703 apr_pool_t *pool)
704 {
705 svn_fs_x__dir_data_t *dir = in;
706
707 /* serialize the dir content into a new serialization context
708 * and return the serialized data */
709 return return_serialized_dir_context(serialize_dir(dir, pool),
710 data,
711 data_len,
712 FALSE);
713 }
714
715 svn_error_t *
svn_fs_x__deserialize_dir_entries(void ** out,void * data,apr_size_t data_len,apr_pool_t * result_pool)716 svn_fs_x__deserialize_dir_entries(void **out,
717 void *data,
718 apr_size_t data_len,
719 apr_pool_t *result_pool)
720 {
721 /* Copy the _full_ buffer as it also contains the sub-structures. */
722 dir_data_t *dir_data = (dir_data_t *)data;
723
724 /* reconstruct the hash from the serialized data */
725 *out = deserialize_dir(dir_data, dir_data, result_pool);
726
727 return SVN_NO_ERROR;
728 }
729
730 svn_error_t *
svn_fs_x__get_sharded_offset(void ** out,const void * data,apr_size_t data_len,void * baton,apr_pool_t * pool)731 svn_fs_x__get_sharded_offset(void **out,
732 const void *data,
733 apr_size_t data_len,
734 void *baton,
735 apr_pool_t *pool)
736 {
737 const apr_off_t *manifest = data;
738 apr_int64_t shard_pos = *(apr_int64_t *)baton;
739
740 *(apr_off_t *)out = manifest[shard_pos];
741
742 return SVN_NO_ERROR;
743 }
744
745 svn_error_t *
svn_fs_x__extract_dir_filesize(void ** out,const void * data,apr_size_t data_len,void * baton,apr_pool_t * pool)746 svn_fs_x__extract_dir_filesize(void **out,
747 const void *data,
748 apr_size_t data_len,
749 void *baton,
750 apr_pool_t *pool)
751 {
752 const dir_data_t *dir_data = data;
753
754 *(svn_filesize_t *)out = dir_data->txn_filesize;
755
756 return SVN_NO_ERROR;
757 }
758
759 /* Utility function that returns the lowest index of the first entry in
760 * *ENTRIES that points to a dir entry with a name equal or larger than NAME.
761 * If an exact match has been found, *FOUND will be set to TRUE. COUNT is
762 * the number of valid entries in ENTRIES.
763 */
764 static apr_size_t
find_entry(svn_fs_x__dirent_t ** entries,const char * name,apr_size_t count,svn_boolean_t * found)765 find_entry(svn_fs_x__dirent_t **entries,
766 const char *name,
767 apr_size_t count,
768 svn_boolean_t *found)
769 {
770 /* binary search for the desired entry by name */
771 apr_size_t lower = 0;
772 apr_size_t upper = count;
773 apr_size_t middle;
774
775 for (middle = upper / 2; lower < upper; middle = (upper + lower) / 2)
776 {
777 const svn_fs_x__dirent_t *entry =
778 svn_temp_deserializer__ptr(entries, (const void *const *)&entries[middle]);
779 const char* entry_name =
780 svn_temp_deserializer__ptr(entry, (const void *const *)&entry->name);
781
782 int diff = strcmp(entry_name, name);
783 if (diff < 0)
784 lower = middle + 1;
785 else
786 upper = middle;
787 }
788
789 /* check whether we actually found a match */
790 *found = FALSE;
791 if (lower < count)
792 {
793 const svn_fs_x__dirent_t *entry =
794 svn_temp_deserializer__ptr(entries, (const void *const *)&entries[lower]);
795 const char* entry_name =
796 svn_temp_deserializer__ptr(entry, (const void *const *)&entry->name);
797
798 if (strcmp(entry_name, name) == 0)
799 *found = TRUE;
800 }
801
802 return lower;
803 }
804
805 /* Utility function that returns TRUE if entry number IDX in ENTRIES has the
806 * name NAME.
807 */
808 static svn_boolean_t
found_entry(const svn_fs_x__dirent_t * const * entries,const char * name,apr_size_t idx)809 found_entry(const svn_fs_x__dirent_t * const *entries,
810 const char *name,
811 apr_size_t idx)
812 {
813 /* check whether we actually found a match */
814 const svn_fs_x__dirent_t *entry =
815 svn_temp_deserializer__ptr(entries, (const void *const *)&entries[idx]);
816 const char* entry_name =
817 svn_temp_deserializer__ptr(entry, (const void *const *)&entry->name);
818
819 return strcmp(entry_name, name) == 0;
820 }
821
822 svn_error_t *
svn_fs_x__extract_dir_entry(void ** out,const void * data,apr_size_t data_len,void * baton,apr_pool_t * pool)823 svn_fs_x__extract_dir_entry(void **out,
824 const void *data,
825 apr_size_t data_len,
826 void *baton,
827 apr_pool_t *pool)
828 {
829 const dir_data_t *dir_data = data;
830 svn_fs_x__ede_baton_t *b = baton;
831 svn_boolean_t found;
832 apr_size_t pos;
833
834 /* resolve the reference to the entries array */
835 const svn_fs_x__dirent_t * const *entries =
836 svn_temp_deserializer__ptr(data, (const void *const *)&dir_data->entries);
837
838 /* resolve the reference to the lengths array */
839 const apr_uint32_t *lengths =
840 svn_temp_deserializer__ptr(data, (const void *const *)&dir_data->lengths);
841
842 /* Before we return, make sure we tell the caller this data is even still
843 relevant. */
844 b->out_of_date = dir_data->txn_filesize != b->txn_filesize;
845
846 /* Special case: Early out for empty directories.
847 That simplifies tests further down the road. */
848 *out = NULL;
849 if (dir_data->count == 0)
850 return SVN_NO_ERROR;
851
852 /* HINT _might_ be the position we hit last time.
853 If within valid range, check whether HINT+1 is a hit. */
854 if ( b->hint < dir_data->count - 1
855 && found_entry(entries, b->name, b->hint + 1))
856 {
857 /* Got lucky. */
858 pos = b->hint + 1;
859 found = TRUE;
860 }
861 else
862 {
863 /* Binary search for the desired entry by name. */
864 pos = find_entry((svn_fs_x__dirent_t **)entries, b->name,
865 dir_data->count, &found);
866 }
867
868 /* Remember the hit index - if we FOUND the entry. */
869 if (found)
870 b->hint = pos;
871
872 /* de-serialize that entry or return NULL, if no match has been found.
873 * Be sure to check that the directory contents is still up-to-date. */
874 if (found && !b->out_of_date)
875 {
876 const svn_fs_x__dirent_t *source =
877 svn_temp_deserializer__ptr(entries, (const void *const *)&entries[pos]);
878
879 /* Entries have been serialized one-by-one, each time including all
880 * nested structures and strings. Therefore, they occupy a single
881 * block of memory whose end-offset is either the beginning of the
882 * next entry or the end of the buffer
883 */
884 apr_size_t size = lengths[pos];
885
886 /* copy & deserialize the entry */
887 svn_fs_x__dirent_t *new_entry = apr_pmemdup(pool, source, size);
888
889 svn_temp_deserializer__resolve(new_entry, (void **)&new_entry->name);
890 *(svn_fs_x__dirent_t **)out = new_entry;
891 }
892
893 return SVN_NO_ERROR;
894 }
895
896 /* Utility function for svn_fs_x__replace_dir_entry that implements the
897 * modification as a simply deserialize / modify / serialize sequence.
898 */
899 static svn_error_t *
slowly_replace_dir_entry(void ** data,apr_size_t * data_len,void * baton,apr_pool_t * pool)900 slowly_replace_dir_entry(void **data,
901 apr_size_t *data_len,
902 void *baton,
903 apr_pool_t *pool)
904 {
905 replace_baton_t *replace_baton = (replace_baton_t *)baton;
906 dir_data_t *dir_data = (dir_data_t *)*data;
907 svn_fs_x__dir_data_t *dir;
908 int idx = -1;
909 svn_fs_x__dirent_t *entry;
910 apr_array_header_t *entries;
911
912 SVN_ERR(svn_fs_x__deserialize_dir_entries((void **)&dir,
913 *data,
914 dir_data->len,
915 pool));
916
917 entries = dir->entries;
918 entry = svn_fs_x__find_dir_entry(entries, replace_baton->name, &idx);
919
920 /* Replacement or removal? */
921 if (replace_baton->new_entry)
922 {
923 /* Replace ENTRY with / insert the NEW_ENTRY */
924 if (entry)
925 APR_ARRAY_IDX(entries, idx, svn_fs_x__dirent_t *)
926 = replace_baton->new_entry;
927 else
928 SVN_ERR(svn_sort__array_insert2(entries, &replace_baton->new_entry, idx));
929 }
930 else
931 {
932 /* Remove the old ENTRY. */
933 if (entry)
934 SVN_ERR(svn_sort__array_delete2(entries, idx, 1));
935 }
936
937 return svn_fs_x__serialize_dir_entries(data, data_len, dir, pool);
938 }
939
940 svn_error_t *
svn_fs_x__replace_dir_entry(void ** data,apr_size_t * data_len,void * baton,apr_pool_t * pool)941 svn_fs_x__replace_dir_entry(void **data,
942 apr_size_t *data_len,
943 void *baton,
944 apr_pool_t *pool)
945 {
946 replace_baton_t *replace_baton = (replace_baton_t *)baton;
947 dir_data_t *dir_data = (dir_data_t *)*data;
948 svn_boolean_t found;
949 svn_fs_x__dirent_t **entries;
950 apr_uint32_t *lengths;
951 apr_uint32_t length;
952 apr_size_t pos;
953
954 svn_temp_serializer__context_t *context;
955
956 /* update the cached file length info.
957 * Because we are writing to the cache, it is fair to assume that the
958 * caller made sure that the current contents is consistent with the
959 * previous state of the directory file. */
960 dir_data->txn_filesize = replace_baton->txn_filesize;
961
962 /* after quite a number of operations, let's re-pack everything.
963 * This is to limit the number of wasted space as we cannot overwrite
964 * existing data but must always append. */
965 if (dir_data->operations > 2 + dir_data->count / 4)
966 return slowly_replace_dir_entry(data, data_len, baton, pool);
967
968 /* resolve the reference to the entries array */
969 entries = (svn_fs_x__dirent_t **)
970 svn_temp_deserializer__ptr((const char *)dir_data,
971 (const void *const *)&dir_data->entries);
972
973 /* resolve the reference to the lengths array */
974 lengths = (apr_uint32_t *)
975 svn_temp_deserializer__ptr((const char *)dir_data,
976 (const void *const *)&dir_data->lengths);
977
978 /* binary search for the desired entry by name */
979 pos = find_entry(entries, replace_baton->name, dir_data->count, &found);
980
981 /* handle entry removal (if found at all) */
982 if (replace_baton->new_entry == NULL)
983 {
984 if (found)
985 {
986 /* remove reference to the entry from the index */
987 memmove(&entries[pos],
988 &entries[pos + 1],
989 sizeof(entries[pos]) * (dir_data->count - pos));
990 memmove(&lengths[pos],
991 &lengths[pos + 1],
992 sizeof(lengths[pos]) * (dir_data->count - pos));
993
994 dir_data->count--;
995 dir_data->over_provision++;
996 dir_data->operations++;
997 }
998
999 return SVN_NO_ERROR;
1000 }
1001
1002 /* if not found, prepare to insert the new entry */
1003 if (!found)
1004 {
1005 /* fallback to slow operation if there is no place left to insert an
1006 * new entry to index. That will automatically give add some spare
1007 * entries ("overprovision"). */
1008 if (dir_data->over_provision == 0)
1009 return slowly_replace_dir_entry(data, data_len, baton, pool);
1010
1011 /* make entries[index] available for pointing to the new entry */
1012 memmove(&entries[pos + 1],
1013 &entries[pos],
1014 sizeof(entries[pos]) * (dir_data->count - pos));
1015 memmove(&lengths[pos + 1],
1016 &lengths[pos],
1017 sizeof(lengths[pos]) * (dir_data->count - pos));
1018
1019 dir_data->count++;
1020 dir_data->over_provision--;
1021 dir_data->operations++;
1022 }
1023
1024 /* de-serialize the new entry */
1025 entries[pos] = replace_baton->new_entry;
1026 context = svn_temp_serializer__init_append(dir_data,
1027 entries,
1028 dir_data->len,
1029 *data_len,
1030 pool);
1031 serialize_dir_entry(context, &entries[pos], &length);
1032
1033 /* return the updated serialized data */
1034 SVN_ERR(return_serialized_dir_context(context, data, data_len, TRUE));
1035
1036 /* since the previous call may have re-allocated the buffer, the lengths
1037 * pointer may no longer point to the entry in that buffer. Therefore,
1038 * re-map it again and store the length value after that. */
1039
1040 dir_data = (dir_data_t *)*data;
1041 lengths = (apr_uint32_t *)
1042 svn_temp_deserializer__ptr((const char *)dir_data,
1043 (const void *const *)&dir_data->lengths);
1044 lengths[pos] = length;
1045
1046 return SVN_NO_ERROR;
1047 }
1048
1049 svn_error_t *
svn_fs_x__reset_txn_filesize(void ** data,apr_size_t * data_len,void * baton,apr_pool_t * pool)1050 svn_fs_x__reset_txn_filesize(void **data,
1051 apr_size_t *data_len,
1052 void *baton,
1053 apr_pool_t *pool)
1054 {
1055 dir_data_t *dir_data = (dir_data_t *)*data;
1056 dir_data->txn_filesize = SVN_INVALID_FILESIZE;
1057
1058 return SVN_NO_ERROR;
1059 }
1060
1061 svn_error_t *
svn_fs_x__serialize_rep_header(void ** data,apr_size_t * data_len,void * in,apr_pool_t * pool)1062 svn_fs_x__serialize_rep_header(void **data,
1063 apr_size_t *data_len,
1064 void *in,
1065 apr_pool_t *pool)
1066 {
1067 *data_len = sizeof(svn_fs_x__rep_header_t);
1068 *data = in;
1069
1070 return SVN_NO_ERROR;
1071 }
1072
1073 svn_error_t *
svn_fs_x__deserialize_rep_header(void ** out,void * data,apr_size_t data_len,apr_pool_t * result_pool)1074 svn_fs_x__deserialize_rep_header(void **out,
1075 void *data,
1076 apr_size_t data_len,
1077 apr_pool_t *result_pool)
1078 {
1079 *out = data;
1080
1081 return SVN_NO_ERROR;
1082 }
1083
1084 /* Utility function to serialize change CHANGE_P in the given serialization
1085 * CONTEXT.
1086 */
1087 static void
serialize_change(svn_temp_serializer__context_t * context,svn_fs_x__change_t * const * change_p)1088 serialize_change(svn_temp_serializer__context_t *context,
1089 svn_fs_x__change_t * const *change_p)
1090 {
1091 const svn_fs_x__change_t * change = *change_p;
1092 if (change == NULL)
1093 return;
1094
1095 /* serialize the change struct itself */
1096 svn_temp_serializer__push(context,
1097 (const void * const *)change_p,
1098 sizeof(*change));
1099
1100 /* serialize sub-structures */
1101 svn_temp_serializer__add_string(context, &change->path.data);
1102 svn_temp_serializer__add_string(context, &change->copyfrom_path);
1103
1104 /* return to the caller's nesting level */
1105 svn_temp_serializer__pop(context);
1106 }
1107
1108 /* Utility function to serialize the CHANGE_P within the given
1109 * serialization CONTEXT.
1110 */
1111 static void
deserialize_change(void * buffer,svn_fs_x__change_t ** change_p)1112 deserialize_change(void *buffer,
1113 svn_fs_x__change_t **change_p)
1114 {
1115 svn_fs_x__change_t * change;
1116
1117 /* fix-up of the pointer to the struct in question */
1118 svn_temp_deserializer__resolve(buffer, (void **)change_p);
1119
1120 change = *change_p;
1121 if (change == NULL)
1122 return;
1123
1124 /* fix-up of sub-structures */
1125 svn_temp_deserializer__resolve(change, (void **)&change->path.data);
1126 svn_temp_deserializer__resolve(change, (void **)&change->copyfrom_path);
1127 }
1128
1129 svn_error_t *
svn_fs_x__serialize_changes(void ** data,apr_size_t * data_len,void * in,apr_pool_t * pool)1130 svn_fs_x__serialize_changes(void **data,
1131 apr_size_t *data_len,
1132 void *in,
1133 apr_pool_t *pool)
1134 {
1135 svn_fs_x__changes_list_t *changes = in;
1136 svn_temp_serializer__context_t *context;
1137 svn_stringbuf_t *serialized;
1138 int i;
1139
1140 /* serialize it and all its elements */
1141 context = svn_temp_serializer__init(changes,
1142 sizeof(*changes),
1143 changes->count * 250,
1144 pool);
1145
1146 svn_temp_serializer__push(context,
1147 (const void * const *)&changes->changes,
1148 changes->count * sizeof(*changes->changes));
1149
1150 for (i = 0; i < changes->count; ++i)
1151 serialize_change(context, &changes->changes[i]);
1152
1153 svn_temp_serializer__pop(context);
1154
1155 /* return the serialized result */
1156 serialized = svn_temp_serializer__get(context);
1157
1158 *data = serialized->data;
1159 *data_len = serialized->len;
1160
1161 return SVN_NO_ERROR;
1162 }
1163
1164 svn_error_t *
svn_fs_x__deserialize_changes(void ** out,void * data,apr_size_t data_len,apr_pool_t * result_pool)1165 svn_fs_x__deserialize_changes(void **out,
1166 void *data,
1167 apr_size_t data_len,
1168 apr_pool_t *result_pool)
1169 {
1170 int i;
1171 svn_fs_x__changes_list_t *changes = (svn_fs_x__changes_list_t *)data;
1172
1173 /* de-serialize our auxiliary data structure */
1174 svn_temp_deserializer__resolve(changes, (void**)&changes->changes);
1175
1176 /* de-serialize each entry and add it to the array */
1177 for (i = 0; i < changes->count; ++i)
1178 deserialize_change(changes->changes,
1179 (svn_fs_x__change_t **)&changes->changes[i]);
1180
1181 /* done */
1182 *out = changes;
1183
1184 return SVN_NO_ERROR;
1185 }
1186