1 /* temp_serializer.c: serialization functions for caching of FSFS structures
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 * ====================================================================
23 #include <apr_pools.h>
25 #include "svn_pools.h"
27 #include "svn_sorts.h"
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"
36 #include "temp_serializer.h"
37 #include "low_level.h"
38 #include "cached_data.h"
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.
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.
48 encode_number(apr_int64_t number, char *key_buffer)
50 /* encode the sign in the first byte */
54 *key_buffer = (char)((number & 63) + ' ' + 65);
57 *key_buffer = (char)((number & 63) + ' ' + 1);
60 /* write 7 bits / byte until no significant bits are left */
63 *++key_buffer = (char)((number & 127) + ' ' + 1);
67 /* return the last written position */
72 svn_fs_fs__combine_number_and_string(apr_int64_t number,
76 apr_size_t len = strlen(string);
78 /* number part requires max. 10x7 bits + 1 space.
79 * Add another 1 for the terminal 0 */
80 char *key_buffer = apr_palloc(pool, len + 12);
81 const char *key = key_buffer;
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);
88 memcpy(++key_buffer, string, len+1);
90 /* return the start of the key */
94 /* Utility function to serialize string S in the given serialization CONTEXT.
97 serialize_svn_string(svn_temp_serializer__context_t *context,
98 const svn_string_t * const *s)
100 const svn_string_t *string = *s;
102 /* Nothing to do for NULL string references. */
106 svn_temp_serializer__push(context,
107 (const void * const *)s,
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,
116 /* back to the caller's nesting level */
117 svn_temp_serializer__pop(context);
120 /* Utility function to deserialize the STRING inside the BUFFER.
123 deserialize_svn_string(void *buffer, svn_string_t **string)
125 svn_temp_deserializer__resolve(buffer, (void **)string);
129 svn_temp_deserializer__resolve(*string, (void **)&(*string)->data);
132 /* Utility function to serialize the REPRESENTATION within the given
133 * serialization CONTEXT.
136 serialize_representation(svn_temp_serializer__context_t *context,
137 representation_t * const *representation)
139 const representation_t * rep = *representation;
143 /* serialize the representation struct itself */
144 svn_temp_serializer__add_leaf(context,
145 (const void * const *)representation,
149 /* auxiliary structure representing the content of a directory array */
150 typedef struct dir_data_t
152 /* number of entries in the directory
153 * (it's int because the directory is an APR array) */
156 /* number of unused dir entry buckets in the index */
157 apr_size_t over_provision;
159 /* internal modifying operations counter
160 * (used to repack data once in a while) */
161 apr_size_t operations;
163 /* size of the serialization buffer actually used.
164 * (we will allocate more than we actually need such that we may
165 * append more data in situ later) */
168 /* reference to the entries */
169 svn_fs_dirent_t **entries;
171 /* size of the serialized entries and don't be too wasteful
172 * (needed since the entries are no longer in sequence) */
173 apr_uint32_t *lengths;
176 /* Utility function to serialize the *ENTRY_P into a the given
177 * serialization CONTEXT. Return the serialized size of the
178 * dir entry in *LENGTH.
181 serialize_dir_entry(svn_temp_serializer__context_t *context,
182 svn_fs_dirent_t **entry_p,
183 apr_uint32_t *length)
185 svn_fs_dirent_t *entry = *entry_p;
186 apr_size_t initial_length = svn_temp_serializer__get_length(context);
188 svn_temp_serializer__push(context,
189 (const void * const *)entry_p,
190 sizeof(svn_fs_dirent_t));
192 svn_fs_fs__id_serialize(context, &entry->id);
193 svn_temp_serializer__add_string(context, &entry->name);
195 *length = (apr_uint32_t)( svn_temp_serializer__get_length(context)
196 - APR_ALIGN_DEFAULT(initial_length));
198 svn_temp_serializer__pop(context);
201 /* Utility function to serialize the ENTRIES into a new serialization
202 * context to be returned. Allocation will be made form POOL.
204 static svn_temp_serializer__context_t *
205 serialize_dir(apr_array_header_t *entries, apr_pool_t *pool)
209 svn_temp_serializer__context_t *context;
211 /* calculate sizes */
212 int count = entries->nelts;
213 apr_size_t over_provision = 2 + count / 4;
214 apr_size_t entries_len = (count + over_provision) * sizeof(svn_fs_dirent_t*);
215 apr_size_t lengths_len = (count + over_provision) * sizeof(apr_uint32_t);
217 /* copy the hash entries to an auxiliary struct of known layout */
218 dir_data.count = count;
219 dir_data.over_provision = over_provision;
220 dir_data.operations = 0;
221 dir_data.entries = apr_palloc(pool, entries_len);
222 dir_data.lengths = apr_palloc(pool, lengths_len);
224 for (i = 0; i < count; ++i)
225 dir_data.entries[i] = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *);
227 /* Serialize that aux. structure into a new one. Also, provide a good
228 * estimate for the size of the buffer that we will need. */
229 context = svn_temp_serializer__init(&dir_data,
231 50 + count * 200 + entries_len,
234 /* serialize entries references */
235 svn_temp_serializer__push(context,
236 (const void * const *)&dir_data.entries,
239 /* serialize the individual entries and their sub-structures */
240 for (i = 0; i < count; ++i)
241 serialize_dir_entry(context,
242 &dir_data.entries[i],
243 &dir_data.lengths[i]);
245 svn_temp_serializer__pop(context);
247 /* serialize entries references */
248 svn_temp_serializer__push(context,
249 (const void * const *)&dir_data.lengths,
255 /* Utility function to reconstruct a dir entries array from serialized data
256 * in BUFFER and DIR_DATA. Allocation will be made form POOL.
258 static apr_array_header_t *
259 deserialize_dir(void *buffer, dir_data_t *dir_data, apr_pool_t *pool)
261 apr_array_header_t *result
262 = apr_array_make(pool, dir_data->count, sizeof(svn_fs_dirent_t *));
265 svn_fs_dirent_t *entry;
266 svn_fs_dirent_t **entries;
268 /* resolve the reference to the entries array */
269 svn_temp_deserializer__resolve(buffer, (void **)&dir_data->entries);
270 entries = dir_data->entries;
272 /* fixup the references within each entry and add it to the hash */
273 for (i = 0, count = dir_data->count; i < count; ++i)
275 svn_temp_deserializer__resolve(entries, (void **)&entries[i]);
276 entry = dir_data->entries[i];
279 svn_temp_deserializer__resolve(entry, (void **)&entry->name);
280 svn_fs_fs__id_deserialize(entry, (svn_fs_id_t **)&entry->id);
282 /* add the entry to the hash */
283 APR_ARRAY_PUSH(result, svn_fs_dirent_t *) = entry;
286 /* return the now complete hash */
291 svn_fs_fs__noderev_serialize(svn_temp_serializer__context_t *context,
292 node_revision_t * const *noderev_p)
294 const node_revision_t *noderev = *noderev_p;
298 /* serialize the representation struct itself */
299 svn_temp_serializer__push(context,
300 (const void * const *)noderev_p,
303 /* serialize sub-structures */
304 svn_fs_fs__id_serialize(context, &noderev->id);
305 svn_fs_fs__id_serialize(context, &noderev->predecessor_id);
306 serialize_representation(context, &noderev->prop_rep);
307 serialize_representation(context, &noderev->data_rep);
309 svn_temp_serializer__add_string(context, &noderev->copyfrom_path);
310 svn_temp_serializer__add_string(context, &noderev->copyroot_path);
311 svn_temp_serializer__add_string(context, &noderev->created_path);
313 /* return to the caller's nesting level */
314 svn_temp_serializer__pop(context);
319 svn_fs_fs__noderev_deserialize(void *buffer,
320 node_revision_t **noderev_p)
322 node_revision_t *noderev;
324 /* fixup the reference to the representation itself,
325 * if this is part of a parent structure. */
326 if (buffer != *noderev_p)
327 svn_temp_deserializer__resolve(buffer, (void **)noderev_p);
329 noderev = *noderev_p;
333 /* fixup of sub-structures */
334 svn_fs_fs__id_deserialize(noderev, (svn_fs_id_t **)&noderev->id);
335 svn_fs_fs__id_deserialize(noderev, (svn_fs_id_t **)&noderev->predecessor_id);
336 svn_temp_deserializer__resolve(noderev, (void **)&noderev->prop_rep);
337 svn_temp_deserializer__resolve(noderev, (void **)&noderev->data_rep);
339 svn_temp_deserializer__resolve(noderev, (void **)&noderev->copyfrom_path);
340 svn_temp_deserializer__resolve(noderev, (void **)&noderev->copyroot_path);
341 svn_temp_deserializer__resolve(noderev, (void **)&noderev->created_path);
345 svn_fs_fs__serialize_raw_window(void **buffer,
346 apr_size_t *buffer_size,
350 svn_fs_fs__raw_cached_window_t *window = item;
351 svn_stringbuf_t *serialized;
353 /* initialize the serialization process and allocate a buffer large
354 * enough to do prevent re-allocations. */
355 svn_temp_serializer__context_t *context =
356 svn_temp_serializer__init(window,
358 sizeof(*window) + window->window.len + 16,
361 /* serialize the sub-structure(s) */
362 svn_temp_serializer__add_leaf(context,
363 (const void * const *)&window->window.data,
364 window->window.len + 1);
366 /* return the serialized result */
367 serialized = svn_temp_serializer__get(context);
369 *buffer = serialized->data;
370 *buffer_size = serialized->len;
376 svn_fs_fs__deserialize_raw_window(void **item,
378 apr_size_t buffer_size,
381 svn_fs_fs__raw_cached_window_t *window =
382 (svn_fs_fs__raw_cached_window_t *)buffer;
384 /* pointer reference fixup */
385 svn_temp_deserializer__resolve(window, (void **)&window->window.data);
394 /* Utility function to serialize COUNT svn_txdelta_op_t objects
395 * at OPS in the given serialization CONTEXT.
398 serialize_txdelta_ops(svn_temp_serializer__context_t *context,
399 const svn_txdelta_op_t * const * ops,
405 /* the ops form a contiguous chunk of memory with no further references */
406 svn_temp_serializer__add_leaf(context,
407 (const void * const *)ops,
408 count * sizeof(svn_txdelta_op_t));
411 /* Utility function to serialize W in the given serialization CONTEXT.
414 serialize_txdeltawindow(svn_temp_serializer__context_t *context,
415 svn_txdelta_window_t * const * w)
417 svn_txdelta_window_t *window = *w;
419 /* serialize the window struct itself */
420 svn_temp_serializer__push(context,
421 (const void * const *)w,
422 sizeof(svn_txdelta_window_t));
424 /* serialize its sub-structures */
425 serialize_txdelta_ops(context, &window->ops, window->num_ops);
426 serialize_svn_string(context, &window->new_data);
428 svn_temp_serializer__pop(context);
432 svn_fs_fs__serialize_txdelta_window(void **buffer,
433 apr_size_t *buffer_size,
437 svn_fs_fs__txdelta_cached_window_t *window_info = item;
438 svn_stringbuf_t *serialized;
440 /* initialize the serialization process and allocate a buffer large
441 * enough to do without the need of re-allocations in most cases. */
442 apr_size_t text_len = window_info->window->new_data
443 ? window_info->window->new_data->len
445 svn_temp_serializer__context_t *context =
446 svn_temp_serializer__init(window_info,
447 sizeof(*window_info),
451 /* serialize the sub-structure(s) */
452 serialize_txdeltawindow(context, &window_info->window);
454 /* return the serialized result */
455 serialized = svn_temp_serializer__get(context);
457 *buffer = serialized->data;
458 *buffer_size = serialized->len;
464 svn_fs_fs__deserialize_txdelta_window(void **item,
466 apr_size_t buffer_size,
469 svn_txdelta_window_t *window;
471 /* Copy the _full_ buffer as it also contains the sub-structures. */
472 svn_fs_fs__txdelta_cached_window_t *window_info =
473 (svn_fs_fs__txdelta_cached_window_t *)buffer;
475 /* pointer reference fixup */
476 svn_temp_deserializer__resolve(window_info,
477 (void **)&window_info->window);
478 window = window_info->window;
480 svn_temp_deserializer__resolve(window, (void **)&window->ops);
482 deserialize_svn_string(window, (svn_string_t**)&window->new_data);
491 svn_fs_fs__serialize_manifest(void **data,
492 apr_size_t *data_len,
496 apr_array_header_t *manifest = in;
498 *data_len = sizeof(apr_off_t) *manifest->nelts;
499 *data = apr_palloc(pool, *data_len);
500 memcpy(*data, manifest->elts, *data_len);
506 svn_fs_fs__deserialize_manifest(void **out,
511 apr_array_header_t *manifest = apr_array_make(pool, 1, sizeof(apr_off_t));
513 manifest->nelts = (int) (data_len / sizeof(apr_off_t));
514 manifest->nalloc = (int) (data_len / sizeof(apr_off_t));
515 manifest->elts = (char*)data;
522 /* Auxiliary structure representing the content of a properties hash.
523 This structure is much easier to (de-)serialize than an apr_hash.
525 typedef struct properties_data_t
527 /* number of entries in the hash */
530 /* reference to the keys */
533 /* reference to the values */
534 const svn_string_t **values;
537 /* Serialize COUNT C-style strings from *STRINGS into CONTEXT. */
539 serialize_cstring_array(svn_temp_serializer__context_t *context,
540 const char ***strings,
544 const char **entries = *strings;
546 /* serialize COUNT entries pointers (the array) */
547 svn_temp_serializer__push(context,
548 (const void * const *)strings,
549 count * sizeof(const char*));
551 /* serialize array elements */
552 for (i = 0; i < count; ++i)
553 svn_temp_serializer__add_string(context, &entries[i]);
555 svn_temp_serializer__pop(context);
558 /* Serialize COUNT svn_string_t* items from *STRINGS into CONTEXT. */
560 serialize_svn_string_array(svn_temp_serializer__context_t *context,
561 const svn_string_t ***strings,
565 const svn_string_t **entries = *strings;
567 /* serialize COUNT entries pointers (the array) */
568 svn_temp_serializer__push(context,
569 (const void * const *)strings,
570 count * sizeof(const char*));
572 /* serialize array elements */
573 for (i = 0; i < count; ++i)
574 serialize_svn_string(context, &entries[i]);
576 svn_temp_serializer__pop(context);
580 svn_fs_fs__serialize_properties(void **data,
581 apr_size_t *data_len,
585 apr_hash_t *hash = in;
586 properties_data_t properties;
587 svn_temp_serializer__context_t *context;
588 apr_hash_index_t *hi;
589 svn_stringbuf_t *serialized;
592 /* create our auxiliary data structure */
593 properties.count = apr_hash_count(hash);
594 properties.keys = apr_palloc(pool, sizeof(const char*) * (properties.count + 1));
595 properties.values = apr_palloc(pool, sizeof(const char*) * properties.count);
597 /* populate it with the hash entries */
598 for (hi = apr_hash_first(pool, hash), i=0; hi; hi = apr_hash_next(hi), ++i)
600 properties.keys[i] = apr_hash_this_key(hi);
601 properties.values[i] = apr_hash_this_val(hi);
605 context = svn_temp_serializer__init(&properties,
607 properties.count * 100,
610 properties.keys[i] = "";
611 serialize_cstring_array(context, &properties.keys, properties.count + 1);
612 serialize_svn_string_array(context, &properties.values, properties.count);
614 /* return the serialized result */
615 serialized = svn_temp_serializer__get(context);
617 *data = serialized->data;
618 *data_len = serialized->len;
624 svn_fs_fs__deserialize_properties(void **out,
629 apr_hash_t *hash = svn_hash__make(pool);
630 properties_data_t *properties = (properties_data_t *)data;
633 /* de-serialize our auxiliary data structure */
634 svn_temp_deserializer__resolve(properties, (void**)&properties->keys);
635 svn_temp_deserializer__resolve(properties, (void**)&properties->values);
637 /* de-serialize each entry and put it into the hash */
638 for (i = 0; i < properties->count; ++i)
640 apr_size_t len = properties->keys[i+1] - properties->keys[i] - 1;
641 svn_temp_deserializer__resolve(properties->keys,
642 (void**)&properties->keys[i]);
644 deserialize_svn_string(properties->values,
645 (svn_string_t **)&properties->values[i]);
648 properties->keys[i], len,
649 properties->values[i]);
659 svn_fs_fs__serialize_id(void **data,
660 apr_size_t *data_len,
664 const svn_fs_id_t *id = in;
665 svn_stringbuf_t *serialized;
667 /* create an (empty) serialization context with plenty of buffer space */
668 svn_temp_serializer__context_t *context =
669 svn_temp_serializer__init(NULL, 0, 250, pool);
671 /* serialize the id */
672 svn_fs_fs__id_serialize(context, &id);
674 /* return serialized data */
675 serialized = svn_temp_serializer__get(context);
676 *data = serialized->data;
677 *data_len = serialized->len;
683 svn_fs_fs__deserialize_id(void **out,
688 /* Copy the _full_ buffer as it also contains the sub-structures. */
689 svn_fs_id_t *id = (svn_fs_id_t *)data;
691 /* fixup of all pointers etc. */
692 svn_fs_fs__id_deserialize(id, &id);
699 /** Caching node_revision_t objects. **/
702 svn_fs_fs__serialize_node_revision(void **buffer,
703 apr_size_t *buffer_size,
707 svn_stringbuf_t *serialized;
708 node_revision_t *noderev = item;
710 /* create an (empty) serialization context with plenty of (initial)
712 svn_temp_serializer__context_t *context =
713 svn_temp_serializer__init(NULL, 0,
714 1024 - SVN_TEMP_SERIALIZER__OVERHEAD,
717 /* serialize the noderev */
718 svn_fs_fs__noderev_serialize(context, &noderev);
720 /* return serialized data */
721 serialized = svn_temp_serializer__get(context);
722 *buffer = serialized->data;
723 *buffer_size = serialized->len;
729 svn_fs_fs__deserialize_node_revision(void **item,
731 apr_size_t buffer_size,
734 /* Copy the _full_ buffer as it also contains the sub-structures. */
735 node_revision_t *noderev = (node_revision_t *)buffer;
737 /* fixup of all pointers etc. */
738 svn_fs_fs__noderev_deserialize(noderev, &noderev);
745 /* Utility function that returns the directory serialized inside CONTEXT
746 * to DATA and DATA_LEN. */
748 return_serialized_dir_context(svn_temp_serializer__context_t *context,
750 apr_size_t *data_len)
752 svn_stringbuf_t *serialized = svn_temp_serializer__get(context);
754 *data = serialized->data;
755 *data_len = serialized->blocksize;
756 ((dir_data_t *)serialized->data)->len = serialized->len;
762 svn_fs_fs__serialize_dir_entries(void **data,
763 apr_size_t *data_len,
767 apr_array_header_t *dir = in;
769 /* serialize the dir content into a new serialization context
770 * and return the serialized data */
771 return return_serialized_dir_context(serialize_dir(dir, pool),
777 svn_fs_fs__deserialize_dir_entries(void **out,
782 /* Copy the _full_ buffer as it also contains the sub-structures. */
783 dir_data_t *dir_data = (dir_data_t *)data;
785 /* reconstruct the hash from the serialized data */
786 *out = deserialize_dir(dir_data, dir_data, pool);
792 svn_fs_fs__get_sharded_offset(void **out,
798 const apr_off_t *manifest = data;
799 apr_int64_t shard_pos = *(apr_int64_t *)baton;
801 *(apr_off_t *)out = manifest[shard_pos];
806 /* Utility function that returns the lowest index of the first entry in
807 * *ENTRIES that points to a dir entry with a name equal or larger than NAME.
808 * If an exact match has been found, *FOUND will be set to TRUE. COUNT is
809 * the number of valid entries in ENTRIES.
812 find_entry(svn_fs_dirent_t **entries,
815 svn_boolean_t *found)
817 /* binary search for the desired entry by name */
818 apr_size_t lower = 0;
819 apr_size_t upper = count;
822 for (middle = upper / 2; lower < upper; middle = (upper + lower) / 2)
824 const svn_fs_dirent_t *entry =
825 svn_temp_deserializer__ptr(entries, (const void *const *)&entries[middle]);
826 const char* entry_name =
827 svn_temp_deserializer__ptr(entry, (const void *const *)&entry->name);
829 int diff = strcmp(entry_name, name);
836 /* check whether we actually found a match */
840 const svn_fs_dirent_t *entry =
841 svn_temp_deserializer__ptr(entries, (const void *const *)&entries[lower]);
842 const char* entry_name =
843 svn_temp_deserializer__ptr(entry, (const void *const *)&entry->name);
845 if (strcmp(entry_name, name) == 0)
853 svn_fs_fs__extract_dir_entry(void **out,
859 const dir_data_t *dir_data = data;
860 const char* name = baton;
863 /* resolve the reference to the entries array */
864 const svn_fs_dirent_t * const *entries =
865 svn_temp_deserializer__ptr(data, (const void *const *)&dir_data->entries);
867 /* resolve the reference to the lengths array */
868 const apr_uint32_t *lengths =
869 svn_temp_deserializer__ptr(data, (const void *const *)&dir_data->lengths);
871 /* binary search for the desired entry by name */
872 apr_size_t pos = find_entry((svn_fs_dirent_t **)entries,
877 /* de-serialize that entry or return NULL, if no match has been found */
881 const svn_fs_dirent_t *source =
882 svn_temp_deserializer__ptr(entries, (const void *const *)&entries[pos]);
884 /* Entries have been serialized one-by-one, each time including all
885 * nested structures and strings. Therefore, they occupy a single
886 * block of memory whose end-offset is either the beginning of the
887 * next entry or the end of the buffer
889 apr_size_t size = lengths[pos];
891 /* copy & deserialize the entry */
892 svn_fs_dirent_t *new_entry = apr_palloc(pool, size);
893 memcpy(new_entry, source, size);
895 svn_temp_deserializer__resolve(new_entry, (void **)&new_entry->name);
896 svn_fs_fs__id_deserialize(new_entry, (svn_fs_id_t **)&new_entry->id);
897 *(svn_fs_dirent_t **)out = new_entry;
903 /* Utility function for svn_fs_fs__replace_dir_entry that implements the
904 * modification as a simply deserialize / modify / serialize sequence.
907 slowly_replace_dir_entry(void **data,
908 apr_size_t *data_len,
912 replace_baton_t *replace_baton = (replace_baton_t *)baton;
913 dir_data_t *dir_data = (dir_data_t *)*data;
914 apr_array_header_t *dir;
916 svn_fs_dirent_t *entry;
918 SVN_ERR(svn_fs_fs__deserialize_dir_entries((void **)&dir,
923 entry = svn_fs_fs__find_dir_entry(dir, replace_baton->name, &idx);
925 /* Replacement or removal? */
926 if (replace_baton->new_entry)
928 /* Replace ENTRY with / insert the NEW_ENTRY */
930 APR_ARRAY_IDX(dir, idx, svn_fs_dirent_t *) = replace_baton->new_entry;
932 svn_sort__array_insert(dir, &replace_baton->new_entry, idx);
936 /* Remove the old ENTRY. */
938 svn_sort__array_delete(dir, idx, 1);
941 return svn_fs_fs__serialize_dir_entries(data, data_len, dir, pool);
945 svn_fs_fs__replace_dir_entry(void **data,
946 apr_size_t *data_len,
950 replace_baton_t *replace_baton = (replace_baton_t *)baton;
951 dir_data_t *dir_data = (dir_data_t *)*data;
953 svn_fs_dirent_t **entries;
954 apr_uint32_t *lengths;
958 svn_temp_serializer__context_t *context;
960 /* after quite a number of operations, let's re-pack everything.
961 * This is to limit the number of wasted space as we cannot overwrite
962 * existing data but must always append. */
963 if (dir_data->operations > 2 + dir_data->count / 4)
964 return slowly_replace_dir_entry(data, data_len, baton, pool);
966 /* resolve the reference to the entries array */
967 entries = (svn_fs_dirent_t **)
968 svn_temp_deserializer__ptr(dir_data,
969 (const void *const *)&dir_data->entries);
971 /* resolve the reference to the lengths array */
972 lengths = (apr_uint32_t *)
973 svn_temp_deserializer__ptr(dir_data,
974 (const void *const *)&dir_data->lengths);
976 /* binary search for the desired entry by name */
977 pos = find_entry(entries, replace_baton->name, dir_data->count, &found);
979 /* handle entry removal (if found at all) */
980 if (replace_baton->new_entry == NULL)
984 /* remove reference to the entry from the index */
985 memmove(&entries[pos],
987 sizeof(entries[pos]) * (dir_data->count - pos));
988 memmove(&lengths[pos],
990 sizeof(lengths[pos]) * (dir_data->count - pos));
993 dir_data->over_provision++;
994 dir_data->operations++;
1000 /* if not found, prepare to insert the new entry */
1003 /* fallback to slow operation if there is no place left to insert an
1004 * new entry to index. That will automatically give add some spare
1005 * entries ("overprovision"). */
1006 if (dir_data->over_provision == 0)
1007 return slowly_replace_dir_entry(data, data_len, baton, pool);
1009 /* make entries[index] available for pointing to the new entry */
1010 memmove(&entries[pos + 1],
1012 sizeof(entries[pos]) * (dir_data->count - pos));
1013 memmove(&lengths[pos + 1],
1015 sizeof(lengths[pos]) * (dir_data->count - pos));
1018 dir_data->over_provision--;
1019 dir_data->operations++;
1022 /* de-serialize the new entry */
1023 entries[pos] = replace_baton->new_entry;
1024 context = svn_temp_serializer__init_append(dir_data,
1029 serialize_dir_entry(context, &entries[pos], &length);
1031 /* return the updated serialized data */
1032 SVN_ERR (return_serialized_dir_context(context,
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. */
1040 dir_data = (dir_data_t *)*data;
1041 lengths = (apr_uint32_t *)
1042 svn_temp_deserializer__ptr(dir_data,
1043 (const void *const *)&dir_data->lengths);
1044 lengths[pos] = length;
1046 return SVN_NO_ERROR;
1050 svn_fs_fs__serialize_rep_header(void **data,
1051 apr_size_t *data_len,
1055 svn_fs_fs__rep_header_t *copy = apr_palloc(pool, sizeof(*copy));
1056 *copy = *(svn_fs_fs__rep_header_t *)in;
1058 *data_len = sizeof(svn_fs_fs__rep_header_t);
1061 return SVN_NO_ERROR;
1065 svn_fs_fs__deserialize_rep_header(void **out,
1067 apr_size_t data_len,
1070 svn_fs_fs__rep_header_t *copy = apr_palloc(pool, sizeof(*copy));
1071 SVN_ERR_ASSERT(data_len == sizeof(*copy));
1073 *copy = *(svn_fs_fs__rep_header_t *)data;
1076 return SVN_NO_ERROR;
1079 /* Utility function to serialize change CHANGE_P in the given serialization
1083 serialize_change(svn_temp_serializer__context_t *context,
1084 change_t * const *change_p)
1086 const change_t * change = *change_p;
1090 /* serialize the change struct itself */
1091 svn_temp_serializer__push(context,
1092 (const void * const *)change_p,
1095 /* serialize sub-structures */
1096 svn_fs_fs__id_serialize(context, &change->info.node_rev_id);
1098 svn_temp_serializer__add_string(context, &change->path.data);
1099 svn_temp_serializer__add_string(context, &change->info.copyfrom_path);
1101 /* return to the caller's nesting level */
1102 svn_temp_serializer__pop(context);
1105 /* Utility function to serialize the CHANGE_P within the given
1106 * serialization CONTEXT.
1109 deserialize_change(void *buffer, change_t **change_p)
1113 /* fix-up of the pointer to the struct in question */
1114 svn_temp_deserializer__resolve(buffer, (void **)change_p);
1120 /* fix-up of sub-structures */
1121 svn_fs_fs__id_deserialize(change, (svn_fs_id_t **)&change->info.node_rev_id);
1123 svn_temp_deserializer__resolve(change, (void **)&change->path.data);
1124 svn_temp_deserializer__resolve(change, (void **)&change->info.copyfrom_path);
1127 /* Auxiliary structure representing the content of a change_t array.
1128 This structure is much easier to (de-)serialize than an APR array.
1130 typedef struct changes_data_t
1132 /* number of entries in the array */
1135 /* reference to the changes */
1140 svn_fs_fs__serialize_changes(void **data,
1141 apr_size_t *data_len,
1145 apr_array_header_t *array = in;
1146 changes_data_t changes;
1147 svn_temp_serializer__context_t *context;
1148 svn_stringbuf_t *serialized;
1151 /* initialize our auxiliary data structure and link it to the
1153 changes.count = array->nelts;
1154 changes.changes = (change_t **)array->elts;
1156 /* serialize it and all its elements */
1157 context = svn_temp_serializer__init(&changes,
1159 changes.count * 250,
1162 svn_temp_serializer__push(context,
1163 (const void * const *)&changes.changes,
1164 changes.count * sizeof(change_t*));
1166 for (i = 0; i < changes.count; ++i)
1167 serialize_change(context, &changes.changes[i]);
1169 svn_temp_serializer__pop(context);
1171 /* return the serialized result */
1172 serialized = svn_temp_serializer__get(context);
1174 *data = serialized->data;
1175 *data_len = serialized->len;
1177 return SVN_NO_ERROR;
1181 svn_fs_fs__deserialize_changes(void **out,
1183 apr_size_t data_len,
1187 changes_data_t *changes = (changes_data_t *)data;
1188 apr_array_header_t *array = apr_array_make(pool, 0, sizeof(change_t *));
1190 /* de-serialize our auxiliary data structure */
1191 svn_temp_deserializer__resolve(changes, (void**)&changes->changes);
1193 /* de-serialize each entry and add it to the array */
1194 for (i = 0; i < changes->count; ++i)
1195 deserialize_change(changes->changes,
1196 (change_t **)&changes->changes[i]);
1198 /* Use the changes buffer as the array's data buffer
1199 * (DATA remains valid for at least as long as POOL). */
1200 array->elts = (char *)changes->changes;
1201 array->nelts = changes->count;
1202 array->nalloc = changes->count;
1207 return SVN_NO_ERROR;
1210 /* Auxiliary structure representing the content of a svn_mergeinfo_t hash.
1211 This structure is much easier to (de-)serialize than an APR array.
1213 typedef struct mergeinfo_data_t
1215 /* number of paths in the hash */
1218 /* COUNT keys (paths) */
1221 /* COUNT keys lengths (strlen of path) */
1222 apr_ssize_t *key_lengths;
1224 /* COUNT entries, each giving the number of ranges for the key */
1227 /* all ranges in a single, concatenated buffer */
1228 svn_merge_range_t *ranges;
1232 svn_fs_fs__serialize_mergeinfo(void **data,
1233 apr_size_t *data_len,
1237 svn_mergeinfo_t mergeinfo = in;
1238 mergeinfo_data_t merges;
1239 svn_temp_serializer__context_t *context;
1240 svn_stringbuf_t *serialized;
1241 apr_hash_index_t *hi;
1244 apr_size_t range_count;
1246 /* initialize our auxiliary data structure */
1247 merges.count = apr_hash_count(mergeinfo);
1248 merges.keys = apr_palloc(pool, sizeof(*merges.keys) * merges.count);
1249 merges.key_lengths = apr_palloc(pool, sizeof(*merges.key_lengths) *
1251 merges.range_counts = apr_palloc(pool, sizeof(*merges.range_counts) *
1256 for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi), ++i)
1258 svn_rangelist_t *ranges;
1259 apr_hash_this(hi, (const void**)&merges.keys[i],
1260 &merges.key_lengths[i],
1262 merges.range_counts[i] = ranges->nelts;
1263 range_count += ranges->nelts;
1266 merges.ranges = apr_palloc(pool, sizeof(*merges.ranges) * range_count);
1269 for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
1271 svn_rangelist_t *ranges = apr_hash_this_val(hi);
1272 for (k = 0; k < ranges->nelts; ++k, ++i)
1273 merges.ranges[i] = *APR_ARRAY_IDX(ranges, k, svn_merge_range_t*);
1276 /* serialize it and all its elements */
1277 context = svn_temp_serializer__init(&merges,
1283 svn_temp_serializer__push(context,
1284 (const void * const *)&merges.keys,
1285 merges.count * sizeof(*merges.keys));
1287 for (i = 0; i < merges.count; ++i)
1288 svn_temp_serializer__add_string(context, &merges.keys[i]);
1290 svn_temp_serializer__pop(context);
1292 /* key lengths array */
1293 svn_temp_serializer__add_leaf(context,
1294 (const void * const *)&merges.key_lengths,
1295 merges.count * sizeof(*merges.key_lengths));
1297 /* range counts array */
1298 svn_temp_serializer__add_leaf(context,
1299 (const void * const *)&merges.range_counts,
1300 merges.count * sizeof(*merges.range_counts));
1303 svn_temp_serializer__add_leaf(context,
1304 (const void * const *)&merges.ranges,
1305 range_count * sizeof(*merges.ranges));
1307 /* return the serialized result */
1308 serialized = svn_temp_serializer__get(context);
1310 *data = serialized->data;
1311 *data_len = serialized->len;
1313 return SVN_NO_ERROR;
1317 svn_fs_fs__deserialize_mergeinfo(void **out,
1319 apr_size_t data_len,
1324 mergeinfo_data_t *merges = (mergeinfo_data_t *)data;
1325 svn_mergeinfo_t mergeinfo;
1327 /* de-serialize our auxiliary data structure */
1328 svn_temp_deserializer__resolve(merges, (void**)&merges->keys);
1329 svn_temp_deserializer__resolve(merges, (void**)&merges->key_lengths);
1330 svn_temp_deserializer__resolve(merges, (void**)&merges->range_counts);
1331 svn_temp_deserializer__resolve(merges, (void**)&merges->ranges);
1333 /* de-serialize keys and add entries to the result */
1335 mergeinfo = svn_hash__make(pool);
1336 for (i = 0; i < merges->count; ++i)
1338 svn_rangelist_t *ranges = apr_array_make(pool,
1339 merges->range_counts[i],
1340 sizeof(svn_merge_range_t*));
1341 for (k = 0; k < merges->range_counts[i]; ++k, ++n)
1342 APR_ARRAY_PUSH(ranges, svn_merge_range_t*) = &merges->ranges[n];
1344 svn_temp_deserializer__resolve(merges->keys,
1345 (void**)&merges->keys[i]);
1346 apr_hash_set(mergeinfo, merges->keys[i], merges->key_lengths[i], ranges);
1352 return SVN_NO_ERROR;