~ubuntu-branches/ubuntu/trusty/subversion/trusty-proposed

« back to all changes in this revision

Viewing changes to subversion/libsvn_delta/svndiff.c

  • Committer: Package Import Robot
  • Author(s): Andy Whitcroft
  • Date: 2012-06-21 15:36:36 UTC
  • mfrom: (0.4.13 sid)
  • Revision ID: package-import@ubuntu.com-20120621153636-amqqmuidgwgxz1ly
Tags: 1.7.5-1ubuntu1
* Merge from Debian unstable.  Remaining changes:
  - Create pot file on build.
  - Build a python-subversion-dbg package.
  - Build-depend on python-dbg.
  - Build-depend on default-jre-headless/-jdk.
  - Do not apply java-build patch.
  - debian/rules: Manually create the doxygen output directory, otherwise
    we get weird build failures when running parallel builds.

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
 * svndiff.c -- Encoding and decoding svndiff-format deltas.
3
3
 *
4
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/.
 
5
 *    Licensed to the Apache Software Foundation (ASF) under one
 
6
 *    or more contributor license agreements.  See the NOTICE file
 
7
 *    distributed with this work for additional information
 
8
 *    regarding copyright ownership.  The ASF licenses this file
 
9
 *    to you under the Apache License, Version 2.0 (the
 
10
 *    "License"); you may not use this file except in compliance
 
11
 *    with the License.  You may obtain a copy of the License at
 
12
 *
 
13
 *      http://www.apache.org/licenses/LICENSE-2.0
 
14
 *
 
15
 *    Unless required by applicable law or agreed to in writing,
 
16
 *    software distributed under the License is distributed on an
 
17
 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 
18
 *    KIND, either express or implied.  See the License for the
 
19
 *    specific language governing permissions and limitations
 
20
 *    under the License.
16
21
 * ====================================================================
17
22
 */
18
23
 
26
31
#include "svn_private_config.h"
27
32
#include <zlib.h>
28
33
 
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. */
 
34
/* The zlib compressBound function was not exported until 1.2.0. */
 
35
#if ZLIB_VERNUM >= 0x1200
 
36
#define svnCompressBound(LEN) compressBound(LEN)
 
37
#else
32
38
#define svnCompressBound(LEN) ((LEN) + ((LEN) >> 12) + ((LEN) >> 14) + 11)
 
39
#endif
33
40
 
34
41
/* For svndiff1, address/instruction/new data under this size will not
35
42
   be compressed using zlib as a secondary compressor.  */
36
43
#define MIN_COMPRESS_SIZE 512
37
44
 
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
45
/* ----- Text delta to svndiff ----- */
47
46
 
48
47
/* We make one of these and get it passed back to us in calls to the
49
48
   window handler.  We only use it to record the write function and
50
 
   baton passed to svn_txdelta_to_svndiff2().  */
 
49
   baton passed to svn_txdelta_to_svndiff3().  */
51
50
struct encoder_baton {
52
51
  svn_stream_t *output;
53
52
  svn_boolean_t header_done;
54
53
  int version;
 
54
  int compression_level;
55
55
  apr_pool_t *pool;
56
56
};
57
57
 
85
85
         129 encodes as [1 0000001] [0 0000001]
86
86
        2000 encodes as [1 0001111] [0 1010000]
87
87
*/
88
 
 
89
 
static char *
90
 
encode_int(char *p, svn_filesize_t val)
 
88
static unsigned char *
 
