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, (const void * const *)s, sizeof(**s));
108 /* the "string" content may actually be arbitrary binary data.
109 * Thus, we cannot use svn_temp_serializer__add_string. */
110 svn_temp_serializer__add_leaf(context,
111 (const void * const *)&string->data,
114 /* back to the caller's nesting level */
115 svn_temp_serializer__pop(context);
118 /* Utility function to deserialize the STRING inside the BUFFER.
121 deserialize_svn_string(void *buffer, svn_string_t **string)
123 svn_temp_deserializer__resolve(buffer, (void **)string);
127 svn_temp_deserializer__resolve(*string, (void **)&(*string)->data);
130 /* Utility function to serialize the REPRESENTATION within the given
131 * serialization CONTEXT.
134 serialize_representation(svn_temp_serializer__context_t *context,
135 representation_t * const *representation)
137 const representation_t * rep = *representation;
141 /* serialize the representation struct itself */
142 svn_temp_serializer__add_leaf(context,
143 (const void * const *)representation,
144 sizeof(**representation));
147 /* auxiliary structure representing the content of a directory array */
148 typedef struct dir_data_t
150 /* number of entries in the directory
151 * (it's int because the directory is an APR array) */
154 /** Current length of the in-txn in-disk representation of the directory.
155 * SVN_INVALID_FILESIZE if unknown (i.e. committed data). */
156 svn_filesize_t txn_filesize;
158 /* number of unused dir entry buckets in the index */
159 apr_size_t over_provision;
161 /* internal modifying operations counter
162 * (used to repack data once in a while) */
163 apr_size_t operations;
165 /* size of the serialization buffer actually used.
166 * (we will allocate more than we actually need such that we may
167 * append more data in situ later) */
170 /* reference to the entries */
171 svn_fs_dirent_t **entries;
173 /* size of the serialized entries and don't be too wasteful
174 * (needed since the entries are no longer in sequence) */
175 apr_uint32_t *lengths;
178 /* Utility function to serialize the *ENTRY_P into a the given
179 * serialization CONTEXT. Return the serialized size of the
180 * dir entry in *LENGTH.
183 serialize_dir_entry(svn_temp_serializer__context_t *context,
184 svn_fs_dirent_t **entry_p,
185 apr_uint32_t *length)
187 svn_fs_dirent_t *entry = *entry_p;
188 apr_size_t initial_length = svn_temp_serializer__get_length(context);
190 svn_temp_serializer__push(context,
191 (const void * const *)entry_p,
194 svn_fs_fs__id_serialize(context, &entry->id);
195 svn_temp_serializer__add_string(context, &entry->name);
197 *length = (apr_uint32_t)( svn_temp_serializer__get_length(context)
198 - APR_ALIGN_DEFAULT(initial_length));
200 svn_temp_serializer__pop(context);
203 /* Utility function to serialize the DIR into a new serialization
204 * context to be returned. Allocation will be made form POOL.
206 static svn_temp_serializer__context_t *
207 serialize_dir(svn_fs_fs__dir_data_t *dir, apr_pool_t *pool)
211 svn_temp_serializer__context_t *context;
212 apr_array_header_t *entries = dir->entries;
214 /* calculate sizes */
215 int count = entries->nelts;
216 apr_size_t over_provision = 2 + count / 4;
217 apr_size_t total_count = count + over_provision;
218 apr_size_t entries_len = total_count * sizeof(*dir_data.entries);
219 apr_size_t lengths_len = total_count * sizeof(*dir_data.lengths);
221 /* copy the hash entries to an auxiliary struct of known layout */
222 dir_data.count = count;
223 dir_data.txn_filesize = dir->txn_filesize;
224 dir_data.over_provision = over_provision;
225 dir_data.operations = 0;
226 dir_data.entries = apr_palloc(pool, entries_len);
227 dir_data.lengths = apr_palloc(pool, lengths_len);
229 for (i = 0; i < count; ++i)
230 dir_data.entries[i] = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *);
232 /* Serialize that aux. structure into a new one. Also, provide a good
233 * estimate for the size of the buffer that we will need. */
234 context = svn_temp_serializer__init(&dir_data,
236 50 + count * 200 + entries_len,
239 /* serialize entries references */
240 svn_temp_serializer__push(context,
241 (const void * const *)&dir_data.entries,
244 /* serialize the individual entries and their sub-structures */
245 for (i = 0; i < count; ++i)
246 serialize_dir_entry(context,
247 &dir_data.entries[i],
248 &dir_data.lengths[i]);
250 svn_temp_serializer__pop(context);
252 /* serialize entries references */
253 svn_temp_serializer__push(context,
254 (const void * const *)&dir_data.lengths,
260 /* Utility function to reconstruct a dir entries struct from serialized data
261 * in BUFFER and DIR_DATA. Allocation will be made form POOL.
263 static svn_fs_fs__dir_data_t *
264 deserialize_dir(void *buffer, dir_data_t *dir_data, apr_pool_t *pool)
266 svn_fs_fs__dir_data_t *result;
269 svn_fs_dirent_t *entry;
270 svn_fs_dirent_t **entries;
272 /* Construct empty directory object. */
273 result = apr_pcalloc(pool, sizeof(*result));
275 = apr_array_make(pool, dir_data->count, sizeof(svn_fs_dirent_t *));
276 result->txn_filesize = dir_data->txn_filesize;
278 /* resolve the reference to the entries array */
279 svn_temp_deserializer__resolve(buffer, (void **)&dir_data->entries);
280 entries = dir_data->entries;
282 /* fixup the references within each entry and add it to the RESULT */
283 for (i = 0, count = dir_data->count; i < count; ++i)
285 svn_temp_deserializer__resolve(entries, (void **)&entries[i]);
286 entry = dir_data->entries[i];
289 svn_temp_deserializer__resolve(entry, (void **)&entry->name);
290 svn_fs_fs__id_deserialize(entry, (svn_fs_id_t **)&entry->id);
292 /* add the entry to the hash */
293 APR_ARRAY_PUSH(result->entries, svn_fs_dirent_t *) = entry;
296 /* return the now complete hash */
301 svn_fs_fs__noderev_serialize(svn_temp_serializer__context_t *context,
302 node_revision_t * const *noderev_p)
304 const node_revision_t *noderev = *noderev_p;
308 /* serialize the representation struct itself */
309 svn_temp_serializer__push(context,
310 (const void * const *)noderev_p,
313 /* serialize sub-structures */
314 svn_fs_fs__id_serialize(context, &noderev->id);
315 svn_fs_fs__id_serialize(context, &noderev->predecessor_id);
316 serialize_representation(context, &noderev->prop_rep);
317 serialize_representation(context, &noderev->data_rep);
319 svn_temp_serializer__add_string(context, &noderev->copyfrom_path);
320 svn_temp_serializer__add_string(context, &noderev->copyroot_path);
321 svn_temp_serializer__add_string(context, &noderev->created_path);
323 /* return to the caller's nesting level */
324 svn_temp_serializer__pop(context);
329 svn_fs_fs__noderev_deserialize(void *buffer,
330 node_revision_t **noderev_p)
332 node_revision_t *noderev;
334 /* fixup the reference to the representation itself,
335 * if this is part of a parent structure. */
336 if (buffer != *noderev_p)
337 svn_temp_deserializer__resolve(buffer, (void **)noderev_p);
339 noderev = *noderev_p;
343 /* fixup of sub-structures */
344 svn_fs_fs__id_deserialize(noderev, (svn_fs_id_t **)&noderev->id);
345 svn_fs_fs__id_deserialize(noderev, (svn_fs_id_t **)&noderev->predecessor_id);
346 svn_temp_deserializer__resolve(noderev, (void **)&noderev->prop_rep);
347 svn_temp_deserializer__resolve(noderev, (void **)&noderev->data_rep);
349 svn_temp_deserializer__resolve(noderev, (void **)&noderev->copyfrom_path);
350 svn_temp_deserializer__resolve(noderev, (void **)&noderev->copyroot_path);
351 svn_temp_deserializer__resolve(noderev, (void **)&noderev->created_path);
355 svn_fs_fs__serialize_raw_window(void **buffer,
356 apr_size_t *buffer_size,
360 svn_fs_fs__raw_cached_window_t *window = item;
361 svn_stringbuf_t *serialized;
363 /* initialize the serialization process and allocate a buffer large
364 * enough to do prevent re-allocations. */
365 svn_temp_serializer__context_t *context =
366 svn_temp_serializer__init(window,
368 sizeof(*window) + window->window.len + 16,
371 /* serialize the sub-structure(s) */
372 svn_temp_serializer__add_leaf(context,
373 (const void * const *)&window->window.data,
374 window->window.len + 1);
376 /* return the serialized result */
377 serialized = svn_temp_serializer__get(context);
379 *buffer = serialized->data;
380 *buffer_size = serialized->len;
386 svn_fs_fs__deserialize_raw_window(void **item,
388 apr_size_t buffer_size,
391 svn_fs_fs__raw_cached_window_t *window =
392 (svn_fs_fs__raw_cached_window_t *)buffer;
394 /* pointer reference fixup */
395 svn_temp_deserializer__resolve(window, (void **)&window->window.data);
404 /* Utility function to serialize COUNT svn_txdelta_op_t objects
405 * at OPS in the given serialization CONTEXT.
408 serialize_txdelta_ops(svn_temp_serializer__context_t *context,
409 const svn_txdelta_op_t * const * ops,
415 /* the ops form a contiguous chunk of memory with no further references */
416 svn_temp_serializer__add_leaf(context,
417 (const void * const *)ops,
418 count * sizeof(**ops));
421 /* Utility function to serialize W in the given serialization CONTEXT.
424 serialize_txdeltawindow(svn_temp_serializer__context_t *context,
425 svn_txdelta_window_t * const * w)
427 svn_txdelta_window_t *window = *w;
429 /* serialize the window struct itself */
430 svn_temp_serializer__push(context, (const void * const *)w, sizeof(**w));
432 /* serialize its sub-structures */
433 serialize_txdelta_ops(context, &window->ops, window->num_ops);
434 serialize_svn_string(context, &window->new_data);
436 svn_temp_serializer__pop(context);
440 svn_fs_fs__serialize_txdelta_window(void **buffer,
441 apr_size_t *buffer_size,
445 svn_fs_fs__txdelta_cached_window_t *window_info = item;
446 svn_stringbuf_t *serialized;
448 /* initialize the serialization process and allocate a buffer large
449 * enough to do without the need of re-allocations in most cases. */
450 apr_size_t text_len = window_info->window->new_data
451 ? window_info->window->new_data->len
453 svn_temp_serializer__context_t *context =
454 svn_temp_serializer__init(window_info,
455 sizeof(*window_info),
459 /* serialize the sub-structure(s) */
460 serialize_txdeltawindow(context, &window_info->window);
462 /* return the serialized result */
463 serialized = svn_temp_serializer__get(context);
465 *buffer = serialized->data;
466 *buffer_size = serialized->len;
472 svn_fs_fs__deserialize_txdelta_window(void **item,
474 apr_size_t buffer_size,
477 svn_txdelta_window_t *window;
479 /* Copy the _full_ buffer as it also contains the sub-structures. */
480 svn_fs_fs__txdelta_cached_window_t *window_info =
481 (svn_fs_fs__txdelta_cached_window_t *)buffer;
483 /* pointer reference fixup */
484 svn_temp_deserializer__resolve(window_info,
485 (void **)&window_info->window);
486 window = window_info->window;
488 svn_temp_deserializer__resolve(window, (void **)&window->ops);
490 deserialize_svn_string(window, (svn_string_t**)&window->new_data);
499 svn_fs_fs__serialize_manifest(void **data,
500 apr_size_t *data_len,
504 apr_array_header_t *manifest = in;
506 *data_len = sizeof(apr_off_t) *manifest->nelts;
507 *data = apr_pmemdup(pool, manifest->elts, *data_len);
513 svn_fs_fs__deserialize_manifest(void **out,
518 apr_array_header_t *manifest = apr_array_make(pool, 1, sizeof(apr_off_t));
520 manifest->nelts = (int) (data_len / sizeof(apr_off_t));
521 manifest->nalloc = (int) (data_len / sizeof(apr_off_t));
522 manifest->elts = (char*)data;
529 /* Auxiliary structure representing the content of a properties hash.
530 This structure is much easier to (de-)serialize than an apr_hash.
532 typedef struct properties_data_t
534 /* number of entries in the hash */
537 /* reference to the keys */
540 /* reference to the values */
541 const svn_string_t **values;
544 /* Serialize COUNT C-style strings from *STRINGS into CONTEXT. */
546 serialize_cstring_array(svn_temp_serializer__context_t *context,
547 const char ***strings,
551 const char **entries = *strings;
553 /* serialize COUNT entries pointers (the array) */
554 svn_temp_serializer__push(context,
555 (const void * const *)strings,
556 count * sizeof(const char*));
558 /* serialize array elements */
559 for (i = 0; i < count; ++i)
560 svn_temp_serializer__add_string(context, &entries[i]);
562 svn_temp_serializer__pop(context);
565 /* Serialize COUNT svn_string_t* items from *STRINGS into CONTEXT. */
567 serialize_svn_string_array(svn_temp_serializer__context_t *context,
568 const svn_string_t ***strings,
572 const svn_string_t **entries = *strings;
574 /* serialize COUNT entries pointers (the array) */
575 svn_temp_serializer__push(context,
576 (const void * const *)strings,
577 count * sizeof(const char*));
579 /* serialize array elements */
580 for (i = 0; i < count; ++i)
581 serialize_svn_string(context, &entries[i]);
583 svn_temp_serializer__pop(context);
587 svn_fs_fs__serialize_properties(void **data,
588 apr_size_t *data_len,
592 apr_hash_t *hash = in;
593 properties_data_t properties;
594 svn_temp_serializer__context_t *context;
595 apr_hash_index_t *hi;
596 svn_stringbuf_t *serialized;
599 /* create our auxiliary data structure */
600 properties.count = apr_hash_count(hash);
601 properties.keys = apr_palloc(pool, sizeof(const char*) * (properties.count + 1));
602 properties.values = apr_palloc(pool, sizeof(const svn_string_t *) * properties.count);
604 /* populate it with the hash entries */
605 for (hi = apr_hash_first(pool, hash), i=0; hi; hi = apr_hash_next(hi), ++i)
607 properties.keys[i] = apr_hash_this_key(hi);
608 properties.values[i] = apr_hash_this_val(hi);
612 context = svn_temp_serializer__init(&properties,
614 properties.count * 100,
617 properties.keys[i] = "";
618 serialize_cstring_array(context, &properties.keys, properties.count + 1);
619 serialize_svn_string_array(context, &properties.values, properties.count);
621 /* return the serialized result */
622 serialized = svn_temp_serializer__get(context);
624 *data = serialized->data;
625 *data_len = serialized->len;
631 svn_fs_fs__deserialize_properties(void **out,
636 apr_hash_t *hash = svn_hash__make(pool);
637 properties_data_t *properties = (properties_data_t *)data;
640 /* de-serialize our auxiliary data structure */
641 svn_temp_deserializer__resolve(properties, (void**)&properties->keys);
642 svn_temp_deserializer__resolve(properties, (void**)&properties->values);
644 /* de-serialize each entry and put it into the hash */
645 for (i = 0; i < properties->count; ++i)
647 apr_size_t len = properties->keys[i+1] - properties->keys[i] - 1;
648 svn_temp_deserializer__resolve(properties->keys,
649 (void**)&properties->keys[i]);
651 deserialize_svn_string(properties->values,
652 (svn_string_t **)&properties->values[i]);
655 properties->keys[i], len,
656 properties->values[i]);
666 svn_fs_fs__serialize_revprops(void **data,
667 apr_size_t *data_len,
671 svn_string_t *buffer = in;
673 *data = (void *)buffer->data;
674 *data_len = buffer->len;
680 svn_fs_fs__deserialize_revprops(void **out,
685 apr_hash_t *properties;
686 svn_stream_t *stream;
690 buffer.len = data_len;
692 stream = svn_stream_from_string(&buffer, pool);
693 properties = svn_hash__make(pool);
695 SVN_ERR(svn_hash_read2(properties, stream, SVN_HASH_TERMINATOR, pool));
704 svn_fs_fs__serialize_id(void **data,
705 apr_size_t *data_len,
709 const svn_fs_id_t *id = in;
710 svn_stringbuf_t *serialized;
712 /* create an (empty) serialization context with plenty of buffer space */
713 svn_temp_serializer__context_t *context =
714 svn_temp_serializer__init(NULL, 0, 250, pool);
716 /* serialize the id */
717 svn_fs_fs__id_serialize(context, &id);
719 /* return serialized data */
720 serialized = svn_temp_serializer__get(context);
721 *data = serialized->data;
722 *data_len = serialized->len;
728 svn_fs_fs__deserialize_id(void **out,
733 /* Copy the _full_ buffer as it also contains the sub-structures. */
734 svn_fs_id_t *id = (svn_fs_id_t *)data;
736 /* fixup of all pointers etc. */
737 svn_fs_fs__id_deserialize(id, &id);
744 /** Caching node_revision_t objects. **/
747 svn_fs_fs__serialize_node_revision(void **buffer,
748 apr_size_t *buffer_size,
752 svn_stringbuf_t *serialized;
753 node_revision_t *noderev = item;
755 /* create an (empty) serialization context with plenty of (initial)
757 svn_temp_serializer__context_t *context =
758 svn_temp_serializer__init(NULL, 0,
759 1024 - SVN_TEMP_SERIALIZER__OVERHEAD,
762 /* serialize the noderev */
763 svn_fs_fs__noderev_serialize(context, &noderev);
765 /* return serialized data */
766 serialized = svn_temp_serializer__get(context);
767 *buffer = serialized->data;
768 *buffer_size = serialized->len;
774 svn_fs_fs__deserialize_node_revision(void **item,
776 apr_size_t buffer_size,
779 /* Copy the _full_ buffer as it also contains the sub-structures. */
780 node_revision_t *noderev = (node_revision_t *)buffer;
782 /* fixup of all pointers etc. */
783 svn_fs_fs__noderev_deserialize(noderev, &noderev);
790 /* Utility function that returns the directory serialized inside CONTEXT
791 * to DATA and DATA_LEN. If OVERPROVISION is set, allocate some extra
792 * room for future in-place changes by svn_fs_fs__replace_dir_entry. */
794 return_serialized_dir_context(svn_temp_serializer__context_t *context,
796 apr_size_t *data_len,
797 svn_boolean_t overprovision)
799 svn_stringbuf_t *serialized = svn_temp_serializer__get(context);
801 *data = serialized->data;
802 *data_len = overprovision ? serialized->blocksize : serialized->len;
803 ((dir_data_t *)serialized->data)->len = serialized->len;
809 svn_fs_fs__serialize_dir_entries(void **data,
810 apr_size_t *data_len,
814 svn_fs_fs__dir_data_t *dir = in;
816 /* serialize the dir content into a new serialization context
817 * and return the serialized data */
818 return return_serialized_dir_context(serialize_dir(dir, pool),
825 svn_fs_fs__serialize_txndir_entries(void **data,
826 apr_size_t *data_len,
830 svn_fs_fs__dir_data_t *dir = in;
832 /* serialize the dir content into a new serialization context
833 * and return the serialized data */
834 return return_serialized_dir_context(serialize_dir(dir, pool),
841 svn_fs_fs__deserialize_dir_entries(void **out,
846 /* Copy the _full_ buffer as it also contains the sub-structures. */
847 dir_data_t *dir_data = (dir_data_t *)data;
849 /* reconstruct the hash from the serialized data */
850 *out = deserialize_dir(dir_data, dir_data, pool);
856 svn_fs_fs__get_sharded_offset(void **out,
862 const apr_off_t *manifest = data;
863 apr_int64_t shard_pos = *(apr_int64_t *)baton;
865 *(apr_off_t *)out = manifest[shard_pos];
871 svn_fs_fs__extract_dir_filesize(void **out,
877 const dir_data_t *dir_data = data;
879 *(svn_filesize_t *)out = dir_data->txn_filesize;
884 /* Utility function that returns the lowest index of the first entry in
885 * *ENTRIES that points to a dir entry with a name equal or larger than NAME.
886 * If an exact match has been found, *FOUND will be set to TRUE. COUNT is
887 * the number of valid entries in ENTRIES.
890 find_entry(svn_fs_dirent_t **entries,
893 svn_boolean_t *found)
895 /* binary search for the desired entry by name */
896 apr_size_t lower = 0;
897 apr_size_t upper = count;
900 for (middle = upper / 2; lower < upper; middle = (upper + lower) / 2)
902 const svn_fs_dirent_t *entry =
903 svn_temp_deserializer__ptr(entries, (const void *const *)&entries[middle]);
904 const char* entry_name =
905 svn_temp_deserializer__ptr(entry, (const void *const *)&entry->name);
907 int diff = strcmp(entry_name, name);
914 /* check whether we actually found a match */
918 const svn_fs_dirent_t *entry =
919 svn_temp_deserializer__ptr(entries, (const void *const *)&entries[lower]);
920 const char* entry_name =
921 svn_temp_deserializer__ptr(entry, (const void *const *)&entry->name);
923 if (strcmp(entry_name, name) == 0)
931 svn_fs_fs__extract_dir_entry(void **out,
937 const dir_data_t *dir_data = data;
938 extract_dir_entry_baton_t *entry_baton = baton;
941 /* resolve the reference to the entries array */
942 const svn_fs_dirent_t * const *entries =
943 svn_temp_deserializer__ptr(data, (const void *const *)&dir_data->entries);
945 /* resolve the reference to the lengths array */
946 const apr_uint32_t *lengths =
947 svn_temp_deserializer__ptr(data, (const void *const *)&dir_data->lengths);
949 /* binary search for the desired entry by name */
950 apr_size_t pos = find_entry((svn_fs_dirent_t **)entries,
955 /* de-serialize that entry or return NULL, if no match has been found.
956 * Be sure to check that the directory contents is still up-to-date. */
957 entry_baton->out_of_date
958 = dir_data->txn_filesize != entry_baton->txn_filesize;
961 if (found && !entry_baton->out_of_date)
963 const svn_fs_dirent_t *source =
964 svn_temp_deserializer__ptr(entries, (const void *const *)&entries[pos]);
966 /* Entries have been serialized one-by-one, each time including all
967 * nested structures and strings. Therefore, they occupy a single
968 * block of memory whose end-offset is either the beginning of the
969 * next entry or the end of the buffer
971 apr_size_t size = lengths[pos];
973 /* copy & deserialize the entry */
974 svn_fs_dirent_t *new_entry = apr_pmemdup(pool, source, size);
976 svn_temp_deserializer__resolve(new_entry, (void **)&new_entry->name);
977 svn_fs_fs__id_deserialize(new_entry, (svn_fs_id_t **)&new_entry->id);
978 *(svn_fs_dirent_t **)out = new_entry;
984 /* Utility function for svn_fs_fs__replace_dir_entry that implements the
985 * modification as a simply deserialize / modify / serialize sequence.
988 slowly_replace_dir_entry(void **data,
989 apr_size_t *data_len,
993 replace_baton_t *replace_baton = (replace_baton_t *)baton;
994 dir_data_t *dir_data = (dir_data_t *)*data;
995 svn_fs_fs__dir_data_t *dir;
997 svn_fs_dirent_t *entry;
998 apr_array_header_t *entries;
1000 SVN_ERR(svn_fs_fs__deserialize_dir_entries((void **)&dir,
1005 entries = dir->entries;
1006 entry = svn_fs_fs__find_dir_entry(entries, replace_baton->name, &idx);
1008 /* Replacement or removal? */
1009 if (replace_baton->new_entry)
1011 /* Replace ENTRY with / insert the NEW_ENTRY */
1013 APR_ARRAY_IDX(entries, idx, svn_fs_dirent_t *)
1014 = replace_baton->new_entry;
1016 svn_sort__array_insert(entries, &replace_baton->new_entry, idx);
1020 /* Remove the old ENTRY. */
1022 svn_sort__array_delete(entries, idx, 1);
1025 return svn_fs_fs__serialize_dir_entries(data, data_len, dir, pool);
1029 svn_fs_fs__replace_dir_entry(void **data,
1030 apr_size_t *data_len,
1034 replace_baton_t *replace_baton = (replace_baton_t *)baton;
1035 dir_data_t *dir_data = (dir_data_t *)*data;
1036 svn_boolean_t found;
1037 svn_fs_dirent_t **entries;
1038 apr_uint32_t *lengths;
1039 apr_uint32_t length;
1042 svn_temp_serializer__context_t *context;
1044 /* update the cached file length info.
1045 * Because we are writing to the cache, it is fair to assume that the
1046 * caller made sure that the current contents is consistent with the
1047 * previous state of the directory file. */
1048 dir_data->txn_filesize = replace_baton->txn_filesize;
1050 /* after quite a number of operations, let's re-pack everything.
1051 * This is to limit the number of wasted space as we cannot overwrite
1052 * existing data but must always append. */
1053 if (dir_data->operations > 2 + dir_data->count / 4)
1054 return slowly_replace_dir_entry(data, data_len, baton, pool);
1056 /* resolve the reference to the entries array */
1057 entries = (svn_fs_dirent_t **)
1058 svn_temp_deserializer__ptr(dir_data,
1059 (const void *const *)&dir_data->entries);
1061 /* resolve the reference to the lengths array */
1062 lengths = (apr_uint32_t *)
1063 svn_temp_deserializer__ptr(dir_data,
1064 (const void *const *)&dir_data->lengths);
1066 /* binary search for the desired entry by name */
1067 pos = find_entry(entries, replace_baton->name, dir_data->count, &found);
1069 /* handle entry removal (if found at all) */
1070 if (replace_baton->new_entry == NULL)
1074 /* remove reference to the entry from the index */
1075 memmove(&entries[pos],
1077 sizeof(entries[pos]) * (dir_data->count - pos));
1078 memmove(&lengths[pos],
1080 sizeof(lengths[pos]) * (dir_data->count - pos));
1083 dir_data->over_provision++;
1084 dir_data->operations++;
1087 return SVN_NO_ERROR;
1090 /* if not found, prepare to insert the new entry */
1093 /* fallback to slow operation if there is no place left to insert an
1094 * new entry to index. That will automatically give add some spare
1095 * entries ("overprovision"). */
1096 if (dir_data->over_provision == 0)
1097 return slowly_replace_dir_entry(data, data_len, baton, pool);
1099 /* make entries[index] available for pointing to the new entry */
1100 memmove(&entries[pos + 1],
1102 sizeof(entries[pos]) * (dir_data->count - pos));
1103 memmove(&lengths[pos + 1],
1105 sizeof(lengths[pos]) * (dir_data->count - pos));
1108 dir_data->over_provision--;
1109 dir_data->operations++;
1112 /* de-serialize the new entry */
1113 entries[pos] = replace_baton->new_entry;
1114 context = svn_temp_serializer__init_append(dir_data,
1119 serialize_dir_entry(context, &entries[pos], &length);
1121 /* return the updated serialized data */
1122 SVN_ERR(return_serialized_dir_context(context, data, data_len, TRUE));
1124 /* since the previous call may have re-allocated the buffer, the lengths
1125 * pointer may no longer point to the entry in that buffer. Therefore,
1126 * re-map it again and store the length value after that. */
1128 dir_data = (dir_data_t *)*data;
1129 lengths = (apr_uint32_t *)
1130 svn_temp_deserializer__ptr(dir_data,
1131 (const void *const *)&dir_data->lengths);
1132 lengths[pos] = length;
1134 return SVN_NO_ERROR;
1138 svn_fs_fs__reset_txn_filesize(void **data,
1139 apr_size_t *data_len,
1143 dir_data_t *dir_data = (dir_data_t *)*data;
1144 dir_data->txn_filesize = SVN_INVALID_FILESIZE;
1146 return SVN_NO_ERROR;
1150 svn_fs_fs__serialize_rep_header(void **data,
1151 apr_size_t *data_len,
1155 svn_fs_fs__rep_header_t *copy = apr_palloc(pool, sizeof(*copy));
1156 *copy = *(svn_fs_fs__rep_header_t *)in;
1158 *data_len = sizeof(*copy);
1161 return SVN_NO_ERROR;
1165 svn_fs_fs__deserialize_rep_header(void **out,
1167 apr_size_t data_len,
1170 svn_fs_fs__rep_header_t *copy = apr_palloc(pool, sizeof(*copy));
1171 SVN_ERR_ASSERT(data_len == sizeof(*copy));
1173 *copy = *(svn_fs_fs__rep_header_t *)data;
1176 return SVN_NO_ERROR;
1179 /* Utility function to serialize change CHANGE_P in the given serialization
1183 serialize_change(svn_temp_serializer__context_t *context,
1184 change_t * const *change_p)
1186 const change_t * change = *change_p;
1190 /* serialize the change struct itself */
1191 svn_temp_serializer__push(context,
1192 (const void * const *)change_p,
1195 /* serialize sub-structures */
1196 svn_fs_fs__id_serialize(context, &change->info.node_rev_id);
1198 svn_temp_serializer__add_string(context, &change->path.data);
1199 svn_temp_serializer__add_string(context, &change->info.copyfrom_path);
1201 /* return to the caller's nesting level */
1202 svn_temp_serializer__pop(context);
1205 /* Utility function to serialize the CHANGE_P within the given
1206 * serialization CONTEXT.
1209 deserialize_change(void *buffer, change_t **change_p)
1213 /* fix-up of the pointer to the struct in question */
1214 svn_temp_deserializer__resolve(buffer, (void **)change_p);
1220 /* fix-up of sub-structures */
1221 svn_fs_fs__id_deserialize(change, (svn_fs_id_t **)&change->info.node_rev_id);
1223 svn_temp_deserializer__resolve(change, (void **)&change->path.data);
1224 svn_temp_deserializer__resolve(change, (void **)&change->info.copyfrom_path);
1228 svn_fs_fs__serialize_changes(void **data,
1229 apr_size_t *data_len,
1233 svn_fs_fs__changes_list_t *changes = in;
1234 svn_temp_serializer__context_t *context;
1235 svn_stringbuf_t *serialized;
1238 /* serialize it and all its elements */
1239 context = svn_temp_serializer__init(changes,
1241 changes->count * 250,
1244 svn_temp_serializer__push(context,
1245 (const void * const *)&changes->changes,
1246 changes->count * sizeof(*changes->changes));
1248 for (i = 0; i < changes->count; ++i)
1249 serialize_change(context, &changes->changes[i]);
1251 svn_temp_serializer__pop(context);
1253 /* return the serialized result */
1254 serialized = svn_temp_serializer__get(context);
1256 *data = serialized->data;
1257 *data_len = serialized->len;
1259 return SVN_NO_ERROR;
1263 svn_fs_fs__deserialize_changes(void **out,
1265 apr_size_t data_len,
1269 svn_fs_fs__changes_list_t *changes = (svn_fs_fs__changes_list_t *)data;
1271 /* de-serialize our auxiliary data structure */
1272 svn_temp_deserializer__resolve(changes, (void**)&changes->changes);
1274 /* de-serialize each entry and add it to the array */
1275 for (i = 0; i < changes->count; ++i)
1276 deserialize_change(changes->changes,
1277 (change_t **)&changes->changes[i]);
1282 return SVN_NO_ERROR;
1285 /* Auxiliary structure representing the content of a svn_mergeinfo_t hash.
1286 This structure is much easier to (de-)serialize than an APR array.
1288 typedef struct mergeinfo_data_t
1290 /* number of paths in the hash */
1293 /* COUNT keys (paths) */
1296 /* COUNT keys lengths (strlen of path) */
1297 apr_ssize_t *key_lengths;
1299 /* COUNT entries, each giving the number of ranges for the key */
1302 /* all ranges in a single, concatenated buffer */
1303 svn_merge_range_t *ranges;
1307 svn_fs_fs__serialize_mergeinfo(void **data,
1308 apr_size_t *data_len,
1312 svn_mergeinfo_t mergeinfo = in;
1313 mergeinfo_data_t merges;
1314 svn_temp_serializer__context_t *context;
1315 svn_stringbuf_t *serialized;
1316 apr_hash_index_t *hi;
1319 apr_size_t range_count;
1321 /* initialize our auxiliary data structure */
1322 merges.count = apr_hash_count(mergeinfo);
1323 merges.keys = apr_palloc(pool, sizeof(*merges.keys) * merges.count);
1324 merges.key_lengths = apr_palloc(pool, sizeof(*merges.key_lengths) *
1326 merges.range_counts = apr_palloc(pool, sizeof(*merges.range_counts) *
1331 for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi), ++i)
1333 svn_rangelist_t *ranges;
1334 apr_hash_this(hi, (const void**)&merges.keys[i],
1335 &merges.key_lengths[i],
1337 merges.range_counts[i] = ranges->nelts;
1338 range_count += ranges->nelts;
1341 merges.ranges = apr_palloc(pool, sizeof(*merges.ranges) * range_count);
1344 for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
1346 svn_rangelist_t *ranges = apr_hash_this_val(hi);
1347 for (k = 0; k < ranges->nelts; ++k, ++i)
1348 merges.ranges[i] = *APR_ARRAY_IDX(ranges, k, svn_merge_range_t*);
1351 /* serialize it and all its elements */
1352 context = svn_temp_serializer__init(&merges,
1358 svn_temp_serializer__push(context,
1359 (const void * const *)&merges.keys,
1360 merges.count * sizeof(*merges.keys));
1362 for (i = 0; i < merges.count; ++i)
1363 svn_temp_serializer__add_string(context, &merges.keys[i]);
1365 svn_temp_serializer__pop(context);
1367 /* key lengths array */
1368 svn_temp_serializer__add_leaf(context,
1369 (const void * const *)&merges.key_lengths,
1370 merges.count * sizeof(*merges.key_lengths));
1372 /* range counts array */
1373 svn_temp_serializer__add_leaf(context,
1374 (const void * const *)&merges.range_counts,
1375 merges.count * sizeof(*merges.range_counts));
1378 svn_temp_serializer__add_leaf(context,
1379 (const void * const *)&merges.ranges,
1380 range_count * sizeof(*merges.ranges));
1382 /* return the serialized result */
1383 serialized = svn_temp_serializer__get(context);
1385 *data = serialized->data;
1386 *data_len = serialized->len;
1388 return SVN_NO_ERROR;
1392 svn_fs_fs__deserialize_mergeinfo(void **out,
1394 apr_size_t data_len,
1399 mergeinfo_data_t *merges = (mergeinfo_data_t *)data;
1400 svn_mergeinfo_t mergeinfo;
1402 /* de-serialize our auxiliary data structure */
1403 svn_temp_deserializer__resolve(merges, (void**)&merges->keys);
1404 svn_temp_deserializer__resolve(merges, (void**)&merges->key_lengths);
1405 svn_temp_deserializer__resolve(merges, (void**)&merges->range_counts);
1406 svn_temp_deserializer__resolve(merges, (void**)&merges->ranges);
1408 /* de-serialize keys and add entries to the result */
1410 mergeinfo = svn_hash__make(pool);
1411 for (i = 0; i < merges->count; ++i)
1413 svn_rangelist_t *ranges = apr_array_make(pool,
1414 merges->range_counts[i],
1415 sizeof(svn_merge_range_t*));
1416 for (k = 0; k < merges->range_counts[i]; ++k, ++n)
1417 APR_ARRAY_PUSH(ranges, svn_merge_range_t*) = &merges->ranges[n];
1419 svn_temp_deserializer__resolve(merges->keys,
1420 (void**)&merges->keys[i]);
1421 apr_hash_set(mergeinfo, merges->keys[i], merges->key_lengths[i], ranges);
1427 return SVN_NO_ERROR;