2
2
* svndiff.c -- Encoding and decoding svndiff-format deltas.
4
4
* ====================================================================
5
* Copyright (c) 2000-2006, 2008 CollabNet. All rights reserved.
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.
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
13
* http://www.apache.org/licenses/LICENSE-2.0
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
16
21
* ====================================================================
26
31
#include "svn_private_config.h"
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)
32
38
#define svnCompressBound(LEN) ((LEN) + ((LEN) >> 12) + ((LEN) >> 14) + 11)
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
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
41
#define SVNDIFF1_COMPRESS_LEVEL 5
46
45
/* ----- Text delta to svndiff ----- */
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
int compression_level;
85
85
129 encodes as [1 0000001] [0 0000001]
86
86
2000 encodes as [1 0001111] [0 1010000]
90
encode_int(char *p, svn_filesize_t val)
88
static unsigned char *
89
encode_int(unsigned char *p, svn_filesize_t val)
123
122
append_encoded_int(svn_stringbuf_t *header, svn_filesize_t val)
125
char buf[MAX_ENCODED_INT_LEN], *p;
124
unsigned char buf[MAX_ENCODED_INT_LEN], *p;
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);
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,
138
svn_stringbuf_t *out,
139
int compression_level)
139
141
unsigned long endlen;
142
144
append_encoded_int(out, len);
143
145
intlen = out->len;
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))
147
155
svn_stringbuf_appendbytes(out, data, len);
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,
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;
185
193
/* Make sure we write the header. */
186
194
if (eb->header_done == FALSE)
188
char svnver[4] = "SVN\0";
196
char svnver[4] = {'S','V','N','\0'};
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;
221
229
switch (op->action_code)
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;
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);
236
244
/* Encode the header. */
239
247
append_encoded_int(header, window->tview_len);
240
248
if (eb->version == 1)
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;
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,
260
temp, eb->compression_level));
252
261
tempstr->data = temp->data;
253
262
tempstr->len = temp->len;
254
263
newdata = tempstr;
291
301
eb->header_done = FALSE;
292
302
eb->pool = subpool;
293
303
eb->version = svndiff_version;
304
eb->compression_level = compression_level;
295
306
*handler = window_handler;
296
307
*handler_baton = eb;
311
svn_txdelta_to_svndiff2(svn_txdelta_window_handler_t *handler,
312
void **handler_baton,
313
svn_stream_t *output,
317
svn_txdelta_to_svndiff3(handler, handler_baton, output, svndiff_version,
318
SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool);
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)
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);
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. */
359
apr_size_t header_bytes;
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
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.
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)
384
svn_filesize_t temp = 0;
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. */
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++;
396
temp = (temp << 7) | (c & 0x7f);
373
/* Same as above, only decide into a size variable. */
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)
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. */
386
*val = (*val << 7) | (*p & 0x7f);
387
if (((*p++ >> 7) & 0x1) == 0)
423
temp = (temp << 7) | (c & 0x7f);
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. */
398
438
static svn_error_t *
399
439
zlib_decode(svn_stringbuf_t *in, svn_stringbuf_t *out, apr_size_t limit)
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. */
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)
501
/* We need this more than once */
459
504
/* Decode the instruction selector. */
460
switch ((*p >> 6) & 0x3)
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;
505
action = (c >> 6) & 0x3;
509
/* This relies on enum svn_delta_action values to match and never to be
511
op->action_code = (enum svn_delta_action)(action);
468
513
/* Decode the length and offset. */
469
op->length = *p++ & 0x3f;
514
op->length = c & 0x3f;
470
515
if (op->length == 0)
472
517
p = decode_size(&op->length, p, end);
476
if (op->action_code != svn_txdelta_new)
521
if (action != svn_txdelta_new)
478
523
p = decode_size(&op->offset, p, end);