89
encode_int(unsigned char *p, svn_filesize_t val)
91
90
{
92
91
  int n;
93
92
  svn_filesize_t v;
111
110
  while (--n >= 0)
112
111
    {
113
112
      cont = ((n > 0) ? 0x1 : 0x0) << 7;
114
 
      *p++ = (char)(((val >> (n * 7)) & 0x7f) | cont);
 
113
      *p++ = (unsigned char)(((val >> (n * 7)) & 0x7f) | cont);
115
114
    }
116
115
 
117
116
  return p;
122
121
static void
123
122
append_encoded_int(svn_stringbuf_t *header, svn_filesize_t val)
124
123
{
125
 
  char buf[MAX_ENCODED_INT_LEN], *p;
 
124
  unsigned char buf[MAX_ENCODED_INT_LEN], *p;
126
125
 
127
126
  p = encode_int(buf, val);
128
 
  svn_stringbuf_appendbytes(header, buf, p - buf);
 
127
  svn_stringbuf_appendbytes(header, (const char *)buf, p - buf);
129
128
}
130
129
 
131
130
/* If IN is a string that is >= MIN_COMPRESS_SIZE, zlib compress it and
134
133
   version of IN was no smaller than the original IN, OUT will be a copy
135
134
   of IN with the size prepended as an integer. */
136
135
static svn_error_t *
137
 
zlib_encode(const char *data, apr_size_t len, svn_stringbuf_t *out)
 
136
zlib_encode(const char *data,
 
137
            apr_size_t len,
 
138
            svn_stringbuf_t *out,
 
139
            int compression_level)
138
140
{
139
141
  unsigned long endlen;
140
 
  unsigned int intlen;
 
142
  apr_size_t intlen;
141
143
 
142
144
  append_encoded_int(out, len);
143
145
  intlen = out->len;
144
146
 
145
 
  if (len < MIN_COMPRESS_SIZE)
 
147
  /* Compression initialization overhead is considered to large for
 
148
     short buffers.  Also, if we don't actually want to compress data,
 
149
     ZLIB will produce an output no shorter than the input.  Hence,
 
150
     the DATA would directly appended to OUT, so we can do that directly
 
151
     without calling ZLIB before. */
 
152
  if (   (len < MIN_COMPRESS_SIZE)
 
153
      || (compression_level == SVN_DELTA_COMPRESSION_LEVEL_NONE))
146
154
    {
147
155
      svn_stringbuf_appendbytes(out, data, len);
148
156
    }
153
161
 
154
162
      if (compress2((unsigned char *)out->data + intlen, &endlen,
155
163
                    (const unsigned char *)data, len,
156
 
                    SVNDIFF1_COMPRESS_LEVEL) != Z_OK)
 
164
                    compression_level) != Z_OK)
157
165
        return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA,
158
166
                                NULL,
159
167
                                _("Compression of svndiff data failed"));
178
186
  svn_stringbuf_t *i1 = svn_stringbuf_create("", pool);
179
187
  svn_stringbuf_t *header = svn_stringbuf_create("", pool);
180
188
  const svn_string_t *newdata;
181
 
  char ibuf[MAX_INSTRUCTION_LEN], *ip;
 
189
  unsigned char ibuf[MAX_INSTRUCTION_LEN], *ip;
182
190
  const svn_txdelta_op_t *op;
183
191
  apr_size_t len;
184
192
 
185
193
  /* Make sure we write the header.  */
186
194
  if (eb->header_done == FALSE)
187
195
    {
188
 
      char svnver[4] = "SVN\0";
 
196
      char svnver[4] = {'S','V','N','\0'};
189
197
      len = 4;
190
 
      svnver[3] = eb->version;
 
198
      svnver[3] = (char)eb->version;
191
199
      SVN_ERR(svn_stream_write(eb->output, svnver, &len));
192
200
      eb->header_done = TRUE;
193
201
    }
220
228
      ip = ibuf;
221
229
      switch (op->action_code)
222
230
        {
223
 
        case svn_txdelta_source: *ip = (char)0; break;
224
 
        case svn_txdelta_target: *ip = (char)(0x1 << 6); break;
225
 
        case svn_txdelta_new:    *ip = (char)(0x2 << 6); break;
 
231
        case svn_txdelta_source: *ip = 0; break;
 
232
        case svn_txdelta_target: *ip = (0x1 << 6); break;
 
233
        case svn_txdelta_new:    *ip = (0x2 << 6); break;
226
234
        }
227
235
      if (op->length >> 6 == 0)
228
236
        *ip++ |= op->length;
230
238
        ip = encode_int(ip + 1, op->length);
231
239
      if (op->action_code != svn_txdelta_new)
232
240
        ip = encode_int(ip, op->offset);
233
 
      svn_stringbuf_appendbytes(instructions, ibuf, ip - ibuf);
 
241
      svn_stringbuf_appendbytes(instructions, (const char *)ibuf, ip - ibuf);
234
242
    }
