1251881Speter/* temp_serializer.c: serialization functions for caching of FSFS structures
2251881Speter *
3251881Speter * ====================================================================
4251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
5251881Speter *    or more contributor license agreements.  See the NOTICE file
6251881Speter *    distributed with this work for additional information
7251881Speter *    regarding copyright ownership.  The ASF licenses this file
8251881Speter *    to you under the Apache License, Version 2.0 (the
9251881Speter *    "License"); you may not use this file except in compliance
10251881Speter *    with the License.  You may obtain a copy of the License at
11251881Speter *
12251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
13251881Speter *
14251881Speter *    Unless required by applicable law or agreed to in writing,
15251881Speter *    software distributed under the License is distributed on an
16251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17251881Speter *    KIND, either express or implied.  See the License for the
18251881Speter *    specific language governing permissions and limitations
19251881Speter *    under the License.
20251881Speter * ====================================================================
21251881Speter */
22251881Speter
23251881Speter#include <apr_pools.h>
24251881Speter
25251881Speter#include "svn_pools.h"
26251881Speter#include "svn_hash.h"
27251881Speter
28251881Speter#include "id.h"
29251881Speter#include "svn_fs.h"
30251881Speter
31251881Speter#include "private/svn_fs_util.h"
32251881Speter#include "private/svn_temp_serializer.h"
33251881Speter#include "private/svn_subr_private.h"
34251881Speter
35251881Speter#include "temp_serializer.h"
36251881Speter
37251881Speter/* Utility to encode a signed NUMBER into a variable-length sequence of
38251881Speter * 8-bit chars in KEY_BUFFER and return the last writen position.
39251881Speter *
40251881Speter * Numbers will be stored in 7 bits / byte and using byte values above
41251881Speter * 32 (' ') to make them combinable with other string by simply separating
42251881Speter * individual parts with spaces.
43251881Speter */
44251881Speterstatic char*
45251881Speterencode_number(apr_int64_t number, char *key_buffer)
46251881Speter{
47251881Speter  /* encode the sign in the first byte */
48251881Speter  if (number < 0)
49251881Speter  {
50251881Speter    number = -number;
51251881Speter    *key_buffer = (char)((number & 63) + ' ' + 65);
52251881Speter  }
53251881Speter  else
54251881Speter    *key_buffer = (char)((number & 63) + ' ' + 1);
55251881Speter  number /= 64;
56251881Speter
57251881Speter  /* write 7 bits / byte until no significant bits are left */
58251881Speter  while (number)
59251881Speter  {
60251881Speter    *++key_buffer = (char)((number & 127) + ' ' + 1);
61251881Speter    number /= 128;
62251881Speter  }
63251881Speter
64251881Speter  /* return the last written position */
65251881Speter  return key_buffer;
66251881Speter}
67251881Speter
68251881Speterconst char*
69251881Spetersvn_fs_fs__combine_number_and_string(apr_int64_t number,
70251881Speter                                     const char *string,
71251881Speter                                     apr_pool_t *pool)
72251881Speter{
73251881Speter  apr_size_t len = strlen(string);
74251881Speter
75251881Speter  /* number part requires max. 10x7 bits + 1 space.
76251881Speter   * Add another 1 for the terminal 0 */
77251881Speter  char *key_buffer = apr_palloc(pool, len + 12);
78251881Speter  const char *key = key_buffer;
79251881Speter
80251881Speter  /* Prepend the number to the string and separate them by space. No other
81251881Speter   * number can result in the same prefix, no other string in the same
82251881Speter   * postfix nor can the boundary between them be ambiguous. */
83251881Speter  key_buffer = encode_number(number, key_buffer);
84251881Speter  *++key_buffer = ' ';
85251881Speter  memcpy(++key_buffer, string, len+1);
86251881Speter
87251881Speter  /* return the start of the key */
88251881Speter  return key;
89251881Speter}
90251881Speter
91251881Speter/* Utility function to serialize string S in the given serialization CONTEXT.
92251881Speter */
93251881Speterstatic void
94251881Speterserialize_svn_string(svn_temp_serializer__context_t *context,
95251881Speter                     const svn_string_t * const *s)
96251881Speter{
97251881Speter  const svn_string_t *string = *s;
98251881Speter
99251881Speter  /* Nothing to do for NULL string references. */
100251881Speter  if (string == NULL)
101251881Speter    return;
102251881Speter
103251881Speter  svn_temp_serializer__push(context,
104251881Speter                            (const void * const *)s,
105251881Speter                            sizeof(*string));
106251881Speter
107251881Speter  /* the "string" content may actually be arbitrary binary data.
108251881Speter   * Thus, we cannot use svn_temp_serializer__add_string. */
109251881Speter  svn_temp_serializer__push(context,
110251881Speter                            (const void * const *)&string->data,
111251881Speter                            string->len + 1);
112251881Speter
113251881Speter  /* back to the caller's nesting level */
114251881Speter  svn_temp_serializer__pop(context);
115251881Speter  svn_temp_serializer__pop(context);
116251881Speter}
117251881Speter
118251881Speter/* Utility function to deserialize the STRING inside the BUFFER.
119251881Speter */
120251881Speterstatic void
121251881Speterdeserialize_svn_string(void *buffer, svn_string_t **string)
122251881Speter{
123251881Speter  svn_temp_deserializer__resolve(buffer, (void **)string);
124251881Speter  if (*string == NULL)
125251881Speter    return;
126251881Speter
127251881Speter  svn_temp_deserializer__resolve(*string, (void **)&(*string)->data);
128251881Speter}
129251881Speter
130251881Speter/* Utility function to serialize checkum CS within the given serialization
131251881Speter * CONTEXT.
132251881Speter */
133251881Speterstatic void
134251881Speterserialize_checksum(svn_temp_serializer__context_t *context,
135251881Speter                   svn_checksum_t * const *cs)
136251881Speter{
137251881Speter  const svn_checksum_t *checksum = *cs;
138251881Speter  if (checksum == NULL)
139251881Speter    return;
140251881Speter
141251881Speter  svn_temp_serializer__push(context,
142251881Speter                            (const void * const *)cs,
143251881Speter                            sizeof(*checksum));
144251881Speter
145251881Speter  /* The digest is arbitrary binary data.
146251881Speter   * Thus, we cannot use svn_temp_serializer__add_string. */
147251881Speter  svn_temp_serializer__push(context,
148251881Speter                            (const void * const *)&checksum->digest,
149251881Speter                            svn_checksum_size(checksum));
150251881Speter
151251881Speter  /* return to the caller's nesting level */
152251881Speter  svn_temp_serializer__pop(context);
153251881Speter  svn_temp_serializer__pop(context);
154251881Speter}
155251881Speter
156251881Speter/* Utility function to deserialize the checksum CS inside the BUFFER.
157251881Speter */
158251881Speterstatic void
159251881Speterdeserialize_checksum(void *buffer, svn_checksum_t **cs)
160251881Speter{
161251881Speter  svn_temp_deserializer__resolve(buffer, (void **)cs);
162251881Speter  if (*cs == NULL)
163251881Speter    return;
164251881Speter
165251881Speter  svn_temp_deserializer__resolve(*cs, (void **)&(*cs)->digest);
166251881Speter}
167251881Speter
168251881Speter/* Utility function to serialize the REPRESENTATION within the given
169251881Speter * serialization CONTEXT.
170251881Speter */
171251881Speterstatic void
172251881Speterserialize_representation(svn_temp_serializer__context_t *context,
173251881Speter                         representation_t * const *representation)
174251881Speter{
175251881Speter  const representation_t * rep = *representation;
176251881Speter  if (rep == NULL)
177251881Speter    return;
178251881Speter
179251881Speter  /* serialize the representation struct itself */
180251881Speter  svn_temp_serializer__push(context,
181251881Speter                            (const void * const *)representation,
182251881Speter                            sizeof(*rep));
183251881Speter
184251881Speter  /* serialize sub-structures */
185251881Speter  serialize_checksum(context, &rep->md5_checksum);
186251881Speter  serialize_checksum(context, &rep->sha1_checksum);
187251881Speter
188251881Speter  svn_temp_serializer__add_string(context, &rep->txn_id);
189251881Speter  svn_temp_serializer__add_string(context, &rep->uniquifier);
190251881Speter
191251881Speter  /* return to the caller's nesting level */
192251881Speter  svn_temp_serializer__pop(context);
193251881Speter}
194251881Speter
195251881Speter/* Utility function to deserialize the REPRESENTATIONS inside the BUFFER.
196251881Speter */
197251881Speterstatic void
198251881Speterdeserialize_representation(void *buffer,
199251881Speter                           representation_t **representation)
200251881Speter{
201251881Speter  representation_t *rep;
202251881Speter
203251881Speter  /* fixup the reference to the representation itself */
204251881Speter  svn_temp_deserializer__resolve(buffer, (void **)representation);
205251881Speter  rep = *representation;
206251881Speter  if (rep == NULL)
207251881Speter    return;
208251881Speter
209251881Speter  /* fixup of sub-structures */
210251881Speter  deserialize_checksum(rep, &rep->md5_checksum);
211251881Speter  deserialize_checksum(rep, &rep->sha1_checksum);
212251881Speter
213251881Speter  svn_temp_deserializer__resolve(rep, (void **)&rep->txn_id);
214251881Speter  svn_temp_deserializer__resolve(rep, (void **)&rep->uniquifier);
215251881Speter}
216251881Speter
217251881Speter/* auxilliary structure representing the content of a directory hash */
218251881Spetertypedef struct hash_data_t
219251881Speter{
220251881Speter  /* number of entries in the directory */
221251881Speter  apr_size_t count;
222251881Speter
223251881Speter  /* number of unused dir entry buckets in the index */
224251881Speter  apr_size_t over_provision;
225251881Speter
226251881Speter  /* internal modifying operations counter
227251881Speter   * (used to repack data once in a while) */
228251881Speter  apr_size_t operations;
229251881Speter
230251881Speter  /* size of the serialization buffer actually used.
231251881Speter   * (we will allocate more than we actually need such that we may
232251881Speter   * append more data in situ later) */
233251881Speter  apr_size_t len;
234251881Speter
235251881Speter  /* reference to the entries */
236251881Speter  svn_fs_dirent_t **entries;
237251881Speter
238251881Speter  /* size of the serialized entries and don't be too wasteful
239251881Speter   * (needed since the entries are no longer in sequence) */
240251881Speter  apr_uint32_t *lengths;
241251881Speter} hash_data_t;
242251881Speter
243251881Speterstatic int
244251881Spetercompare_dirent_id_names(const void *lhs, const void *rhs)
245251881Speter{
246251881Speter  return strcmp((*(const svn_fs_dirent_t *const *)lhs)->name,
247251881Speter                (*(const svn_fs_dirent_t *const *)rhs)->name);
248251881Speter}
249251881Speter
250251881Speter/* Utility function to serialize the *ENTRY_P into a the given
251251881Speter * serialization CONTEXT. Return the serialized size of the
252251881Speter * dir entry in *LENGTH.
253251881Speter */
254251881Speterstatic void
255251881Speterserialize_dir_entry(svn_temp_serializer__context_t *context,
256251881Speter                    svn_fs_dirent_t **entry_p,
257251881Speter                    apr_uint32_t *length)
258251881Speter{
259251881Speter  svn_fs_dirent_t *entry = *entry_p;
260251881Speter  apr_size_t initial_length = svn_temp_serializer__get_length(context);
261251881Speter
262251881Speter  svn_temp_serializer__push(context,
263251881Speter                            (const void * const *)entry_p,
264251881Speter                            sizeof(svn_fs_dirent_t));
265251881Speter
266251881Speter  svn_fs_fs__id_serialize(context, &entry->id);
267251881Speter  svn_temp_serializer__add_string(context, &entry->name);
268251881Speter
269251881Speter  *length = (apr_uint32_t)(  svn_temp_serializer__get_length(context)
270251881Speter                           - APR_ALIGN_DEFAULT(initial_length));
271251881Speter
272251881Speter  svn_temp_serializer__pop(context);
273251881Speter}
274251881Speter
275251881Speter/* Utility function to serialize the ENTRIES into a new serialization
276251881Speter * context to be returned. Allocation will be made form POOL.
277251881Speter */
278251881Speterstatic svn_temp_serializer__context_t *
279251881Speterserialize_dir(apr_hash_t *entries, apr_pool_t *pool)
280251881Speter{
281251881Speter  hash_data_t hash_data;
282251881Speter  apr_hash_index_t *hi;
283251881Speter  apr_size_t i = 0;
284251881Speter  svn_temp_serializer__context_t *context;
285251881Speter
286251881Speter  /* calculate sizes */
287251881Speter  apr_size_t count = apr_hash_count(entries);
288251881Speter  apr_size_t over_provision = 2 + count / 4;
289251881Speter  apr_size_t entries_len = (count + over_provision) * sizeof(svn_fs_dirent_t*);
290251881Speter  apr_size_t lengths_len = (count + over_provision) * sizeof(apr_uint32_t);
291251881Speter
292251881Speter  /* copy the hash entries to an auxilliary struct of known layout */
293251881Speter  hash_data.count = count;
294251881Speter  hash_data.over_provision = over_provision;
295251881Speter  hash_data.operations = 0;
296251881Speter  hash_data.entries = apr_palloc(pool, entries_len);
297251881Speter  hash_data.lengths = apr_palloc(pool, lengths_len);
298251881Speter
299251881Speter  for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi), ++i)
300251881Speter    hash_data.entries[i] = svn__apr_hash_index_val(hi);
301251881Speter
302251881Speter  /* sort entry index by ID name */
303251881Speter  qsort(hash_data.entries,
304251881Speter        count,
305251881Speter        sizeof(*hash_data.entries),
306251881Speter        compare_dirent_id_names);
307251881Speter
308251881Speter  /* Serialize that aux. structure into a new one. Also, provide a good
309251881Speter   * estimate for the size of the buffer that we will need. */
310251881Speter  context = svn_temp_serializer__init(&hash_data,
311251881Speter                                      sizeof(hash_data),
312251881Speter                                      50 + count * 200 + entries_len,
313251881Speter                                      pool);
314251881Speter
315251881Speter  /* serialize entries references */
316251881Speter  svn_temp_serializer__push(context,
317251881Speter                            (const void * const *)&hash_data.entries,
318251881Speter                            entries_len);
319251881Speter
320251881Speter  /* serialize the individual entries and their sub-structures */
321251881Speter  for (i = 0; i < count; ++i)
322251881Speter    serialize_dir_entry(context,
323251881Speter                        &hash_data.entries[i],
324251881Speter                        &hash_data.lengths[i]);
325251881Speter
326251881Speter  svn_temp_serializer__pop(context);
327251881Speter
328251881Speter  /* serialize entries references */
329251881Speter  svn_temp_serializer__push(context,
330251881Speter                            (const void * const *)&hash_data.lengths,
331251881Speter                            lengths_len);
332251881Speter
333251881Speter  return context;
334251881Speter}
335251881Speter
336251881Speter/* Utility function to reconstruct a dir entries hash from serialized data
337251881Speter * in BUFFER and HASH_DATA. Allocation will be made form POOL.
338251881Speter */
339251881Speterstatic apr_hash_t *
340251881Speterdeserialize_dir(void *buffer, hash_data_t *hash_data, apr_pool_t *pool)
341251881Speter{
342251881Speter  apr_hash_t *result = svn_hash__make(pool);
343251881Speter  apr_size_t i;
344251881Speter  apr_size_t count;
345251881Speter  svn_fs_dirent_t *entry;
346251881Speter  svn_fs_dirent_t **entries;
347251881Speter
348251881Speter  /* resolve the reference to the entries array */
349251881Speter  svn_temp_deserializer__resolve(buffer, (void **)&hash_data->entries);
350251881Speter  entries = hash_data->entries;
351251881Speter
352251881Speter  /* fixup the references within each entry and add it to the hash */
353251881Speter  for (i = 0, count = hash_data->count; i < count; ++i)
354251881Speter    {
355251881Speter      svn_temp_deserializer__resolve(entries, (void **)&entries[i]);
356251881Speter      entry = hash_data->entries[i];
357251881Speter
358251881Speter      /* pointer fixup */
359251881Speter      svn_temp_deserializer__resolve(entry, (void **)&entry->name);
360251881Speter      svn_fs_fs__id_deserialize(entry, (svn_fs_id_t **)&entry->id);
361251881Speter
362251881Speter      /* add the entry to the hash */
363251881Speter      svn_hash_sets(result, entry->name, entry);
364251881Speter    }
365251881Speter
366251881Speter  /* return the now complete hash */
367251881Speter  return result;
368251881Speter}
369251881Speter
370251881Spetervoid
371251881Spetersvn_fs_fs__noderev_serialize(svn_temp_serializer__context_t *context,
372251881Speter                             node_revision_t * const *noderev_p)
373251881Speter{
374251881Speter  const node_revision_t *noderev = *noderev_p;
375251881Speter  if (noderev == NULL)
376251881Speter    return;
377251881Speter
378251881Speter  /* serialize the representation struct itself */
379251881Speter  svn_temp_serializer__push(context,
380251881Speter                            (const void * const *)noderev_p,
381251881Speter                            sizeof(*noderev));
382251881Speter
383251881Speter  /* serialize sub-structures */
384251881Speter  svn_fs_fs__id_serialize(context, &noderev->id);
385251881Speter  svn_fs_fs__id_serialize(context, &noderev->predecessor_id);
386251881Speter  serialize_representation(context, &noderev->prop_rep);
387251881Speter  serialize_representation(context, &noderev->data_rep);
388251881Speter
389251881Speter  svn_temp_serializer__add_string(context, &noderev->copyfrom_path);
390251881Speter  svn_temp_serializer__add_string(context, &noderev->copyroot_path);
391251881Speter  svn_temp_serializer__add_string(context, &noderev->created_path);
392251881Speter
393251881Speter  /* return to the caller's nesting level */
394251881Speter  svn_temp_serializer__pop(context);
395251881Speter}
396251881Speter
397251881Speter
398251881Spetervoid
399251881Spetersvn_fs_fs__noderev_deserialize(void *buffer,
400251881Speter                               node_revision_t **noderev_p)
401251881Speter{
402251881Speter  node_revision_t *noderev;
403251881Speter
404251881Speter  /* fixup the reference to the representation itself,
405251881Speter   * if this is part of a parent structure. */
406251881Speter  if (buffer != *noderev_p)
407251881Speter    svn_temp_deserializer__resolve(buffer, (void **)noderev_p);
408251881Speter
409251881Speter  noderev = *noderev_p;
410251881Speter  if (noderev == NULL)
411251881Speter    return;
412251881Speter
413251881Speter  /* fixup of sub-structures */
414251881Speter  svn_fs_fs__id_deserialize(noderev, (svn_fs_id_t **)&noderev->id);
415251881Speter  svn_fs_fs__id_deserialize(noderev, (svn_fs_id_t **)&noderev->predecessor_id);
416251881Speter  deserialize_representation(noderev, &noderev->prop_rep);
417251881Speter  deserialize_representation(noderev, &noderev->data_rep);
418251881Speter
419251881Speter  svn_temp_deserializer__resolve(noderev, (void **)&noderev->copyfrom_path);
420251881Speter  svn_temp_deserializer__resolve(noderev, (void **)&noderev->copyroot_path);
421251881Speter  svn_temp_deserializer__resolve(noderev, (void **)&noderev->created_path);
422251881Speter}
423251881Speter
424251881Speter
425251881Speter/* Utility function to serialize COUNT svn_txdelta_op_t objects
426251881Speter * at OPS in the given serialization CONTEXT.
427251881Speter */
428251881Speterstatic void
429251881Speterserialize_txdelta_ops(svn_temp_serializer__context_t *context,
430251881Speter                      const svn_txdelta_op_t * const * ops,
431251881Speter                      apr_size_t count)
432251881Speter{
433251881Speter  if (*ops == NULL)
434251881Speter    return;
435251881Speter
436251881Speter  /* the ops form a contiguous chunk of memory with no further references */
437251881Speter  svn_temp_serializer__push(context,
438251881Speter                            (const void * const *)ops,
439251881Speter                            count * sizeof(svn_txdelta_op_t));
440251881Speter  svn_temp_serializer__pop(context);
441251881Speter}
442251881Speter
443251881Speter/* Utility function to serialize W in the given serialization CONTEXT.
444251881Speter */
445251881Speterstatic void
446251881Speterserialize_txdeltawindow(svn_temp_serializer__context_t *context,
447251881Speter                        svn_txdelta_window_t * const * w)
448251881Speter{
449251881Speter  svn_txdelta_window_t *window = *w;
450251881Speter
451251881Speter  /* serialize the window struct itself */
452251881Speter  svn_temp_serializer__push(context,
453251881Speter                            (const void * const *)w,
454251881Speter                            sizeof(svn_txdelta_window_t));
455251881Speter
456251881Speter  /* serialize its sub-structures */
457251881Speter  serialize_txdelta_ops(context, &window->ops, window->num_ops);
458251881Speter  serialize_svn_string(context, &window->new_data);
459251881Speter
460251881Speter  svn_temp_serializer__pop(context);
461251881Speter}
462251881Speter
463251881Spetersvn_error_t *
464251881Spetersvn_fs_fs__serialize_txdelta_window(void **buffer,
465251881Speter                                    apr_size_t *buffer_size,
466251881Speter                                    void *item,
467251881Speter                                    apr_pool_t *pool)
468251881Speter{
469251881Speter  svn_fs_fs__txdelta_cached_window_t *window_info = item;
470251881Speter  svn_stringbuf_t *serialized;
471251881Speter
472251881Speter  /* initialize the serialization process and allocate a buffer large
473251881Speter   * enough to do without the need of re-allocations in most cases. */
474251881Speter  apr_size_t text_len = window_info->window->new_data
475251881Speter                      ? window_info->window->new_data->len
476251881Speter                      : 0;
477251881Speter  svn_temp_serializer__context_t *context =
478251881Speter      svn_temp_serializer__init(window_info,
479251881Speter                                sizeof(*window_info),
480251881Speter                                500 + text_len,
481251881Speter                                pool);
482251881Speter
483251881Speter  /* serialize the sub-structure(s) */
484251881Speter  serialize_txdeltawindow(context, &window_info->window);
485251881Speter
486251881Speter  /* return the serialized result */
487251881Speter  serialized = svn_temp_serializer__get(context);
488251881Speter
489251881Speter  *buffer = serialized->data;
490251881Speter  *buffer_size = serialized->len;
491251881Speter
492251881Speter  return SVN_NO_ERROR;
493251881Speter}
494251881Speter
495251881Spetersvn_error_t *
496251881Spetersvn_fs_fs__deserialize_txdelta_window(void **item,
497251881Speter                                      void *buffer,
498251881Speter                                      apr_size_t buffer_size,
499251881Speter                                      apr_pool_t *pool)
500251881Speter{
501251881Speter  svn_txdelta_window_t *window;
502251881Speter
503251881Speter  /* Copy the _full_ buffer as it also contains the sub-structures. */
504251881Speter  svn_fs_fs__txdelta_cached_window_t *window_info =
505251881Speter      (svn_fs_fs__txdelta_cached_window_t *)buffer;
506251881Speter
507251881Speter  /* pointer reference fixup */
508251881Speter  svn_temp_deserializer__resolve(window_info,
509251881Speter                                 (void **)&window_info->window);
510251881Speter  window = window_info->window;
511251881Speter
512251881Speter  svn_temp_deserializer__resolve(window, (void **)&window->ops);
513251881Speter
514251881Speter  deserialize_svn_string(window, (svn_string_t**)&window->new_data);
515251881Speter
516251881Speter  /* done */
517251881Speter  *item = window_info;
518251881Speter
519251881Speter  return SVN_NO_ERROR;
520251881Speter}
521251881Speter
522251881Spetersvn_error_t *
523251881Spetersvn_fs_fs__serialize_manifest(void **data,
524251881Speter                              apr_size_t *data_len,
525251881Speter                              void *in,
526251881Speter                              apr_pool_t *pool)
527251881Speter{
528251881Speter  apr_array_header_t *manifest = in;
529251881Speter
530251881Speter  *data_len = sizeof(apr_off_t) *manifest->nelts;
531251881Speter  *data = apr_palloc(pool, *data_len);
532251881Speter  memcpy(*data, manifest->elts, *data_len);
533251881Speter
534251881Speter  return SVN_NO_ERROR;
535251881Speter}
536251881Speter
537251881Spetersvn_error_t *
538251881Spetersvn_fs_fs__deserialize_manifest(void **out,
539251881Speter                                void *data,
540251881Speter                                apr_size_t data_len,
541251881Speter                                apr_pool_t *pool)
542251881Speter{
543251881Speter  apr_array_header_t *manifest = apr_array_make(pool, 1, sizeof(apr_off_t));
544251881Speter
545251881Speter  manifest->nelts = (int) (data_len / sizeof(apr_off_t));
546251881Speter  manifest->nalloc = (int) (data_len / sizeof(apr_off_t));
547251881Speter  manifest->elts = (char*)data;
548251881Speter
549251881Speter  *out = manifest;
550251881Speter
551251881Speter  return SVN_NO_ERROR;
552251881Speter}
553251881Speter
554251881Speter/* Auxilliary structure representing the content of a properties hash.
555251881Speter   This structure is much easier to (de-)serialize than an apr_hash.
556251881Speter */
557251881Spetertypedef struct properties_data_t
558251881Speter{
559251881Speter  /* number of entries in the hash */
560251881Speter  apr_size_t count;
561251881Speter
562251881Speter  /* reference to the keys */
563251881Speter  const char **keys;
564251881Speter
565251881Speter  /* reference to the values */
566251881Speter  const svn_string_t **values;
567251881Speter} properties_data_t;
568251881Speter
569251881Speter/* Serialize COUNT C-style strings from *STRINGS into CONTEXT. */
570251881Speterstatic void
571251881Speterserialize_cstring_array(svn_temp_serializer__context_t *context,
572251881Speter                        const char ***strings,
573251881Speter                        apr_size_t count)
574251881Speter{
575251881Speter  apr_size_t i;
576251881Speter  const char **entries = *strings;
577251881Speter
578251881Speter  /* serialize COUNT entries pointers (the array) */
579251881Speter  svn_temp_serializer__push(context,
580251881Speter                            (const void * const *)strings,
581251881Speter                            count * sizeof(const char*));
582251881Speter
583251881Speter  /* serialize array elements */
584251881Speter  for (i = 0; i < count; ++i)
585251881Speter    svn_temp_serializer__add_string(context, &entries[i]);
586251881Speter
587251881Speter  svn_temp_serializer__pop(context);
588251881Speter}
589251881Speter
590251881Speter/* Serialize COUNT svn_string_t* items from *STRINGS into CONTEXT. */
591251881Speterstatic void
592251881Speterserialize_svn_string_array(svn_temp_serializer__context_t *context,
593251881Speter                           const svn_string_t ***strings,
594251881Speter                           apr_size_t count)
595251881Speter{
596251881Speter  apr_size_t i;
597251881Speter  const svn_string_t **entries = *strings;
598251881Speter
599251881Speter  /* serialize COUNT entries pointers (the array) */
600251881Speter  svn_temp_serializer__push(context,
601251881Speter                            (const void * const *)strings,
602251881Speter                            count * sizeof(const char*));
603251881Speter
604251881Speter  /* serialize array elements */
605251881Speter  for (i = 0; i < count; ++i)
606251881Speter    serialize_svn_string(context, &entries[i]);
607251881Speter
608251881Speter  svn_temp_serializer__pop(context);
609251881Speter}
610251881Speter
611251881Spetersvn_error_t *
612251881Spetersvn_fs_fs__serialize_properties(void **data,
613251881Speter                                apr_size_t *data_len,
614251881Speter                                void *in,
615251881Speter                                apr_pool_t *pool)
616251881Speter{
617251881Speter  apr_hash_t *hash = in;
618251881Speter  properties_data_t properties;
619251881Speter  svn_temp_serializer__context_t *context;
620251881Speter  apr_hash_index_t *hi;
621251881Speter  svn_stringbuf_t *serialized;
622251881Speter  apr_size_t i;
623251881Speter
624251881Speter  /* create our auxilliary data structure */
625251881Speter  properties.count = apr_hash_count(hash);
626251881Speter  properties.keys = apr_palloc(pool, sizeof(const char*) * (properties.count + 1));
627251881Speter  properties.values = apr_palloc(pool, sizeof(const char*) * properties.count);
628251881Speter
629251881Speter  /* populate it with the hash entries */
630251881Speter  for (hi = apr_hash_first(pool, hash), i=0; hi; hi = apr_hash_next(hi), ++i)
631251881Speter    {
632251881Speter      properties.keys[i] = svn__apr_hash_index_key(hi);
633251881Speter      properties.values[i] = svn__apr_hash_index_val(hi);
634251881Speter    }
635251881Speter
636251881Speter  /* serialize it */
637251881Speter  context = svn_temp_serializer__init(&properties,
638251881Speter                                      sizeof(properties),
639251881Speter                                      properties.count * 100,
640251881Speter                                      pool);
641251881Speter
642251881Speter  properties.keys[i] = "";
643251881Speter  serialize_cstring_array(context, &properties.keys, properties.count + 1);
644251881Speter  serialize_svn_string_array(context, &properties.values, properties.count);
645251881Speter
646251881Speter  /* return the serialized result */
647251881Speter  serialized = svn_temp_serializer__get(context);
648251881Speter
649251881Speter  *data = serialized->data;
650251881Speter  *data_len = serialized->len;
651251881Speter
652251881Speter  return SVN_NO_ERROR;
653251881Speter}
654251881Speter
655251881Spetersvn_error_t *
656251881Spetersvn_fs_fs__deserialize_properties(void **out,
657251881Speter                                  void *data,
658251881Speter                                  apr_size_t data_len,
659251881Speter                                  apr_pool_t *pool)
660251881Speter{
661251881Speter  apr_hash_t *hash = svn_hash__make(pool);
662251881Speter  properties_data_t *properties = (properties_data_t *)data;
663251881Speter  size_t i;
664251881Speter
665251881Speter  /* de-serialize our auxilliary data structure */
666251881Speter  svn_temp_deserializer__resolve(properties, (void**)&properties->keys);
667251881Speter  svn_temp_deserializer__resolve(properties, (void**)&properties->values);
668251881Speter
669251881Speter  /* de-serialize each entry and put it into the hash */
670251881Speter  for (i = 0; i < properties->count; ++i)
671251881Speter    {
672251881Speter      apr_size_t len = properties->keys[i+1] - properties->keys[i] - 1;
673251881Speter      svn_temp_deserializer__resolve((void*)properties->keys,
674251881Speter                                     (void**)&properties->keys[i]);
675251881Speter
676251881Speter      deserialize_svn_string((void*)properties->values,
677251881Speter                             (svn_string_t **)&properties->values[i]);
678251881Speter
679251881Speter      apr_hash_set(hash,
680251881Speter                   properties->keys[i], len,
681251881Speter                   properties->values[i]);
682251881Speter    }
683251881Speter
684251881Speter  /* done */
685251881Speter  *out = hash;
686251881Speter
687251881Speter  return SVN_NO_ERROR;
688251881Speter}
689251881Speter
690251881Spetersvn_error_t *
691251881Spetersvn_fs_fs__serialize_id(void **data,
692251881Speter                        apr_size_t *data_len,
693251881Speter                        void *in,
694251881Speter                        apr_pool_t *pool)
695251881Speter{
696251881Speter  const svn_fs_id_t *id = in;
697251881Speter  svn_stringbuf_t *serialized;
698251881Speter
699251881Speter  /* create an (empty) serialization context with plenty of buffer space */
700251881Speter  svn_temp_serializer__context_t *context =
701251881Speter      svn_temp_serializer__init(NULL, 0, 250, pool);
702251881Speter
703251881Speter  /* serialize the id */
704251881Speter  svn_fs_fs__id_serialize(context, &id);
705251881Speter
706251881Speter  /* return serialized data */
707251881Speter  serialized = svn_temp_serializer__get(context);
708251881Speter  *data = serialized->data;
709251881Speter  *data_len = serialized->len;
710251881Speter
711251881Speter  return SVN_NO_ERROR;
712251881Speter}
713251881Speter
714251881Spetersvn_error_t *
715251881Spetersvn_fs_fs__deserialize_id(void **out,
716251881Speter                          void *data,
717251881Speter                          apr_size_t data_len,
718251881Speter                          apr_pool_t *pool)
719251881Speter{
720251881Speter  /* Copy the _full_ buffer as it also contains the sub-structures. */
721251881Speter  svn_fs_id_t *id = (svn_fs_id_t *)data;
722251881Speter
723251881Speter  /* fixup of all pointers etc. */
724251881Speter  svn_fs_fs__id_deserialize(id, &id);
725251881Speter
726251881Speter  /* done */
727251881Speter  *out = id;
728251881Speter  return SVN_NO_ERROR;
729251881Speter}
730251881Speter
731251881Speter/** Caching node_revision_t objects. **/
732251881Speter
733251881Spetersvn_error_t *
734251881Spetersvn_fs_fs__serialize_node_revision(void **buffer,
735251881Speter                                   apr_size_t *buffer_size,
736251881Speter                                   void *item,
737251881Speter                                   apr_pool_t *pool)
738251881Speter{
739251881Speter  svn_stringbuf_t *serialized;
740251881Speter  node_revision_t *noderev = item;
741251881Speter
742251881Speter  /* create an (empty) serialization context with plenty of (initial)
743251881Speter   * buffer space. */
744251881Speter  svn_temp_serializer__context_t *context =
745251881Speter      svn_temp_serializer__init(NULL, 0,
746251881Speter                                1024 - SVN_TEMP_SERIALIZER__OVERHEAD,
747251881Speter                                pool);
748251881Speter
749251881Speter  /* serialize the noderev */
750251881Speter  svn_fs_fs__noderev_serialize(context, &noderev);
751251881Speter
752251881Speter  /* return serialized data */
753251881Speter  serialized = svn_temp_serializer__get(context);
754251881Speter  *buffer = serialized->data;
755251881Speter  *buffer_size = serialized->len;
756251881Speter
757251881Speter  return SVN_NO_ERROR;
758251881Speter}
759251881Speter
760251881Spetersvn_error_t *
761251881Spetersvn_fs_fs__deserialize_node_revision(void **item,
762251881Speter                                     void *buffer,
763251881Speter                                     apr_size_t buffer_size,
764251881Speter                                     apr_pool_t *pool)
765251881Speter{
766251881Speter  /* Copy the _full_ buffer as it also contains the sub-structures. */
767251881Speter  node_revision_t *noderev = (node_revision_t *)buffer;
768251881Speter
769251881Speter  /* fixup of all pointers etc. */
770251881Speter  svn_fs_fs__noderev_deserialize(noderev, &noderev);
771251881Speter
772251881Speter  /* done */
773251881Speter  *item = noderev;
774251881Speter  return SVN_NO_ERROR;
775251881Speter}
776251881Speter
777251881Speter/* Utility function that returns the directory serialized inside CONTEXT
778251881Speter * to DATA and DATA_LEN. */
779251881Speterstatic svn_error_t *
780251881Speterreturn_serialized_dir_context(svn_temp_serializer__context_t *context,
781251881Speter                              void **data,
782251881Speter                              apr_size_t *data_len)
783251881Speter{
784251881Speter  svn_stringbuf_t *serialized = svn_temp_serializer__get(context);
785251881Speter
786251881Speter  *data = serialized->data;
787251881Speter  *data_len = serialized->blocksize;
788251881Speter  ((hash_data_t *)serialized->data)->len = serialized->len;
789251881Speter
790251881Speter  return SVN_NO_ERROR;
791251881Speter}
792251881Speter
793251881Spetersvn_error_t *
794251881Spetersvn_fs_fs__serialize_dir_entries(void **data,
795251881Speter                                 apr_size_t *data_len,
796251881Speter                                 void *in,
797251881Speter                                 apr_pool_t *pool)
798251881Speter{
799251881Speter  apr_hash_t *dir = in;
800251881Speter
801251881Speter  /* serialize the dir content into a new serialization context
802251881Speter   * and return the serialized data */
803251881Speter  return return_serialized_dir_context(serialize_dir(dir, pool),
804251881Speter                                       data,
805251881Speter                                       data_len);
806251881Speter}
807251881Speter
808251881Spetersvn_error_t *
809251881Spetersvn_fs_fs__deserialize_dir_entries(void **out,
810251881Speter                                   void *data,
811251881Speter                                   apr_size_t data_len,
812251881Speter                                   apr_pool_t *pool)
813251881Speter{
814251881Speter  /* Copy the _full_ buffer as it also contains the sub-structures. */
815251881Speter  hash_data_t *hash_data = (hash_data_t *)data;
816251881Speter
817251881Speter  /* reconstruct the hash from the serialized data */
818251881Speter  *out = deserialize_dir(hash_data, hash_data, pool);
819251881Speter
820251881Speter  return SVN_NO_ERROR;
821251881Speter}
822251881Speter
823251881Spetersvn_error_t *
824251881Spetersvn_fs_fs__get_sharded_offset(void **out,
825251881Speter                              const void *data,
826251881Speter                              apr_size_t data_len,
827251881Speter                              void *baton,
828251881Speter                              apr_pool_t *pool)
829251881Speter{
830251881Speter  const apr_off_t *manifest = data;
831251881Speter  apr_int64_t shard_pos = *(apr_int64_t *)baton;
832251881Speter
833251881Speter  *(apr_off_t *)out = manifest[shard_pos];
834251881Speter
835251881Speter  return SVN_NO_ERROR;
836251881Speter}
837251881Speter
838251881Speter/* Utility function that returns the lowest index of the first entry in
839251881Speter * *ENTRIES that points to a dir entry with a name equal or larger than NAME.
840251881Speter * If an exact match has been found, *FOUND will be set to TRUE. COUNT is
841251881Speter * the number of valid entries in ENTRIES.
842251881Speter */
843251881Speterstatic apr_size_t
844251881Speterfind_entry(svn_fs_dirent_t **entries,
845251881Speter           const char *name,
846251881Speter           apr_size_t count,
847251881Speter           svn_boolean_t *found)
848251881Speter{
849251881Speter  /* binary search for the desired entry by name */
850251881Speter  apr_size_t lower = 0;
851251881Speter  apr_size_t upper = count;
852251881Speter  apr_size_t middle;
853251881Speter
854251881Speter  for (middle = upper / 2; lower < upper; middle = (upper + lower) / 2)
855251881Speter    {
856251881Speter      const svn_fs_dirent_t *entry =
857251881Speter          svn_temp_deserializer__ptr(entries, (const void *const *)&entries[middle]);
858251881Speter      const char* entry_name =
859251881Speter          svn_temp_deserializer__ptr(entry, (const void *const *)&entry->name);
860251881Speter
861251881Speter      int diff = strcmp(entry_name, name);
862251881Speter      if (diff < 0)
863251881Speter        lower = middle + 1;
864251881Speter      else
865251881Speter        upper = middle;
866251881Speter    }
867251881Speter
868251881Speter  /* check whether we actually found a match */
869251881Speter  *found = FALSE;
870251881Speter  if (lower < count)
871251881Speter    {
872251881Speter      const svn_fs_dirent_t *entry =
873251881Speter          svn_temp_deserializer__ptr(entries, (const void *const *)&entries[lower]);
874251881Speter      const char* entry_name =
875251881Speter          svn_temp_deserializer__ptr(entry, (const void *const *)&entry->name);
876251881Speter
877251881Speter      if (strcmp(entry_name, name) == 0)
878251881Speter        *found = TRUE;
879251881Speter    }
880251881Speter
881251881Speter  return lower;
882251881Speter}
883251881Speter
884251881Spetersvn_error_t *
885251881Spetersvn_fs_fs__extract_dir_entry(void **out,
886251881Speter                             const void *data,
887251881Speter                             apr_size_t data_len,
888251881Speter                             void *baton,
889251881Speter                             apr_pool_t *pool)
890251881Speter{
891251881Speter  const hash_data_t *hash_data = data;
892251881Speter  const char* name = baton;
893251881Speter  svn_boolean_t found;
894251881Speter
895251881Speter  /* resolve the reference to the entries array */
896251881Speter  const svn_fs_dirent_t * const *entries =
897251881Speter    svn_temp_deserializer__ptr(data, (const void *const *)&hash_data->entries);
898251881Speter
899251881Speter  /* resolve the reference to the lengths array */
900251881Speter  const apr_uint32_t *lengths =
901251881Speter    svn_temp_deserializer__ptr(data, (const void *const *)&hash_data->lengths);
902251881Speter
903251881Speter  /* binary search for the desired entry by name */
904251881Speter  apr_size_t pos = find_entry((svn_fs_dirent_t **)entries,
905251881Speter                              name,
906251881Speter                              hash_data->count,
907251881Speter                              &found);
908251881Speter
909251881Speter  /* de-serialize that entry or return NULL, if no match has been found */
910251881Speter  *out = NULL;
911251881Speter  if (found)
912251881Speter    {
913251881Speter      const svn_fs_dirent_t *source =
914251881Speter          svn_temp_deserializer__ptr(entries, (const void *const *)&entries[pos]);
915251881Speter
916251881Speter      /* Entries have been serialized one-by-one, each time including all
917251881Speter       * nested structures and strings. Therefore, they occupy a single
918251881Speter       * block of memory whose end-offset is either the beginning of the
919251881Speter       * next entry or the end of the buffer
920251881Speter       */
921251881Speter      apr_size_t size = lengths[pos];
922251881Speter
923251881Speter      /* copy & deserialize the entry */
924251881Speter      svn_fs_dirent_t *new_entry = apr_palloc(pool, size);
925251881Speter      memcpy(new_entry, source, size);
926251881Speter
927251881Speter      svn_temp_deserializer__resolve(new_entry, (void **)&new_entry->name);
928251881Speter      svn_fs_fs__id_deserialize(new_entry, (svn_fs_id_t **)&new_entry->id);
929251881Speter      *(svn_fs_dirent_t **)out = new_entry;
930251881Speter    }
931251881Speter
932251881Speter  return SVN_NO_ERROR;
933251881Speter}
934251881Speter
935251881Speter/* Utility function for svn_fs_fs__replace_dir_entry that implements the
936251881Speter * modification as a simply deserialize / modify / serialize sequence.
937251881Speter */
938251881Speterstatic svn_error_t *
939251881Speterslowly_replace_dir_entry(void **data,
940251881Speter                         apr_size_t *data_len,
941251881Speter                         void *baton,
942251881Speter                         apr_pool_t *pool)
943251881Speter{
944251881Speter  replace_baton_t *replace_baton = (replace_baton_t *)baton;
945251881Speter  hash_data_t *hash_data = (hash_data_t *)*data;
946251881Speter  apr_hash_t *dir;
947251881Speter
948251881Speter  SVN_ERR(svn_fs_fs__deserialize_dir_entries((void **)&dir,
949251881Speter                                             *data,
950251881Speter                                             hash_data->len,
951251881Speter                                             pool));
952251881Speter  svn_hash_sets(dir, replace_baton->name, replace_baton->new_entry);
953251881Speter
954251881Speter  return svn_fs_fs__serialize_dir_entries(data, data_len, dir, pool);
955251881Speter}
956251881Speter
957251881Spetersvn_error_t *
958251881Spetersvn_fs_fs__replace_dir_entry(void **data,
959251881Speter                             apr_size_t *data_len,
960251881Speter                             void *baton,
961251881Speter                             apr_pool_t *pool)
962251881Speter{
963251881Speter  replace_baton_t *replace_baton = (replace_baton_t *)baton;
964251881Speter  hash_data_t *hash_data = (hash_data_t *)*data;
965251881Speter  svn_boolean_t found;
966251881Speter  svn_fs_dirent_t **entries;
967251881Speter  apr_uint32_t *lengths;
968251881Speter  apr_uint32_t length;
969251881Speter  apr_size_t pos;
970251881Speter
971251881Speter  svn_temp_serializer__context_t *context;
972251881Speter
973251881Speter  /* after quite a number of operations, let's re-pack everything.
974251881Speter   * This is to limit the number of vasted space as we cannot overwrite
975251881Speter   * existing data but must always append. */
976251881Speter  if (hash_data->operations > 2 + hash_data->count / 4)
977251881Speter    return slowly_replace_dir_entry(data, data_len, baton, pool);
978251881Speter
979251881Speter  /* resolve the reference to the entries array */
980251881Speter  entries = (svn_fs_dirent_t **)
981251881Speter    svn_temp_deserializer__ptr((const char *)hash_data,
982251881Speter                               (const void *const *)&hash_data->entries);
983251881Speter
984251881Speter  /* resolve the reference to the lengths array */
985251881Speter  lengths = (apr_uint32_t *)
986251881Speter    svn_temp_deserializer__ptr((const char *)hash_data,
987251881Speter                               (const void *const *)&hash_data->lengths);
988251881Speter
989251881Speter  /* binary search for the desired entry by name */
990251881Speter  pos = find_entry(entries, replace_baton->name, hash_data->count, &found);
991251881Speter
992251881Speter  /* handle entry removal (if found at all) */
993251881Speter  if (replace_baton->new_entry == NULL)
994251881Speter    {
995251881Speter      if (found)
996251881Speter        {
997251881Speter          /* remove reference to the entry from the index */
998251881Speter          memmove(&entries[pos],
999251881Speter                  &entries[pos + 1],
1000251881Speter                  sizeof(entries[pos]) * (hash_data->count - pos));
1001251881Speter          memmove(&lengths[pos],
1002251881Speter                  &lengths[pos + 1],
1003251881Speter                  sizeof(lengths[pos]) * (hash_data->count - pos));
1004251881Speter
1005251881Speter          hash_data->count--;
1006251881Speter          hash_data->over_provision++;
1007251881Speter          hash_data->operations++;
1008251881Speter        }
1009251881Speter
1010251881Speter      return SVN_NO_ERROR;
1011251881Speter    }
1012251881Speter
1013251881Speter  /* if not found, prepare to insert the new entry */
1014251881Speter  if (!found)
1015251881Speter    {
1016251881Speter      /* fallback to slow operation if there is no place left to insert an
1017251881Speter       * new entry to index. That will automatically give add some spare
1018251881Speter       * entries ("overprovision"). */
1019251881Speter      if (hash_data->over_provision == 0)
1020251881Speter        return slowly_replace_dir_entry(data, data_len, baton, pool);
1021251881Speter
1022251881Speter      /* make entries[index] available for pointing to the new entry */
1023251881Speter      memmove(&entries[pos + 1],
1024251881Speter              &entries[pos],
1025251881Speter              sizeof(entries[pos]) * (hash_data->count - pos));
1026251881Speter      memmove(&lengths[pos + 1],
1027251881Speter              &lengths[pos],
1028251881Speter              sizeof(lengths[pos]) * (hash_data->count - pos));
1029251881Speter
1030251881Speter      hash_data->count++;
1031251881Speter      hash_data->over_provision--;
1032251881Speter      hash_data->operations++;
1033251881Speter    }
1034251881Speter
1035251881Speter  /* de-serialize the new entry */
1036251881Speter  entries[pos] = replace_baton->new_entry;
1037251881Speter  context = svn_temp_serializer__init_append(hash_data,
1038251881Speter                                             entries,
1039251881Speter                                             hash_data->len,
1040251881Speter                                             *data_len,
1041251881Speter                                             pool);
1042251881Speter  serialize_dir_entry(context, &entries[pos], &length);
1043251881Speter
1044251881Speter  /* return the updated serialized data */
1045251881Speter  SVN_ERR (return_serialized_dir_context(context,
1046251881Speter                                         data,
1047251881Speter                                         data_len));
1048251881Speter
1049251881Speter  /* since the previous call may have re-allocated the buffer, the lengths
1050251881Speter   * pointer may no longer point to the entry in that buffer. Therefore,
1051251881Speter   * re-map it again and store the length value after that. */
1052251881Speter
1053251881Speter  hash_data = (hash_data_t *)*data;
1054251881Speter  lengths = (apr_uint32_t *)
1055251881Speter    svn_temp_deserializer__ptr((const char *)hash_data,
1056251881Speter                               (const void *const *)&hash_data->lengths);
1057251881Speter  lengths[pos] = length;
1058251881Speter
1059251881Speter  return SVN_NO_ERROR;
1060251881Speter}
1061251881Speter
1062251881Speter/* Utility function to serialize change CHANGE_P in the given serialization
1063251881Speter * CONTEXT.
1064251881Speter */
1065251881Speterstatic void
1066251881Speterserialize_change(svn_temp_serializer__context_t *context,
1067251881Speter                 change_t * const *change_p)
1068251881Speter{
1069251881Speter  const change_t * change = *change_p;
1070251881Speter  if (change == NULL)
1071251881Speter    return;
1072251881Speter
1073251881Speter  /* serialize the change struct itself */
1074251881Speter  svn_temp_serializer__push(context,
1075251881Speter                            (const void * const *)change_p,
1076251881Speter                            sizeof(*change));
1077251881Speter
1078251881Speter  /* serialize sub-structures */
1079251881Speter  svn_fs_fs__id_serialize(context, &change->noderev_id);
1080251881Speter
1081251881Speter  svn_temp_serializer__add_string(context, &change->path);
1082251881Speter  svn_temp_serializer__add_string(context, &change->copyfrom_path);
1083251881Speter
1084251881Speter  /* return to the caller's nesting level */
1085251881Speter  svn_temp_serializer__pop(context);
1086251881Speter}
1087251881Speter
1088251881Speter/* Utility function to serialize the CHANGE_P within the given
1089251881Speter * serialization CONTEXT.
1090251881Speter */
1091251881Speterstatic void
1092251881Speterdeserialize_change(void *buffer, change_t **change_p)
1093251881Speter{
1094251881Speter  change_t * change;
1095251881Speter
1096251881Speter  /* fix-up of the pointer to the struct in question */
1097251881Speter  svn_temp_deserializer__resolve(buffer, (void **)change_p);
1098251881Speter
1099251881Speter  change = *change_p;
1100251881Speter  if (change == NULL)
1101251881Speter    return;
1102251881Speter
1103251881Speter  /* fix-up of sub-structures */
1104251881Speter  svn_fs_fs__id_deserialize(change, (svn_fs_id_t **)&change->noderev_id);
1105251881Speter
1106251881Speter  svn_temp_deserializer__resolve(change, (void **)&change->path);
1107251881Speter  svn_temp_deserializer__resolve(change, (void **)&change->copyfrom_path);
1108251881Speter}
1109251881Speter
1110251881Speter/* Auxiliary structure representing the content of a change_t array.
1111251881Speter   This structure is much easier to (de-)serialize than an APR array.
1112251881Speter */
1113251881Spetertypedef struct changes_data_t
1114251881Speter{
1115251881Speter  /* number of entries in the array */
1116251881Speter  int count;
1117251881Speter
1118251881Speter  /* reference to the changes */
1119251881Speter  change_t **changes;
1120251881Speter} changes_data_t;
1121251881Speter
1122251881Spetersvn_error_t *
1123251881Spetersvn_fs_fs__serialize_changes(void **data,
1124251881Speter                             apr_size_t *data_len,
1125251881Speter                             void *in,
1126251881Speter                             apr_pool_t *pool)
1127251881Speter{
1128251881Speter  apr_array_header_t *array = in;
1129251881Speter  changes_data_t changes;
1130251881Speter  svn_temp_serializer__context_t *context;
1131251881Speter  svn_stringbuf_t *serialized;
1132251881Speter  int i;
1133251881Speter
1134251881Speter  /* initialize our auxiliary data structure */
1135251881Speter  changes.count = array->nelts;
1136251881Speter  changes.changes = apr_palloc(pool, sizeof(change_t*) * changes.count);
1137251881Speter
1138251881Speter  /* populate it with the array elements */
1139251881Speter  for (i = 0; i < changes.count; ++i)
1140251881Speter    changes.changes[i] = APR_ARRAY_IDX(array, i, change_t*);
1141251881Speter
1142251881Speter  /* serialize it and all its elements */
1143251881Speter  context = svn_temp_serializer__init(&changes,
1144251881Speter                                      sizeof(changes),
1145251881Speter                                      changes.count * 100,
1146251881Speter                                      pool);
1147251881Speter
1148251881Speter  svn_temp_serializer__push(context,
1149251881Speter                            (const void * const *)&changes.changes,
1150251881Speter                            changes.count * sizeof(change_t*));
1151251881Speter
1152251881Speter  for (i = 0; i < changes.count; ++i)
1153251881Speter    serialize_change(context, &changes.changes[i]);
1154251881Speter
1155251881Speter  svn_temp_serializer__pop(context);
1156251881Speter
1157251881Speter  /* return the serialized result */
1158251881Speter  serialized = svn_temp_serializer__get(context);
1159251881Speter
1160251881Speter  *data = serialized->data;
1161251881Speter  *data_len = serialized->len;
1162251881Speter
1163251881Speter  return SVN_NO_ERROR;
1164251881Speter}
1165251881Speter
1166251881Spetersvn_error_t *
1167251881Spetersvn_fs_fs__deserialize_changes(void **out,
1168251881Speter                               void *data,
1169251881Speter                               apr_size_t data_len,
1170251881Speter                               apr_pool_t *pool)
1171251881Speter{
1172251881Speter  int i;
1173251881Speter  changes_data_t *changes = (changes_data_t *)data;
1174251881Speter  apr_array_header_t *array = apr_array_make(pool, changes->count,
1175251881Speter                                             sizeof(change_t *));
1176251881Speter
1177251881Speter  /* de-serialize our auxiliary data structure */
1178251881Speter  svn_temp_deserializer__resolve(changes, (void**)&changes->changes);
1179251881Speter
1180251881Speter  /* de-serialize each entry and add it to the array */
1181251881Speter  for (i = 0; i < changes->count; ++i)
1182251881Speter    {
1183251881Speter      deserialize_change((void*)changes->changes,
1184251881Speter                         (change_t **)&changes->changes[i]);
1185251881Speter      APR_ARRAY_PUSH(array, change_t *) = changes->changes[i];
1186251881Speter    }
1187251881Speter
1188251881Speter  /* done */
1189251881Speter  *out = array;
1190251881Speter
1191251881Speter  return SVN_NO_ERROR;
1192251881Speter}
1193251881Speter
1194251881Speter/* Auxiliary structure representing the content of a svn_mergeinfo_t hash.
1195251881Speter   This structure is much easier to (de-)serialize than an APR array.
1196251881Speter */
1197251881Spetertypedef struct mergeinfo_data_t
1198251881Speter{
1199251881Speter  /* number of paths in the hash */
1200251881Speter  unsigned count;
1201251881Speter
1202251881Speter  /* COUNT keys (paths) */
1203251881Speter  const char **keys;
1204251881Speter
1205251881Speter  /* COUNT keys lengths (strlen of path) */
1206251881Speter  apr_ssize_t *key_lengths;
1207251881Speter
1208251881Speter  /* COUNT entries, each giving the number of ranges for the key */
1209251881Speter  int *range_counts;
1210251881Speter
1211251881Speter  /* all ranges in a single, concatenated buffer */
1212251881Speter  svn_merge_range_t *ranges;
1213251881Speter} mergeinfo_data_t;
1214251881Speter
1215251881Spetersvn_error_t *
1216251881Spetersvn_fs_fs__serialize_mergeinfo(void **data,
1217251881Speter                               apr_size_t *data_len,
1218251881Speter                               void *in,
1219251881Speter                               apr_pool_t *pool)
1220251881Speter{
1221251881Speter  svn_mergeinfo_t mergeinfo = in;
1222251881Speter  mergeinfo_data_t merges;
1223251881Speter  svn_temp_serializer__context_t *context;
1224251881Speter  svn_stringbuf_t *serialized;
1225251881Speter  apr_hash_index_t *hi;
1226251881Speter  unsigned i;
1227251881Speter  int k;
1228251881Speter  apr_size_t range_count;
1229251881Speter
1230251881Speter  /* initialize our auxiliary data structure */
1231251881Speter  merges.count = apr_hash_count(mergeinfo);
1232251881Speter  merges.keys = apr_palloc(pool, sizeof(*merges.keys) * merges.count);
1233251881Speter  merges.key_lengths = apr_palloc(pool, sizeof(*merges.key_lengths) *
1234251881Speter                                        merges.count);
1235251881Speter  merges.range_counts = apr_palloc(pool, sizeof(*merges.range_counts) *
1236251881Speter                                         merges.count);
1237251881Speter
1238251881Speter  i = 0;
1239251881Speter  range_count = 0;
1240251881Speter  for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi), ++i)
1241251881Speter    {
1242251881Speter      svn_rangelist_t *ranges;
1243251881Speter      apr_hash_this(hi, (const void**)&merges.keys[i],
1244251881Speter                        &merges.key_lengths[i],
1245251881Speter                        (void **)&ranges);
1246251881Speter      merges.range_counts[i] = ranges->nelts;
1247251881Speter      range_count += ranges->nelts;
1248251881Speter    }
1249251881Speter
1250251881Speter  merges.ranges = apr_palloc(pool, sizeof(*merges.ranges) * range_count);
1251251881Speter
1252251881Speter  i = 0;
1253251881Speter  for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
1254251881Speter    {
1255251881Speter      svn_rangelist_t *ranges = svn__apr_hash_index_val(hi);
1256251881Speter      for (k = 0; k < ranges->nelts; ++k, ++i)
1257251881Speter        merges.ranges[i] = *APR_ARRAY_IDX(ranges, k, svn_merge_range_t*);
1258251881Speter    }
1259251881Speter
1260251881Speter  /* serialize it and all its elements */
1261251881Speter  context = svn_temp_serializer__init(&merges,
1262251881Speter                                      sizeof(merges),
1263251881Speter                                      range_count * 30,
1264251881Speter                                      pool);
1265251881Speter
1266251881Speter  /* keys array */
1267251881Speter  svn_temp_serializer__push(context,
1268251881Speter                            (const void * const *)&merges.keys,
1269251881Speter                            merges.count * sizeof(*merges.keys));
1270251881Speter
1271251881Speter  for (i = 0; i < merges.count; ++i)
1272251881Speter    svn_temp_serializer__add_string(context, &merges.keys[i]);
1273251881Speter
1274251881Speter  svn_temp_serializer__pop(context);
1275251881Speter
1276251881Speter  /* key lengths array */
1277251881Speter  svn_temp_serializer__push(context,
1278251881Speter                            (const void * const *)&merges.key_lengths,
1279251881Speter                            merges.count * sizeof(*merges.key_lengths));
1280251881Speter  svn_temp_serializer__pop(context);
1281251881Speter
1282251881Speter  /* range counts array */
1283251881Speter  svn_temp_serializer__push(context,
1284251881Speter                            (const void * const *)&merges.range_counts,
1285251881Speter                            merges.count * sizeof(*merges.range_counts));
1286251881Speter  svn_temp_serializer__pop(context);
1287251881Speter
1288251881Speter  /* ranges */
1289251881Speter  svn_temp_serializer__push(context,
1290251881Speter                            (const void * const *)&merges.ranges,
1291251881Speter                            range_count * sizeof(*merges.ranges));
1292251881Speter  svn_temp_serializer__pop(context);
1293251881Speter
1294251881Speter  /* return the serialized result */
1295251881Speter  serialized = svn_temp_serializer__get(context);
1296251881Speter
1297251881Speter  *data = serialized->data;
1298251881Speter  *data_len = serialized->len;
1299251881Speter
1300251881Speter  return SVN_NO_ERROR;
1301251881Speter}
1302251881Speter
1303251881Spetersvn_error_t *
1304251881Spetersvn_fs_fs__deserialize_mergeinfo(void **out,
1305251881Speter                                 void *data,
1306251881Speter                                 apr_size_t data_len,
1307251881Speter                                 apr_pool_t *pool)
1308251881Speter{
1309251881Speter  unsigned i;
1310251881Speter  int k, n;
1311251881Speter  mergeinfo_data_t *merges = (mergeinfo_data_t *)data;
1312251881Speter  svn_mergeinfo_t mergeinfo;
1313251881Speter
1314251881Speter  /* de-serialize our auxiliary data structure */
1315251881Speter  svn_temp_deserializer__resolve(merges, (void**)&merges->keys);
1316251881Speter  svn_temp_deserializer__resolve(merges, (void**)&merges->key_lengths);
1317251881Speter  svn_temp_deserializer__resolve(merges, (void**)&merges->range_counts);
1318251881Speter  svn_temp_deserializer__resolve(merges, (void**)&merges->ranges);
1319251881Speter
1320251881Speter  /* de-serialize keys and add entries to the result */
1321251881Speter  n = 0;
1322251881Speter  mergeinfo = svn_hash__make(pool);
1323251881Speter  for (i = 0; i < merges->count; ++i)
1324251881Speter    {
1325251881Speter      svn_rangelist_t *ranges = apr_array_make(pool,
1326251881Speter                                               merges->range_counts[i],
1327251881Speter                                               sizeof(svn_merge_range_t*));
1328251881Speter      for (k = 0; k < merges->range_counts[i]; ++k, ++n)
1329251881Speter        APR_ARRAY_PUSH(ranges, svn_merge_range_t*) = &merges->ranges[n];
1330251881Speter
1331251881Speter      svn_temp_deserializer__resolve((void*)merges->keys,
1332251881Speter                                     (void**)&merges->keys[i]);
1333251881Speter      apr_hash_set(mergeinfo, merges->keys[i], merges->key_lengths[i], ranges);
1334251881Speter    }
1335251881Speter
1336251881Speter  /* done */
1337251881Speter  *out = mergeinfo;
1338251881Speter
1339251881Speter  return SVN_NO_ERROR;
1340251881Speter}
1341251881Speter
1342