2 * svndiff.c -- Encoding and decoding svndiff-format deltas.
4 * ====================================================================
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
21 * ====================================================================
27 #include "svn_delta.h"
30 #include "svn_pools.h"
31 #include "svn_private_config.h"
34 #include "private/svn_error_private.h"
35 #include "private/svn_delta_private.h"
37 /* The zlib compressBound function was not exported until 1.2.0. */
38 #if ZLIB_VERNUM >= 0x1200
39 #define svnCompressBound(LEN) compressBound(LEN)
41 #define svnCompressBound(LEN) ((LEN) + ((LEN) >> 12) + ((LEN) >> 14) + 11)
44 /* For svndiff1, address/instruction/new data under this size will not
45 be compressed using zlib as a secondary compressor. */
46 #define MIN_COMPRESS_SIZE 512
48 /* ----- Text delta to svndiff ----- */
50 /* We make one of these and get it passed back to us in calls to the
51 window handler. We only use it to record the write function and
52 baton passed to svn_txdelta_to_svndiff3(). */
53 struct encoder_baton {
55 svn_boolean_t header_done;
57 int compression_level;
61 /* This is at least as big as the largest size of an integer that
62 encode_int can generate; it is sufficient for creating buffers for
63 it to write into. This assumes that integers are at most 64 bits,
64 and so 10 bytes (with 7 bits of information each) are sufficient to
66 #define MAX_ENCODED_INT_LEN 10
67 /* This is at least as big as the largest size for a single instruction. */
68 #define MAX_INSTRUCTION_LEN (2*MAX_ENCODED_INT_LEN+1)
69 /* This is at least as big as the largest possible instructions
70 section: in theory, the instructions could be SVN_DELTA_WINDOW_SIZE
71 1-byte copy-from-source instructions (though this is very unlikely). */
72 #define MAX_INSTRUCTION_SECTION_LEN (SVN_DELTA_WINDOW_SIZE*MAX_INSTRUCTION_LEN)
74 /* Encode VAL into the buffer P using the variable-length svndiff
75 integer format. Return the incremented value of P after the
76 encoded bytes have been written. P must point to a buffer of size
77 at least MAX_ENCODED_INT_LEN.
79 This encoding uses the high bit of each byte as a continuation bit
80 and the other seven bits as data bits. High-order data bits are
81 encoded first, followed by lower-order bits, so the value can be
82 reconstructed by concatenating the data bits from left to right and
83 interpreting the result as a binary number. Examples (brackets
84 denote byte boundaries, spaces are for clarity only):
86 1 encodes as [0 0000001]
87 33 encodes as [0 0100001]
88 129 encodes as [1 0000001] [0 0000001]
89 2000 encodes as [1 0001111] [0 1010000]
91 static unsigned char *
92 encode_int(unsigned char *p, svn_filesize_t val)
98 SVN_ERR_ASSERT_NO_RETURN(val >= 0);
100 /* Figure out how many bytes we'll need. */
109 SVN_ERR_ASSERT_NO_RETURN(n <= MAX_ENCODED_INT_LEN);
111 /* Encode the remaining bytes; n is always the number of bytes
112 coming after the one we're encoding. */
115 cont = ((n > 0) ? 0x1 : 0x0) << 7;
116 *p++ = (unsigned char)(((val >> (n * 7)) & 0x7f) | cont);
123 /* Append an encoded integer to a string. */
125 append_encoded_int(svn_stringbuf_t *header, svn_filesize_t val)
127 unsigned char buf[MAX_ENCODED_INT_LEN], *p;
129 p = encode_int(buf, val);
130 svn_stringbuf_appendbytes(header, (const char *)buf, p - buf);
133 /* If IN is a string that is >= MIN_COMPRESS_SIZE and the COMPRESSION_LEVEL
134 is not SVN_DELTA_COMPRESSION_LEVEL_NONE, zlib compress it and places the
135 result in OUT, with an integer prepended specifying the original size.
136 If IN is < MIN_COMPRESS_SIZE, or if the compressed version of IN was no
137 smaller than the original IN, OUT will be a copy of IN with the size
138 prepended as an integer. */
140 zlib_encode(const char *data,
142 svn_stringbuf_t *out,
143 int compression_level)
145 unsigned long endlen;
148 svn_stringbuf_setempty(out);
149 append_encoded_int(out, len);
152 /* Compression initialization overhead is considered to large for
153 short buffers. Also, if we don't actually want to compress data,
154 ZLIB will produce an output no shorter than the input. Hence,
155 the DATA would directly appended to OUT, so we can do that directly
156 without calling ZLIB before. */
157 if ( (len < MIN_COMPRESS_SIZE)
158 || (compression_level == SVN_DELTA_COMPRESSION_LEVEL_NONE))
160 svn_stringbuf_appendbytes(out, data, len);
166 svn_stringbuf_ensure(out, svnCompressBound(len) + intlen);
167 endlen = out->blocksize;
169 zerr = compress2((unsigned char *)out->data + intlen, &endlen,
170 (const unsigned char *)data, len,
173 return svn_error_trace(svn_error__wrap_zlib(
175 _("Compression of svndiff data failed")));
177 /* Compression didn't help :(, just append the original text */
180 svn_stringbuf_appendbytes(out, data, len);
183 out->len = endlen + intlen;
184 out->data[out->len] = 0;
190 send_simple_insertion_window(svn_txdelta_window_t *window,
191 struct encoder_baton *eb)
193 unsigned char headers[4 + 5 * MAX_ENCODED_INT_LEN + MAX_INSTRUCTION_LEN];
194 unsigned char ibuf[MAX_INSTRUCTION_LEN];
195 unsigned char *header_current;
196 apr_size_t header_len;
197 apr_size_t ip_len, i;
198 apr_size_t len = window->new_data->len;
200 /* there is only one target copy op. It must span the whole window */
201 assert(window->ops[0].action_code == svn_txdelta_new);
202 assert(window->ops[0].length == window->tview_len);
203 assert(window->ops[0].offset == 0);
205 /* write stream header if necessary */
206 if (!eb->header_done)
208 eb->header_done = TRUE;
212 headers[3] = (unsigned char)eb->version;
213 header_current = headers + 4;
217 header_current = headers;
220 /* Encode the action code and length. */
221 if (window->tview_len >> 6 == 0)
223 ibuf[0] = (unsigned char)(window->tview_len + (0x2 << 6));
228 ibuf[0] = (0x2 << 6);
229 ip_len = encode_int(ibuf + 1, window->tview_len) - ibuf;
232 /* encode the window header. Please note that the source window may
233 * have content despite not being used for deltification. */
234 header_current = encode_int(header_current, window->sview_offset);
235 header_current = encode_int(header_current, window->sview_len);
236 header_current = encode_int(header_current, window->tview_len);
237 header_current[0] = (unsigned char)ip_len; /* 1 instruction */
238 header_current = encode_int(&header_current[1], len);
240 /* append instructions (1 to a handful of bytes) */
241 for (i = 0; i < ip_len; ++i)
242 header_current[i] = ibuf[i];
244 header_len = header_current - headers + ip_len;
246 /* Write out the window. */
247 SVN_ERR(svn_stream_write(eb->output, (const char *)headers, &header_len));
249 SVN_ERR(svn_stream_write(eb->output, window->new_data->data, &len));
255 window_handler(svn_txdelta_window_t *window, void *baton)
257 struct encoder_baton *eb = baton;
259 svn_stringbuf_t *instructions;
261 svn_stringbuf_t *header;
262 const svn_string_t *newdata;
263 unsigned char ibuf[MAX_INSTRUCTION_LEN], *ip;
264 const svn_txdelta_op_t *op;
267 /* use specialized code if there is no source */
268 if (window && !window->src_ops && window->num_ops == 1 && !eb->version)
269 return svn_error_trace(send_simple_insertion_window(window, eb));
271 /* Make sure we write the header. */
272 if (!eb->header_done)
274 char svnver[4] = {'S','V','N','\0'};
276 svnver[3] = (char)eb->version;
277 SVN_ERR(svn_stream_write(eb->output, svnver, &len));
278 eb->header_done = TRUE;
283 svn_stream_t *output = eb->output;
285 /* We're done; clean up.
287 We clean our pool first. Given that the output stream was passed
288 TO us, we'll assume it has a longer lifetime, and that it will not
289 be affected by our pool destruction.
291 The contrary point of view (close the stream first): that could
292 tell our user that everything related to the output stream is done,
293 and a cleanup of the user pool should occur. However, that user
294 pool could include the subpool we created for our work (eb->pool),
295 which would then make our call to svn_pool_destroy() puke.
297 svn_pool_destroy(eb->pool);
299 return svn_stream_close(output);
302 /* create the necessary data buffers */
303 pool = svn_pool_create(eb->pool);
304 instructions = svn_stringbuf_create_empty(pool);
305 i1 = svn_stringbuf_create_empty(pool);
306 header = svn_stringbuf_create_empty(pool);
308 /* Encode the instructions. */
309 for (op = window->ops; op < window->ops + window->num_ops; op++)
311 /* Encode the action code and length. */
313 switch (op->action_code)
315 case svn_txdelta_source: *ip = 0; break;
316 case svn_txdelta_target: *ip = (0x1 << 6); break;
317 case svn_txdelta_new: *ip = (0x2 << 6); break;
319 if (op->length >> 6 == 0)
320 *ip++ |= (unsigned char)op->length;
322 ip = encode_int(ip + 1, op->length);
323 if (op->action_code != svn_txdelta_new)
324 ip = encode_int(ip, op->offset);
325 svn_stringbuf_appendbytes(instructions, (const char *)ibuf, ip - ibuf);
328 /* Encode the header. */
329 append_encoded_int(header, window->sview_offset);
330 append_encoded_int(header, window->sview_len);
331 append_encoded_int(header, window->tview_len);
332 if (eb->version == 1)
334 SVN_ERR(zlib_encode(instructions->data, instructions->len,
335 i1, eb->compression_level));
338 append_encoded_int(header, instructions->len);
339 if (eb->version == 1)
341 svn_stringbuf_t *temp = svn_stringbuf_create_empty(pool);
342 svn_string_t *tempstr = svn_string_create_empty(pool);
343 SVN_ERR(zlib_encode(window->new_data->data, window->new_data->len,
344 temp, eb->compression_level));
345 tempstr->data = temp->data;
346 tempstr->len = temp->len;
350 newdata = window->new_data;
352 append_encoded_int(header, newdata->len);
354 /* Write out the window. */
356 SVN_ERR(svn_stream_write(eb->output, header->data, &len));
357 if (instructions->len > 0)
359 len = instructions->len;
360 SVN_ERR(svn_stream_write(eb->output, instructions->data, &len));
362 if (newdata->len > 0)
365 SVN_ERR(svn_stream_write(eb->output, newdata->data, &len));
368 svn_pool_destroy(pool);
373 svn_txdelta_to_svndiff3(svn_txdelta_window_handler_t *handler,
374 void **handler_baton,
375 svn_stream_t *output,
377 int compression_level,
380 apr_pool_t *subpool = svn_pool_create(pool);
381 struct encoder_baton *eb;
383 eb = apr_palloc(subpool, sizeof(*eb));
385 eb->header_done = FALSE;
387 eb->version = svndiff_version;
388 eb->compression_level = compression_level;
390 *handler = window_handler;
395 svn_txdelta_to_svndiff2(svn_txdelta_window_handler_t *handler,
396 void **handler_baton,
397 svn_stream_t *output,
401 svn_txdelta_to_svndiff3(handler, handler_baton, output, svndiff_version,
402 SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool);
406 svn_txdelta_to_svndiff(svn_stream_t *output,
408 svn_txdelta_window_handler_t *handler,
409 void **handler_baton)
411 svn_txdelta_to_svndiff3(handler, handler_baton, output, 0,
412 SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool);
416 /* ----- svndiff to text delta ----- */
418 /* An svndiff parser object. */
421 /* Once the svndiff parser has enough data buffered to create a
422 "window", it passes this window to the caller's consumer routine. */
423 svn_txdelta_window_handler_t consumer_func;
424 void *consumer_baton;
426 /* Pool to create subpools from; each developing window will be a
430 /* The current subpool which contains our current window-buffer. */
433 /* The actual svndiff data buffer, living within subpool. */
434 svn_stringbuf_t *buffer;
436 /* The offset and size of the last source view, so that we can check
437 to make sure the next one isn't sliding backwards. */
438 svn_filesize_t last_sview_offset;
439 apr_size_t last_sview_len;
441 /* We have to discard four bytes at the beginning for the header.
442 This field keeps track of how many of those bytes we have read. */
443 apr_size_t header_bytes;
445 /* Do we want an error to occur when we close the stream that
446 indicates we didn't send the whole svndiff data? If you plan to
447 not transmit the whole svndiff data stream, you will want this to
449 svn_boolean_t error_on_early_close;
451 /* svndiff version in use by delta. */
452 unsigned char version;
456 /* Decode an svndiff-encoded integer into *VAL and return a pointer to
457 the byte after the integer. The bytes to be decoded live in the
458 range [P..END-1]. If these bytes do not contain a whole encoded
459 integer, return NULL; in this case *VAL is undefined.
461 See the comment for encode_int() earlier in this file for more detail on
462 the encoding format. */
463 static const unsigned char *
464 decode_file_offset(svn_filesize_t *val,
465 const unsigned char *p,
466 const unsigned char *end)
468 svn_filesize_t temp = 0;
470 if (p + MAX_ENCODED_INT_LEN < end)
471 end = p + MAX_ENCODED_INT_LEN;
472 /* Decode bytes until we're done. */
475 /* Don't use svn_filesize_t here, because this might be 64 bits
476 * on 32 bit targets. Optimizing compilers may or may not be
477 * able to reduce that to the effective code below. */
478 unsigned int c = *p++;
480 temp = (temp << 7) | (c & 0x7f);
492 /* Same as above, only decode into a size variable. */
493 static const unsigned char *
494 decode_size(apr_size_t *val,
495 const unsigned char *p,
496 const unsigned char *end)
500 if (p + MAX_ENCODED_INT_LEN < end)
501 end = p + MAX_ENCODED_INT_LEN;
502 /* Decode bytes until we're done. */
507 temp = (temp << 7) | (c & 0x7f);
518 /* Decode the possibly-zlib compressed string of length INLEN that is in
519 IN, into OUT. We expect an integer is prepended to IN that specifies
520 the original size, and that if encoded size == original size, that the
521 remaining data is not compressed.
522 In that case, we will simply return pointer into IN as data pointer for
523 OUT, COPYLESS_ALLOWED has been set. The, the caller is expected not to
524 modify the contents of OUT.
525 An error is returned if the decoded length exceeds the given LIMIT.
528 zlib_decode(const unsigned char *in, apr_size_t inLen, svn_stringbuf_t *out,
532 const unsigned char *oldplace = in;
534 /* First thing in the string is the original length. */
535 in = decode_size(&len, in, in + inLen);
537 return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA, NULL,
538 _("Decompression of svndiff data failed: no size"));
540 return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA, NULL,
541 _("Decompression of svndiff data failed: "
543 /* We need to subtract the size of the encoded original length off the
544 * still remaining input length. */
545 inLen -= (in - oldplace);
548 svn_stringbuf_ensure(out, len);
549 memcpy(out->data, in, len);
557 unsigned long zlen = len;
560 svn_stringbuf_ensure(out, len);
561 zerr = uncompress((unsigned char *)out->data, &zlen, in, inLen);
563 return svn_error_trace(svn_error__wrap_zlib(
565 _("Decompression of svndiff data failed")));
567 /* Zlib should not produce something that has a different size than the
568 original length we stored. */
570 return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA,
572 _("Size of uncompressed data "
573 "does not match stored original length"));
580 /* Decode an instruction into OP, returning a pointer to the text
581 after the instruction. Note that if the action code is
582 svn_txdelta_new, the offset field of *OP will not be set. */
583 static const unsigned char *
584 decode_instruction(svn_txdelta_op_t *op,
585 const unsigned char *p,
586 const unsigned char *end)
594 /* We need this more than once */
597 /* Decode the instruction selector. */
598 action = (c >> 6) & 0x3;
602 /* This relies on enum svn_delta_action values to match and never to be
604 op->action_code = (enum svn_delta_action)(action);
606 /* Decode the length and offset. */
607 op->length = c & 0x3f;
610 p = decode_size(&op->length, p, end);
614 if (action != svn_txdelta_new)
616 p = decode_size(&op->offset, p, end);
624 /* Count the instructions in the range [P..END-1] and make sure they
625 are valid for the given window lengths. Return an error if the
626 instructions are invalid; otherwise set *NINST to the number of
629 count_and_verify_instructions(int *ninst,
630 const unsigned char *p,
631 const unsigned char *end,
632 apr_size_t sview_len,
633 apr_size_t tview_len,
638 apr_size_t tpos = 0, npos = 0;
642 p = decode_instruction(&op, p, end);
644 /* Detect any malformed operations from the instruction stream. */
646 return svn_error_createf
647 (SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
648 _("Invalid diff stream: insn %d cannot be decoded"), n);
649 else if (op.length == 0)
650 return svn_error_createf
651 (SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
652 _("Invalid diff stream: insn %d has length zero"), n);
653 else if (op.length > tview_len - tpos)
654 return svn_error_createf
655 (SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
656 _("Invalid diff stream: insn %d overflows the target view"), n);
658 switch (op.action_code)
660 case svn_txdelta_source:
661 if (op.length > sview_len - op.offset ||
662 op.offset > sview_len)
663 return svn_error_createf
664 (SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
665 _("Invalid diff stream: "
666 "[src] insn %d overflows the source view"), n);
668 case svn_txdelta_target:
669 if (op.offset >= tpos)
670 return svn_error_createf
671 (SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
672 _("Invalid diff stream: "
673 "[tgt] insn %d starts beyond the target view position"), n);
675 case svn_txdelta_new:
676 if (op.length > new_len - npos)
677 return svn_error_createf
678 (SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
679 _("Invalid diff stream: "
680 "[new] insn %d overflows the new data section"), n);
687 if (tpos != tview_len)
688 return svn_error_create(SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
689 _("Delta does not fill the target window"));
691 return svn_error_create(SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
692 _("Delta does not contain enough new data"));
698 /* Given the five integer fields of a window header and a pointer to
699 the remainder of the window contents, fill in a delta window
700 structure *WINDOW. New allocations will be performed in POOL;
701 the new_data field of *WINDOW will refer directly to memory pointed
704 decode_window(svn_txdelta_window_t *window, svn_filesize_t sview_offset,
705 apr_size_t sview_len, apr_size_t tview_len, apr_size_t inslen,
706 apr_size_t newlen, const unsigned char *data, apr_pool_t *pool,
707 unsigned int version)
709 const unsigned char *insend;
712 svn_txdelta_op_t *ops, *op;
713 svn_string_t *new_data = apr_palloc(pool, sizeof(*new_data));
715 window->sview_offset = sview_offset;
716 window->sview_len = sview_len;
717 window->tview_len = tview_len;
719 insend = data + inslen;
723 svn_stringbuf_t *instout = svn_stringbuf_create_empty(pool);
724 svn_stringbuf_t *ndout = svn_stringbuf_create_empty(pool);
726 /* these may in fact simply return references to insend */
728 SVN_ERR(zlib_decode(insend, newlen, ndout,
729 SVN_DELTA_WINDOW_SIZE));
730 SVN_ERR(zlib_decode(data, insend - data, instout,
731 MAX_INSTRUCTION_SECTION_LEN));
734 data = (unsigned char *)instout->data;
735 insend = (unsigned char *)instout->data + instout->len;
737 new_data->data = (const char *) ndout->data;
738 new_data->len = newlen;
742 new_data->data = (const char *) insend;
743 new_data->len = newlen;
746 /* Count the instructions and make sure they are all valid. */
747 SVN_ERR(count_and_verify_instructions(&ninst, data, insend,
748 sview_len, tview_len, newlen));
750 /* Allocate a buffer for the instructions and decode them. */
751 ops = apr_palloc(pool, ninst * sizeof(*ops));
754 for (op = ops; op < ops + ninst; op++)
756 data = decode_instruction(op, data, insend);
757 if (op->action_code == svn_txdelta_source)
759 else if (op->action_code == svn_txdelta_new)
765 SVN_ERR_ASSERT(data == insend);
768 window->num_ops = ninst;
769 window->new_data = new_data;
775 write_handler(void *baton,
779 struct decode_baton *db = (struct decode_baton *) baton;
780 const unsigned char *p, *end;
781 svn_filesize_t sview_offset;
782 apr_size_t sview_len, tview_len, inslen, newlen, remaining;
783 apr_size_t buflen = *len;
785 /* Chew up four bytes at the beginning for the header. */
786 if (db->header_bytes < 4)
788 apr_size_t nheader = 4 - db->header_bytes;
789 if (nheader > buflen)
791 if (memcmp(buffer, "SVN\0" + db->header_bytes, nheader) == 0)
793 else if (memcmp(buffer, "SVN\1" + db->header_bytes, nheader) == 0)
796 return svn_error_create(SVN_ERR_SVNDIFF_INVALID_HEADER, NULL,
797 _("Svndiff has invalid header"));
800 db->header_bytes += nheader;
803 /* Concatenate the old with the new. */
804 svn_stringbuf_appendbytes(db->buffer, buffer, buflen);
806 /* We have a buffer of svndiff data that might be good for:
808 a) an integral number of windows' worth of data - this is a
809 trivial case. Make windows from our data and ship them off.
811 b) a non-integral number of windows' worth of data - we shall
812 consume the integral portion of the window data, and then
813 somewhere in the following loop the decoding of the svndiff
814 data will run out of stuff to decode, and will simply return
815 SVN_NO_ERROR, anxiously awaiting more data.
821 svn_txdelta_window_t window;
823 /* Read the header, if we have enough bytes for that. */
824 p = (const unsigned char *) db->buffer->data;
825 end = (const unsigned char *) db->buffer->data + db->buffer->len;
827 p = decode_file_offset(&sview_offset, p, end);
831 p = decode_size(&sview_len, p, end);
835 p = decode_size(&tview_len, p, end);
839 p = decode_size(&inslen, p, end);
843 p = decode_size(&newlen, p, end);
847 if (tview_len > SVN_DELTA_WINDOW_SIZE ||
848 sview_len > SVN_DELTA_WINDOW_SIZE ||
849 /* for svndiff1, newlen includes the original length */
850 newlen > SVN_DELTA_WINDOW_SIZE + MAX_ENCODED_INT_LEN ||
851 inslen > MAX_INSTRUCTION_SECTION_LEN)
852 return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL,
853 _("Svndiff contains a too-large window"));
855 /* Check for integer overflow. */
856 if (sview_offset < 0 || inslen + newlen < inslen
857 || sview_len + tview_len < sview_len
858 || (apr_size_t)sview_offset + sview_len < (apr_size_t)sview_offset)
859 return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL,
860 _("Svndiff contains corrupt window header"));
862 /* Check for source windows which slide backwards. */
864 && (sview_offset < db->last_sview_offset
865 || (sview_offset + sview_len
866 < db->last_sview_offset + db->last_sview_len)))
867 return svn_error_create
868 (SVN_ERR_SVNDIFF_BACKWARD_VIEW, NULL,
869 _("Svndiff has backwards-sliding source views"));
871 /* Wait for more data if we don't have enough bytes for the
873 if ((apr_size_t) (end - p) < inslen + newlen)
876 /* Decode the window and send it off. */
877 SVN_ERR(decode_window(&window, sview_offset, sview_len, tview_len,
878 inslen, newlen, p, db->subpool,
880 SVN_ERR(db->consumer_func(&window, db->consumer_baton));
882 /* Make a new subpool and buffer, saving aside the remaining
883 data in the old buffer. */
884 newpool = svn_pool_create(db->pool);
885 p += inslen + newlen;
886 remaining = db->buffer->data + db->buffer->len - (const char *) p;
888 svn_stringbuf_ncreate((const char *) p, remaining, newpool);
890 /* Remember the offset and length of the source view for next time. */
891 db->last_sview_offset = sview_offset;
892 db->last_sview_len = sview_len;
894 /* We've copied stuff out of the old pool. Toss that pool and use
896 ### might be nice to avoid the copy and just use svn_pool_clear
897 ### to get rid of whatever the "other stuff" is. future project...
899 svn_pool_destroy(db->subpool);
900 db->subpool = newpool;
906 /* Minimal svn_stream_t write handler, doing nothing */
908 noop_write_handler(void *baton,
916 close_handler(void *baton)
918 struct decode_baton *db = (struct decode_baton *) baton;
921 /* Make sure that we're at a plausible end of stream, returning an
922 error if we are expected to do so. */
923 if ((db->error_on_early_close)
924 && (db->header_bytes < 4 || db->buffer->len != 0))
925 return svn_error_create(SVN_ERR_SVNDIFF_UNEXPECTED_END, NULL,
926 _("Unexpected end of svndiff input"));
928 /* Tell the window consumer that we're done, and clean up. */
929 err = db->consumer_func(NULL, db->consumer_baton);
930 svn_pool_destroy(db->pool);
936 svn_txdelta_parse_svndiff(svn_txdelta_window_handler_t handler,
938 svn_boolean_t error_on_early_close,
941 apr_pool_t *subpool = svn_pool_create(pool);
942 struct decode_baton *db = apr_palloc(pool, sizeof(*db));
943 svn_stream_t *stream;
945 db->consumer_func = handler;
946 db->consumer_baton = handler_baton;
948 db->subpool = svn_pool_create(subpool);
949 db->buffer = svn_stringbuf_create_empty(db->subpool);
950 db->last_sview_offset = 0;
951 db->last_sview_len = 0;
952 db->header_bytes = 0;
953 db->error_on_early_close = error_on_early_close;
954 stream = svn_stream_create(db, pool);
956 if (handler != svn_delta_noop_window_handler)
958 svn_stream_set_write(stream, write_handler);
959 svn_stream_set_close(stream, close_handler);
963 /* And else we just ignore everything as efficiently as we can.
964 by only hooking a no-op handler */
965 svn_stream_set_write(stream, noop_write_handler);
971 /* Routines for reading one svndiff window at a time. */
973 /* Read one byte from STREAM into *BYTE. */
975 read_one_byte(unsigned char *byte, svn_stream_t *stream)
980 SVN_ERR(svn_stream_read(stream, &c, &len));
982 return svn_error_create(SVN_ERR_SVNDIFF_UNEXPECTED_END, NULL,
983 _("Unexpected end of svndiff input"));
984 *byte = (unsigned char) c;
988 /* Read and decode one integer from STREAM into *SIZE. */
990 read_one_size(apr_size_t *size, svn_stream_t *stream)
997 SVN_ERR(read_one_byte(&c, stream));
998 *size = (*size << 7) | (c & 0x7f);
1002 return SVN_NO_ERROR;
1005 /* Read a window header from STREAM and check it for integer overflow. */
1006 static svn_error_t *
1007 read_window_header(svn_stream_t *stream, svn_filesize_t *sview_offset,
1008 apr_size_t *sview_len, apr_size_t *tview_len,
1009 apr_size_t *inslen, apr_size_t *newlen)
1013 /* Read the source view offset by hand, since it's not an apr_size_t. */
1017 SVN_ERR(read_one_byte(&c, stream));
1018 *sview_offset = (*sview_offset << 7) | (c & 0x7f);
1023 /* Read the four size fields. */
1024 SVN_ERR(read_one_size(sview_len, stream));
1025 SVN_ERR(read_one_size(tview_len, stream));
1026 SVN_ERR(read_one_size(inslen, stream));
1027 SVN_ERR(read_one_size(newlen, stream));
1029 if (*tview_len > SVN_DELTA_WINDOW_SIZE ||
1030 *sview_len > SVN_DELTA_WINDOW_SIZE ||
1031 /* for svndiff1, newlen includes the original length */
1032 *newlen > SVN_DELTA_WINDOW_SIZE + MAX_ENCODED_INT_LEN ||
1033 *inslen > MAX_INSTRUCTION_SECTION_LEN)
1034 return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL,
1035 _("Svndiff contains a too-large window"));
1037 /* Check for integer overflow. */
1038 if (*sview_offset < 0 || *inslen + *newlen < *inslen
1039 || *sview_len + *tview_len < *sview_len
1040 || (apr_size_t)*sview_offset + *sview_len < (apr_size_t)*sview_offset)
1041 return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL,
1042 _("Svndiff contains corrupt window header"));
1044 return SVN_NO_ERROR;
1048 svn_txdelta_read_svndiff_window(svn_txdelta_window_t **window,
1049 svn_stream_t *stream,
1050 int svndiff_version,
1053 svn_filesize_t sview_offset;
1054 apr_size_t sview_len, tview_len, inslen, newlen, len;
1057 SVN_ERR(read_window_header(stream, &sview_offset, &sview_len, &tview_len,
1059 len = inslen + newlen;
1060 buf = apr_palloc(pool, len);
1061 SVN_ERR(svn_stream_read(stream, (char*)buf, &len));
1062 if (len < inslen + newlen)
1063 return svn_error_create(SVN_ERR_SVNDIFF_UNEXPECTED_END, NULL,
1064 _("Unexpected end of svndiff input"));
1065 *window = apr_palloc(pool, sizeof(**window));
1066 return decode_window(*window, sview_offset, sview_len, tview_len, inslen,
1067 newlen, buf, pool, svndiff_version);
1072 svn_txdelta_skip_svndiff_window(apr_file_t *file,
1073 int svndiff_version,
1076 svn_stream_t *stream = svn_stream_from_aprfile2(file, TRUE, pool);
1077 svn_filesize_t sview_offset;
1078 apr_size_t sview_len, tview_len, inslen, newlen;
1081 SVN_ERR(read_window_header(stream, &sview_offset, &sview_len, &tview_len,
1084 offset = inslen + newlen;
1085 return svn_io_file_seek(file, APR_CUR, &offset, pool);
1090 svn__compress(svn_string_t *in,
1091 svn_stringbuf_t *out,
1092 int compression_level)
1094 return zlib_encode(in->data, in->len, out, compression_level);
1098 svn__decompress(svn_string_t *in,
1099 svn_stringbuf_t *out,
1102 return zlib_decode((const unsigned char*)in->data, in->len, out, limit);