235
243
 
236
244
  /* Encode the header.  */
239
247
  append_encoded_int(header, window->tview_len);
240
248
  if (eb->version == 1)
241
249
    {
242
 
      SVN_ERR(zlib_encode(instructions->data, instructions->len, i1));
 
250
      SVN_ERR(zlib_encode(instructions->data, instructions->len,
 
251
                          i1, eb->compression_level));
243
252
      instructions = i1;
244
253
    }
245
254
  append_encoded_int(header, instructions->len);
248
257
      svn_stringbuf_t *temp = svn_stringbuf_create("", pool);
249
258
      svn_string_t *tempstr = svn_string_create("", pool);
250
259
      SVN_ERR(zlib_encode(window->new_data->data, window->new_data->len,
251
 
                          temp));
 
260
                          temp, eb->compression_level));
252
261
      tempstr->data = temp->data;
253
262
      tempstr->len = temp->len;
254
263
      newdata = tempstr;
277
286
}
278
287
 
279
288
void
280
 
svn_txdelta_to_svndiff2(svn_txdelta_window_handler_t *handler,
 
289
svn_txdelta_to_svndiff3(svn_txdelta_window_handler_t *handler,
281
290
                        void **handler_baton,
282
291
                        svn_stream_t *output,
283
292
                        int svndiff_version,
 
293
                        int compression_level,
284
294
                        apr_pool_t *pool)
285
295
{
286
296
  apr_pool_t *subpool = svn_pool_create(pool);
291
301
  eb->header_done = FALSE;
292
302
  eb->pool = subpool;
293
303
  eb->version = svndiff_version;
 
304
  eb->compression_level = compression_level;
294
305
 
295
306
  *handler = window_handler;
296
307
  *handler_baton = eb;
297
308
}
298
309
 
299
310
void
 
311
svn_txdelta_to_svndiff2(svn_txdelta_window_handler_t *handler,
 
312
                        void **handler_baton,
 
313
                        svn_stream_t *output,
 
314
                        int svndiff_version,
 
315
                        apr_pool_t *pool)
 
316
{
 
317
  svn_txdelta_to_svndiff3(handler, handler_baton, output, svndiff_version,
 
318
                          SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool);
 
319
}
 
320
 
 
321
void
300
322
svn_txdelta_to_svndiff(svn_stream_t *output,
301
323
                       apr_pool_t *pool,
302
324
                       svn_txdelta_window_handler_t *handler,
303
325
                       void **handler_baton)
304
326
{
305
 
  svn_txdelta_to_svndiff2(handler, handler_baton, output, 0, pool);
 
327
  svn_txdelta_to_svndiff3(handler, handler_baton, output, 0,
 
328
                          SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool);
306
329
}
307
330
 
308
331
 
333
356
 
334
357
  /* We have to discard four bytes at the beginning for the header.
335
358
     This field keeps track of how many of those bytes we have read.  */
336
 
  int header_bytes;
 
359
  apr_size_t header_bytes;
337
360
 
338
361
  /* Do we want an error to occur when we close the stream that
339
362
     indicates we didn't send the whole svndiff data?  If you plan to
346
369
};
347
370
 
348
371
 
349
 
/* Decode an svndiff-encoded integer into VAL and return a pointer to
 
372
/* Decode an svndiff-encoded integer into *VAL and return a pointer to
350
373
   the byte after the integer.  The bytes to be decoded live in the
351
 
   range [P..END-1].  See the comment for encode_int earlier in this
352
 
   file for more detail on the encoding format.  */
 
374
   range [P..END-1].  If these bytes do not contain a whole encoded
 
375
   integer, return NULL; in this case *VAL is undefined.
353
376
 
 
377
   See the comment for encode_int() earlier in this file for more detail on
 
378
   the encoding format.  */
354
379
static const unsigned char *
355
380
decode_file_offset(svn_filesize_t *val,
356
381
                   const unsigned char *p,
357
382
                   const unsigned char *end)
358
383
{
 
384
  svn_filesize_t temp = 0;
 
385
 
359
386
  if (p + MAX_ENCODED_INT_LEN < end)
360
387
    end = p + MAX_ENCODED_INT_LEN;
361
388
  /* Decode bytes until we're done.  */
362
 
  *val = 0;
363
389
  while (p < end)
364
390
    {
365
 
      *val = (*val << 7) | (*p & 0x7f);
366
 
      if (((*p++ >> 7) & 0x1) == 0)
 
391
      /* Don't use svn_filesize_t here, because this might be 64 bits
 
392
       * on 32 bit targets. Optimizing compilers may or may not be
 
393
       * able to reduce that to the effective code below. */
 
394
      unsigned int c = *p++;
 
395
 
 
396
      temp = (temp << 7) | (c & 0x7f);
 
397
      if (c < 0x80)
 
398
      {
 
399
        *val = temp;
367
400
        return p;
 
401
      }
368
402
    }
 
403
 
369
404
  return NULL;
370
405
}
371
406
 
372
407
 
373
 
/* Same as above, only decide into a size variable. */
374
 
 
 
408
/* Same as above, only decode into a size variable. */
375
409
static const unsigned char *
376
410
decode_size(apr_size_t *val,
377
411
            const unsigned char *p,
378
412
            const unsigned char *end)
379
413
{
 
414
  apr_size_t temp = 0;
 
415
 
380
416
  if (p + MAX_ENCODED_INT_LEN < end)
381
417
    end = p + MAX_ENCODED_INT_LEN;
382
418
  /* Decode bytes until we're done.  */
383
 
  *val = 0;
384
419
  while (p < end)
385
420
    {
386
 
      *val = (*val << 7) | (*p & 0x7f);
387
 
      if (((*p++ >> 7) & 0x1) == 0)
 
421
      apr_size_t c = *p++;
 
422
 
 
423
      temp = (temp << 7) | (c & 0x7f);
 
424
      if (c < 0x80)
 
425
      {
 
426
        *val = temp;
388
427
        return p;
 
428
      }
389
429
    }
 
430
 
390
431
  return NULL;
391
432
}
392
433
 
394
435
   We expect an integer is prepended to IN that specifies the original
395
436
   size, and that if encoded size == original size, that the remaining
396
437
   data is not compressed.  */
397
 
 
398
438
static svn_error_t *
399
439
zlib_decode(svn_stringbuf_t *in, svn_stringbuf_t *out, apr_size_t limit)
400
440
{
447
487
/* Decode an instruction into OP, returning a pointer to the text
448
488
   after the instruction.  Note that if the action code is
449
489
   svn_txdelta_new, the offset field of *OP will not be set.  */
450
 
 
451
490
static const unsigned char *
452
491
decode_instruction(svn_txdelta_op_t *op,
453
492
                   const unsigned char *p,
454
493
                   const unsigned char *end)
455
494
{
 
495
  apr_size_t c;
 
496
  apr_size_t action;
 
497
 
456
498
  if (p == end)
457
499
    return NULL;
458
500
 
 
501
  /* We need this more than once */
 
502
  c = *p++;
 
503
 
459
504
  /* Decode the instruction selector.  */
460
 
  switch ((*p >> 6) & 0x3)
461
 
    {
462
 
    case 0x0: op->action_code = svn_txdelta_source; break;
463
 
    case 0x1: op->action_code = svn_txdelta_target; break;
464
 
    case 0x2: op->action_code = svn_txdelta_new; break;
465
 
    case 0x3: return NULL;
466
 
    }
 
505
  action = (c >> 6) & 0x3;
 
506
  if (action >= 0x3)
 
507
      return NULL;
 
508
 
 
509
  /* This relies on enum svn_delta_action values to match and never to be
 
510
     redefined. */
 
511
  op->action_code = (enum svn_delta_action)(action);
467
512
 
468
513
  /* Decode the length and offset.  */
469
 
  op->length = *p++ & 0x3f;
 
514
  op->length = c & 0x3f;
470
515
  if (op->length == 0)
471
516
    {
472
517
      p = decode_size(&op->length, p, end);
473
518
      if (p == NULL)
474
519
        return NULL;
475
520
    }
476
 
  if (op->action_code != svn_txdelta_new)
 
521
  if (action != svn_txdelta_new)
477
522
    {
478
523
      p = decode_size(&op->offset, p, end);
479
524
      if (p == NULL)