2 * svndiff.c -- Encoding and decoding svndiff-format deltas.
4 * ====================================================================
5 * Copyright (c) 2000-2006 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/.
16 * ====================================================================
22 #include "svn_delta.h"
25 #include "svn_pools.h"
26 #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. */
32 #define svnCompressBound(LEN) ((LEN) + ((LEN) >> 12) + ((LEN) >> 14) + 11)
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
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 /* ----- Text delta to svndiff ----- */
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_svndiff (). */
51 struct encoder_baton
{
53 svn_boolean_t header_done
;
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.
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):
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]
77 encode_int(char *p
, svn_filesize_t val
)
85 /* Figure out how many bytes we'll need. */
94 /* Encode the remaining bytes; n is always the number of bytes
95 coming after the one we're encoding. */
98 cont
= ((n
> 0) ? 0x1 : 0x0) << 7;
99 *p
++ = (char)(((val
>> (n
* 7)) & 0x7f) | cont
);
106 /* Append an encoded integer to a string. */
108 append_encoded_int(svn_stringbuf_t
*header
, svn_filesize_t val
)
112 p
= encode_int(buf
, val
);
113 svn_stringbuf_appendbytes(header
, buf
, p
- buf
);
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. */
122 zlib_encode(const char *data
, apr_size_t len
, svn_stringbuf_t
*out
)
124 unsigned long endlen
;
127 append_encoded_int(out
, len
);
130 if (len
< MIN_COMPRESS_SIZE
)
132 svn_stringbuf_appendbytes(out
, data
, len
);
136 svn_stringbuf_ensure(out
, svnCompressBound(len
) + intlen
);
137 endlen
= out
->blocksize
;
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
,
144 _("Compression of svndiff data failed"));
146 /* Compression didn't help :(, just append the original text */
149 svn_stringbuf_appendbytes(out
, data
, len
);
152 out
->len
= endlen
+ intlen
;
158 window_handler(svn_txdelta_window_t
*window
, void *baton
)
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
;
167 const svn_txdelta_op_t
*op
;
170 /* Make sure we write the header. */
171 if (eb
->header_done
== FALSE
)
173 char svnver
[4] = "SVN\0";
175 svnver
[3] = eb
->version
;
176 SVN_ERR(svn_stream_write(eb
->output
, svnver
, &len
));
177 eb
->header_done
= TRUE
;
182 svn_stream_t
*output
= eb
->output
;
184 /* We're done; clean up.
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.
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.
196 svn_pool_destroy(eb
->pool
);
198 return svn_stream_close(output
);
201 /* Encode the instructions. */
202 for (op
= window
->ops
; op
< window
->ops
+ window
->num_ops
; op
++)
204 /* Encode the action code and length. */
206 switch (op
->action_code
)
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;
212 if (op
->length
>> 6 == 0)
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
);
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)
227 SVN_ERR(zlib_encode(instructions
->data
, instructions
->len
, i1
));
230 append_encoded_int(header
, instructions
->len
);
231 if (eb
->version
== 1)
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
,
237 tempstr
->data
= temp
->data
;
238 tempstr
->len
= temp
->len
;
242 newdata
= window
->new_data
;
244 append_encoded_int(header
, newdata
->len
);
246 /* Write out the window. */
248 SVN_ERR(svn_stream_write(eb
->output
, header
->data
, &len
));
249 if (instructions
->len
> 0)
251 len
= instructions
->len
;
252 SVN_ERR(svn_stream_write(eb
->output
, instructions
->data
, &len
));
254 if (newdata
->len
> 0)
257 SVN_ERR(svn_stream_write(eb
->output
, newdata
->data
, &len
));
260 svn_pool_destroy(pool
);
265 svn_txdelta_to_svndiff2(svn_txdelta_window_handler_t
*handler
,
266 void **handler_baton
,
267 svn_stream_t
*output
,
271 apr_pool_t
*subpool
= svn_pool_create(pool
);
272 struct encoder_baton
*eb
;
274 eb
= apr_palloc(subpool
, sizeof(*eb
));
276 eb
->header_done
= FALSE
;
278 eb
->version
= svndiff_version
;
280 *handler
= window_handler
;
285 svn_txdelta_to_svndiff(svn_stream_t
*output
,
287 svn_txdelta_window_handler_t
*handler
,
288 void **handler_baton
)
290 svn_txdelta_to_svndiff2(handler
, handler_baton
, output
, 0, pool
);
294 /* ----- svndiff to text delta ----- */
296 /* An svndiff parser object. */
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
;
304 /* Pool to create subpools from; each developing window will be a
308 /* The current subpool which contains our current window-buffer. */
311 /* The actual svndiff data buffer, living within subpool. */
312 svn_stringbuf_t
*buffer
;
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
;
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. */
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
327 svn_boolean_t error_on_early_close
;
329 /* svndiff version in use by delta. */
330 unsigned char version
;
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. */
339 static const unsigned char *
340 decode_file_offset(svn_filesize_t
*val
,
341 const unsigned char *p
,
342 const unsigned char *end
)
344 /* Decode bytes until we're done. */
348 *val
= (*val
<< 7) | (*p
& 0x7f);
349 if (((*p
++ >> 7) & 0x1) == 0)
356 /* Same as above, only decide into a size variable. */
358 static const unsigned char *
359 decode_size(apr_size_t
*val
,
360 const unsigned char *p
,
361 const unsigned char *end
)
363 /* Decode bytes until we're done. */
367 *val
= (*val
<< 7) | (*p
& 0x7f);
368 if (((*p
++ >> 7) & 0x1) == 0)
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. */
380 zlib_decode(svn_stringbuf_t
*in
, svn_stringbuf_t
*out
)
383 char *oldplace
= in
->data
;
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
);
393 svn_stringbuf_appendstr(out
, in
);
398 unsigned long zliblen
;
400 svn_stringbuf_ensure(out
, 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
,
407 _("Decompression of svndiff data failed"));
409 /* Zlib should not produce something that has a different size than the
410 original length we stored. */
412 return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA
,
414 _("Size of uncompressed data "
415 "does not match stored original length"));
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. */
425 static const unsigned char *
426 decode_instruction(svn_txdelta_op_t
*op
,
427 const unsigned char *p
,
428 const unsigned char *end
)
433 /* Decode the instruction selector. */
434 switch ((*p
>> 6) & 0x3)
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
;
442 /* Decode the length and offset. */
443 op
->length
= *p
++ & 0x3f;
446 p
= decode_size(&op
->length
, p
, end
);
450 if (op
->action_code
!= svn_txdelta_new
)
452 p
= decode_size(&op
->offset
, p
, end
);
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
465 count_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
,
474 apr_size_t tpos
= 0, npos
= 0;
478 p
= decode_instruction(&op
, p
, end
);
480 /* Detect any malformed operations from the instruction stream. */
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
);
494 switch (op
.action_code
)
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
);
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
);
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
);
522 if (tpos
!= tview_len
)
523 return svn_error_create(SVN_ERR_SVNDIFF_INVALID_OPS
, NULL
,
524 _("Delta does not fill the target window"));
526 return svn_error_create(SVN_ERR_SVNDIFF_INVALID_OPS
, NULL
,
527 _("Delta does not contain enough new data"));
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
539 decode_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
)
544 const unsigned char *insend
;
547 svn_txdelta_op_t
*ops
, *op
;
548 svn_string_t
*new_data
= apr_palloc(pool
, sizeof(*new_data
));
550 window
->sview_offset
= sview_offset
;
551 window
->sview_len
= sview_len
;
552 window
->tview_len
= tview_len
;
554 insend
= data
+ inslen
;
558 svn_stringbuf_t
*instin
, *ndin
;
559 svn_stringbuf_t
*instout
, *ndout
;
561 instin
= svn_stringbuf_ncreate((const char *)data
, insend
- data
, pool
);
562 instout
= svn_stringbuf_create("", pool
);
563 SVN_ERR(zlib_decode(instin
, instout
));
565 ndin
= svn_stringbuf_ncreate((const char *)insend
, newlen
, pool
);
566 ndout
= svn_stringbuf_create("", pool
);
567 SVN_ERR(zlib_decode(ndin
, ndout
));
570 data
= (unsigned char *)instout
->data
;
571 insend
= (unsigned char *)instout
->data
+ instout
->len
;
573 new_data
->data
= (const char *) ndout
->data
;
574 new_data
->len
= newlen
;
578 new_data
->data
= (const char *) insend
;
579 new_data
->len
= newlen
;
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
));
586 /* Allocate a buffer for the instructions and decode them. */
587 ops
= apr_palloc(pool
, ninst
* sizeof(*ops
));
590 for (op
= ops
; op
< ops
+ ninst
; op
++)
592 data
= decode_instruction(op
, data
, insend
);
593 if (op
->action_code
== svn_txdelta_source
)
595 else if (op
->action_code
== svn_txdelta_new
)
601 assert(data
== insend
);
604 window
->num_ops
= ninst
;
605 window
->new_data
= new_data
;
611 write_handler(void *baton
,
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
;
621 /* Chew up four bytes at the beginning for the header. */
622 if (db
->header_bytes
< 4)
624 apr_size_t nheader
= 4 - db
->header_bytes
;
625 if (nheader
> buflen
)
627 if (memcmp(buffer
, "SVN\0" + db
->header_bytes
, nheader
) == 0)
629 else if (memcmp(buffer
, "SVN\1" + db
->header_bytes
, nheader
) == 0)
632 return svn_error_create(SVN_ERR_SVNDIFF_INVALID_HEADER
, NULL
,
633 _("Svndiff has invalid header"));
636 db
->header_bytes
+= nheader
;
639 /* Concatenate the old with the new. */
640 svn_stringbuf_appendbytes(db
->buffer
, buffer
, buflen
);
642 /* We have a buffer of svndiff data that might be good for:
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.
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.
657 svn_txdelta_window_t window
;
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
;
663 p
= decode_file_offset(&sview_offset
, p
, end
);
667 p
= decode_size(&sview_len
, p
, end
);
671 p
= decode_size(&tview_len
, p
, end
);
675 p
= decode_size(&inslen
, p
, end
);
679 p
= decode_size(&newlen
, p
, end
);
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"));
690 /* Check for source windows which slide backwards. */
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"));
699 /* Wait for more data if we don't have enough bytes for the
701 if ((apr_size_t
) (end
- p
) < inslen
+ newlen
)
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
,
708 SVN_ERR(db
->consumer_func(&window
, db
->consumer_baton
));
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
;
716 svn_stringbuf_ncreate((const char *) p
, remaining
, newpool
);
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
;
722 /* We've copied stuff out of the old pool. Toss that pool and use
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...
727 svn_pool_destroy(db
->subpool
);
728 db
->subpool
= newpool
;
736 close_handler(void *baton
)
738 struct decode_baton
*db
= (struct decode_baton
*) baton
;
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"));
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
);
756 svn_txdelta_parse_svndiff(svn_txdelta_window_handler_t handler
,
758 svn_boolean_t error_on_early_close
,
761 apr_pool_t
*subpool
= svn_pool_create(pool
);
762 struct decode_baton
*db
= apr_palloc(pool
, sizeof(*db
));
763 svn_stream_t
*stream
;
765 db
->consumer_func
= handler
;
766 db
->consumer_baton
= handler_baton
;
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
);
781 /* Routines for reading one svndiff window at a time. */
783 /* Read one byte from STREAM into *BYTE. */
785 read_one_byte(unsigned char *byte
, svn_stream_t
*stream
)
790 SVN_ERR(svn_stream_read(stream
, &c
, &len
));
792 return svn_error_create(SVN_ERR_SVNDIFF_UNEXPECTED_END
, NULL
,
793 _("Unexpected end of svndiff input"));
794 *byte
= (unsigned char) c
;
798 /* Read and decode one integer from STREAM into *SIZE. */
800 read_one_size(apr_size_t
*size
, svn_stream_t
*stream
)
807 SVN_ERR(read_one_byte(&c
, stream
));
808 *size
= (*size
<< 7) | (c
& 0x7f);
815 /* Read a window header from STREAM and check it for integer overflow. */
817 read_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
)
823 /* Read the source view offset by hand, since it's not an apr_size_t. */
827 SVN_ERR(read_one_byte(&c
, stream
));
828 *sview_offset
= (*sview_offset
<< 7) | (c
& 0x7f);
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
));
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"));
850 svn_txdelta_read_svndiff_window(svn_txdelta_window_t
**window
,
851 svn_stream_t
*stream
,
855 svn_filesize_t sview_offset
;
856 apr_size_t sview_len
, tview_len
, inslen
, newlen
, len
;
859 SVN_ERR(read_window_header(stream
, &sview_offset
, &sview_len
, &tview_len
,
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 SVN_ERR(decode_window(*window
, sview_offset
, sview_len
, tview_len
, inslen
,
869 newlen
, buf
, pool
, svndiff_version
));
875 svn_txdelta_skip_svndiff_window(apr_file_t
*file
,
879 svn_stream_t
*stream
= svn_stream_from_aprfile(file
, pool
);
880 svn_filesize_t sview_offset
;
881 apr_size_t sview_len
, tview_len
, inslen
, newlen
;
884 SVN_ERR(read_window_header(stream
, &sview_offset
, &sview_len
, &tview_len
,
887 offset
= inslen
+ newlen
;
888 return svn_io_file_seek(file
, APR_CUR
, &offset
, pool
);