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