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"
31 #include "private/svn_fs_util.h"
32 #include "private/svn_temp_serializer.h"
33 #include "private/svn_subr_private.h"
35 #include "temp_serializer.h"
37 /* Utility to encode a signed NUMBER into a variable-length sequence of
38 * 8-bit chars in KEY_BUFFER and return the last writen position.
40 * Numbers will be stored in 7 bits / byte and using byte values above
41 * 32 (' ') to make them combinable with other string by simply separating
42 * individual parts with spaces.
45 encode_number(apr_int64_t number, char *key_buffer)
47 /* encode the sign in the first byte */
51 *key_buffer = (char)((number & 63) + ' ' + 65);
54 *key_buffer = (char)((number & 63) + ' ' + 1);
57 /* write 7 bits / byte until no significant bits are left */
60 *++key_buffer = (char)((number & 127) + ' ' + 1);
64 /* return the last written position */
69 svn_fs_fs__combine_number_and_string(apr_int64_t number,
73 apr_size_t len = strlen(string);
75 /* number part requires max. 10x7 bits + 1 space.
76 * Add another 1 for the terminal 0 */
77 char *key_buffer = apr_palloc(pool, len + 12);
78 const char *key = key_buffer;
80 /* Prepend the number to the string and separate them by space. No other
81 * number can result in the same prefix, no other string in the same
82 * postfix nor can the boundary between them be ambiguous. */
83 key_buffer = encode_number(number, key_buffer);
85 memcpy(++key_buffer, string, len+1);
87 /* return the start of the key */
91 /* Utility function to serialize string S in the given serialization CONTEXT.
94 serialize_svn_string(svn_temp_serializer__context_t *context,
95 const svn_string_t * const *s)
97 const svn_string_t *string = *s;
99 /* Nothing to do for NULL string references. */
103 svn_temp_serializer__push(context,
104 (const void * const *)s,
107 /* the "string" content may actually be arbitrary binary data.
108 * Thus, we cannot use svn_temp_serializer__add_string. */
109 svn_temp_serializer__push(context,
110 (const void * const *)&string->data,
113 /* back to the caller's nesting level */
114 svn_temp_serializer__pop(context);
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 checkum CS within the given serialization
134 serialize_checksum(svn_temp_serializer__context_t *context,
135 svn_checksum_t * const *cs)
137 const svn_checksum_t *checksum = *cs;
138 if (checksum == NULL)
141 svn_temp_serializer__push(context,
142 (const void * const *)cs,
145 /* The digest is arbitrary binary data.
146 * Thus, we cannot use svn_temp_serializer__add_string. */
147 svn_temp_serializer__push(context,
148 (const void * const *)&checksum->digest,
149 svn_checksum_size(checksum));
151 /* return to the caller's nesting level */
152 svn_temp_serializer__pop(context);
153 svn_temp_serializer__pop(context);
156 /* Utility function to deserialize the checksum CS inside the BUFFER.
159 deserialize_checksum(void *buffer, svn_checksum_t **cs)
161 svn_temp_deserializer__resolve(buffer, (void **)cs);
165 svn_temp_deserializer__resolve(*cs, (void **)&(*cs)->digest);
168 /* Utility function to serialize the REPRESENTATION within the given
169 * serialization CONTEXT.
172 serialize_representation(svn_temp_serializer__context_t *context,
173 representation_t * const *representation)
175 const representation_t * rep = *representation;
179 /* serialize the representation struct itself */
180 svn_temp_serializer__push(context,
181 (const void * const *)representation,
184 /* serialize sub-structures */
185 serialize_checksum(context, &rep->md5_checksum);
186 serialize_checksum(context, &rep->sha1_checksum);
188 svn_temp_serializer__add_string(context, &rep->txn_id);
189 svn_temp_serializer__add_string(context, &rep->uniquifier);
191 /* return to the caller's nesting level */
192 svn_temp_serializer__pop(context);
195 /* Utility function to deserialize the REPRESENTATIONS inside the BUFFER.
198 deserialize_representation(void *buffer,
199 representation_t **representation)
201 representation_t *rep;
203 /* fixup the reference to the representation itself */
204 svn_temp_deserializer__resolve(buffer, (void **)representation);
205 rep = *representation;
209 /* fixup of sub-structures */
210 deserialize_checksum(rep, &rep->md5_checksum);
211 deserialize_checksum(rep, &rep->sha1_checksum);
213 svn_temp_deserializer__resolve(rep, (void **)&rep->txn_id);
214 svn_temp_deserializer__resolve(rep, (void **)&rep->uniquifier);
217 /* auxilliary structure representing the content of a directory hash */
218 typedef struct hash_data_t
220 /* number of entries in the directory */
223 /* number of unused dir entry buckets in the index */
224 apr_size_t over_provision;
226 /* internal modifying operations counter
227 * (used to repack data once in a while) */
228 apr_size_t operations;
230 /* size of the serialization buffer actually used.
231 * (we will allocate more than we actually need such that we may
232 * append more data in situ later) */
235 /* reference to the entries */
236 svn_fs_dirent_t **entries;
238 /* size of the serialized entries and don't be too wasteful
239 * (needed since the entries are no longer in sequence) */
240 apr_uint32_t *lengths;
244 compare_dirent_id_names(const void *lhs, const void *rhs)
246 return strcmp((*(const svn_fs_dirent_t *const *)lhs)->name,
247 (*(const svn_fs_dirent_t *const *)rhs)->name);
250 /* Utility function to serialize the *ENTRY_P into a the given
251 * serialization CONTEXT. Return the serialized size of the
252 * dir entry in *LENGTH.
255 serialize_dir_entry(svn_temp_serializer__context_t *context,
256 svn_fs_dirent_t **entry_p,
257 apr_uint32_t *length)
259 svn_fs_dirent_t *entry = *entry_p;
260 apr_size_t initial_length = svn_temp_serializer__get_length(context);
262 svn_temp_serializer__push(context,
263 (const void * const *)entry_p,
264 sizeof(svn_fs_dirent_t));
266 svn_fs_fs__id_serialize(context, &entry->id);
267 svn_temp_serializer__add_string(context, &entry->name);
269 *length = (apr_uint32_t)( svn_temp_serializer__get_length(context)
270 - APR_ALIGN_DEFAULT(initial_length));
272 svn_temp_serializer__pop(context);
275 /* Utility function to serialize the ENTRIES into a new serialization
276 * context to be returned. Allocation will be made form POOL.
278 static svn_temp_serializer__context_t *
279 serialize_dir(apr_hash_t *entries, apr_pool_t *pool)
281 hash_data_t hash_data;
282 apr_hash_index_t *hi;
284 svn_temp_serializer__context_t *context;
286 /* calculate sizes */
287 apr_size_t count = apr_hash_count(entries);
288 apr_size_t over_provision = 2 + count / 4;
289 apr_size_t entries_len = (count + over_provision) * sizeof(svn_fs_dirent_t*);
290 apr_size_t lengths_len = (count + over_provision) * sizeof(apr_uint32_t);
292 /* copy the hash entries to an auxilliary struct of known layout */
293 hash_data.count = count;
294 hash_data.over_provision = over_provision;
295 hash_data.operations = 0;
296 hash_data.entries = apr_palloc(pool, entries_len);
297 hash_data.lengths = apr_palloc(pool, lengths_len);
299 for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi), ++i)
300 hash_data.entries[i] = svn__apr_hash_index_val(hi);
302 /* sort entry index by ID name */
303 qsort(hash_data.entries,
305 sizeof(*hash_data.entries),
306 compare_dirent_id_names);
308 /* Serialize that aux. structure into a new one. Also, provide a good
309 * estimate for the size of the buffer that we will need. */
310 context = svn_temp_serializer__init(&hash_data,
312 50 + count * 200 + entries_len,
315 /* serialize entries references */
316 svn_temp_serializer__push(context,
317 (const void * const *)&hash_data.entries,
320 /* serialize the individual entries and their sub-structures */
321 for (i = 0; i < count; ++i)
322 serialize_dir_entry(context,
323 &hash_data.entries[i],
324 &hash_data.lengths[i]);
326 svn_temp_serializer__pop(context);
328 /* serialize entries references */
329 svn_temp_serializer__push(context,
330 (const void * const *)&hash_data.lengths,
336 /* Utility function to reconstruct a dir entries hash from serialized data
337 * in BUFFER and HASH_DATA. Allocation will be made form POOL.
340 deserialize_dir(void *buffer, hash_data_t *hash_data, apr_pool_t *pool)
342 apr_hash_t *result = svn_hash__make(pool);
345 svn_fs_dirent_t *entry;
346 svn_fs_dirent_t **entries;
348 /* resolve the reference to the entries array */
349 svn_temp_deserializer__resolve(buffer, (void **)&hash_data->entries);
350 entries = hash_data->entries;
352 /* fixup the references within each entry and add it to the hash */
353 for (i = 0, count = hash_data->count; i < count; ++i)
355 svn_temp_deserializer__resolve(entries, (void **)&entries[i]);
356 entry = hash_data->entries[i];
359 svn_temp_deserializer__resolve(entry, (void **)&entry->name);
360 svn_fs_fs__id_deserialize(entry, (svn_fs_id_t **)&entry->id);
362 /* add the entry to the hash */
363 svn_hash_sets(result, entry->name, entry);
366 /* return the now complete hash */
371 svn_fs_fs__noderev_serialize(svn_temp_serializer__context_t *context,
372 node_revision_t * const *noderev_p)
374 const node_revision_t *noderev = *noderev_p;
378 /* serialize the representation struct itself */
379 svn_temp_serializer__push(context,
380 (const void * const *)noderev_p,
383 /* serialize sub-structures */
384 svn_fs_fs__id_serialize(context, &noderev->id);
385 svn_fs_fs__id_serialize(context, &noderev->predecessor_id);
386 serialize_representation(context, &noderev->prop_rep);
387 serialize_representation(context, &noderev->data_rep);
389 svn_temp_serializer__add_string(context, &noderev->copyfrom_path);
390 svn_temp_serializer__add_string(context, &noderev->copyroot_path);
391 svn_temp_serializer__add_string(context, &noderev->created_path);
393 /* return to the caller's nesting level */
394 svn_temp_serializer__pop(context);
399 svn_fs_fs__noderev_deserialize(void *buffer,
400 node_revision_t **noderev_p)
402 node_revision_t *noderev;
404 /* fixup the reference to the representation itself,
405 * if this is part of a parent structure. */
406 if (buffer != *noderev_p)
407 svn_temp_deserializer__resolve(buffer, (void **)noderev_p);
409 noderev = *noderev_p;
413 /* fixup of sub-structures */
414 svn_fs_fs__id_deserialize(noderev, (svn_fs_id_t **)&noderev->id);
415 svn_fs_fs__id_deserialize(noderev, (svn_fs_id_t **)&noderev->predecessor_id);
416 deserialize_representation(noderev, &noderev->prop_rep);
417 deserialize_representation(noderev, &noderev->data_rep);
419 svn_temp_deserializer__resolve(noderev, (void **)&noderev->copyfrom_path);
420 svn_temp_deserializer__resolve(noderev, (void **)&noderev->copyroot_path);
421 svn_temp_deserializer__resolve(noderev, (void **)&noderev->created_path);
425 /* Utility function to serialize COUNT svn_txdelta_op_t objects
426 * at OPS in the given serialization CONTEXT.
429 serialize_txdelta_ops(svn_temp_serializer__context_t *context,
430 const svn_txdelta_op_t * const * ops,
436 /* the ops form a contiguous chunk of memory with no further references */
437 svn_temp_serializer__push(context,
438 (const void * const *)ops,
439 count * sizeof(svn_txdelta_op_t));
440 svn_temp_serializer__pop(context);
443 /* Utility function to serialize W in the given serialization CONTEXT.
446 serialize_txdeltawindow(svn_temp_serializer__context_t *context,
447 svn_txdelta_window_t * const * w)
449 svn_txdelta_window_t *window = *w;
451 /* serialize the window struct itself */
452 svn_temp_serializer__push(context,
453 (const void * const *)w,
454 sizeof(svn_txdelta_window_t));
456 /* serialize its sub-structures */
457 serialize_txdelta_ops(context, &window->ops, window->num_ops);
458 serialize_svn_string(context, &window->new_data);
460 svn_temp_serializer__pop(context);
464 svn_fs_fs__serialize_txdelta_window(void **buffer,
465 apr_size_t *buffer_size,
469 svn_fs_fs__txdelta_cached_window_t *window_info = item;
470 svn_stringbuf_t *serialized;
472 /* initialize the serialization process and allocate a buffer large
473 * enough to do without the need of re-allocations in most cases. */
474 apr_size_t text_len = window_info->window->new_data
475 ? window_info->window->new_data->len
477 svn_temp_serializer__context_t *context =
478 svn_temp_serializer__init(window_info,
479 sizeof(*window_info),
483 /* serialize the sub-structure(s) */
484 serialize_txdeltawindow(context, &window_info->window);
486 /* return the serialized result */
487 serialized = svn_temp_serializer__get(context);
489 *buffer = serialized->data;
490 *buffer_size = serialized->len;
496 svn_fs_fs__deserialize_txdelta_window(void **item,
498 apr_size_t buffer_size,
501 svn_txdelta_window_t *window;
503 /* Copy the _full_ buffer as it also contains the sub-structures. */
504 svn_fs_fs__txdelta_cached_window_t *window_info =
505 (svn_fs_fs__txdelta_cached_window_t *)buffer;
507 /* pointer reference fixup */
508 svn_temp_deserializer__resolve(window_info,
509 (void **)&window_info->window);
510 window = window_info->window;
512 svn_temp_deserializer__resolve(window, (void **)&window->ops);
514 deserialize_svn_string(window, (svn_string_t**)&window->new_data);
523 svn_fs_fs__serialize_manifest(void **data,
524 apr_size_t *data_len,
528 apr_array_header_t *manifest = in;
530 *data_len = sizeof(apr_off_t) *manifest->nelts;
531 *data = apr_palloc(pool, *data_len);
532 memcpy(*data, manifest->elts, *data_len);
538 svn_fs_fs__deserialize_manifest(void **out,
543 apr_array_header_t *manifest = apr_array_make(pool, 1, sizeof(apr_off_t));
545 manifest->nelts = (int) (data_len / sizeof(apr_off_t));
546 manifest->nalloc = (int) (data_len / sizeof(apr_off_t));
547 manifest->elts = (char*)data;
554 /* Auxilliary structure representing the content of a properties hash.
555 This structure is much easier to (de-)serialize than an apr_hash.
557 typedef struct properties_data_t
559 /* number of entries in the hash */
562 /* reference to the keys */
565 /* reference to the values */
566 const svn_string_t **values;
569 /* Serialize COUNT C-style strings from *STRINGS into CONTEXT. */
571 serialize_cstring_array(svn_temp_serializer__context_t *context,
572 const char ***strings,
576 const char **entries = *strings;
578 /* serialize COUNT entries pointers (the array) */
579 svn_temp_serializer__push(context,
580 (const void * const *)strings,
581 count * sizeof(const char*));
583 /* serialize array elements */
584 for (i = 0; i < count; ++i)
585 svn_temp_serializer__add_string(context, &entries[i]);
587 svn_temp_serializer__pop(context);
590 /* Serialize COUNT svn_string_t* items from *STRINGS into CONTEXT. */
592 serialize_svn_string_array(svn_temp_serializer__context_t *context,
593 const svn_string_t ***strings,
597 const svn_string_t **entries = *strings;
599 /* serialize COUNT entries pointers (the array) */
600 svn_temp_serializer__push(context,
601 (const void * const *)strings,
602 count * sizeof(const char*));
604 /* serialize array elements */
605 for (i = 0; i < count; ++i)
606 serialize_svn_string(context, &entries[i]);
608 svn_temp_serializer__pop(context);
612 svn_fs_fs__serialize_properties(void **data,
613 apr_size_t *data_len,
617 apr_hash_t *hash = in;
618 properties_data_t properties;
619 svn_temp_serializer__context_t *context;
620 apr_hash_index_t *hi;
621 svn_stringbuf_t *serialized;
624 /* create our auxilliary data structure */
625 properties.count = apr_hash_count(hash);
626 properties.keys = apr_palloc(pool, sizeof(const char*) * (properties.count + 1));
627 properties.values = apr_palloc(pool, sizeof(const char*) * properties.count);
629 /* populate it with the hash entries */
630 for (hi = apr_hash_first(pool, hash), i=0; hi; hi = apr_hash_next(hi), ++i)
632 properties.keys[i] = svn__apr_hash_index_key(hi);
633 properties.values[i] = svn__apr_hash_index_val(hi);
637 context = svn_temp_serializer__init(&properties,
639 properties.count * 100,
642 properties.keys[i] = "";
643 serialize_cstring_array(context, &properties.keys, properties.count + 1);
644 serialize_svn_string_array(context, &properties.values, properties.count);
646 /* return the serialized result */
647 serialized = svn_temp_serializer__get(context);
649 *data = serialized->data;
650 *data_len = serialized->len;
656 svn_fs_fs__deserialize_properties(void **out,
661 apr_hash_t *hash = svn_hash__make(pool);
662 properties_data_t *properties = (properties_data_t *)data;
665 /* de-serialize our auxilliary data structure */
666 svn_temp_deserializer__resolve(properties, (void**)&properties->keys);
667 svn_temp_deserializer__resolve(properties, (void**)&properties->values);
669 /* de-serialize each entry and put it into the hash */
670 for (i = 0; i < properties->count; ++i)
672 apr_size_t len = properties->keys[i+1] - properties->keys[i] - 1;
673 svn_temp_deserializer__resolve((void*)properties->keys,
674 (void**)&properties->keys[i]);
676 deserialize_svn_string((void*)properties->values,
677 (svn_string_t **)&properties->values[i]);
680 properties->keys[i], len,
681 properties->values[i]);
691 svn_fs_fs__serialize_id(void **data,
692 apr_size_t *data_len,
696 const svn_fs_id_t *id = in;
697 svn_stringbuf_t *serialized;
699 /* create an (empty) serialization context with plenty of buffer space */
700 svn_temp_serializer__context_t *context =
701 svn_temp_serializer__init(NULL, 0, 250, pool);
703 /* serialize the id */
704 svn_fs_fs__id_serialize(context, &id);
706 /* return serialized data */
707 serialized = svn_temp_serializer__get(context);
708 *data = serialized->data;
709 *data_len = serialized->len;
715 svn_fs_fs__deserialize_id(void **out,
720 /* Copy the _full_ buffer as it also contains the sub-structures. */
721 svn_fs_id_t *id = (svn_fs_id_t *)data;
723 /* fixup of all pointers etc. */
724 svn_fs_fs__id_deserialize(id, &id);
731 /** Caching node_revision_t objects. **/
734 svn_fs_fs__serialize_node_revision(void **buffer,
735 apr_size_t *buffer_size,
739 svn_stringbuf_t *serialized;
740 node_revision_t *noderev = item;
742 /* create an (empty) serialization context with plenty of (initial)
744 svn_temp_serializer__context_t *context =
745 svn_temp_serializer__init(NULL, 0,
746 1024 - SVN_TEMP_SERIALIZER__OVERHEAD,
749 /* serialize the noderev */
750 svn_fs_fs__noderev_serialize(context, &noderev);
752 /* return serialized data */
753 serialized = svn_temp_serializer__get(context);
754 *buffer = serialized->data;
755 *buffer_size = serialized->len;
761 svn_fs_fs__deserialize_node_revision(void **item,
763 apr_size_t buffer_size,
766 /* Copy the _full_ buffer as it also contains the sub-structures. */
767 node_revision_t *noderev = (node_revision_t *)buffer;
769 /* fixup of all pointers etc. */
770 svn_fs_fs__noderev_deserialize(noderev, &noderev);
777 /* Utility function that returns the directory serialized inside CONTEXT
778 * to DATA and DATA_LEN. */
780 return_serialized_dir_context(svn_temp_serializer__context_t *context,
782 apr_size_t *data_len)
784 svn_stringbuf_t *serialized = svn_temp_serializer__get(context);
786 *data = serialized->data;
787 *data_len = serialized->blocksize;
788 ((hash_data_t *)serialized->data)->len = serialized->len;
794 svn_fs_fs__serialize_dir_entries(void **data,
795 apr_size_t *data_len,
799 apr_hash_t *dir = in;
801 /* serialize the dir content into a new serialization context
802 * and return the serialized data */
803 return return_serialized_dir_context(serialize_dir(dir, pool),
809 svn_fs_fs__deserialize_dir_entries(void **out,
814 /* Copy the _full_ buffer as it also contains the sub-structures. */
815 hash_data_t *hash_data = (hash_data_t *)data;
817 /* reconstruct the hash from the serialized data */
818 *out = deserialize_dir(hash_data, hash_data, pool);
824 svn_fs_fs__get_sharded_offset(void **out,
830 const apr_off_t *manifest = data;
831 apr_int64_t shard_pos = *(apr_int64_t *)baton;
833 *(apr_off_t *)out = manifest[shard_pos];
838 /* Utility function that returns the lowest index of the first entry in
839 * *ENTRIES that points to a dir entry with a name equal or larger than NAME.
840 * If an exact match has been found, *FOUND will be set to TRUE. COUNT is
841 * the number of valid entries in ENTRIES.
844 find_entry(svn_fs_dirent_t **entries,
847 svn_boolean_t *found)
849 /* binary search for the desired entry by name */
850 apr_size_t lower = 0;
851 apr_size_t upper = count;
854 for (middle = upper / 2; lower < upper; middle = (upper + lower) / 2)
856 const svn_fs_dirent_t *entry =
857 svn_temp_deserializer__ptr(entries, (const void *const *)&entries[middle]);
858 const char* entry_name =
859 svn_temp_deserializer__ptr(entry, (const void *const *)&entry->name);
861 int diff = strcmp(entry_name, name);
868 /* check whether we actually found a match */
872 const svn_fs_dirent_t *entry =
873 svn_temp_deserializer__ptr(entries, (const void *const *)&entries[lower]);
874 const char* entry_name =
875 svn_temp_deserializer__ptr(entry, (const void *const *)&entry->name);
877 if (strcmp(entry_name, name) == 0)
885 svn_fs_fs__extract_dir_entry(void **out,
891 const hash_data_t *hash_data = data;
892 const char* name = baton;
895 /* resolve the reference to the entries array */
896 const svn_fs_dirent_t * const *entries =
897 svn_temp_deserializer__ptr(data, (const void *const *)&hash_data->entries);
899 /* resolve the reference to the lengths array */
900 const apr_uint32_t *lengths =
901 svn_temp_deserializer__ptr(data, (const void *const *)&hash_data->lengths);
903 /* binary search for the desired entry by name */
904 apr_size_t pos = find_entry((svn_fs_dirent_t **)entries,
909 /* de-serialize that entry or return NULL, if no match has been found */
913 const svn_fs_dirent_t *source =
914 svn_temp_deserializer__ptr(entries, (const void *const *)&entries[pos]);
916 /* Entries have been serialized one-by-one, each time including all
917 * nested structures and strings. Therefore, they occupy a single
918 * block of memory whose end-offset is either the beginning of the
919 * next entry or the end of the buffer
921 apr_size_t size = lengths[pos];
923 /* copy & deserialize the entry */
924 svn_fs_dirent_t *new_entry = apr_palloc(pool, size);
925 memcpy(new_entry, source, size);
927 svn_temp_deserializer__resolve(new_entry, (void **)&new_entry->name);
928 svn_fs_fs__id_deserialize(new_entry, (svn_fs_id_t **)&new_entry->id);
929 *(svn_fs_dirent_t **)out = new_entry;
935 /* Utility function for svn_fs_fs__replace_dir_entry that implements the
936 * modification as a simply deserialize / modify / serialize sequence.
939 slowly_replace_dir_entry(void **data,
940 apr_size_t *data_len,
944 replace_baton_t *replace_baton = (replace_baton_t *)baton;
945 hash_data_t *hash_data = (hash_data_t *)*data;
948 SVN_ERR(svn_fs_fs__deserialize_dir_entries((void **)&dir,
952 svn_hash_sets(dir, replace_baton->name, replace_baton->new_entry);
954 return svn_fs_fs__serialize_dir_entries(data, data_len, dir, pool);
958 svn_fs_fs__replace_dir_entry(void **data,
959 apr_size_t *data_len,
963 replace_baton_t *replace_baton = (replace_baton_t *)baton;
964 hash_data_t *hash_data = (hash_data_t *)*data;
966 svn_fs_dirent_t **entries;
967 apr_uint32_t *lengths;
971 svn_temp_serializer__context_t *context;
973 /* after quite a number of operations, let's re-pack everything.
974 * This is to limit the number of vasted space as we cannot overwrite
975 * existing data but must always append. */
976 if (hash_data->operations > 2 + hash_data->count / 4)
977 return slowly_replace_dir_entry(data, data_len, baton, pool);
979 /* resolve the reference to the entries array */
980 entries = (svn_fs_dirent_t **)
981 svn_temp_deserializer__ptr((const char *)hash_data,
982 (const void *const *)&hash_data->entries);
984 /* resolve the reference to the lengths array */
985 lengths = (apr_uint32_t *)
986 svn_temp_deserializer__ptr((const char *)hash_data,
987 (const void *const *)&hash_data->lengths);
989 /* binary search for the desired entry by name */
990 pos = find_entry(entries, replace_baton->name, hash_data->count, &found);
992 /* handle entry removal (if found at all) */
993 if (replace_baton->new_entry == NULL)
997 /* remove reference to the entry from the index */
998 memmove(&entries[pos],
1000 sizeof(entries[pos]) * (hash_data->count - pos));
1001 memmove(&lengths[pos],
1003 sizeof(lengths[pos]) * (hash_data->count - pos));
1006 hash_data->over_provision++;
1007 hash_data->operations++;
1010 return SVN_NO_ERROR;
1013 /* if not found, prepare to insert the new entry */
1016 /* fallback to slow operation if there is no place left to insert an
1017 * new entry to index. That will automatically give add some spare
1018 * entries ("overprovision"). */
1019 if (hash_data->over_provision == 0)
1020 return slowly_replace_dir_entry(data, data_len, baton, pool);
1022 /* make entries[index] available for pointing to the new entry */
1023 memmove(&entries[pos + 1],
1025 sizeof(entries[pos]) * (hash_data->count - pos));
1026 memmove(&lengths[pos + 1],
1028 sizeof(lengths[pos]) * (hash_data->count - pos));
1031 hash_data->over_provision--;
1032 hash_data->operations++;
1035 /* de-serialize the new entry */
1036 entries[pos] = replace_baton->new_entry;
1037 context = svn_temp_serializer__init_append(hash_data,
1042 serialize_dir_entry(context, &entries[pos], &length);
1044 /* return the updated serialized data */
1045 SVN_ERR (return_serialized_dir_context(context,
1049 /* since the previous call may have re-allocated the buffer, the lengths
1050 * pointer may no longer point to the entry in that buffer. Therefore,
1051 * re-map it again and store the length value after that. */
1053 hash_data = (hash_data_t *)*data;
1054 lengths = (apr_uint32_t *)
1055 svn_temp_deserializer__ptr((const char *)hash_data,
1056 (const void *const *)&hash_data->lengths);
1057 lengths[pos] = length;
1059 return SVN_NO_ERROR;
1062 /* Utility function to serialize change CHANGE_P in the given serialization
1066 serialize_change(svn_temp_serializer__context_t *context,
1067 change_t * const *change_p)
1069 const change_t * change = *change_p;
1073 /* serialize the change struct itself */
1074 svn_temp_serializer__push(context,
1075 (const void * const *)change_p,
1078 /* serialize sub-structures */
1079 svn_fs_fs__id_serialize(context, &change->noderev_id);
1081 svn_temp_serializer__add_string(context, &change->path);
1082 svn_temp_serializer__add_string(context, &change->copyfrom_path);
1084 /* return to the caller's nesting level */
1085 svn_temp_serializer__pop(context);
1088 /* Utility function to serialize the CHANGE_P within the given
1089 * serialization CONTEXT.
1092 deserialize_change(void *buffer, change_t **change_p)
1096 /* fix-up of the pointer to the struct in question */
1097 svn_temp_deserializer__resolve(buffer, (void **)change_p);
1103 /* fix-up of sub-structures */
1104 svn_fs_fs__id_deserialize(change, (svn_fs_id_t **)&change->noderev_id);
1106 svn_temp_deserializer__resolve(change, (void **)&change->path);
1107 svn_temp_deserializer__resolve(change, (void **)&change->copyfrom_path);
1110 /* Auxiliary structure representing the content of a change_t array.
1111 This structure is much easier to (de-)serialize than an APR array.
1113 typedef struct changes_data_t
1115 /* number of entries in the array */
1118 /* reference to the changes */
1123 svn_fs_fs__serialize_changes(void **data,
1124 apr_size_t *data_len,
1128 apr_array_header_t *array = in;
1129 changes_data_t changes;
1130 svn_temp_serializer__context_t *context;
1131 svn_stringbuf_t *serialized;
1134 /* initialize our auxiliary data structure */
1135 changes.count = array->nelts;
1136 changes.changes = apr_palloc(pool, sizeof(change_t*) * changes.count);
1138 /* populate it with the array elements */
1139 for (i = 0; i < changes.count; ++i)
1140 changes.changes[i] = APR_ARRAY_IDX(array, i, change_t*);
1142 /* serialize it and all its elements */
1143 context = svn_temp_serializer__init(&changes,
1145 changes.count * 100,
1148 svn_temp_serializer__push(context,
1149 (const void * const *)&changes.changes,
1150 changes.count * sizeof(change_t*));
1152 for (i = 0; i < changes.count; ++i)
1153 serialize_change(context, &changes.changes[i]);
1155 svn_temp_serializer__pop(context);
1157 /* return the serialized result */
1158 serialized = svn_temp_serializer__get(context);
1160 *data = serialized->data;
1161 *data_len = serialized->len;
1163 return SVN_NO_ERROR;
1167 svn_fs_fs__deserialize_changes(void **out,
1169 apr_size_t data_len,
1173 changes_data_t *changes = (changes_data_t *)data;
1174 apr_array_header_t *array = apr_array_make(pool, changes->count,
1175 sizeof(change_t *));
1177 /* de-serialize our auxiliary data structure */
1178 svn_temp_deserializer__resolve(changes, (void**)&changes->changes);
1180 /* de-serialize each entry and add it to the array */
1181 for (i = 0; i < changes->count; ++i)
1183 deserialize_change((void*)changes->changes,
1184 (change_t **)&changes->changes[i]);
1185 APR_ARRAY_PUSH(array, change_t *) = changes->changes[i];
1191 return SVN_NO_ERROR;
1194 /* Auxiliary structure representing the content of a svn_mergeinfo_t hash.
1195 This structure is much easier to (de-)serialize than an APR array.
1197 typedef struct mergeinfo_data_t
1199 /* number of paths in the hash */
1202 /* COUNT keys (paths) */
1205 /* COUNT keys lengths (strlen of path) */
1206 apr_ssize_t *key_lengths;
1208 /* COUNT entries, each giving the number of ranges for the key */
1211 /* all ranges in a single, concatenated buffer */
1212 svn_merge_range_t *ranges;
1216 svn_fs_fs__serialize_mergeinfo(void **data,
1217 apr_size_t *data_len,
1221 svn_mergeinfo_t mergeinfo = in;
1222 mergeinfo_data_t merges;
1223 svn_temp_serializer__context_t *context;
1224 svn_stringbuf_t *serialized;
1225 apr_hash_index_t *hi;
1228 apr_size_t range_count;
1230 /* initialize our auxiliary data structure */
1231 merges.count = apr_hash_count(mergeinfo);
1232 merges.keys = apr_palloc(pool, sizeof(*merges.keys) * merges.count);
1233 merges.key_lengths = apr_palloc(pool, sizeof(*merges.key_lengths) *
1235 merges.range_counts = apr_palloc(pool, sizeof(*merges.range_counts) *
1240 for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi), ++i)
1242 svn_rangelist_t *ranges;
1243 apr_hash_this(hi, (const void**)&merges.keys[i],
1244 &merges.key_lengths[i],
1246 merges.range_counts[i] = ranges->nelts;
1247 range_count += ranges->nelts;
1250 merges.ranges = apr_palloc(pool, sizeof(*merges.ranges) * range_count);
1253 for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
1255 svn_rangelist_t *ranges = svn__apr_hash_index_val(hi);
1256 for (k = 0; k < ranges->nelts; ++k, ++i)
1257 merges.ranges[i] = *APR_ARRAY_IDX(ranges, k, svn_merge_range_t*);
1260 /* serialize it and all its elements */
1261 context = svn_temp_serializer__init(&merges,
1267 svn_temp_serializer__push(context,
1268 (const void * const *)&merges.keys,
1269 merges.count * sizeof(*merges.keys));
1271 for (i = 0; i < merges.count; ++i)
1272 svn_temp_serializer__add_string(context, &merges.keys[i]);
1274 svn_temp_serializer__pop(context);
1276 /* key lengths array */
1277 svn_temp_serializer__push(context,
1278 (const void * const *)&merges.key_lengths,
1279 merges.count * sizeof(*merges.key_lengths));
1280 svn_temp_serializer__pop(context);
1282 /* range counts array */
1283 svn_temp_serializer__push(context,
1284 (const void * const *)&merges.range_counts,
1285 merges.count * sizeof(*merges.range_counts));
1286 svn_temp_serializer__pop(context);
1289 svn_temp_serializer__push(context,
1290 (const void * const *)&merges.ranges,
1291 range_count * sizeof(*merges.ranges));
1292 svn_temp_serializer__pop(context);
1294 /* return the serialized result */
1295 serialized = svn_temp_serializer__get(context);
1297 *data = serialized->data;
1298 *data_len = serialized->len;
1300 return SVN_NO_ERROR;
1304 svn_fs_fs__deserialize_mergeinfo(void **out,
1306 apr_size_t data_len,
1311 mergeinfo_data_t *merges = (mergeinfo_data_t *)data;
1312 svn_mergeinfo_t mergeinfo;
1314 /* de-serialize our auxiliary data structure */
1315 svn_temp_deserializer__resolve(merges, (void**)&merges->keys);
1316 svn_temp_deserializer__resolve(merges, (void**)&merges->key_lengths);
1317 svn_temp_deserializer__resolve(merges, (void**)&merges->range_counts);
1318 svn_temp_deserializer__resolve(merges, (void**)&merges->ranges);
1320 /* de-serialize keys and add entries to the result */
1322 mergeinfo = svn_hash__make(pool);
1323 for (i = 0; i < merges->count; ++i)
1325 svn_rangelist_t *ranges = apr_array_make(pool,
1326 merges->range_counts[i],
1327 sizeof(svn_merge_range_t*));
1328 for (k = 0; k < merges->range_counts[i]; ++k, ++n)
1329 APR_ARRAY_PUSH(ranges, svn_merge_range_t*) = &merges->ranges[n];
1331 svn_temp_deserializer__resolve((void*)merges->keys,
1332 (void**)&merges->keys[i]);
1333 apr_hash_set(mergeinfo, merges->keys[i], merges->key_lengths[i], ranges);
1339 return SVN_NO_ERROR;