source: valtobtest/subversion-1.6.2/subversion/libsvn_delta/svndiff.c @ 3

Last change on this file since 3 was 3, checked in by valtob, 15 years ago

subversion source 1.6.2 as test

File size: 27.7 KB
Line 
1/*
2 * svndiff.c -- Encoding and decoding svndiff-format deltas.
3 *
4 * ====================================================================
5 * Copyright (c) 2000-2006, 2008 CollabNet.  All rights reserved.
6 *
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution.  The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
12 *
13 * This software consists of voluntary contributions made by many
14 * individuals.  For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
17 */
18
19
20#include <assert.h>
21#include <string.h>
22#include "svn_delta.h"
23#include "svn_io.h"
24#include "delta.h"
25#include "svn_pools.h"
26#include "svn_private_config.h"
27#include <zlib.h>
28
29/* This macro is taken from zlib, and was originally the function
30   compressBound.  It shouldn't ever change, but once every millenium,
31   it may be useful for someone to make sure. */
32#define svnCompressBound(LEN) ((LEN) + ((LEN) >> 12) + ((LEN) >> 14) + 11)
33
34/* For svndiff1, address/instruction/new data under this size will not
35   be compressed using zlib as a secondary compressor.  */
36#define MIN_COMPRESS_SIZE 512
37
38/* For svndiff, this is the compression level we pass to zlib.  It
39   should be between 0 and 9, with higher numbers being greater
40   compression.  */
41#define SVNDIFF1_COMPRESS_LEVEL 5
42#define NORMAL_BITS 7
43#define LENGTH_BITS 5
44
45
46/* ----- Text delta to svndiff ----- */
47
48/* We make one of these and get it passed back to us in calls to the
49   window handler.  We only use it to record the write function and
50   baton passed to svn_txdelta_to_svndiff2().  */
51struct encoder_baton {
52  svn_stream_t *output;
53  svn_boolean_t header_done;
54  int version;
55  apr_pool_t *pool;
56};
57
58
59/* Encode VAL into the buffer P using the variable-length svndiff
60   integer format.  Return the incremented value of P after the
61   encoded bytes have been written.
62
63   This encoding uses the high bit of each byte as a continuation bit
64   and the other seven bits as data bits.  High-order data bits are
65   encoded first, followed by lower-order bits, so the value can be
66   reconstructed by concatenating the data bits from left to right and
67   interpreting the result as a binary number.  Examples (brackets
68   denote byte boundaries, spaces are for clarity only):
69
70           1 encodes as [0 0000001]
71          33 encodes as [0 0100001]
72         129 encodes as [1 0000001] [0 0000001]
73        2000 encodes as [1 0001111] [0 1010000]
74*/
75
76static char *
77encode_int(char *p, svn_filesize_t val)
78{
79  int n;
80  svn_filesize_t v;
81  unsigned char cont;
82
83  assert(val >= 0);
84
85  /* Figure out how many bytes we'll need.  */
86  v = val >> 7;
87  n = 1;
88  while (v > 0)
89    {
90      v = v >> 7;
91      n++;
92    }
93
94  /* Encode the remaining bytes; n is always the number of bytes
95     coming after the one we're encoding.  */
96  while (--n >= 0)
97    {
98      cont = ((n > 0) ? 0x1 : 0x0) << 7;
99      *p++ = (char)(((val >> (n * 7)) & 0x7f) | cont);
100    }
101
102  return p;
103}
104
105
106/* Append an encoded integer to a string.  */
107static void
108append_encoded_int(svn_stringbuf_t *header, svn_filesize_t val)
109{
110  char buf[128], *p;
111
112  p = encode_int(buf, val);
113  svn_stringbuf_appendbytes(header, buf, p - buf);
114}
115
116/* If IN is a string that is >= MIN_COMPRESS_SIZE, zlib compress it and
117   place the result in OUT, with an integer prepended specifying the
118   original size.  If IN is < MIN_COMPRESS_SIZE, or if the compressed
119   version of IN was no smaller than the original IN, OUT will be a copy
120   of IN with the size prepended as an integer. */
121static svn_error_t *
122zlib_encode(const char *data, apr_size_t len, svn_stringbuf_t *out)
123{
124  unsigned long endlen;
125  unsigned int intlen;
126
127  append_encoded_int(out, len);
128  intlen = out->len;
129
130  if (len < MIN_COMPRESS_SIZE)
131    {
132      svn_stringbuf_appendbytes(out, data, len);
133    }
134  else
135    {
136      svn_stringbuf_ensure(out, svnCompressBound(len) + intlen);
137      endlen = out->blocksize;
138
139      if (compress2((unsigned char *)out->data + intlen, &endlen,
140                    (const unsigned char *)data, len,
141                    SVNDIFF1_COMPRESS_LEVEL) != Z_OK)
142        return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA,
143                                NULL,
144                                _("Compression of svndiff data failed"));
145
146      /* Compression didn't help :(, just append the original text */
147      if (endlen >= len)
148        {
149          svn_stringbuf_appendbytes(out, data, len);
150          return SVN_NO_ERROR;
151        }
152      out->len = endlen + intlen;
153    }
154  return SVN_NO_ERROR;
155}
156
157static svn_error_t *
158window_handler(svn_txdelta_window_t *window, void *baton)
159{
160  struct encoder_baton *eb = baton;
161  apr_pool_t *pool = svn_pool_create(eb->pool);
162  svn_stringbuf_t *instructions = svn_stringbuf_create("", pool);
163  svn_stringbuf_t *i1 = svn_stringbuf_create("", pool);
164  svn_stringbuf_t *header = svn_stringbuf_create("", pool);
165  const svn_string_t *newdata;
166  char ibuf[128], *ip;
167  const svn_txdelta_op_t *op;
168  apr_size_t len;
169
170  /* Make sure we write the header.  */
171  if (eb->header_done == FALSE)
172    {
173      char svnver[4] = "SVN\0";
174      len = 4;
175      svnver[3] = eb->version;
176      SVN_ERR(svn_stream_write(eb->output, svnver, &len));
177      eb->header_done = TRUE;
178    }
179
180  if (window == NULL)
181    {
182      svn_stream_t *output = eb->output;
183
184      /* We're done; clean up.
185
186         We clean our pool first. Given that the output stream was passed
187         TO us, we'll assume it has a longer lifetime, and that it will not
188         be affected by our pool destruction.
189
190         The contrary point of view (close the stream first): that could
191         tell our user that everything related to the output stream is done,
192         and a cleanup of the user pool should occur. However, that user
193         pool could include the subpool we created for our work (eb->pool),
194         which would then make our call to svn_pool_destroy() puke.
195       */
196      svn_pool_destroy(eb->pool);
197
198      return svn_stream_close(output);
199    }
200
201  /* Encode the instructions.  */
202  for (op = window->ops; op < window->ops + window->num_ops; op++)
203    {
204      /* Encode the action code and length.  */
205      ip = ibuf;
206      switch (op->action_code)
207        {
208        case svn_txdelta_source: *ip = (char)0; break;
209        case svn_txdelta_target: *ip = (char)(0x1 << 6); break;
210        case svn_txdelta_new:    *ip = (char)(0x2 << 6); break;
211        }
212      if (op->length >> 6 == 0)
213        *ip++ |= op->length;
214      else
215        ip = encode_int(ip + 1, op->length);
216      if (op->action_code != svn_txdelta_new)
217        ip = encode_int(ip, op->offset);
218      svn_stringbuf_appendbytes(instructions, ibuf, ip - ibuf);
219    }
220
221  /* Encode the header.  */
222  append_encoded_int(header, window->sview_offset);
223  append_encoded_int(header, window->sview_len);
224  append_encoded_int(header, window->tview_len);
225  if (eb->version == 1)
226    {
227      SVN_ERR(zlib_encode(instructions->data, instructions->len, i1));
228      instructions = i1;
229    }
230  append_encoded_int(header, instructions->len);
231  if (eb->version == 1)
232    {
233      svn_stringbuf_t *temp = svn_stringbuf_create("", pool);
234      svn_string_t *tempstr = svn_string_create("", pool);
235      SVN_ERR(zlib_encode(window->new_data->data, window->new_data->len,
236                          temp));
237      tempstr->data = temp->data;
238      tempstr->len = temp->len;
239      newdata = tempstr;
240    }
241  else
242    newdata = window->new_data;
243
244  append_encoded_int(header, newdata->len);
245
246  /* Write out the window.  */
247  len = header->len;
248  SVN_ERR(svn_stream_write(eb->output, header->data, &len));
249  if (instructions->len > 0)
250    {
251      len = instructions->len;
252      SVN_ERR(svn_stream_write(eb->output, instructions->data, &len));
253    }
254  if (newdata->len > 0)
255    {
256      len = newdata->len;
257      SVN_ERR(svn_stream_write(eb->output, newdata->data, &len));
258    }
259
260  svn_pool_destroy(pool);
261  return SVN_NO_ERROR;
262}
263
264void
265svn_txdelta_to_svndiff2(svn_txdelta_window_handler_t *handler,
266                        void **handler_baton,
267                        svn_stream_t *output,
268                        int svndiff_version,
269                        apr_pool_t *pool)
270{
271  apr_pool_t *subpool = svn_pool_create(pool);
272  struct encoder_baton *eb;
273
274  eb = apr_palloc(subpool, sizeof(*eb));
275  eb->output = output;
276  eb->header_done = FALSE;
277  eb->pool = subpool;
278  eb->version = svndiff_version;
279
280  *handler = window_handler;
281  *handler_baton = eb;
282}
283
284void
285svn_txdelta_to_svndiff(svn_stream_t *output,
286                       apr_pool_t *pool,
287                       svn_txdelta_window_handler_t *handler,
288                       void **handler_baton)
289{
290  svn_txdelta_to_svndiff2(handler, handler_baton, output, 0, pool);
291}
292
293
294/* ----- svndiff to text delta ----- */
295
296/* An svndiff parser object.  */
297struct decode_baton
298{
299  /* Once the svndiff parser has enough data buffered to create a
300     "window", it passes this window to the caller's consumer routine.  */
301  svn_txdelta_window_handler_t consumer_func;
302  void *consumer_baton;
303
304  /* Pool to create subpools from; each developing window will be a
305     subpool.  */
306  apr_pool_t *pool;
307
308  /* The current subpool which contains our current window-buffer.  */
309  apr_pool_t *subpool;
310
311  /* The actual svndiff data buffer, living within subpool.  */
312  svn_stringbuf_t *buffer;
313
314  /* The offset and size of the last source view, so that we can check
315     to make sure the next one isn't sliding backwards.  */
316  svn_filesize_t last_sview_offset;
317  apr_size_t last_sview_len;
318
319  /* We have to discard four bytes at the beginning for the header.
320     This field keeps track of how many of those bytes we have read.  */
321  int header_bytes;
322
323  /* Do we want an error to occur when we close the stream that
324     indicates we didn't send the whole svndiff data?  If you plan to
325     not transmit the whole svndiff data stream, you will want this to
326     be FALSE. */
327  svn_boolean_t error_on_early_close;
328
329  /* svndiff version in use by delta.  */
330  unsigned char version;
331};
332
333
334/* Decode an svndiff-encoded integer into VAL and return a pointer to
335   the byte after the integer.  The bytes to be decoded live in the
336   range [P..END-1].  See the comment for encode_int earlier in this
337   file for more detail on the encoding format.  */
338
339static const unsigned char *
340decode_file_offset(svn_filesize_t *val,
341                   const unsigned char *p,
342                   const unsigned char *end)
343{
344  /* Decode bytes until we're done.  */
345  *val = 0;
346  while (p < end)
347    {
348      *val = (*val << 7) | (*p & 0x7f);
349      if (((*p++ >> 7) & 0x1) == 0)
350        return p;
351    }
352  return NULL;
353}
354
355
356/* Same as above, only decide into a size variable. */
357
358static const unsigned char *
359decode_size(apr_size_t *val,
360            const unsigned char *p,
361            const unsigned char *end)
362{
363  /* Decode bytes until we're done.  */
364  *val = 0;
365  while (p < end)
366    {
367      *val = (*val << 7) | (*p & 0x7f);
368      if (((*p++ >> 7) & 0x1) == 0)
369        return p;
370    }
371  return NULL;
372}
373
374/* Decode the possibly-zlib compressed string that is in IN, into OUT.
375   We expect an integer is prepended to IN that specifies the original
376   size, and that if encoded size == original size, that the remaining
377   data is not compressed.  */
378
379static svn_error_t *
380zlib_decode(svn_stringbuf_t *in, svn_stringbuf_t *out)
381{
382  apr_size_t len;
383  char *oldplace = in->data;
384
385  /* First thing in the string is the original length.  */
386  in->data = (char *)decode_size(&len, (unsigned char *)in->data,
387                                 (unsigned char *)in->data+in->len);
388  /* We need to subtract the size of the encoded original length off the
389   *      still remaining input length.  */
390  in->len -= (in->data - oldplace);
391  if (in->len == len)
392    {
393      svn_stringbuf_appendstr(out, in);
394      return SVN_NO_ERROR;
395    }
396  else
397    {
398      unsigned long zliblen;
399
400      svn_stringbuf_ensure(out, len);
401
402      zliblen = len;
403      if (uncompress  ((unsigned char *)out->data, &zliblen,
404                       (const unsigned char *)in->data, in->len) != Z_OK)
405        return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA,
406                                NULL,
407                                _("Decompression of svndiff data failed"));
408
409      /* Zlib should not produce something that has a different size than the
410         original length we stored. */
411      if (zliblen != len)
412        return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA,
413                                NULL,
414                                _("Size of uncompressed data "
415                                  "does not match stored original length"));
416      out->len = zliblen;
417    }
418  return SVN_NO_ERROR;
419}
420
421/* Decode an instruction into OP, returning a pointer to the text
422   after the instruction.  Note that if the action code is
423   svn_txdelta_new, the offset field of *OP will not be set.  */
424
425static const unsigned char *
426decode_instruction(svn_txdelta_op_t *op,
427                   const unsigned char *p,
428                   const unsigned char *end)
429{
430  if (p == end)
431    return NULL;
432
433  /* Decode the instruction selector.  */
434  switch ((*p >> 6) & 0x3)
435    {
436    case 0x0: op->action_code = svn_txdelta_source; break;
437    case 0x1: op->action_code = svn_txdelta_target; break;
438    case 0x2: op->action_code = svn_txdelta_new; break;
439    case 0x3: return NULL;
440    }
441
442  /* Decode the length and offset.  */
443  op->length = *p++ & 0x3f;
444  if (op->length == 0)
445    {
446      p = decode_size(&op->length, p, end);
447      if (p == NULL)
448        return NULL;
449    }
450  if (op->action_code != svn_txdelta_new)
451    {
452      p = decode_size(&op->offset, p, end);
453      if (p == NULL)
454        return NULL;
455    }
456
457  return p;
458}
459
460/* Count the instructions in the range [P..END-1] and make sure they
461   are valid for the given window lengths.  Return an error if the
462   instructions are invalid; otherwise set *NINST to the number of
463   instructions.  */
464static svn_error_t *
465count_and_verify_instructions(int *ninst,
466                              const unsigned char *p,
467                              const unsigned char *end,
468                              apr_size_t sview_len,
469                              apr_size_t tview_len,
470                              apr_size_t new_len)
471{
472  int n = 0;
473  svn_txdelta_op_t op;
474  apr_size_t tpos = 0, npos = 0;
475
476  while (p < end)
477    {
478      p = decode_instruction(&op, p, end);
479
480      /* Detect any malformed operations from the instruction stream. */
481      if (p == NULL)
482        return svn_error_createf
483          (SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
484           _("Invalid diff stream: insn %d cannot be decoded"), n);
485      else if (op.length <= 0)
486        return svn_error_createf
487          (SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
488           _("Invalid diff stream: insn %d has non-positive length"), n);
489      else if (op.length > tview_len - tpos)
490        return svn_error_createf
491          (SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
492           _("Invalid diff stream: insn %d overflows the target view"), n);
493
494      switch (op.action_code)
495        {
496        case svn_txdelta_source:
497          if (op.length > sview_len - op.offset)
498            return svn_error_createf
499              (SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
500               _("Invalid diff stream: "
501                 "[src] insn %d overflows the source view"), n);
502          break;
503        case svn_txdelta_target:
504          if (op.offset >= tpos)
505            return svn_error_createf
506              (SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
507               _("Invalid diff stream: "
508                 "[tgt] insn %d starts beyond the target view position"), n);
509          break;
510        case svn_txdelta_new:
511          if (op.length > new_len - npos)
512            return svn_error_createf
513              (SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
514               _("Invalid diff stream: "
515                 "[new] insn %d overflows the new data section"), n);
516          npos += op.length;
517          break;
518        }
519      tpos += op.length;
520      n++;
521    }
522  if (tpos != tview_len)
523    return svn_error_create(SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
524                            _("Delta does not fill the target window"));
525  if (npos != new_len)
526    return svn_error_create(SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
527                            _("Delta does not contain enough new data"));
528
529  *ninst = n;
530  return SVN_NO_ERROR;
531}
532
533/* Given the five integer fields of a window header and a pointer to
534   the remainder of the window contents, fill in a delta window
535   structure *WINDOW.  New allocations will be performed in POOL;
536   the new_data field of *WINDOW will refer directly to memory pointed
537   to by DATA. */
538static svn_error_t *
539decode_window(svn_txdelta_window_t *window, svn_filesize_t sview_offset,
540              apr_size_t sview_len, apr_size_t tview_len, apr_size_t inslen,
541              apr_size_t newlen, const unsigned char *data, apr_pool_t *pool,
542              unsigned int version)
543{
544  const unsigned char *insend;
545  int ninst;
546  apr_size_t npos;
547  svn_txdelta_op_t *ops, *op;
548  svn_string_t *new_data = apr_palloc(pool, sizeof(*new_data));
549
550  window->sview_offset = sview_offset;
551  window->sview_len = sview_len;
552  window->tview_len = tview_len;
553
554  insend = data + inslen;
555
556  if (version == 1)
557    {
558      svn_stringbuf_t *instin, *ndin;
559      svn_stringbuf_t *instout, *ndout;
560
561      instin = svn_stringbuf_ncreate((const char *)data, insend - data, pool);
562      instout = svn_stringbuf_create("", pool);
563      SVN_ERR(zlib_decode(instin, instout));
564
565      ndin = svn_stringbuf_ncreate((const char *)insend, newlen, pool);
566      ndout = svn_stringbuf_create("", pool);
567      SVN_ERR(zlib_decode(ndin, ndout));
568
569      newlen = ndout->len;
570      data = (unsigned char *)instout->data;
571      insend = (unsigned char *)instout->data + instout->len;
572
573      new_data->data = (const char *) ndout->data;
574      new_data->len = newlen;
575    }
576  else
577    {
578      new_data->data = (const char *) insend;
579      new_data->len = newlen;
580    }
581
582  /* Count the instructions and make sure they are all valid.  */
583  SVN_ERR(count_and_verify_instructions(&ninst, data, insend,
584                                        sview_len, tview_len, newlen));
585
586  /* Allocate a buffer for the instructions and decode them. */
587  ops = apr_palloc(pool, ninst * sizeof(*ops));
588  npos = 0;
589  window->src_ops = 0;
590  for (op = ops; op < ops + ninst; op++)
591    {
592      data = decode_instruction(op, data, insend);
593      if (op->action_code == svn_txdelta_source)
594        ++window->src_ops;
595      else if (op->action_code == svn_txdelta_new)
596        {
597          op->offset = npos;
598          npos += op->length;
599        }
600    }
601  SVN_ERR_ASSERT(data == insend);
602
603  window->ops = ops;
604  window->num_ops = ninst;
605  window->new_data = new_data;
606
607  return SVN_NO_ERROR;
608}
609
610static svn_error_t *
611write_handler(void *baton,
612              const char *buffer,
613              apr_size_t *len)
614{
615  struct decode_baton *db = (struct decode_baton *) baton;
616  const unsigned char *p, *end;
617  svn_filesize_t sview_offset;
618  apr_size_t sview_len, tview_len, inslen, newlen, remaining;
619  apr_size_t buflen = *len;
620
621  /* Chew up four bytes at the beginning for the header.  */
622  if (db->header_bytes < 4)
623    {
624      apr_size_t nheader = 4 - db->header_bytes;
625      if (nheader > buflen)
626        nheader = buflen;
627      if (memcmp(buffer, "SVN\0" + db->header_bytes, nheader) == 0)
628        db->version = 0;
629      else if (memcmp(buffer, "SVN\1" + db->header_bytes, nheader) == 0)
630        db->version = 1;
631      else
632        return svn_error_create(SVN_ERR_SVNDIFF_INVALID_HEADER, NULL,
633                                _("Svndiff has invalid header"));
634      buflen -= nheader;
635      buffer += nheader;
636      db->header_bytes += nheader;
637    }
638
639  /* Concatenate the old with the new.  */
640  svn_stringbuf_appendbytes(db->buffer, buffer, buflen);
641
642  /* We have a buffer of svndiff data that might be good for:
643
644     a) an integral number of windows' worth of data - this is a
645        trivial case.  Make windows from our data and ship them off.
646
647     b) a non-integral number of windows' worth of data - we shall
648        consume the integral portion of the window data, and then
649        somewhere in the following loop the decoding of the svndiff
650        data will run out of stuff to decode, and will simply return
651        SVN_NO_ERROR, anxiously awaiting more data.
652  */
653
654  while (1)
655    {
656      apr_pool_t *newpool;
657      svn_txdelta_window_t window;
658
659      /* Read the header, if we have enough bytes for that.  */
660      p = (const unsigned char *) db->buffer->data;
661      end = (const unsigned char *) db->buffer->data + db->buffer->len;
662
663      p = decode_file_offset(&sview_offset, p, end);
664      if (p == NULL)
665        return SVN_NO_ERROR;
666
667      p = decode_size(&sview_len, p, end);
668      if (p == NULL)
669        return SVN_NO_ERROR;
670
671      p = decode_size(&tview_len, p, end);
672      if (p == NULL)
673        return SVN_NO_ERROR;
674
675      p = decode_size(&inslen, p, end);
676      if (p == NULL)
677        return SVN_NO_ERROR;
678
679      p = decode_size(&newlen, p, end);
680      if (p == NULL)
681        return SVN_NO_ERROR;
682
683      /* Check for integer overflow.  */
684      if (sview_offset < 0 || inslen + newlen < inslen
685          || sview_len + tview_len < sview_len
686          || sview_offset + sview_len < sview_offset)
687        return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL,
688                                _("Svndiff contains corrupt window header"));
689
690      /* Check for source windows which slide backwards.  */
691      if (sview_len > 0
692          && (sview_offset < db->last_sview_offset
693              || (sview_offset + sview_len
694                  < db->last_sview_offset + db->last_sview_len)))
695        return svn_error_create
696          (SVN_ERR_SVNDIFF_BACKWARD_VIEW, NULL,
697           _("Svndiff has backwards-sliding source views"));
698
699      /* Wait for more data if we don't have enough bytes for the
700         whole window.  */
701      if ((apr_size_t) (end - p) < inslen + newlen)
702        return SVN_NO_ERROR;
703
704      /* Decode the window and send it off. */
705      SVN_ERR(decode_window(&window, sview_offset, sview_len, tview_len,
706                            inslen, newlen, p, db->subpool,
707                            db->version));
708      SVN_ERR(db->consumer_func(&window, db->consumer_baton));
709
710      /* Make a new subpool and buffer, saving aside the remaining
711         data in the old buffer.  */
712      newpool = svn_pool_create(db->pool);
713      p += inslen + newlen;
714      remaining = db->buffer->data + db->buffer->len - (const char *) p;
715      db->buffer =
716        svn_stringbuf_ncreate((const char *) p, remaining, newpool);
717
718      /* Remember the offset and length of the source view for next time.  */
719      db->last_sview_offset = sview_offset;
720      db->last_sview_len = sview_len;
721
722      /* We've copied stuff out of the old pool. Toss that pool and use
723         our new pool.
724         ### might be nice to avoid the copy and just use svn_pool_clear
725         ### to get rid of whatever the "other stuff" is. future project...
726      */
727      svn_pool_destroy(db->subpool);
728      db->subpool = newpool;
729    }
730
731  /* NOTREACHED */
732}
733
734
735static svn_error_t *
736close_handler(void *baton)
737{
738  struct decode_baton *db = (struct decode_baton *) baton;
739  svn_error_t *err;
740
741  /* Make sure that we're at a plausible end of stream, returning an
742     error if we are expected to do so.  */
743  if ((db->error_on_early_close)
744      && (db->header_bytes < 4 || db->buffer->len != 0))
745    return svn_error_create(SVN_ERR_SVNDIFF_UNEXPECTED_END, NULL,
746                            _("Unexpected end of svndiff input"));
747
748  /* Tell the window consumer that we're done, and clean up.  */
749  err = db->consumer_func(NULL, db->consumer_baton);
750  svn_pool_destroy(db->pool);
751  return err;
752}
753
754
755svn_stream_t *
756svn_txdelta_parse_svndiff(svn_txdelta_window_handler_t handler,
757                          void *handler_baton,
758                          svn_boolean_t error_on_early_close,
759                          apr_pool_t *pool)
760{
761  apr_pool_t *subpool = svn_pool_create(pool);
762  struct decode_baton *db = apr_palloc(pool, sizeof(*db));
763  svn_stream_t *stream;
764
765  db->consumer_func = handler;
766  db->consumer_baton = handler_baton;
767  db->pool = subpool;
768  db->subpool = svn_pool_create(subpool);
769  db->buffer = svn_stringbuf_create("", db->subpool);
770  db->last_sview_offset = 0;
771  db->last_sview_len = 0;
772  db->header_bytes = 0;
773  db->error_on_early_close = error_on_early_close;
774  stream = svn_stream_create(db, pool);
775  svn_stream_set_write(stream, write_handler);
776  svn_stream_set_close(stream, close_handler);
777  return stream;
778}
779
780
781/* Routines for reading one svndiff window at a time. */
782
783/* Read one byte from STREAM into *BYTE. */
784static svn_error_t *
785read_one_byte(unsigned char *byte, svn_stream_t *stream)
786{
787  char c;
788  apr_size_t len = 1;
789
790  SVN_ERR(svn_stream_read(stream, &c, &len));
791  if (len == 0)
792    return svn_error_create(SVN_ERR_SVNDIFF_UNEXPECTED_END, NULL,
793                            _("Unexpected end of svndiff input"));
794  *byte = (unsigned char) c;
795  return SVN_NO_ERROR;
796}
797
798/* Read and decode one integer from STREAM into *SIZE. */
799static svn_error_t *
800read_one_size(apr_size_t *size, svn_stream_t *stream)
801{
802  unsigned char c;
803
804  *size = 0;
805  while (1)
806    {
807      SVN_ERR(read_one_byte(&c, stream));
808      *size = (*size << 7) | (c & 0x7f);
809      if (!(c & 0x80))
810        break;
811    }
812  return SVN_NO_ERROR;
813}
814
815/* Read a window header from STREAM and check it for integer overflow. */
816static svn_error_t *
817read_window_header(svn_stream_t *stream, svn_filesize_t *sview_offset,
818                   apr_size_t *sview_len, apr_size_t *tview_len,
819                   apr_size_t *inslen, apr_size_t *newlen)
820{
821  unsigned char c;
822
823  /* Read the source view offset by hand, since it's not an apr_size_t. */
824  *sview_offset = 0;
825  while (1)
826    {
827      SVN_ERR(read_one_byte(&c, stream));
828      *sview_offset = (*sview_offset << 7) | (c & 0x7f);
829      if (!(c & 0x80))
830        break;
831    }
832
833  /* Read the four size fields. */
834  SVN_ERR(read_one_size(sview_len, stream));
835  SVN_ERR(read_one_size(tview_len, stream));
836  SVN_ERR(read_one_size(inslen, stream));
837  SVN_ERR(read_one_size(newlen, stream));
838
839  /* Check for integer overflow.  */
840  if (*sview_offset < 0 || *inslen + *newlen < *inslen
841      || *sview_len + *tview_len < *sview_len
842      || *sview_offset + *sview_len < *sview_offset)
843    return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL,
844                            _("Svndiff contains corrupt window header"));
845
846  return SVN_NO_ERROR;
847}
848
849svn_error_t *
850svn_txdelta_read_svndiff_window(svn_txdelta_window_t **window,
851                                svn_stream_t *stream,
852                                int svndiff_version,
853                                apr_pool_t *pool)
854{
855  svn_filesize_t sview_offset;
856  apr_size_t sview_len, tview_len, inslen, newlen, len;
857  unsigned char *buf;
858
859  SVN_ERR(read_window_header(stream, &sview_offset, &sview_len, &tview_len,
860                             &inslen, &newlen));
861  len = inslen + newlen;
862  buf = apr_palloc(pool, len);
863  SVN_ERR(svn_stream_read(stream, (char*)buf, &len));
864  if (len < inslen + newlen)
865    return svn_error_create(SVN_ERR_SVNDIFF_UNEXPECTED_END, NULL,
866                            _("Unexpected end of svndiff input"));
867  *window = apr_palloc(pool, sizeof(**window));
868  return decode_window(*window, sview_offset, sview_len, tview_len, inslen,
869                       newlen, buf, pool, svndiff_version);
870}
871
872
873svn_error_t *
874svn_txdelta_skip_svndiff_window(apr_file_t *file,
875                                int svndiff_version,
876                                apr_pool_t *pool)
877{
878  svn_stream_t *stream = svn_stream_from_aprfile2(file, TRUE, pool);
879  svn_filesize_t sview_offset;
880  apr_size_t sview_len, tview_len, inslen, newlen;
881  apr_off_t offset;
882
883  SVN_ERR(read_window_header(stream, &sview_offset, &sview_len, &tview_len,
884                             &inslen, &newlen));
885
886  offset = inslen + newlen;
887  return svn_io_file_seek(file, APR_CUR, &offset, pool);
888}
Note: See TracBrowser for help on using the repository browser.