1289177Speter/* noderevs.h --- FSX node revision container
2289177Speter *
3289177Speter * ====================================================================
4289177Speter *    Licensed to the Apache Software Foundation (ASF) under one
5289177Speter *    or more contributor license agreements.  See the NOTICE file
6289177Speter *    distributed with this work for additional information
7289177Speter *    regarding copyright ownership.  The ASF licenses this file
8289177Speter *    to you under the Apache License, Version 2.0 (the
9289177Speter *    "License"); you may not use this file except in compliance
10289177Speter *    with the License.  You may obtain a copy of the License at
11289177Speter *
12289177Speter *      http://www.apache.org/licenses/LICENSE-2.0
13289177Speter *
14289177Speter *    Unless required by applicable law or agreed to in writing,
15289177Speter *    software distributed under the License is distributed on an
16289177Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17289177Speter *    KIND, either express or implied.  See the License for the
18289177Speter *    specific language governing permissions and limitations
19289177Speter *    under the License.
20289177Speter * ====================================================================
21289177Speter */
22289177Speter
23289177Speter#include "svn_private_config.h"
24289177Speter
25289177Speter#include "private/svn_dep_compat.h"
26289177Speter#include "private/svn_packed_data.h"
27289177Speter#include "private/svn_subr_private.h"
28289177Speter#include "private/svn_temp_serializer.h"
29289177Speter
30289177Speter#include "noderevs.h"
31289177Speter#include "string_table.h"
32289177Speter#include "temp_serializer.h"
33289177Speter
34289177Speter/* These flags will be used with the FLAGS field in binary_noderev_t.
35289177Speter */
36289177Speter
37289177Speter/* (flags & NODEREV_KIND_MASK) extracts the noderev type */
38289177Speter#define NODEREV_KIND_MASK    0x00007
39289177Speter
40289177Speter/* the noderev has merge info */
41289177Speter#define NODEREV_HAS_MINFO    0x00008
42289177Speter
43289177Speter/* the noderev has copy-from-path and revision */
44289177Speter#define NODEREV_HAS_COPYFROM 0x00010
45289177Speter
46289177Speter/* the noderev has copy-root path and revision */
47289177Speter#define NODEREV_HAS_COPYROOT 0x00020
48289177Speter
49289177Speter/* the noderev has copy-root path and revision */
50289177Speter#define NODEREV_HAS_CPATH    0x00040
51289177Speter
52289177Speter/* Our internal representation of a svn_fs_x__noderev_t.
53289177Speter *
54289177Speter * We will store path strings in a string container and reference them
55289177Speter * from here.  Similarly, IDs and representations are being stored in
56289177Speter * separate containers and then also referenced here.  This eliminates
57289177Speter * the need to store the same IDs and representations more than once.
58289177Speter */
59289177Spetertypedef struct binary_noderev_t
60289177Speter{
61289177Speter  /* node type and presence indicators */
62289177Speter  apr_uint32_t flags;
63289177Speter
64289177Speter  /* Index+1 of the noderev-id for this node-rev. */
65289177Speter  int id;
66289177Speter
67289177Speter  /* Index+1 of the node-id for this node-rev. */
68289177Speter  int node_id;
69289177Speter
70289177Speter  /* Index+1 of the copy-id for this node-rev. */
71289177Speter  int copy_id;
72289177Speter
73289177Speter  /* Index+1 of the predecessor node revision id, or 0 if there is no
74289177Speter     predecessor for this node revision */
75289177Speter  int predecessor_id;
76289177Speter
77289177Speter  /* number of predecessors this node revision has (recursively), or
78289177Speter     -1 if not known (for backward compatibility). */
79289177Speter  int predecessor_count;
80289177Speter
81289177Speter  /* If this node-rev is a copy, what revision was it copied from? */
82289177Speter  svn_revnum_t copyfrom_rev;
83289177Speter
84289177Speter  /* Helper for history tracing, root revision of the parent tree from
85289177Speter     whence this node-rev was copied. */
86289177Speter  svn_revnum_t copyroot_rev;
87289177Speter
88289177Speter  /* If this node-rev is a copy, this is the string index+1 of the path
89289177Speter     from which that copy way made. 0, otherwise. */
90289177Speter  apr_size_t copyfrom_path;
91289177Speter
92289177Speter  /* String index+1 of the root of the parent tree from whence this node-
93289177Speter   * rev was copied. */
94289177Speter  apr_size_t copyroot_path;
95289177Speter
96289177Speter  /* Index+1 of the representation key for this node's properties.
97289177Speter     May be 0 if there are no properties.  */
98289177Speter  int prop_rep;
99289177Speter
100289177Speter  /* Index+1 of the representation for this node's data.
101289177Speter     May be 0 if there is no data. */
102289177Speter  int data_rep;
103289177Speter
104289177Speter  /* String index+1 of the path at which this node first came into
105289177Speter     existence.  */
106289177Speter  apr_size_t created_path;
107289177Speter
108289177Speter  /* Number of nodes with svn:mergeinfo properties that are
109289177Speter     descendants of this node (including it itself) */
110289177Speter  apr_int64_t mergeinfo_count;
111289177Speter
112289177Speter} binary_noderev_t;
113289177Speter
114289177Speter/* The actual container object.  Node revisions are concatenated into
115289177Speter * NODEREVS, referenced representations are stored in DATA_REPS / PROP_REPS
116289177Speter * and the ids in IDs.  PATHS is the string table for all paths.
117289177Speter *
118289177Speter * During construction, BUILDER will be used instead of PATHS. IDS_DICT,
119289177Speter * DATA_REPS_DICT and PROP_REPS_DICT are also only used during construction
120289177Speter * and are NULL otherwise.
121289177Speter */
122289177Speterstruct svn_fs_x__noderevs_t
123289177Speter{
124289177Speter  /* The paths - either in 'builder' mode or finalized mode.
125289177Speter   * The respective other pointer will be NULL. */
126289177Speter  string_table_builder_t *builder;
127289177Speter  string_table_t *paths;
128289177Speter
129289177Speter  /* During construction, maps a full binary_id_t to an index into IDS */
130289177Speter  apr_hash_t *ids_dict;
131289177Speter
132289177Speter  /* During construction, maps a full binary_representation_t to an index
133289177Speter   * into REPS. */
134289177Speter  apr_hash_t *reps_dict;
135289177Speter
136289177Speter  /* array of binary_id_t */
137289177Speter  apr_array_header_t *ids;
138289177Speter
139289177Speter  /* array of binary_representation_t */
140289177Speter  apr_array_header_t *reps;
141289177Speter
142289177Speter  /* array of binary_noderev_t. */
143289177Speter  apr_array_header_t *noderevs;
144289177Speter};
145289177Speter
146289177Spetersvn_fs_x__noderevs_t *
147289177Spetersvn_fs_x__noderevs_create(int initial_count,
148289177Speter                          apr_pool_t* result_pool)
149289177Speter{
150289177Speter  svn_fs_x__noderevs_t *noderevs
151289177Speter    = apr_palloc(result_pool, sizeof(*noderevs));
152289177Speter
153289177Speter  noderevs->builder = svn_fs_x__string_table_builder_create(result_pool);
154289177Speter  noderevs->ids_dict = svn_hash__make(result_pool);
155289177Speter  noderevs->reps_dict = svn_hash__make(result_pool);
156289177Speter  noderevs->paths = NULL;
157289177Speter
158289177Speter  noderevs->ids
159289177Speter    = apr_array_make(result_pool, 2 * initial_count, sizeof(svn_fs_x__id_t));
160289177Speter  noderevs->reps
161289177Speter    = apr_array_make(result_pool, 2 * initial_count,
162289177Speter                     sizeof(svn_fs_x__representation_t));
163289177Speter  noderevs->noderevs
164289177Speter    = apr_array_make(result_pool, initial_count, sizeof(binary_noderev_t));
165289177Speter
166289177Speter  return noderevs;
167289177Speter}
168289177Speter
169289177Speter/* Given the ID, return the index+1 into IDS that contains a binary_id
170289177Speter * for it.  Returns 0 for NULL IDs.  We use DICT to detect duplicates.
171289177Speter */
172289177Speterstatic int
173289177Speterstore_id(apr_array_header_t *ids,
174289177Speter         apr_hash_t *dict,
175289177Speter         const svn_fs_x__id_t *id)
176289177Speter{
177289177Speter  int idx;
178289177Speter  void *idx_void;
179289177Speter
180289177Speter  if (!svn_fs_x__id_used(id))
181289177Speter    return 0;
182289177Speter
183289177Speter  idx_void = apr_hash_get(dict, &id, sizeof(id));
184289177Speter  idx = (int)(apr_uintptr_t)idx_void;
185289177Speter  if (idx == 0)
186289177Speter    {
187289177Speter      APR_ARRAY_PUSH(ids, svn_fs_x__id_t) = *id;
188289177Speter      idx = ids->nelts;
189289177Speter      apr_hash_set(dict, ids->elts + (idx-1) * ids->elt_size,
190289177Speter                   ids->elt_size, (void*)(apr_uintptr_t)idx);
191289177Speter    }
192289177Speter
193289177Speter  return idx;
194289177Speter}
195289177Speter
196289177Speter/* Given the REP, return the index+1 into REPS that contains a copy of it.
197289177Speter * Returns 0 for NULL IDs.  We use DICT to detect duplicates.
198289177Speter */
199289177Speterstatic int
200289177Speterstore_representation(apr_array_header_t *reps,
201289177Speter                     apr_hash_t *dict,
202289177Speter                     const svn_fs_x__representation_t *rep)
203289177Speter{
204289177Speter  int idx;
205289177Speter  void *idx_void;
206289177Speter
207289177Speter  if (rep == NULL)
208289177Speter    return 0;
209289177Speter
210289177Speter  idx_void = apr_hash_get(dict, rep, sizeof(*rep));
211289177Speter  idx = (int)(apr_uintptr_t)idx_void;
212289177Speter  if (idx == 0)
213289177Speter    {
214289177Speter      APR_ARRAY_PUSH(reps, svn_fs_x__representation_t) = *rep;
215289177Speter      idx = reps->nelts;
216289177Speter      apr_hash_set(dict, reps->elts + (idx-1) * reps->elt_size,
217289177Speter                   reps->elt_size, (void*)(apr_uintptr_t)idx);
218289177Speter    }
219289177Speter
220289177Speter  return idx;
221289177Speter}
222289177Speter
223289177Speterapr_size_t
224289177Spetersvn_fs_x__noderevs_add(svn_fs_x__noderevs_t *container,
225289177Speter                       svn_fs_x__noderev_t *noderev)
226289177Speter{
227289177Speter  binary_noderev_t binary_noderev = { 0 };
228289177Speter
229289177Speter  binary_noderev.flags = (noderev->has_mergeinfo ? NODEREV_HAS_MINFO : 0)
230289177Speter                       | (noderev->copyfrom_path ? NODEREV_HAS_COPYFROM : 0)
231289177Speter                       | (noderev->copyroot_path  ? NODEREV_HAS_COPYROOT : 0)
232289177Speter                       | (noderev->created_path  ? NODEREV_HAS_CPATH : 0)
233289177Speter                       | (int)noderev->kind;
234289177Speter
235289177Speter  binary_noderev.id
236289177Speter    = store_id(container->ids, container->ids_dict, &noderev->noderev_id);
237289177Speter  binary_noderev.node_id
238289177Speter    = store_id(container->ids, container->ids_dict, &noderev->node_id);
239289177Speter  binary_noderev.copy_id
240289177Speter    = store_id(container->ids, container->ids_dict, &noderev->copy_id);
241289177Speter  binary_noderev.predecessor_id
242289177Speter    = store_id(container->ids, container->ids_dict, &noderev->predecessor_id);
243289177Speter
244289177Speter  if (noderev->copyfrom_path)
245289177Speter    {
246289177Speter      binary_noderev.copyfrom_path
247289177Speter        = svn_fs_x__string_table_builder_add(container->builder,
248289177Speter                                             noderev->copyfrom_path,
249289177Speter                                             0);
250289177Speter      binary_noderev.copyfrom_rev = noderev->copyfrom_rev;
251289177Speter    }
252289177Speter
253289177Speter  if (noderev->copyroot_path)
254289177Speter    {
255289177Speter      binary_noderev.copyroot_path
256289177Speter        = svn_fs_x__string_table_builder_add(container->builder,
257289177Speter                                             noderev->copyroot_path,
258289177Speter                                             0);
259289177Speter      binary_noderev.copyroot_rev = noderev->copyroot_rev;
260289177Speter    }
261289177Speter
262289177Speter  binary_noderev.predecessor_count = noderev->predecessor_count;
263289177Speter  binary_noderev.prop_rep = store_representation(container->reps,
264289177Speter                                                 container->reps_dict,
265289177Speter                                                 noderev->prop_rep);
266289177Speter  binary_noderev.data_rep = store_representation(container->reps,
267289177Speter                                                 container->reps_dict,
268289177Speter                                                 noderev->data_rep);
269289177Speter
270289177Speter  if (noderev->created_path)
271289177Speter    binary_noderev.created_path
272289177Speter      = svn_fs_x__string_table_builder_add(container->builder,
273289177Speter                                           noderev->created_path,
274289177Speter                                           0);
275289177Speter
276289177Speter  binary_noderev.mergeinfo_count = noderev->mergeinfo_count;
277289177Speter
278289177Speter  APR_ARRAY_PUSH(container->noderevs, binary_noderev_t) = binary_noderev;
279289177Speter
280289177Speter  return container->noderevs->nelts - 1;
281289177Speter}
282289177Speter
283289177Speterapr_size_t
284289177Spetersvn_fs_x__noderevs_estimate_size(const svn_fs_x__noderevs_t *container)
285289177Speter{
286289177Speter  /* CONTAINER must be in 'builder' mode */
287289177Speter  if (container->builder == NULL)
288289177Speter    return 0;
289289177Speter
290289177Speter  /* string table code makes its own prediction,
291289177Speter   * noderevs should be < 16 bytes each,
292289177Speter   * id parts < 4 bytes each,
293289177Speter   * data representations < 40 bytes each,
294289177Speter   * property representations < 30 bytes each,
295289177Speter   * some static overhead should be assumed */
296289177Speter  return svn_fs_x__string_table_builder_estimate_size(container->builder)
297289177Speter       + container->noderevs->nelts * 16
298289177Speter       + container->ids->nelts * 4
299289177Speter       + container->reps->nelts * 40
300289177Speter       + 100;
301289177Speter}
302289177Speter
303289177Speter/* Set *ID to the ID part stored at index IDX in IDS.
304289177Speter */
305289177Speterstatic svn_error_t *
306289177Speterget_id(svn_fs_x__id_t *id,
307289177Speter       const apr_array_header_t *ids,
308289177Speter       int idx)
309289177Speter{
310289177Speter  /* handle NULL IDs  */
311289177Speter  if (idx == 0)
312289177Speter    {
313289177Speter      svn_fs_x__id_reset(id);
314289177Speter      return SVN_NO_ERROR;
315289177Speter    }
316289177Speter
317289177Speter  /* check for corrupted data */
318289177Speter  if (idx < 0 || idx > ids->nelts)
319289177Speter    return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL,
320289177Speter                             _("ID part index %d exceeds container size %d"),
321289177Speter                             idx, ids->nelts);
322289177Speter
323289177Speter  /* Return the requested ID. */
324289177Speter  *id = APR_ARRAY_IDX(ids, idx - 1, svn_fs_x__id_t);
325289177Speter
326289177Speter  return SVN_NO_ERROR;
327289177Speter}
328289177Speter
329289177Speter/* Create a svn_fs_x__representation_t in *REP, allocated in POOL based on the
330289177Speter * representation stored at index IDX in REPS.
331289177Speter */
332289177Speterstatic svn_error_t *
333289177Speterget_representation(svn_fs_x__representation_t **rep,
334289177Speter                   const apr_array_header_t *reps,
335289177Speter                   int idx,
336289177Speter                   apr_pool_t *pool)
337289177Speter{
338289177Speter  /* handle NULL representations  */
339289177Speter  if (idx == 0)
340289177Speter    {
341289177Speter      *rep = NULL;
342289177Speter      return SVN_NO_ERROR;
343289177Speter    }
344289177Speter
345289177Speter  /* check for corrupted data */
346289177Speter  if (idx < 0 || idx > reps->nelts)
347289177Speter    return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL,
348289177Speter                             _("Node revision ID index %d"
349289177Speter                               " exceeds container size %d"),
350289177Speter                             idx, reps->nelts);
351289177Speter
352289177Speter  /* no translation required. Just duplicate the info */
353289177Speter  *rep = apr_pmemdup(pool,
354289177Speter                     &APR_ARRAY_IDX(reps, idx - 1, svn_fs_x__representation_t),
355289177Speter                     sizeof(**rep));
356289177Speter
357289177Speter  return SVN_NO_ERROR;
358289177Speter}
359289177Speter
360289177Spetersvn_error_t *
361289177Spetersvn_fs_x__noderevs_get(svn_fs_x__noderev_t **noderev_p,
362289177Speter                       const svn_fs_x__noderevs_t *container,
363289177Speter                       apr_size_t idx,
364289177Speter                       apr_pool_t *pool)
365289177Speter{
366289177Speter  svn_fs_x__noderev_t *noderev;
367289177Speter  binary_noderev_t *binary_noderev;
368289177Speter
369289177Speter  /* CONTAINER must be in 'finalized' mode */
370289177Speter  SVN_ERR_ASSERT(container->builder == NULL);
371289177Speter  SVN_ERR_ASSERT(container->paths);
372289177Speter
373289177Speter  /* validate index */
374289177Speter  if (idx >= (apr_size_t)container->noderevs->nelts)
375289177Speter    return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL,
376289177Speter                             apr_psprintf(pool,
377289177Speter                                          _("Node revision index %%%s"
378289177Speter                                            " exceeds container size %%d"),
379289177Speter                                          APR_SIZE_T_FMT),
380289177Speter                             idx, container->noderevs->nelts);
381289177Speter
382289177Speter  /* allocate result struct and fill it field by field */
383289177Speter  noderev = apr_pcalloc(pool, sizeof(*noderev));
384289177Speter  binary_noderev = &APR_ARRAY_IDX(container->noderevs, idx, binary_noderev_t);
385289177Speter
386289177Speter  noderev->kind = (svn_node_kind_t)(binary_noderev->flags & NODEREV_KIND_MASK);
387289177Speter  SVN_ERR(get_id(&noderev->noderev_id, container->ids, binary_noderev->id));
388289177Speter  SVN_ERR(get_id(&noderev->node_id, container->ids,
389289177Speter                 binary_noderev->node_id));
390289177Speter  SVN_ERR(get_id(&noderev->copy_id, container->ids,
391289177Speter                 binary_noderev->copy_id));
392289177Speter  SVN_ERR(get_id(&noderev->predecessor_id, container->ids,
393289177Speter                 binary_noderev->predecessor_id));
394289177Speter
395289177Speter  if (binary_noderev->flags & NODEREV_HAS_COPYFROM)
396289177Speter    {
397289177Speter      noderev->copyfrom_path
398289177Speter        = svn_fs_x__string_table_get(container->paths,
399289177Speter                                     binary_noderev->copyfrom_path,
400289177Speter                                     NULL,
401289177Speter                                     pool);
402289177Speter      noderev->copyfrom_rev = binary_noderev->copyfrom_rev;
403289177Speter    }
404289177Speter  else
405289177Speter    {
406289177Speter      noderev->copyfrom_path = NULL;
407289177Speter      noderev->copyfrom_rev = SVN_INVALID_REVNUM;
408289177Speter    }
409289177Speter
410289177Speter  if (binary_noderev->flags & NODEREV_HAS_COPYROOT)
411289177Speter    {
412289177Speter      noderev->copyroot_path
413289177Speter        = svn_fs_x__string_table_get(container->paths,
414289177Speter                                     binary_noderev->copyroot_path,
415289177Speter                                     NULL,
416289177Speter                                     pool);
417289177Speter      noderev->copyroot_rev = binary_noderev->copyroot_rev;
418289177Speter    }
419289177Speter  else
420289177Speter    {
421289177Speter      noderev->copyroot_path = NULL;
422289177Speter      noderev->copyroot_rev = 0;
423289177Speter    }
424289177Speter
425289177Speter  noderev->predecessor_count = binary_noderev->predecessor_count;
426289177Speter
427289177Speter  SVN_ERR(get_representation(&noderev->prop_rep, container->reps,
428289177Speter                             binary_noderev->prop_rep, pool));
429289177Speter  SVN_ERR(get_representation(&noderev->data_rep, container->reps,
430289177Speter                             binary_noderev->data_rep, pool));
431289177Speter
432289177Speter  if (binary_noderev->flags & NODEREV_HAS_CPATH)
433289177Speter    noderev->created_path
434289177Speter      = svn_fs_x__string_table_get(container->paths,
435289177Speter                                   binary_noderev->created_path,
436289177Speter                                   NULL,
437289177Speter                                   pool);
438289177Speter
439289177Speter  noderev->mergeinfo_count = binary_noderev->mergeinfo_count;
440289177Speter
441289177Speter  noderev->has_mergeinfo = (binary_noderev->flags & NODEREV_HAS_MINFO) ? 1 : 0;
442289177Speter  *noderev_p = noderev;
443289177Speter
444289177Speter  return SVN_NO_ERROR;
445289177Speter}
446289177Speter
447289177Speter/* Create and return a stream for representations in PARENT.
448289177Speter * Initialize the sub-streams for all fields, except checksums.
449289177Speter */
450289177Speterstatic svn_packed__int_stream_t *
451289177Spetercreate_rep_stream(svn_packed__int_stream_t *parent)
452289177Speter{
453289177Speter  svn_packed__int_stream_t *stream
454289177Speter    = svn_packed__create_int_substream(parent, FALSE, FALSE);
455289177Speter
456289177Speter  /* sub-streams for members - except for checksums */
457289177Speter  /* has_sha1 */
458289177Speter  svn_packed__create_int_substream(stream, FALSE, FALSE);
459289177Speter
460289177Speter  /* rev, item_index, size, expanded_size */
461289177Speter  svn_packed__create_int_substream(stream, TRUE, FALSE);
462289177Speter  svn_packed__create_int_substream(stream, FALSE, FALSE);
463289177Speter  svn_packed__create_int_substream(stream, FALSE, FALSE);
464289177Speter  svn_packed__create_int_substream(stream, FALSE, FALSE);
465289177Speter
466289177Speter  return stream;
467289177Speter}
468289177Speter
469289177Speter/* Serialize all representations in REP.  Store checksums in DIGEST_STREAM,
470289177Speter * put all other fields into REP_STREAM.
471289177Speter */
472289177Speterstatic void
473289177Speterwrite_reps(svn_packed__int_stream_t *rep_stream,
474289177Speter           svn_packed__byte_stream_t *digest_stream,
475289177Speter           apr_array_header_t *reps)
476289177Speter{
477289177Speter  int i;
478289177Speter  for (i = 0; i < reps->nelts; ++i)
479289177Speter    {
480289177Speter      svn_fs_x__representation_t *rep
481289177Speter        = &APR_ARRAY_IDX(reps, i, svn_fs_x__representation_t);
482289177Speter
483289177Speter      svn_packed__add_uint(rep_stream, rep->has_sha1);
484289177Speter
485289177Speter      svn_packed__add_uint(rep_stream, rep->id.change_set);
486289177Speter      svn_packed__add_uint(rep_stream, rep->id.number);
487289177Speter      svn_packed__add_uint(rep_stream, rep->size);
488289177Speter      svn_packed__add_uint(rep_stream, rep->expanded_size);
489289177Speter
490289177Speter      svn_packed__add_bytes(digest_stream,
491289177Speter                            (const char *)rep->md5_digest,
492289177Speter                            sizeof(rep->md5_digest));
493289177Speter      if (rep->has_sha1)
494289177Speter        svn_packed__add_bytes(digest_stream,
495289177Speter                              (const char *)rep->sha1_digest,
496289177Speter                              sizeof(rep->sha1_digest));
497289177Speter    }
498289177Speter}
499289177Speter
500289177Spetersvn_error_t *
501289177Spetersvn_fs_x__write_noderevs_container(svn_stream_t *stream,
502289177Speter                                   const svn_fs_x__noderevs_t *container,
503289177Speter                                   apr_pool_t *scratch_pool)
504289177Speter{
505289177Speter  int i;
506289177Speter
507289177Speter  string_table_t *paths = container->paths
508289177Speter                        ? container->paths
509289177Speter                        : svn_fs_x__string_table_create(container->builder,
510289177Speter                                                        scratch_pool);
511289177Speter
512289177Speter  svn_packed__data_root_t *root = svn_packed__data_create_root(scratch_pool);
513289177Speter
514289177Speter  /* one common top-level stream for all arrays. One sub-stream */
515289177Speter  svn_packed__int_stream_t *structs_stream
516289177Speter    = svn_packed__create_int_stream(root, FALSE, FALSE);
517289177Speter  svn_packed__int_stream_t *ids_stream
518289177Speter    = svn_packed__create_int_substream(structs_stream, FALSE, FALSE);
519289177Speter  svn_packed__int_stream_t *reps_stream
520289177Speter    = create_rep_stream(structs_stream);
521289177Speter  svn_packed__int_stream_t *noderevs_stream
522289177Speter    = svn_packed__create_int_substream(structs_stream, FALSE, FALSE);
523289177Speter  svn_packed__byte_stream_t *digests_stream
524289177Speter    = svn_packed__create_bytes_stream(root);
525289177Speter
526289177Speter  /* structure the IDS_STREAM such we can extract much of the redundancy
527289177Speter   * from the svn_fs_x__ip_part_t structs */
528289177Speter  for (i = 0; i < 2; ++i)
529289177Speter    svn_packed__create_int_substream(ids_stream, TRUE, FALSE);
530289177Speter
531289177Speter  /* Same storing binary_noderev_t in the NODEREVS_STREAM */
532289177Speter  svn_packed__create_int_substream(noderevs_stream, FALSE, FALSE);
533289177Speter  for (i = 0; i < 13; ++i)
534289177Speter    svn_packed__create_int_substream(noderevs_stream, TRUE, FALSE);
535289177Speter
536289177Speter  /* serialize ids array */
537289177Speter  for (i = 0; i < container->ids->nelts; ++i)
538289177Speter    {
539289177Speter      svn_fs_x__id_t *id = &APR_ARRAY_IDX(container->ids, i, svn_fs_x__id_t);
540289177Speter
541289177Speter      svn_packed__add_int(ids_stream, id->change_set);
542289177Speter      svn_packed__add_uint(ids_stream, id->number);
543289177Speter    }
544289177Speter
545289177Speter  /* serialize rep arrays */
546289177Speter  write_reps(reps_stream, digests_stream, container->reps);
547289177Speter
548289177Speter  /* serialize noderevs array */
549289177Speter  for (i = 0; i < container->noderevs->nelts; ++i)
550289177Speter    {
551289177Speter      const binary_noderev_t *noderev
552289177Speter        = &APR_ARRAY_IDX(container->noderevs, i, binary_noderev_t);
553289177Speter
554289177Speter      svn_packed__add_uint(noderevs_stream, noderev->flags);
555289177Speter
556289177Speter      svn_packed__add_uint(noderevs_stream, noderev->id);
557289177Speter      svn_packed__add_uint(noderevs_stream, noderev->node_id);
558289177Speter      svn_packed__add_uint(noderevs_stream, noderev->copy_id);
559289177Speter      svn_packed__add_uint(noderevs_stream, noderev->predecessor_id);
560289177Speter      svn_packed__add_uint(noderevs_stream, noderev->predecessor_count);
561289177Speter
562289177Speter      svn_packed__add_uint(noderevs_stream, noderev->copyfrom_path);
563289177Speter      svn_packed__add_int(noderevs_stream, noderev->copyfrom_rev);
564289177Speter      svn_packed__add_uint(noderevs_stream, noderev->copyroot_path);
565289177Speter      svn_packed__add_int(noderevs_stream, noderev->copyroot_rev);
566289177Speter
567289177Speter      svn_packed__add_uint(noderevs_stream, noderev->prop_rep);
568289177Speter      svn_packed__add_uint(noderevs_stream, noderev->data_rep);
569289177Speter
570289177Speter      svn_packed__add_uint(noderevs_stream, noderev->created_path);
571289177Speter      svn_packed__add_uint(noderevs_stream, noderev->mergeinfo_count);
572289177Speter    }
573289177Speter
574289177Speter  /* write to disk */
575289177Speter  SVN_ERR(svn_fs_x__write_string_table(stream, paths, scratch_pool));
576289177Speter  SVN_ERR(svn_packed__data_write(stream, root, scratch_pool));
577289177Speter
578289177Speter  return SVN_NO_ERROR;
579289177Speter}
580289177Speter
581289177Speter/* Allocate a svn_fs_x__representation_t array in POOL and return it in
582289177Speter * REPS_P.  Deserialize the data in REP_STREAM and DIGEST_STREAM and store
583289177Speter * the resulting representations into the *REPS_P.
584289177Speter */
585289177Speterstatic svn_error_t *
586289177Speterread_reps(apr_array_header_t **reps_p,
587289177Speter          svn_packed__int_stream_t *rep_stream,
588289177Speter          svn_packed__byte_stream_t *digest_stream,
589289177Speter          apr_pool_t *pool)
590289177Speter{
591289177Speter  apr_size_t i;
592289177Speter  apr_size_t len;
593289177Speter  const char *bytes;
594289177Speter
595289177Speter  apr_size_t count
596289177Speter    = svn_packed__int_count(svn_packed__first_int_substream(rep_stream));
597289177Speter  apr_array_header_t *reps
598289177Speter    = apr_array_make(pool, (int)count, sizeof(svn_fs_x__representation_t));
599289177Speter
600289177Speter  for (i = 0; i < count; ++i)
601289177Speter    {
602289177Speter      svn_fs_x__representation_t rep;
603289177Speter
604289177Speter      rep.has_sha1 = (svn_boolean_t)svn_packed__get_uint(rep_stream);
605289177Speter
606289177Speter      rep.id.change_set = (svn_revnum_t)svn_packed__get_uint(rep_stream);
607289177Speter      rep.id.number = svn_packed__get_uint(rep_stream);
608289177Speter      rep.size = svn_packed__get_uint(rep_stream);
609289177Speter      rep.expanded_size = svn_packed__get_uint(rep_stream);
610289177Speter
611289177Speter      /* when extracting the checksums, beware of buffer under/overflows
612289177Speter         caused by disk data corruption. */
613289177Speter      bytes = svn_packed__get_bytes(digest_stream, &len);
614289177Speter      if (len != sizeof(rep.md5_digest))
615289177Speter        return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL,
616289177Speter                                 apr_psprintf(pool,
617289177Speter                                              _("Unexpected MD5"
618289177Speter                                                " digest size %%%s"),
619289177Speter                                              APR_SIZE_T_FMT),
620289177Speter                                 len);
621289177Speter
622289177Speter      memcpy(rep.md5_digest, bytes, sizeof(rep.md5_digest));
623289177Speter      if (rep.has_sha1)
624289177Speter        {
625289177Speter          bytes = svn_packed__get_bytes(digest_stream, &len);
626289177Speter          if (len != sizeof(rep.sha1_digest))
627289177Speter            return svn_error_createf(SVN_ERR_FS_CONTAINER_INDEX, NULL,
628289177Speter                                     apr_psprintf(pool,
629289177Speter                                                  _("Unexpected SHA1"
630289177Speter                                                    " digest size %%%s"),
631289177Speter                                                  APR_SIZE_T_FMT),
632289177Speter                                     len);
633289177Speter
634289177Speter          memcpy(rep.sha1_digest, bytes, sizeof(rep.sha1_digest));
635289177Speter        }
636289177Speter
637289177Speter      APR_ARRAY_PUSH(reps, svn_fs_x__representation_t) = rep;
638289177Speter    }
639289177Speter
640289177Speter  *reps_p = reps;
641289177Speter
642289177Speter  return SVN_NO_ERROR;
643289177Speter}
644289177Speter
645289177Spetersvn_error_t *
646289177Spetersvn_fs_x__read_noderevs_container(svn_fs_x__noderevs_t **container,
647289177Speter                                  svn_stream_t *stream,
648289177Speter                                  apr_pool_t *result_pool,
649289177Speter                                  apr_pool_t *scratch_pool)
650289177Speter{
651289177Speter  apr_size_t i;
652289177Speter  apr_size_t count;
653289177Speter
654289177Speter  svn_fs_x__noderevs_t *noderevs
655289177Speter    = apr_pcalloc(result_pool, sizeof(*noderevs));
656289177Speter
657289177Speter  svn_packed__data_root_t *root;
658289177Speter  svn_packed__int_stream_t *structs_stream;
659289177Speter  svn_packed__int_stream_t *ids_stream;
660289177Speter  svn_packed__int_stream_t *reps_stream;
661289177Speter  svn_packed__int_stream_t *noderevs_stream;
662289177Speter  svn_packed__byte_stream_t *digests_stream;
663289177Speter
664289177Speter  /* read everything from disk */
665289177Speter  SVN_ERR(svn_fs_x__read_string_table(&noderevs->paths, stream,
666289177Speter                                      result_pool, scratch_pool));
667289177Speter  SVN_ERR(svn_packed__data_read(&root, stream, result_pool, scratch_pool));
668289177Speter
669289177Speter  /* get streams */
670289177Speter  structs_stream = svn_packed__first_int_stream(root);
671289177Speter  ids_stream = svn_packed__first_int_substream(structs_stream);
672289177Speter  reps_stream = svn_packed__next_int_stream(ids_stream);
673289177Speter  noderevs_stream = svn_packed__next_int_stream(reps_stream);
674289177Speter  digests_stream = svn_packed__first_byte_stream(root);
675289177Speter
676289177Speter  /* read ids array */
677289177Speter  count
678289177Speter    = svn_packed__int_count(svn_packed__first_int_substream(ids_stream));
679289177Speter  noderevs->ids
680289177Speter    = apr_array_make(result_pool, (int)count, sizeof(svn_fs_x__id_t));
681289177Speter  for (i = 0; i < count; ++i)
682289177Speter    {
683289177Speter      svn_fs_x__id_t id;
684289177Speter
685289177Speter      id.change_set = (svn_revnum_t)svn_packed__get_int(ids_stream);
686289177Speter      id.number = svn_packed__get_uint(ids_stream);
687289177Speter
688289177Speter      APR_ARRAY_PUSH(noderevs->ids, svn_fs_x__id_t) = id;
689289177Speter    }
690289177Speter
691289177Speter  /* read rep arrays */
692289177Speter  SVN_ERR(read_reps(&noderevs->reps, reps_stream, digests_stream,
693289177Speter                    result_pool));
694289177Speter
695289177Speter  /* read noderevs array */
696289177Speter  count
697289177Speter    = svn_packed__int_count(svn_packed__first_int_substream(noderevs_stream));
698289177Speter  noderevs->noderevs
699289177Speter    = apr_array_make(result_pool, (int)count, sizeof(binary_noderev_t));
700289177Speter  for (i = 0; i < count; ++i)
701289177Speter    {
702289177Speter      binary_noderev_t noderev;
703289177Speter
704289177Speter      noderev.flags = (apr_uint32_t)svn_packed__get_uint(noderevs_stream);
705289177Speter
706289177Speter      noderev.id = (int)svn_packed__get_uint(noderevs_stream);
707289177Speter      noderev.node_id = (int)svn_packed__get_uint(noderevs_stream);
708289177Speter      noderev.copy_id = (int)svn_packed__get_uint(noderevs_stream);
709289177Speter      noderev.predecessor_id = (int)svn_packed__get_uint(noderevs_stream);
710289177Speter      noderev.predecessor_count = (int)svn_packed__get_uint(noderevs_stream);
711289177Speter
712289177Speter      noderev.copyfrom_path = (apr_size_t)svn_packed__get_uint(noderevs_stream);
713289177Speter      noderev.copyfrom_rev = (svn_revnum_t)svn_packed__get_int(noderevs_stream);
714289177Speter      noderev.copyroot_path = (apr_size_t)svn_packed__get_uint(noderevs_stream);
715289177Speter      noderev.copyroot_rev = (svn_revnum_t)svn_packed__get_int(noderevs_stream);
716289177Speter
717289177Speter      noderev.prop_rep = (int)svn_packed__get_uint(noderevs_stream);
718289177Speter      noderev.data_rep = (int)svn_packed__get_uint(noderevs_stream);
719289177Speter
720289177Speter      noderev.created_path = (apr_size_t)svn_packed__get_uint(noderevs_stream);
721289177Speter      noderev.mergeinfo_count = svn_packed__get_uint(noderevs_stream);
722289177Speter
723289177Speter      APR_ARRAY_PUSH(noderevs->noderevs, binary_noderev_t) = noderev;
724289177Speter    }
725289177Speter
726289177Speter  *container = noderevs;
727289177Speter
728289177Speter  return SVN_NO_ERROR;
729289177Speter}
730289177Speter
731289177Spetersvn_error_t *
732289177Spetersvn_fs_x__serialize_noderevs_container(void **data,
733289177Speter                                       apr_size_t *data_len,
734289177Speter                                       void *in,
735289177Speter                                       apr_pool_t *pool)
736289177Speter{
737289177Speter  svn_fs_x__noderevs_t *noderevs = in;
738289177Speter  svn_stringbuf_t *serialized;
739289177Speter  apr_size_t size
740289177Speter    = noderevs->ids->elt_size * noderevs->ids->nelts
741289177Speter    + noderevs->reps->elt_size * noderevs->reps->nelts
742289177Speter    + noderevs->noderevs->elt_size * noderevs->noderevs->nelts
743289177Speter    + 10 * noderevs->noderevs->elt_size
744289177Speter    + 100;
745289177Speter
746289177Speter  /* serialize array header and all its elements */
747289177Speter  svn_temp_serializer__context_t *context
748289177Speter    = svn_temp_serializer__init(noderevs, sizeof(*noderevs), size, pool);
749289177Speter
750289177Speter  /* serialize sub-structures */
751289177Speter  svn_fs_x__serialize_string_table(context, &noderevs->paths);
752289177Speter  svn_fs_x__serialize_apr_array(context, &noderevs->ids);
753289177Speter  svn_fs_x__serialize_apr_array(context, &noderevs->reps);
754289177Speter  svn_fs_x__serialize_apr_array(context, &noderevs->noderevs);
755289177Speter
756289177Speter  /* return the serialized result */
757289177Speter  serialized = svn_temp_serializer__get(context);
758289177Speter
759289177Speter  *data = serialized->data;
760289177Speter  *data_len = serialized->len;
761289177Speter
762289177Speter  return SVN_NO_ERROR;
763289177Speter}
764289177Speter
765289177Spetersvn_error_t *
766289177Spetersvn_fs_x__deserialize_noderevs_container(void **out,
767289177Speter                                         void *data,
768289177Speter                                         apr_size_t data_len,
769289177Speter                                         apr_pool_t *pool)
770289177Speter{
771289177Speter  svn_fs_x__noderevs_t *noderevs = (svn_fs_x__noderevs_t *)data;
772289177Speter
773289177Speter  /* de-serialize sub-structures */
774289177Speter  svn_fs_x__deserialize_string_table(noderevs, &noderevs->paths);
775289177Speter  svn_fs_x__deserialize_apr_array(noderevs, &noderevs->ids, pool);
776289177Speter  svn_fs_x__deserialize_apr_array(noderevs, &noderevs->reps, pool);
777289177Speter  svn_fs_x__deserialize_apr_array(noderevs, &noderevs->noderevs, pool);
778289177Speter
779289177Speter  /* done */
780289177Speter  *out = noderevs;
781289177Speter
782289177Speter  return SVN_NO_ERROR;
783289177Speter}
784289177Speter
785289177Speter/* Deserialize the cache serialized APR struct at *IN in BUFFER and write
786289177Speter * the result to OUT.  Note that this will only resolve the pointers and
787289177Speter * not the array elements themselves. */
788289177Speterstatic void
789289177Speterresolve_apr_array_header(apr_array_header_t *out,
790289177Speter                         const void *buffer,
791289177Speter                         apr_array_header_t * const *in)
792289177Speter{
793289177Speter  const apr_array_header_t *array
794289177Speter    = svn_temp_deserializer__ptr(buffer, (const void *const *)in);
795289177Speter  const char *elements
796289177Speter    = svn_temp_deserializer__ptr(array, (const void *const *)&array->elts);
797289177Speter
798289177Speter  *out = *array;
799289177Speter  out->elts = (char *)elements;
800289177Speter  out->pool = NULL;
801289177Speter}
802289177Speter
803289177Spetersvn_error_t *
804289177Spetersvn_fs_x__noderevs_get_func(void **out,
805289177Speter                            const void *data,
806289177Speter                            apr_size_t data_len,
807289177Speter                            void *baton,
808289177Speter                            apr_pool_t *pool)
809289177Speter{
810289177Speter  svn_fs_x__noderev_t *noderev;
811289177Speter  binary_noderev_t *binary_noderev;
812289177Speter
813289177Speter  apr_array_header_t ids;
814289177Speter  apr_array_header_t reps;
815289177Speter  apr_array_header_t noderevs;
816289177Speter
817289177Speter  apr_uint32_t idx = *(apr_uint32_t *)baton;
818289177Speter  const svn_fs_x__noderevs_t *container = data;
819289177Speter
820289177Speter  /* Resolve all container pointers */
821289177Speter  const string_table_t *paths
822289177Speter    = svn_temp_deserializer__ptr(container,
823289177Speter                         (const void *const *)&container->paths);
824289177Speter
825289177Speter  resolve_apr_array_header(&ids, container, &container->ids);
826289177Speter  resolve_apr_array_header(&reps, container, &container->reps);
827289177Speter  resolve_apr_array_header(&noderevs, container, &container->noderevs);
828289177Speter
829289177Speter  /* allocate result struct and fill it field by field */
830289177Speter  noderev = apr_pcalloc(pool, sizeof(*noderev));
831289177Speter  binary_noderev = &APR_ARRAY_IDX(&noderevs, idx, binary_noderev_t);
832289177Speter
833289177Speter  noderev->kind = (svn_node_kind_t)(binary_noderev->flags & NODEREV_KIND_MASK);
834289177Speter  SVN_ERR(get_id(&noderev->noderev_id, &ids, binary_noderev->id));
835289177Speter  SVN_ERR(get_id(&noderev->node_id, &ids, binary_noderev->node_id));
836289177Speter  SVN_ERR(get_id(&noderev->copy_id, &ids, binary_noderev->copy_id));
837289177Speter  SVN_ERR(get_id(&noderev->predecessor_id, &ids,
838289177Speter                 binary_noderev->predecessor_id));
839289177Speter
840289177Speter  if (binary_noderev->flags & NODEREV_HAS_COPYFROM)
841289177Speter    {
842289177Speter      noderev->copyfrom_path
843289177Speter        = svn_fs_x__string_table_get_func(paths,
844289177Speter                                          binary_noderev->copyfrom_path,
845289177Speter                                          NULL,
846289177Speter                                          pool);
847289177Speter      noderev->copyfrom_rev = binary_noderev->copyfrom_rev;
848289177Speter    }
849289177Speter  else
850289177Speter    {
851289177Speter      noderev->copyfrom_path = NULL;
852289177Speter      noderev->copyfrom_rev = SVN_INVALID_REVNUM;
853289177Speter    }
854289177Speter
855289177Speter  if (binary_noderev->flags & NODEREV_HAS_COPYROOT)
856289177Speter    {
857289177Speter      noderev->copyroot_path
858289177Speter        = svn_fs_x__string_table_get_func(paths,
859289177Speter                                          binary_noderev->copyroot_path,
860289177Speter                                          NULL,
861289177Speter                                          pool);
862289177Speter      noderev->copyroot_rev = binary_noderev->copyroot_rev;
863289177Speter    }
864289177Speter  else
865289177Speter    {
866289177Speter      noderev->copyroot_path = NULL;
867289177Speter      noderev->copyroot_rev = 0;
868289177Speter    }
869289177Speter
870289177Speter  noderev->predecessor_count = binary_noderev->predecessor_count;
871289177Speter
872289177Speter  SVN_ERR(get_representation(&noderev->prop_rep, &reps,
873289177Speter                             binary_noderev->prop_rep, pool));
874289177Speter  SVN_ERR(get_representation(&noderev->data_rep, &reps,
875289177Speter                             binary_noderev->data_rep, pool));
876289177Speter
877289177Speter  if (binary_noderev->flags & NODEREV_HAS_CPATH)
878289177Speter    noderev->created_path
879289177Speter      = svn_fs_x__string_table_get_func(paths,
880289177Speter                                        binary_noderev->created_path,
881289177Speter                                        NULL,
882289177Speter                                        pool);
883289177Speter
884289177Speter  noderev->mergeinfo_count = binary_noderev->mergeinfo_count;
885289177Speter
886289177Speter  noderev->has_mergeinfo = (binary_noderev->flags & NODEREV_HAS_MINFO) ? 1 : 0;
887289177Speter  *out = noderev;
888289177Speter
889289177Speter  return SVN_NO_ERROR;
890289177Speter}
891289177Speter
892289177Spetersvn_error_t *
893289177Spetersvn_fs_x__mergeinfo_count_get_func(void **out,
894289177Speter                                   const void *data,
895289177Speter                                   apr_size_t data_len,
896289177Speter                                   void *baton,
897289177Speter                                   apr_pool_t *pool)
898289177Speter{
899289177Speter  binary_noderev_t *binary_noderev;
900289177Speter  apr_array_header_t noderevs;
901289177Speter
902289177Speter  apr_uint32_t idx = *(apr_uint32_t *)baton;
903289177Speter  const svn_fs_x__noderevs_t *container = data;
904289177Speter
905289177Speter  /* Resolve all container pointers */
906289177Speter  resolve_apr_array_header(&noderevs, container, &container->noderevs);
907289177Speter  binary_noderev = &APR_ARRAY_IDX(&noderevs, idx, binary_noderev_t);
908289177Speter
909289177Speter  *(apr_int64_t *)out = binary_noderev->mergeinfo_count;
910289177Speter
911289177Speter  return SVN_NO_ERROR;
912289177Speter}
913