1251881Speter/* id.c : operations on node-revision IDs
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 <string.h>
24251881Speter#include <stdlib.h>
25251881Speter
26251881Speter#include "id.h"
27289180Speter#include "index.h"
28289180Speter
29251881Speter#include "../libsvn_fs/fs-loader.h"
30251881Speter#include "private/svn_temp_serializer.h"
31251881Speter#include "private/svn_string_private.h"
32251881Speter
33251881Speter
34289180Spetertypedef struct fs_fs__id_t
35289180Speter{
36289180Speter  /* API visible part */
37289180Speter  svn_fs_id_t generic_id;
38251881Speter
39289180Speter  /* private members */
40289180Speter  struct
41289180Speter    {
42289180Speter      svn_fs_fs__id_part_t node_id;
43289180Speter      svn_fs_fs__id_part_t copy_id;
44289180Speter      svn_fs_fs__id_part_t txn_id;
45289180Speter      svn_fs_fs__id_part_t rev_item;
46289180Speter    } private_id;
47289180Speter} fs_fs__id_t;
48289180Speter
49251881Speter
50251881Speter
51289180Speter/** Like strtol but with a fixed base of 10, locale independent and limited
52289180Speter * to non-negative values.  Overflows are indicated by a FALSE return value
53289180Speter * in which case *RESULT_P will not be modified.
54289180Speter *
55289180Speter * This allows the compiler to generate massively faster code.
56289180Speter * (E.g. Avoiding locale specific processing).  ID parsing is one of the
57289180Speter * most CPU consuming parts of FSFS data access.  Better be quick.
58289180Speter */
59289180Speterstatic svn_boolean_t
60289180Speterlocale_independent_strtol(long *result_p,
61289180Speter                          const char* buffer,
62289180Speter                          const char** end)
63251881Speter{
64289180Speter  /* We allow positive values only.  We use unsigned arithmetics to get
65289180Speter   * well-defined overflow behavior.  It also happens to allow for a wider
66289180Speter   * range of compiler-side optimizations. */
67289180Speter  unsigned long result = 0;
68289180Speter  while (1)
69289180Speter    {
70289180Speter      unsigned long c = (unsigned char)*buffer - (unsigned char)'0';
71289180Speter      unsigned long next;
72251881Speter
73289180Speter      /* This implies the NUL check. */
74289180Speter      if (c > 9)
75289180Speter        break;
76289180Speter
77289180Speter      /* Overflow check.  Passing this, NEXT can be no more than ULONG_MAX+9
78289180Speter       * before being truncated to ULONG but it still covers 0 .. ULONG_MAX.
79289180Speter       */
80289180Speter      if (result > ULONG_MAX / 10)
81289180Speter        return FALSE;
82289180Speter
83289180Speter      next = result * 10 + c;
84289180Speter
85362181Sdim      /* Overflow check.  In case of an overflow, NEXT is 0..9 and RESULT
86362181Sdim       * is much larger than 10.  We will then return FALSE.
87362181Sdim       *
88362181Sdim       * In the non-overflow case, NEXT is >= 10 * RESULT but never smaller.
89362181Sdim       * We will continue the loop in that case. */
90289180Speter      if (next < result)
91289180Speter        return FALSE;
92289180Speter
93289180Speter      result = next;
94289180Speter      ++buffer;
95289180Speter    }
96289180Speter
97289180Speter  *end = buffer;
98289180Speter  if (result > LONG_MAX)
99289180Speter    return FALSE;
100289180Speter
101289180Speter  *result_p = (long)result;
102289180Speter
103289180Speter  return TRUE;
104251881Speter}
105251881Speter
106289180Speter/* Parse the NUL-terminated ID part at DATA and write the result into *PART.
107289180Speter * Return TRUE if no errors were detected. */
108289180Speterstatic svn_boolean_t
109289180Speterpart_parse(svn_fs_fs__id_part_t *part,
110289180Speter           const char *data)
111289180Speter{
112289180Speter  const char *end;
113251881Speter
114289180Speter  /* special case: ID inside some transaction */
115289180Speter  if (data[0] == '_')
116289180Speter    {
117289180Speter      part->revision = SVN_INVALID_REVNUM;
118289180Speter      part->number = svn__base36toui64(&data, data + 1);
119289180Speter      return *data == '\0';
120289180Speter    }
121289180Speter
122289180Speter  /* special case: 0 / default ID */
123289180Speter  if (data[0] == '0' && data[1] == '\0')
124289180Speter    {
125289180Speter      part->revision = 0;
126289180Speter      part->number = 0;
127289180Speter      return TRUE;
128289180Speter    }
129289180Speter
130289180Speter  /* read old style / new style ID */
131289180Speter  part->number = svn__base36toui64(&data, data);
132289180Speter  if (data[0] != '-')
133289180Speter    {
134289180Speter      part->revision = 0;
135289180Speter      return *data == '\0';
136289180Speter    }
137289180Speter
138289180Speter  return locale_independent_strtol(&part->revision, data+1, &end);
139289180Speter}
140289180Speter
141289180Speter/* Parse the transaction id in DATA and store the result in *TXN_ID.
142289180Speter * Return FALSE if there was some problem.
143289180Speter */
144289180Speterstatic svn_boolean_t
145289180Spetertxn_id_parse(svn_fs_fs__id_part_t *txn_id,
146289180Speter             const char *data)
147251881Speter{
148289180Speter  const char *end;
149289180Speter  if (!locale_independent_strtol(&txn_id->revision, data, &end))
150289180Speter    return FALSE;
151251881Speter
152289180Speter  data = end;
153289180Speter  if (*data != '-')
154289180Speter    return FALSE;
155289180Speter
156289180Speter  ++data;
157289180Speter  txn_id->number = svn__base36toui64(&data, data);
158289180Speter  return *data == '\0';
159251881Speter}
160251881Speter
161289180Speter/* Write the textual representation of *PART into P and return a pointer
162289180Speter * to the first position behind that string.
163289180Speter */
164289180Speterstatic char *
165289180Speterunparse_id_part(char *p,
166289180Speter                const svn_fs_fs__id_part_t *part)
167289180Speter{
168289180Speter  if (SVN_IS_VALID_REVNUM(part->revision))
169289180Speter    {
170289180Speter      /* ordinary old style / new style ID */
171289180Speter      p += svn__ui64tobase36(p, part->number);
172289180Speter      if (part->revision > 0)
173289180Speter        {
174289180Speter          *(p++) = '-';
175289180Speter          p += svn__i64toa(p, part->revision);
176289180Speter        }
177289180Speter    }
178289180Speter  else
179289180Speter    {
180289180Speter      /* in txn: mark with "_" prefix */
181289180Speter      *(p++) = '_';
182289180Speter      p += svn__ui64tobase36(p, part->number);
183289180Speter    }
184251881Speter
185289180Speter  *(p++) = '.';
186289180Speter
187289180Speter  return p;
188289180Speter}
189289180Speter
190289180Speter
191289180Speter
192289180Speter/* Operations on ID parts */
193289180Speter
194289180Spetersvn_boolean_t
195289180Spetersvn_fs_fs__id_part_is_root(const svn_fs_fs__id_part_t* part)
196289180Speter{
197289180Speter  return part->revision == 0 && part->number == 0;
198289180Speter}
199289180Speter
200289180Spetersvn_boolean_t
201289180Spetersvn_fs_fs__id_part_eq(const svn_fs_fs__id_part_t *lhs,
202289180Speter                      const svn_fs_fs__id_part_t *rhs)
203289180Speter{
204289180Speter  return lhs->revision == rhs->revision && lhs->number == rhs->number;
205289180Speter}
206289180Speter
207289180Spetersvn_boolean_t
208289180Spetersvn_fs_fs__id_txn_used(const svn_fs_fs__id_part_t *txn_id)
209289180Speter{
210289180Speter  return SVN_IS_VALID_REVNUM(txn_id->revision) || (txn_id->number != 0);
211289180Speter}
212289180Speter
213289180Spetervoid
214289180Spetersvn_fs_fs__id_txn_reset(svn_fs_fs__id_part_t *txn_id)
215289180Speter{
216289180Speter  txn_id->revision = SVN_INVALID_REVNUM;
217289180Speter  txn_id->number = 0;
218289180Speter}
219289180Speter
220289180Spetersvn_error_t *
221289180Spetersvn_fs_fs__id_txn_parse(svn_fs_fs__id_part_t *txn_id,
222289180Speter                        const char *data)
223289180Speter{
224289180Speter  if (! txn_id_parse(txn_id, data))
225289180Speter    return svn_error_createf(SVN_ERR_FS_MALFORMED_TXN_ID, NULL,
226289180Speter                             "malformed txn id '%s'", data);
227289180Speter
228289180Speter  return SVN_NO_ERROR;
229289180Speter}
230289180Speter
231251881Speterconst char *
232289180Spetersvn_fs_fs__id_txn_unparse(const svn_fs_fs__id_part_t *txn_id,
233289180Speter                          apr_pool_t *pool)
234251881Speter{
235289180Speter  char string[2 * SVN_INT64_BUFFER_SIZE + 1];
236289180Speter  char *p = string;
237251881Speter
238289180Speter  p += svn__i64toa(p, txn_id->revision);
239289180Speter  *(p++) = '-';
240289180Speter  p += svn__ui64tobase36(p, txn_id->number);
241289180Speter
242289180Speter  return apr_pstrmemdup(pool, string, p - string);
243251881Speter}
244251881Speter
245289180Speter
246251881Speter
247289180Speter/* Accessing ID Pieces.  */
248289180Speter
249289180Speterconst svn_fs_fs__id_part_t *
250289180Spetersvn_fs_fs__id_node_id(const svn_fs_id_t *fs_id)
251289180Speter{
252289180Speter  const fs_fs__id_t *id = (const fs_fs__id_t *)fs_id;
253289180Speter
254289180Speter  return &id->private_id.node_id;
255289180Speter}
256289180Speter
257289180Speter
258289180Speterconst svn_fs_fs__id_part_t *
259289180Spetersvn_fs_fs__id_copy_id(const svn_fs_id_t *fs_id)
260289180Speter{
261289180Speter  const fs_fs__id_t *id = (const fs_fs__id_t *)fs_id;
262289180Speter
263289180Speter  return &id->private_id.copy_id;
264289180Speter}
265289180Speter
266289180Speter
267289180Speterconst svn_fs_fs__id_part_t *
268289180Spetersvn_fs_fs__id_txn_id(const svn_fs_id_t *fs_id)
269289180Speter{
270289180Speter  const fs_fs__id_t *id = (const fs_fs__id_t *)fs_id;
271289180Speter
272289180Speter  return &id->private_id.txn_id;
273289180Speter}
274289180Speter
275289180Speter
276289180Speterconst svn_fs_fs__id_part_t *
277289180Spetersvn_fs_fs__id_rev_item(const svn_fs_id_t *fs_id)
278289180Speter{
279289180Speter  const fs_fs__id_t *id = (const fs_fs__id_t *)fs_id;
280289180Speter
281289180Speter  return &id->private_id.rev_item;
282289180Speter}
283289180Speter
284251881Spetersvn_revnum_t
285289180Spetersvn_fs_fs__id_rev(const svn_fs_id_t *fs_id)
286251881Speter{
287289180Speter  const fs_fs__id_t *id = (const fs_fs__id_t *)fs_id;
288251881Speter
289289180Speter  return id->private_id.rev_item.revision;
290251881Speter}
291251881Speter
292289180Speterapr_uint64_t
293289180Spetersvn_fs_fs__id_item(const svn_fs_id_t *fs_id)
294289180Speter{
295289180Speter  const fs_fs__id_t *id = (const fs_fs__id_t *)fs_id;
296251881Speter
297289180Speter  return id->private_id.rev_item.number;
298289180Speter}
299289180Speter
300289180Spetersvn_boolean_t
301289180Spetersvn_fs_fs__id_is_txn(const svn_fs_id_t *fs_id)
302251881Speter{
303289180Speter  const fs_fs__id_t *id = (const fs_fs__id_t *)fs_id;
304251881Speter
305289180Speter  return svn_fs_fs__id_txn_used(&id->private_id.txn_id);
306251881Speter}
307251881Speter
308251881Spetersvn_string_t *
309289180Spetersvn_fs_fs__id_unparse(const svn_fs_id_t *fs_id,
310251881Speter                      apr_pool_t *pool)
311251881Speter{
312289180Speter  char string[6 * SVN_INT64_BUFFER_SIZE + 10];
313289180Speter  const fs_fs__id_t *id = (const fs_fs__id_t *)fs_id;
314251881Speter
315289180Speter  char *p = unparse_id_part(string, &id->private_id.node_id);
316289180Speter  p = unparse_id_part(p, &id->private_id.copy_id);
317289180Speter
318289180Speter  if (svn_fs_fs__id_txn_used(&id->private_id.txn_id))
319251881Speter    {
320289180Speter      *(p++) = 't';
321289180Speter      p += svn__i64toa(p, id->private_id.txn_id.revision);
322289180Speter      *(p++) = '-';
323289180Speter      p += svn__ui64tobase36(p, id->private_id.txn_id.number);
324251881Speter    }
325251881Speter  else
326251881Speter    {
327289180Speter      *(p++) = 'r';
328289180Speter      p += svn__i64toa(p, id->private_id.rev_item.revision);
329289180Speter      *(p++) = '/';
330289180Speter      p += svn__i64toa(p, id->private_id.rev_item.number);
331251881Speter    }
332289180Speter
333289180Speter  return svn_string_ncreate(string, p - string, pool);
334251881Speter}
335251881Speter
336251881Speter
337251881Speter/*** Comparing node IDs ***/
338251881Speter
339251881Spetersvn_boolean_t
340251881Spetersvn_fs_fs__id_eq(const svn_fs_id_t *a,
341251881Speter                 const svn_fs_id_t *b)
342251881Speter{
343289180Speter  const fs_fs__id_t *id_a = (const fs_fs__id_t *)a;
344289180Speter  const fs_fs__id_t *id_b = (const fs_fs__id_t *)b;
345251881Speter
346251881Speter  if (a == b)
347251881Speter    return TRUE;
348289180Speter
349289180Speter  return svn_fs_fs__id_part_eq(&id_a->private_id.node_id,
350289180Speter                               &id_b->private_id.node_id)
351289180Speter      && svn_fs_fs__id_part_eq(&id_a->private_id.copy_id,
352289180Speter                               &id_b->private_id.copy_id)
353289180Speter      && svn_fs_fs__id_part_eq(&id_a->private_id.txn_id,
354289180Speter                               &id_b->private_id.txn_id)
355289180Speter      && svn_fs_fs__id_part_eq(&id_a->private_id.rev_item,
356289180Speter                               &id_b->private_id.rev_item);
357251881Speter}
358251881Speter
359251881Speter
360251881Spetersvn_boolean_t
361251881Spetersvn_fs_fs__id_check_related(const svn_fs_id_t *a,
362251881Speter                            const svn_fs_id_t *b)
363251881Speter{
364289180Speter  const fs_fs__id_t *id_a = (const fs_fs__id_t *)a;
365289180Speter  const fs_fs__id_t *id_b = (const fs_fs__id_t *)b;
366251881Speter
367251881Speter  if (a == b)
368251881Speter    return TRUE;
369289180Speter
370289180Speter  /* If both node_ids have been created within _different_ transactions
371289180Speter     (and are still uncommitted), then it is impossible for them to be
372289180Speter     related.
373289180Speter
374289180Speter     Due to our txn-local temporary IDs, however, they might have been
375289180Speter     given the same temporary node ID.  We need to detect that case.
376289180Speter   */
377289180Speter  if (   id_a->private_id.node_id.revision == SVN_INVALID_REVNUM
378289180Speter      && id_b->private_id.node_id.revision == SVN_INVALID_REVNUM)
379251881Speter    {
380289180Speter      if (!svn_fs_fs__id_part_eq(&id_a->private_id.txn_id,
381289180Speter                                 &id_b->private_id.txn_id))
382251881Speter        return FALSE;
383289180Speter
384289180Speter      /* At this point, matching node_ids implies relatedness. */
385251881Speter    }
386251881Speter
387289180Speter  return svn_fs_fs__id_part_eq(&id_a->private_id.node_id,
388289180Speter                               &id_b->private_id.node_id);
389251881Speter}
390251881Speter
391251881Speter
392289180Spetersvn_fs_node_relation_t
393251881Spetersvn_fs_fs__id_compare(const svn_fs_id_t *a,
394251881Speter                      const svn_fs_id_t *b)
395251881Speter{
396251881Speter  if (svn_fs_fs__id_eq(a, b))
397289180Speter    return svn_fs_node_unchanged;
398289180Speter  return (svn_fs_fs__id_check_related(a, b) ? svn_fs_node_common_ancestor
399289180Speter                                            : svn_fs_node_unrelated);
400251881Speter}
401251881Speter
402289180Speterint
403289180Spetersvn_fs_fs__id_part_compare(const svn_fs_fs__id_part_t *a,
404289180Speter                           const svn_fs_fs__id_part_t *b)
405289180Speter{
406289180Speter  if (a->revision < b->revision)
407289180Speter    return -1;
408289180Speter  if (a->revision > b->revision)
409289180Speter    return 1;
410251881Speter
411289180Speter  return a->number < b->number ? -1 : a->number == b->number ? 0 : 1;
412289180Speter}
413289180Speter
414289180Speter
415251881Speter
416251881Speter/* Creating ID's.  */
417251881Speter
418251881Speterstatic id_vtable_t id_vtable = {
419251881Speter  svn_fs_fs__id_unparse,
420251881Speter  svn_fs_fs__id_compare
421251881Speter};
422251881Speter
423251881Spetersvn_fs_id_t *
424289180Spetersvn_fs_fs__id_txn_create_root(const svn_fs_fs__id_part_t *txn_id,
425289180Speter                              apr_pool_t *pool)
426251881Speter{
427289180Speter  fs_fs__id_t *id = apr_pcalloc(pool, sizeof(*id));
428251881Speter
429289180Speter  /* node ID and copy ID are "0" */
430251881Speter
431289180Speter  id->private_id.txn_id = *txn_id;
432289180Speter  id->private_id.rev_item.revision = SVN_INVALID_REVNUM;
433289180Speter
434289180Speter  id->generic_id.vtable = &id_vtable;
435289180Speter  id->generic_id.fsap_data = id;
436289180Speter
437289180Speter  return (svn_fs_id_t *)id;
438251881Speter}
439251881Speter
440289180Spetersvn_fs_id_t *svn_fs_fs__id_create_root(const svn_revnum_t revision,
441289180Speter                                       apr_pool_t *pool)
442289180Speter{
443289180Speter  fs_fs__id_t *id = apr_pcalloc(pool, sizeof(*id));
444251881Speter
445289180Speter  id->private_id.txn_id.revision = SVN_INVALID_REVNUM;
446289180Speter  id->private_id.rev_item.revision = revision;
447289180Speter  id->private_id.rev_item.number = SVN_FS_FS__ITEM_INDEX_ROOT_NODE;
448289180Speter
449289180Speter  id->generic_id.vtable = &id_vtable;
450289180Speter  id->generic_id.fsap_data = id;
451289180Speter
452289180Speter  return (svn_fs_id_t *)id;
453289180Speter}
454289180Speter
455251881Spetersvn_fs_id_t *
456289180Spetersvn_fs_fs__id_txn_create(const svn_fs_fs__id_part_t *node_id,
457289180Speter                         const svn_fs_fs__id_part_t *copy_id,
458289180Speter                         const svn_fs_fs__id_part_t *txn_id,
459251881Speter                         apr_pool_t *pool)
460251881Speter{
461289180Speter  fs_fs__id_t *id = apr_pcalloc(pool, sizeof(*id));
462251881Speter
463289180Speter  id->private_id.node_id = *node_id;
464289180Speter  id->private_id.copy_id = *copy_id;
465289180Speter  id->private_id.txn_id = *txn_id;
466289180Speter  id->private_id.rev_item.revision = SVN_INVALID_REVNUM;
467251881Speter
468289180Speter  id->generic_id.vtable = &id_vtable;
469289180Speter  id->generic_id.fsap_data = id;
470289180Speter
471289180Speter  return (svn_fs_id_t *)id;
472251881Speter}
473251881Speter
474251881Speter
475251881Spetersvn_fs_id_t *
476289180Spetersvn_fs_fs__id_rev_create(const svn_fs_fs__id_part_t *node_id,
477289180Speter                         const svn_fs_fs__id_part_t *copy_id,
478289180Speter                         const svn_fs_fs__id_part_t *rev_item,
479289180Speter                         apr_pool_t *pool)
480251881Speter{
481289180Speter  fs_fs__id_t *id = apr_pcalloc(pool, sizeof(*id));
482251881Speter
483289180Speter  id->private_id.node_id = *node_id;
484289180Speter  id->private_id.copy_id = *copy_id;
485289180Speter  id->private_id.txn_id.revision = SVN_INVALID_REVNUM;
486289180Speter  id->private_id.rev_item = *rev_item;
487251881Speter
488289180Speter  id->generic_id.vtable = &id_vtable;
489289180Speter  id->generic_id.fsap_data = id;
490289180Speter
491289180Speter  return (svn_fs_id_t *)id;
492251881Speter}
493251881Speter
494251881Speter
495251881Spetersvn_fs_id_t *
496289180Spetersvn_fs_fs__id_copy(const svn_fs_id_t *source, apr_pool_t *pool)
497251881Speter{
498289180Speter  const fs_fs__id_t *id = (const fs_fs__id_t *)source;
499289180Speter  fs_fs__id_t *new_id = apr_pmemdup(pool, id, sizeof(*new_id));
500251881Speter
501289180Speter  new_id->generic_id.fsap_data = new_id;
502251881Speter
503289180Speter  return (svn_fs_id_t *)new_id;
504289180Speter}
505289180Speter
506289180Speter/* Return an ID resulting from parsing the string DATA, or NULL if DATA is
507289180Speter   an invalid ID string. *DATA will be modified / invalidated by this call. */
508289180Speterstatic svn_fs_id_t *
509289180Speterid_parse(char *data,
510289180Speter         apr_pool_t *pool)
511289180Speter{
512289180Speter  fs_fs__id_t *id;
513289180Speter  char *str;
514289180Speter
515251881Speter  /* Alloc a new svn_fs_id_t structure. */
516289180Speter  id = apr_pcalloc(pool, sizeof(*id));
517289180Speter  id->generic_id.vtable = &id_vtable;
518289180Speter  id->generic_id.fsap_data = id;
519251881Speter
520251881Speter  /* Now, we basically just need to "split" this data on `.'
521251881Speter     characters.  We will use svn_cstring_tokenize, which will put
522251881Speter     terminators where each of the '.'s used to be.  Then our new
523251881Speter     id field will reference string locations inside our duplicate
524251881Speter     string.*/
525251881Speter
526251881Speter  /* Node Id */
527289180Speter  str = svn_cstring_tokenize(".", &data);
528251881Speter  if (str == NULL)
529251881Speter    return NULL;
530289180Speter  if (! part_parse(&id->private_id.node_id, str))
531289180Speter    return NULL;
532251881Speter
533251881Speter  /* Copy Id */
534289180Speter  str = svn_cstring_tokenize(".", &data);
535251881Speter  if (str == NULL)
536251881Speter    return NULL;
537289180Speter  if (! part_parse(&id->private_id.copy_id, str))
538289180Speter    return NULL;
539251881Speter
540251881Speter  /* Txn/Rev Id */
541289180Speter  str = svn_cstring_tokenize(".", &data);
542251881Speter  if (str == NULL)
543251881Speter    return NULL;
544251881Speter
545251881Speter  if (str[0] == 'r')
546251881Speter    {
547251881Speter      apr_int64_t val;
548289180Speter      const char *tmp;
549251881Speter      svn_error_t *err;
550251881Speter
551251881Speter      /* This is a revision type ID */
552289180Speter      id->private_id.txn_id.revision = SVN_INVALID_REVNUM;
553289180Speter      id->private_id.txn_id.number = 0;
554251881Speter
555289180Speter      data = str + 1;
556289180Speter      str = svn_cstring_tokenize("/", &data);
557251881Speter      if (str == NULL)
558251881Speter        return NULL;
559289180Speter      if (!locale_independent_strtol(&id->private_id.rev_item.revision,
560289180Speter                                     str, &tmp))
561289180Speter        return NULL;
562251881Speter
563289180Speter      err = svn_cstring_atoi64(&val, data);
564251881Speter      if (err)
565251881Speter        {
566251881Speter          svn_error_clear(err);
567251881Speter          return NULL;
568251881Speter        }
569289180Speter      id->private_id.rev_item.number = (apr_uint64_t)val;
570251881Speter    }
571251881Speter  else if (str[0] == 't')
572251881Speter    {
573251881Speter      /* This is a transaction type ID */
574289180Speter      id->private_id.rev_item.revision = SVN_INVALID_REVNUM;
575289180Speter      id->private_id.rev_item.number = 0;
576289180Speter
577289180Speter      if (! txn_id_parse(&id->private_id.txn_id, str + 1))
578289180Speter        return NULL;
579251881Speter    }
580251881Speter  else
581251881Speter    return NULL;
582251881Speter
583289180Speter  return (svn_fs_id_t *)id;
584251881Speter}
585251881Speter
586289180Spetersvn_error_t *
587289180Spetersvn_fs_fs__id_parse(const svn_fs_id_t **id_p,
588289180Speter                    char *data,
589289180Speter                    apr_pool_t *pool)
590251881Speter{
591289180Speter  svn_fs_id_t *id = id_parse(data, pool);
592289180Speter  if (id == NULL)
593289180Speter    return svn_error_createf(SVN_ERR_FS_MALFORMED_NODEREV_ID, NULL,
594362181Sdim                             "Malformed node revision ID string '%s'",
595362181Sdim                             data);
596251881Speter
597289180Speter  *id_p = id;
598251881Speter
599289180Speter  return SVN_NO_ERROR;
600251881Speter}
601251881Speter
602289180Speter/* (de-)serialization support */
603289180Speter
604251881Speter/* Serialize an ID within the serialization CONTEXT.
605251881Speter */
606251881Spetervoid
607251881Spetersvn_fs_fs__id_serialize(svn_temp_serializer__context_t *context,
608289180Speter                        const svn_fs_id_t * const *in)
609251881Speter{
610289180Speter  const fs_fs__id_t *id = (const fs_fs__id_t *)*in;
611289180Speter
612251881Speter  /* nothing to do for NULL ids */
613289180Speter  if (id == NULL)
614251881Speter    return;
615251881Speter
616362181Sdim  /* Serialize the id data struct itself.
617362181Sdim   * Note that the structure behind IN is actually larger than a mere
618362181Sdim   * svn_fs_id_t . */
619289180Speter  svn_temp_serializer__add_leaf(context,
620289180Speter                                (const void * const *)in,
621289180Speter                                sizeof(fs_fs__id_t));
622251881Speter}
623251881Speter
624251881Speter/* Deserialize an ID inside the BUFFER.
625251881Speter */
626251881Spetervoid
627289180Spetersvn_fs_fs__id_deserialize(void *buffer, svn_fs_id_t **in_out)
628251881Speter{
629289180Speter  fs_fs__id_t *id;
630289180Speter
631251881Speter  /* The id maybe all what is in the whole buffer.
632251881Speter   * Don't try to fixup the pointer in that case*/
633289180Speter  if (*in_out != buffer)
634289180Speter    svn_temp_deserializer__resolve(buffer, (void**)in_out);
635251881Speter
636289180Speter  id = (fs_fs__id_t *)*in_out;
637289180Speter
638251881Speter  /* no id, no sub-structure fixup necessary */
639289180Speter  if (id == NULL)
640251881Speter    return;
641251881Speter
642251881Speter  /* the stored vtable is bogus at best -> set the right one */
643289180Speter  id->generic_id.vtable = &id_vtable;
644289180Speter  id->generic_id.fsap_data = id;
645251881Speter}
646251881Speter
647