1251881Speter/*
2251881Speter * svndiff.c -- Encoding and decoding svndiff-format deltas.
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter
25251881Speter#include <assert.h>
26251881Speter#include <string.h>
27251881Speter#include "svn_delta.h"
28251881Speter#include "svn_io.h"
29251881Speter#include "delta.h"
30251881Speter#include "svn_pools.h"
31251881Speter#include "svn_private_config.h"
32251881Speter#include <zlib.h>
33251881Speter
34251881Speter#include "private/svn_error_private.h"
35251881Speter#include "private/svn_delta_private.h"
36251881Speter
37251881Speter/* The zlib compressBound function was not exported until 1.2.0. */
38251881Speter#if ZLIB_VERNUM >= 0x1200
39251881Speter#define svnCompressBound(LEN) compressBound(LEN)
40251881Speter#else
41251881Speter#define svnCompressBound(LEN) ((LEN) + ((LEN) >> 12) + ((LEN) >> 14) + 11)
42251881Speter#endif
43251881Speter
44251881Speter/* For svndiff1, address/instruction/new data under this size will not
45251881Speter   be compressed using zlib as a secondary compressor.  */
46251881Speter#define MIN_COMPRESS_SIZE 512
47251881Speter
48251881Speter/* ----- Text delta to svndiff ----- */
49251881Speter
50251881Speter/* We make one of these and get it passed back to us in calls to the
51251881Speter   window handler.  We only use it to record the write function and
52251881Speter   baton passed to svn_txdelta_to_svndiff3().  */
53251881Speterstruct encoder_baton {
54251881Speter  svn_stream_t *output;
55251881Speter  svn_boolean_t header_done;
56251881Speter  int version;
57251881Speter  int compression_level;
58251881Speter  apr_pool_t *pool;
59251881Speter};
60251881Speter
61251881Speter/* This is at least as big as the largest size of an integer that
62251881Speter   encode_int can generate; it is sufficient for creating buffers for
63251881Speter   it to write into.  This assumes that integers are at most 64 bits,
64251881Speter   and so 10 bytes (with 7 bits of information each) are sufficient to
65251881Speter   represent them. */
66251881Speter#define MAX_ENCODED_INT_LEN 10
67251881Speter/* This is at least as big as the largest size for a single instruction. */
68251881Speter#define MAX_INSTRUCTION_LEN (2*MAX_ENCODED_INT_LEN+1)
69251881Speter/* This is at least as big as the largest possible instructions
70251881Speter   section: in theory, the instructions could be SVN_DELTA_WINDOW_SIZE
71251881Speter   1-byte copy-from-source instructions (though this is very unlikely). */
72251881Speter#define MAX_INSTRUCTION_SECTION_LEN (SVN_DELTA_WINDOW_SIZE*MAX_INSTRUCTION_LEN)
73251881Speter
74251881Speter/* Encode VAL into the buffer P using the variable-length svndiff
75251881Speter   integer format.  Return the incremented value of P after the
76251881Speter   encoded bytes have been written.  P must point to a buffer of size
77251881Speter   at least MAX_ENCODED_INT_LEN.
78251881Speter
79251881Speter   This encoding uses the high bit of each byte as a continuation bit
80251881Speter   and the other seven bits as data bits.  High-order data bits are
81251881Speter   encoded first, followed by lower-order bits, so the value can be
82251881Speter   reconstructed by concatenating the data bits from left to right and
83251881Speter   interpreting the result as a binary number.  Examples (brackets
84251881Speter   denote byte boundaries, spaces are for clarity only):
85251881Speter
86251881Speter           1 encodes as [0 0000001]
87251881Speter          33 encodes as [0 0100001]
88251881Speter         129 encodes as [1 0000001] [0 0000001]
89251881Speter        2000 encodes as [1 0001111] [0 1010000]
90251881Speter*/
91251881Speterstatic unsigned char *
92251881Speterencode_int(unsigned char *p, svn_filesize_t val)
93251881Speter{
94251881Speter  int n;
95251881Speter  svn_filesize_t v;
96251881Speter  unsigned char cont;
97251881Speter
98251881Speter  SVN_ERR_ASSERT_NO_RETURN(val >= 0);
99251881Speter
100251881Speter  /* Figure out how many bytes we'll need.  */
101251881Speter  v = val >> 7;
102251881Speter  n = 1;
103251881Speter  while (v > 0)
104251881Speter    {
105251881Speter      v = v >> 7;
106251881Speter      n++;
107251881Speter    }
108251881Speter
109251881Speter  SVN_ERR_ASSERT_NO_RETURN(n <= MAX_ENCODED_INT_LEN);
110251881Speter
111251881Speter  /* Encode the remaining bytes; n is always the number of bytes
112251881Speter     coming after the one we're encoding.  */
113251881Speter  while (--n >= 0)
114251881Speter    {
115251881Speter      cont = ((n > 0) ? 0x1 : 0x0) << 7;
116251881Speter      *p++ = (unsigned char)(((val >> (n * 7)) & 0x7f) | cont);
117251881Speter    }
118251881Speter
119251881Speter  return p;
120251881Speter}
121251881Speter
122251881Speter
123251881Speter/* Append an encoded integer to a string.  */
124251881Speterstatic void
125251881Speterappend_encoded_int(svn_stringbuf_t *header, svn_filesize_t val)
126251881Speter{
127251881Speter  unsigned char buf[MAX_ENCODED_INT_LEN], *p;
128251881Speter
129251881Speter  p = encode_int(buf, val);
130251881Speter  svn_stringbuf_appendbytes(header, (const char *)buf, p - buf);
131251881Speter}
132251881Speter
133251881Speter/* If IN is a string that is >= MIN_COMPRESS_SIZE and the COMPRESSION_LEVEL
134251881Speter   is not SVN_DELTA_COMPRESSION_LEVEL_NONE, zlib compress it and places the
135251881Speter   result in OUT, with an integer prepended specifying the original size.
136251881Speter   If IN is < MIN_COMPRESS_SIZE, or if the compressed version of IN was no
137251881Speter   smaller than the original IN, OUT will be a copy of IN with the size
138251881Speter   prepended as an integer. */
139251881Speterstatic svn_error_t *
140251881Speterzlib_encode(const char *data,
141251881Speter            apr_size_t len,
142251881Speter            svn_stringbuf_t *out,
143251881Speter            int compression_level)
144251881Speter{
145251881Speter  unsigned long endlen;
146251881Speter  apr_size_t intlen;
147251881Speter
148251881Speter  svn_stringbuf_setempty(out);
149251881Speter  append_encoded_int(out, len);
150251881Speter  intlen = out->len;
151251881Speter
152251881Speter  /* Compression initialization overhead is considered to large for
153251881Speter     short buffers.  Also, if we don't actually want to compress data,
154251881Speter     ZLIB will produce an output no shorter than the input.  Hence,
155251881Speter     the DATA would directly appended to OUT, so we can do that directly
156251881Speter     without calling ZLIB before. */
157251881Speter  if (   (len < MIN_COMPRESS_SIZE)
158251881Speter      || (compression_level == SVN_DELTA_COMPRESSION_LEVEL_NONE))
159251881Speter    {
160251881Speter      svn_stringbuf_appendbytes(out, data, len);
161251881Speter    }
162251881Speter  else
163251881Speter    {
164251881Speter      int zerr;
165251881Speter
166251881Speter      svn_stringbuf_ensure(out, svnCompressBound(len) + intlen);
167251881Speter      endlen = out->blocksize;
168251881Speter
169251881Speter      zerr = compress2((unsigned char *)out->data + intlen, &endlen,
170251881Speter                       (const unsigned char *)data, len,
171251881Speter                       compression_level);
172251881Speter      if (zerr != Z_OK)
173251881Speter        return svn_error_trace(svn_error__wrap_zlib(
174251881Speter                                 zerr, "compress2",
175251881Speter                                 _("Compression of svndiff data failed")));
176251881Speter
177251881Speter      /* Compression didn't help :(, just append the original text */
178251881Speter      if (endlen >= len)
179251881Speter        {
180251881Speter          svn_stringbuf_appendbytes(out, data, len);
181251881Speter          return SVN_NO_ERROR;
182251881Speter        }
183251881Speter      out->len = endlen + intlen;
184251881Speter      out->data[out->len] = 0;
185251881Speter    }
186251881Speter  return SVN_NO_ERROR;
187251881Speter}
188251881Speter
189251881Speterstatic svn_error_t *
190251881Spetersend_simple_insertion_window(svn_txdelta_window_t *window,
191251881Speter                             struct encoder_baton *eb)
192251881Speter{
193251881Speter  unsigned char headers[4 + 5 * MAX_ENCODED_INT_LEN + MAX_INSTRUCTION_LEN];
194251881Speter  unsigned char ibuf[MAX_INSTRUCTION_LEN];
195251881Speter  unsigned char *header_current;
196251881Speter  apr_size_t header_len;
197251881Speter  apr_size_t ip_len, i;
198251881Speter  apr_size_t len = window->new_data->len;
199251881Speter
200251881Speter  /* there is only one target copy op. It must span the whole window */
201251881Speter  assert(window->ops[0].action_code == svn_txdelta_new);
202251881Speter  assert(window->ops[0].length == window->tview_len);
203251881Speter  assert(window->ops[0].offset == 0);
204251881Speter
205251881Speter  /* write stream header if necessary */
206251881Speter  if (!eb->header_done)
207251881Speter    {
208251881Speter      eb->header_done = TRUE;
209251881Speter      headers[0] = 'S';
210251881Speter      headers[1] = 'V';
211251881Speter      headers[2] = 'N';
212251881Speter      headers[3] = (unsigned char)eb->version;
213251881Speter      header_current = headers + 4;
214251881Speter    }
215251881Speter  else
216251881Speter    {
217251881Speter      header_current = headers;
218251881Speter    }
219251881Speter
220251881Speter  /* Encode the action code and length.  */
221251881Speter  if (window->tview_len >> 6 == 0)
222251881Speter    {
223251881Speter      ibuf[0] = (unsigned char)(window->tview_len + (0x2 << 6));
224251881Speter      ip_len = 1;
225251881Speter    }
226251881Speter  else
227251881Speter    {
228251881Speter      ibuf[0] = (0x2 << 6);
229251881Speter      ip_len = encode_int(ibuf + 1, window->tview_len) - ibuf;
230251881Speter    }
231251881Speter
232251881Speter  /* encode the window header.  Please note that the source window may
233251881Speter   * have content despite not being used for deltification. */
234251881Speter  header_current = encode_int(header_current, window->sview_offset);
235251881Speter  header_current = encode_int(header_current, window->sview_len);
236251881Speter  header_current = encode_int(header_current, window->tview_len);
237251881Speter  header_current[0] = (unsigned char)ip_len;  /* 1 instruction */
238251881Speter  header_current = encode_int(&header_current[1], len);
239251881Speter
240251881Speter  /* append instructions (1 to a handful of bytes) */
241251881Speter  for (i = 0; i < ip_len; ++i)
242251881Speter    header_current[i] = ibuf[i];
243251881Speter
244251881Speter  header_len = header_current - headers + ip_len;
245251881Speter
246251881Speter  /* Write out the window.  */
247251881Speter  SVN_ERR(svn_stream_write(eb->output, (const char *)headers, &header_len));
248251881Speter  if (len)
249251881Speter    SVN_ERR(svn_stream_write(eb->output, window->new_data->data, &len));
250251881Speter
251251881Speter  return SVN_NO_ERROR;
252251881Speter}
253251881Speter
254251881Speterstatic svn_error_t *
255251881Speterwindow_handler(svn_txdelta_window_t *window, void *baton)
256251881Speter{
257251881Speter  struct encoder_baton *eb = baton;
258251881Speter  apr_pool_t *pool;
259251881Speter  svn_stringbuf_t *instructions;
260251881Speter  svn_stringbuf_t *i1;
261251881Speter  svn_stringbuf_t *header;
262251881Speter  const svn_string_t *newdata;
263251881Speter  unsigned char ibuf[MAX_INSTRUCTION_LEN], *ip;
264251881Speter  const svn_txdelta_op_t *op;
265251881Speter  apr_size_t len;
266251881Speter
267251881Speter  /* use specialized code if there is no source */
268251881Speter  if (window && !window->src_ops && window->num_ops == 1 && !eb->version)
269251881Speter    return svn_error_trace(send_simple_insertion_window(window, eb));
270251881Speter
271251881Speter  /* Make sure we write the header.  */
272251881Speter  if (!eb->header_done)
273251881Speter    {
274251881Speter      char svnver[4] = {'S','V','N','\0'};
275251881Speter      len = 4;
276251881Speter      svnver[3] = (char)eb->version;
277251881Speter      SVN_ERR(svn_stream_write(eb->output, svnver, &len));
278251881Speter      eb->header_done = TRUE;
279251881Speter    }
280251881Speter
281251881Speter  if (window == NULL)
282251881Speter    {
283251881Speter      svn_stream_t *output = eb->output;
284251881Speter
285251881Speter      /* We're done; clean up.
286251881Speter
287251881Speter         We clean our pool first. Given that the output stream was passed
288251881Speter         TO us, we'll assume it has a longer lifetime, and that it will not
289251881Speter         be affected by our pool destruction.
290251881Speter
291251881Speter         The contrary point of view (close the stream first): that could
292251881Speter         tell our user that everything related to the output stream is done,
293251881Speter         and a cleanup of the user pool should occur. However, that user
294251881Speter         pool could include the subpool we created for our work (eb->pool),
295251881Speter         which would then make our call to svn_pool_destroy() puke.
296251881Speter       */
297251881Speter      svn_pool_destroy(eb->pool);
298251881Speter
299251881Speter      return svn_stream_close(output);
300251881Speter    }
301251881Speter
302251881Speter  /* create the necessary data buffers */
303251881Speter  pool = svn_pool_create(eb->pool);
304251881Speter  instructions = svn_stringbuf_create_empty(pool);
305251881Speter  i1 = svn_stringbuf_create_empty(pool);
306251881Speter  header = svn_stringbuf_create_empty(pool);
307251881Speter
308251881Speter  /* Encode the instructions.  */
309251881Speter  for (op = window->ops; op < window->ops + window->num_ops; op++)
310251881Speter    {
311251881Speter      /* Encode the action code and length.  */
312251881Speter      ip = ibuf;
313251881Speter      switch (op->action_code)
314251881Speter        {
315251881Speter        case svn_txdelta_source: *ip = 0; break;
316251881Speter        case svn_txdelta_target: *ip = (0x1 << 6); break;
317251881Speter        case svn_txdelta_new:    *ip = (0x2 << 6); break;
318251881Speter        }
319251881Speter      if (op->length >> 6 == 0)
320251881Speter        *ip++ |= (unsigned char)op->length;
321251881Speter      else
322251881Speter        ip = encode_int(ip + 1, op->length);
323251881Speter      if (op->action_code != svn_txdelta_new)
324251881Speter        ip = encode_int(ip, op->offset);
325251881Speter      svn_stringbuf_appendbytes(instructions, (const char *)ibuf, ip - ibuf);
326251881Speter    }
327251881Speter
328251881Speter  /* Encode the header.  */
329251881Speter  append_encoded_int(header, window->sview_offset);
330251881Speter  append_encoded_int(header, window->sview_len);
331251881Speter  append_encoded_int(header, window->tview_len);
332251881Speter  if (eb->version == 1)
333251881Speter    {
334251881Speter      SVN_ERR(zlib_encode(instructions->data, instructions->len,
335251881Speter                          i1, eb->compression_level));
336251881Speter      instructions = i1;
337251881Speter    }
338251881Speter  append_encoded_int(header, instructions->len);
339251881Speter  if (eb->version == 1)
340251881Speter    {
341251881Speter      svn_stringbuf_t *temp = svn_stringbuf_create_empty(pool);
342251881Speter      svn_string_t *tempstr = svn_string_create_empty(pool);
343251881Speter      SVN_ERR(zlib_encode(window->new_data->data, window->new_data->len,
344251881Speter                          temp, eb->compression_level));
345251881Speter      tempstr->data = temp->data;
346251881Speter      tempstr->len = temp->len;
347251881Speter      newdata = tempstr;
348251881Speter    }
349251881Speter  else
350251881Speter    newdata = window->new_data;
351251881Speter
352251881Speter  append_encoded_int(header, newdata->len);
353251881Speter
354251881Speter  /* Write out the window.  */
355251881Speter  len = header->len;
356251881Speter  SVN_ERR(svn_stream_write(eb->output, header->data, &len));
357251881Speter  if (instructions->len > 0)
358251881Speter    {
359251881Speter      len = instructions->len;
360251881Speter      SVN_ERR(svn_stream_write(eb->output, instructions->data, &len));
361251881Speter    }
362251881Speter  if (newdata->len > 0)
363251881Speter    {
364251881Speter      len = newdata->len;
365251881Speter      SVN_ERR(svn_stream_write(eb->output, newdata->data, &len));
366251881Speter    }
367251881Speter
368251881Speter  svn_pool_destroy(pool);
369251881Speter  return SVN_NO_ERROR;
370251881Speter}
371251881Speter
372251881Spetervoid
373251881Spetersvn_txdelta_to_svndiff3(svn_txdelta_window_handler_t *handler,
374251881Speter                        void **handler_baton,
375251881Speter                        svn_stream_t *output,
376251881Speter                        int svndiff_version,
377251881Speter                        int compression_level,
378251881Speter                        apr_pool_t *pool)
379251881Speter{
380251881Speter  apr_pool_t *subpool = svn_pool_create(pool);
381251881Speter  struct encoder_baton *eb;
382251881Speter
383251881Speter  eb = apr_palloc(subpool, sizeof(*eb));
384251881Speter  eb->output = output;
385251881Speter  eb->header_done = FALSE;
386251881Speter  eb->pool = subpool;
387251881Speter  eb->version = svndiff_version;
388251881Speter  eb->compression_level = compression_level;
389251881Speter
390251881Speter  *handler = window_handler;
391251881Speter  *handler_baton = eb;
392251881Speter}
393251881Speter
394251881Spetervoid
395251881Spetersvn_txdelta_to_svndiff2(svn_txdelta_window_handler_t *handler,
396251881Speter                        void **handler_baton,
397251881Speter                        svn_stream_t *output,
398251881Speter                        int svndiff_version,
399251881Speter                        apr_pool_t *pool)
400251881Speter{
401251881Speter  svn_txdelta_to_svndiff3(handler, handler_baton, output, svndiff_version,
402251881Speter                          SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool);
403251881Speter}
404251881Speter
405251881Spetervoid
406251881Spetersvn_txdelta_to_svndiff(svn_stream_t *output,
407251881Speter                       apr_pool_t *pool,
408251881Speter                       svn_txdelta_window_handler_t *handler,
409251881Speter                       void **handler_baton)
410251881Speter{
411251881Speter  svn_txdelta_to_svndiff3(handler, handler_baton, output, 0,
412251881Speter                          SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool);
413251881Speter}
414251881Speter
415251881Speter
416251881Speter/* ----- svndiff to text delta ----- */
417251881Speter
418251881Speter/* An svndiff parser object.  */
419251881Speterstruct decode_baton
420251881Speter{
421251881Speter  /* Once the svndiff parser has enough data buffered to create a
422251881Speter     "window", it passes this window to the caller's consumer routine.  */
423251881Speter  svn_txdelta_window_handler_t consumer_func;
424251881Speter  void *consumer_baton;
425251881Speter
426251881Speter  /* Pool to create subpools from; each developing window will be a
427251881Speter     subpool.  */
428251881Speter  apr_pool_t *pool;
429251881Speter
430251881Speter  /* The current subpool which contains our current window-buffer.  */
431251881Speter  apr_pool_t *subpool;
432251881Speter
433251881Speter  /* The actual svndiff data buffer, living within subpool.  */
434251881Speter  svn_stringbuf_t *buffer;
435251881Speter
436251881Speter  /* The offset and size of the last source view, so that we can check
437251881Speter     to make sure the next one isn't sliding backwards.  */
438251881Speter  svn_filesize_t last_sview_offset;
439251881Speter  apr_size_t last_sview_len;
440251881Speter
441251881Speter  /* We have to discard four bytes at the beginning for the header.
442251881Speter     This field keeps track of how many of those bytes we have read.  */
443251881Speter  apr_size_t header_bytes;
444251881Speter
445251881Speter  /* Do we want an error to occur when we close the stream that
446251881Speter     indicates we didn't send the whole svndiff data?  If you plan to
447251881Speter     not transmit the whole svndiff data stream, you will want this to
448251881Speter     be FALSE. */
449251881Speter  svn_boolean_t error_on_early_close;
450251881Speter
451251881Speter  /* svndiff version in use by delta.  */
452251881Speter  unsigned char version;
453251881Speter};
454251881Speter
455251881Speter
456251881Speter/* Decode an svndiff-encoded integer into *VAL and return a pointer to
457251881Speter   the byte after the integer.  The bytes to be decoded live in the
458251881Speter   range [P..END-1].  If these bytes do not contain a whole encoded
459251881Speter   integer, return NULL; in this case *VAL is undefined.
460251881Speter
461251881Speter   See the comment for encode_int() earlier in this file for more detail on
462251881Speter   the encoding format.  */
463251881Speterstatic const unsigned char *
464251881Speterdecode_file_offset(svn_filesize_t *val,
465251881Speter                   const unsigned char *p,
466251881Speter                   const unsigned char *end)
467251881Speter{
468251881Speter  svn_filesize_t temp = 0;
469251881Speter
470251881Speter  if (p + MAX_ENCODED_INT_LEN < end)
471251881Speter    end = p + MAX_ENCODED_INT_LEN;
472251881Speter  /* Decode bytes until we're done.  */
473251881Speter  while (p < end)
474251881Speter    {
475251881Speter      /* Don't use svn_filesize_t here, because this might be 64 bits
476251881Speter       * on 32 bit targets. Optimizing compilers may or may not be
477251881Speter       * able to reduce that to the effective code below. */
478251881Speter      unsigned int c = *p++;
479251881Speter
480251881Speter      temp = (temp << 7) | (c & 0x7f);
481251881Speter      if (c < 0x80)
482251881Speter      {
483251881Speter        *val = temp;
484251881Speter        return p;
485251881Speter      }
486251881Speter    }
487251881Speter
488251881Speter  return NULL;
489251881Speter}
490251881Speter
491251881Speter
492251881Speter/* Same as above, only decode into a size variable. */
493251881Speterstatic const unsigned char *
494251881Speterdecode_size(apr_size_t *val,
495251881Speter            const unsigned char *p,
496251881Speter            const unsigned char *end)
497251881Speter{
498251881Speter  apr_size_t temp = 0;
499251881Speter
500251881Speter  if (p + MAX_ENCODED_INT_LEN < end)
501251881Speter    end = p + MAX_ENCODED_INT_LEN;
502251881Speter  /* Decode bytes until we're done.  */
503251881Speter  while (p < end)
504251881Speter    {
505251881Speter      apr_size_t c = *p++;
506251881Speter
507251881Speter      temp = (temp << 7) | (c & 0x7f);
508251881Speter      if (c < 0x80)
509251881Speter      {
510251881Speter        *val = temp;
511251881Speter        return p;
512251881Speter      }
513251881Speter    }
514251881Speter
515251881Speter  return NULL;
516251881Speter}
517251881Speter
518251881Speter/* Decode the possibly-zlib compressed string of length INLEN that is in
519251881Speter   IN, into OUT.  We expect an integer is prepended to IN that specifies
520251881Speter   the original size, and that if encoded size == original size, that the
521251881Speter   remaining data is not compressed.
522251881Speter   In that case, we will simply return pointer into IN as data pointer for
523251881Speter   OUT, COPYLESS_ALLOWED has been set.  The, the caller is expected not to
524251881Speter   modify the contents of OUT.
525251881Speter   An error is returned if the decoded length exceeds the given LIMIT.
526251881Speter */
527251881Speterstatic svn_error_t *
528251881Speterzlib_decode(const unsigned char *in, apr_size_t inLen, svn_stringbuf_t *out,
529251881Speter            apr_size_t limit)
530251881Speter{
531251881Speter  apr_size_t len;
532251881Speter  const unsigned char *oldplace = in;
533251881Speter
534251881Speter  /* First thing in the string is the original length.  */
535251881Speter  in = decode_size(&len, in, in + inLen);
536251881Speter  if (in == NULL)
537251881Speter    return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA, NULL,
538251881Speter                            _("Decompression of svndiff data failed: no size"));
539251881Speter  if (len > limit)
540251881Speter    return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA, NULL,
541251881Speter                            _("Decompression of svndiff data failed: "
542251881Speter                              "size too large"));
543251881Speter  /* We need to subtract the size of the encoded original length off the
544251881Speter   *      still remaining input length.  */
545251881Speter  inLen -= (in - oldplace);
546251881Speter  if (inLen == len)
547251881Speter    {
548251881Speter      svn_stringbuf_ensure(out, len);
549251881Speter      memcpy(out->data, in, len);
550251881Speter      out->data[len] = 0;
551251881Speter      out->len = len;
552251881Speter
553251881Speter      return SVN_NO_ERROR;
554251881Speter    }
555251881Speter  else
556251881Speter    {
557251881Speter      unsigned long zlen = len;
558251881Speter      int zerr;
559251881Speter
560251881Speter      svn_stringbuf_ensure(out, len);
561251881Speter      zerr = uncompress((unsigned char *)out->data, &zlen, in, inLen);
562251881Speter      if (zerr != Z_OK)
563251881Speter        return svn_error_trace(svn_error__wrap_zlib(
564251881Speter                                 zerr, "uncompress",
565251881Speter                                 _("Decompression of svndiff data failed")));
566251881Speter
567251881Speter      /* Zlib should not produce something that has a different size than the
568251881Speter         original length we stored. */
569251881Speter      if (zlen != len)
570251881Speter        return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA,
571251881Speter                                NULL,
572251881Speter                                _("Size of uncompressed data "
573251881Speter                                  "does not match stored original length"));
574251881Speter      out->data[zlen] = 0;
575251881Speter      out->len = zlen;
576251881Speter    }
577251881Speter  return SVN_NO_ERROR;
578251881Speter}
579251881Speter
580251881Speter/* Decode an instruction into OP, returning a pointer to the text
581251881Speter   after the instruction.  Note that if the action code is
582251881Speter   svn_txdelta_new, the offset field of *OP will not be set.  */
583251881Speterstatic const unsigned char *
584251881Speterdecode_instruction(svn_txdelta_op_t *op,
585251881Speter                   const unsigned char *p,
586251881Speter                   const unsigned char *end)
587251881Speter{
588251881Speter  apr_size_t c;
589251881Speter  apr_size_t action;
590251881Speter
591251881Speter  if (p == end)
592251881Speter    return NULL;
593251881Speter
594251881Speter  /* We need this more than once */
595251881Speter  c = *p++;
596251881Speter
597251881Speter  /* Decode the instruction selector.  */
598251881Speter  action = (c >> 6) & 0x3;
599251881Speter  if (action >= 0x3)
600251881Speter      return NULL;
601251881Speter
602251881Speter  /* This relies on enum svn_delta_action values to match and never to be
603251881Speter     redefined. */
604251881Speter  op->action_code = (enum svn_delta_action)(action);
605251881Speter
606251881Speter  /* Decode the length and offset.  */
607251881Speter  op->length = c & 0x3f;
608251881Speter  if (op->length == 0)
609251881Speter    {
610251881Speter      p = decode_size(&op->length, p, end);
611251881Speter      if (p == NULL)
612251881Speter        return NULL;
613251881Speter    }
614251881Speter  if (action != svn_txdelta_new)
615251881Speter    {
616251881Speter      p = decode_size(&op->offset, p, end);
617251881Speter      if (p == NULL)
618251881Speter        return NULL;
619251881Speter    }
620251881Speter
621251881Speter  return p;
622251881Speter}
623251881Speter
624251881Speter/* Count the instructions in the range [P..END-1] and make sure they
625251881Speter   are valid for the given window lengths.  Return an error if the
626251881Speter   instructions are invalid; otherwise set *NINST to the number of
627251881Speter   instructions.  */
628251881Speterstatic svn_error_t *
629251881Spetercount_and_verify_instructions(int *ninst,
630251881Speter                              const unsigned char *p,
631251881Speter                              const unsigned char *end,
632251881Speter                              apr_size_t sview_len,
633251881Speter                              apr_size_t tview_len,
634251881Speter                              apr_size_t new_len)
635251881Speter{
636251881Speter  int n = 0;
637251881Speter  svn_txdelta_op_t op;
638251881Speter  apr_size_t tpos = 0, npos = 0;
639251881Speter
640251881Speter  while (p < end)
641251881Speter    {
642251881Speter      p = decode_instruction(&op, p, end);
643251881Speter
644251881Speter      /* Detect any malformed operations from the instruction stream. */
645251881Speter      if (p == NULL)
646251881Speter        return svn_error_createf
647251881Speter          (SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
648251881Speter           _("Invalid diff stream: insn %d cannot be decoded"), n);
649251881Speter      else if (op.length == 0)
650251881Speter        return svn_error_createf
651251881Speter          (SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
652251881Speter           _("Invalid diff stream: insn %d has length zero"), n);
653251881Speter      else if (op.length > tview_len - tpos)
654251881Speter        return svn_error_createf
655251881Speter          (SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
656251881Speter           _("Invalid diff stream: insn %d overflows the target view"), n);
657251881Speter
658251881Speter      switch (op.action_code)
659251881Speter        {
660251881Speter        case svn_txdelta_source:
661251881Speter          if (op.length > sview_len - op.offset ||
662251881Speter              op.offset > sview_len)
663251881Speter            return svn_error_createf
664251881Speter              (SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
665251881Speter               _("Invalid diff stream: "
666251881Speter                 "[src] insn %d overflows the source view"), n);
667251881Speter          break;
668251881Speter        case svn_txdelta_target:
669251881Speter          if (op.offset >= tpos)
670251881Speter            return svn_error_createf
671251881Speter              (SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
672251881Speter               _("Invalid diff stream: "
673251881Speter                 "[tgt] insn %d starts beyond the target view position"), n);
674251881Speter          break;
675251881Speter        case svn_txdelta_new:
676251881Speter          if (op.length > new_len - npos)
677251881Speter            return svn_error_createf
678251881Speter              (SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
679251881Speter               _("Invalid diff stream: "
680251881Speter                 "[new] insn %d overflows the new data section"), n);
681251881Speter          npos += op.length;
682251881Speter          break;
683251881Speter        }
684251881Speter      tpos += op.length;
685251881Speter      n++;
686251881Speter    }
687251881Speter  if (tpos != tview_len)
688251881Speter    return svn_error_create(SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
689251881Speter                            _("Delta does not fill the target window"));
690251881Speter  if (npos != new_len)
691251881Speter    return svn_error_create(SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
692251881Speter                            _("Delta does not contain enough new data"));
693251881Speter
694251881Speter  *ninst = n;
695251881Speter  return SVN_NO_ERROR;
696251881Speter}
697251881Speter
698251881Speter/* Given the five integer fields of a window header and a pointer to
699251881Speter   the remainder of the window contents, fill in a delta window
700251881Speter   structure *WINDOW.  New allocations will be performed in POOL;
701251881Speter   the new_data field of *WINDOW will refer directly to memory pointed
702251881Speter   to by DATA. */
703251881Speterstatic svn_error_t *
704251881Speterdecode_window(svn_txdelta_window_t *window, svn_filesize_t sview_offset,
705251881Speter              apr_size_t sview_len, apr_size_t tview_len, apr_size_t inslen,
706251881Speter              apr_size_t newlen, const unsigned char *data, apr_pool_t *pool,
707251881Speter              unsigned int version)
708251881Speter{
709251881Speter  const unsigned char *insend;
710251881Speter  int ninst;
711251881Speter  apr_size_t npos;
712251881Speter  svn_txdelta_op_t *ops, *op;
713251881Speter  svn_string_t *new_data = apr_palloc(pool, sizeof(*new_data));
714251881Speter
715251881Speter  window->sview_offset = sview_offset;
716251881Speter  window->sview_len = sview_len;
717251881Speter  window->tview_len = tview_len;
718251881Speter
719251881Speter  insend = data + inslen;
720251881Speter
721251881Speter  if (version == 1)
722251881Speter    {
723251881Speter      svn_stringbuf_t *instout = svn_stringbuf_create_empty(pool);
724251881Speter      svn_stringbuf_t *ndout = svn_stringbuf_create_empty(pool);
725251881Speter
726251881Speter      /* these may in fact simply return references to insend */
727251881Speter
728251881Speter      SVN_ERR(zlib_decode(insend, newlen, ndout,
729251881Speter                          SVN_DELTA_WINDOW_SIZE));
730251881Speter      SVN_ERR(zlib_decode(data, insend - data, instout,
731251881Speter                          MAX_INSTRUCTION_SECTION_LEN));
732251881Speter
733251881Speter      newlen = ndout->len;
734251881Speter      data = (unsigned char *)instout->data;
735251881Speter      insend = (unsigned char *)instout->data + instout->len;
736251881Speter
737251881Speter      new_data->data = (const char *) ndout->data;
738251881Speter      new_data->len = newlen;
739251881Speter    }
740251881Speter  else
741251881Speter    {
742251881Speter      new_data->data = (const char *) insend;
743251881Speter      new_data->len = newlen;
744251881Speter    }
745251881Speter
746251881Speter  /* Count the instructions and make sure they are all valid.  */
747251881Speter  SVN_ERR(count_and_verify_instructions(&ninst, data, insend,
748251881Speter                                        sview_len, tview_len, newlen));
749251881Speter
750251881Speter  /* Allocate a buffer for the instructions and decode them. */
751251881Speter  ops = apr_palloc(pool, ninst * sizeof(*ops));
752251881Speter  npos = 0;
753251881Speter  window->src_ops = 0;
754251881Speter  for (op = ops; op < ops + ninst; op++)
755251881Speter    {
756251881Speter      data = decode_instruction(op, data, insend);
757251881Speter      if (op->action_code == svn_txdelta_source)
758251881Speter        ++window->src_ops;
759251881Speter      else if (op->action_code == svn_txdelta_new)
760251881Speter        {
761251881Speter          op->offset = npos;
762251881Speter          npos += op->length;
763251881Speter        }
764251881Speter    }
765251881Speter  SVN_ERR_ASSERT(data == insend);
766251881Speter
767251881Speter  window->ops = ops;
768251881Speter  window->num_ops = ninst;
769251881Speter  window->new_data = new_data;
770251881Speter
771251881Speter  return SVN_NO_ERROR;
772251881Speter}
773251881Speter
774251881Speterstatic svn_error_t *
775251881Speterwrite_handler(void *baton,
776251881Speter              const char *buffer,
777251881Speter              apr_size_t *len)
778251881Speter{
779251881Speter  struct decode_baton *db = (struct decode_baton *) baton;
780251881Speter  const unsigned char *p, *end;
781251881Speter  svn_filesize_t sview_offset;
782251881Speter  apr_size_t sview_len, tview_len, inslen, newlen, remaining;
783251881Speter  apr_size_t buflen = *len;
784251881Speter
785251881Speter  /* Chew up four bytes at the beginning for the header.  */
786251881Speter  if (db->header_bytes < 4)
787251881Speter    {
788251881Speter      apr_size_t nheader = 4 - db->header_bytes;
789251881Speter      if (nheader > buflen)
790251881Speter        nheader = buflen;
791251881Speter      if (memcmp(buffer, "SVN\0" + db->header_bytes, nheader) == 0)
792251881Speter        db->version = 0;
793251881Speter      else if (memcmp(buffer, "SVN\1" + db->header_bytes, nheader) == 0)
794251881Speter        db->version = 1;
795251881Speter      else
796251881Speter        return svn_error_create(SVN_ERR_SVNDIFF_INVALID_HEADER, NULL,
797251881Speter                                _("Svndiff has invalid header"));
798251881Speter      buflen -= nheader;
799251881Speter      buffer += nheader;
800251881Speter      db->header_bytes += nheader;
801251881Speter    }
802251881Speter
803251881Speter  /* Concatenate the old with the new.  */
804251881Speter  svn_stringbuf_appendbytes(db->buffer, buffer, buflen);
805251881Speter
806251881Speter  /* We have a buffer of svndiff data that might be good for:
807251881Speter
808251881Speter     a) an integral number of windows' worth of data - this is a
809251881Speter        trivial case.  Make windows from our data and ship them off.
810251881Speter
811251881Speter     b) a non-integral number of windows' worth of data - we shall
812251881Speter        consume the integral portion of the window data, and then
813251881Speter        somewhere in the following loop the decoding of the svndiff
814251881Speter        data will run out of stuff to decode, and will simply return
815251881Speter        SVN_NO_ERROR, anxiously awaiting more data.
816251881Speter  */
817251881Speter
818251881Speter  while (1)
819251881Speter    {
820251881Speter      apr_pool_t *newpool;
821251881Speter      svn_txdelta_window_t window;
822251881Speter
823251881Speter      /* Read the header, if we have enough bytes for that.  */
824251881Speter      p = (const unsigned char *) db->buffer->data;
825251881Speter      end = (const unsigned char *) db->buffer->data + db->buffer->len;
826251881Speter
827251881Speter      p = decode_file_offset(&sview_offset, p, end);
828251881Speter      if (p == NULL)
829251881Speter        return SVN_NO_ERROR;
830251881Speter
831251881Speter      p = decode_size(&sview_len, p, end);
832251881Speter      if (p == NULL)
833251881Speter        return SVN_NO_ERROR;
834251881Speter
835251881Speter      p = decode_size(&tview_len, p, end);
836251881Speter      if (p == NULL)
837251881Speter        return SVN_NO_ERROR;
838251881Speter
839251881Speter      p = decode_size(&inslen, p, end);
840251881Speter      if (p == NULL)
841251881Speter        return SVN_NO_ERROR;
842251881Speter
843251881Speter      p = decode_size(&newlen, p, end);
844251881Speter      if (p == NULL)
845251881Speter        return SVN_NO_ERROR;
846251881Speter
847251881Speter      if (tview_len > SVN_DELTA_WINDOW_SIZE ||
848251881Speter          sview_len > SVN_DELTA_WINDOW_SIZE ||
849251881Speter          /* for svndiff1, newlen includes the original length */
850251881Speter          newlen > SVN_DELTA_WINDOW_SIZE + MAX_ENCODED_INT_LEN ||
851251881Speter          inslen > MAX_INSTRUCTION_SECTION_LEN)
852251881Speter        return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL,
853251881Speter                                _("Svndiff contains a too-large window"));
854251881Speter
855251881Speter      /* Check for integer overflow.  */
856251881Speter      if (sview_offset < 0 || inslen + newlen < inslen
857251881Speter          || sview_len + tview_len < sview_len
858251881Speter          || (apr_size_t)sview_offset + sview_len < (apr_size_t)sview_offset)
859251881Speter        return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL,
860251881Speter                                _("Svndiff contains corrupt window header"));
861251881Speter
862251881Speter      /* Check for source windows which slide backwards.  */
863251881Speter      if (sview_len > 0
864251881Speter          && (sview_offset < db->last_sview_offset
865251881Speter              || (sview_offset + sview_len
866251881Speter                  < db->last_sview_offset + db->last_sview_len)))
867251881Speter        return svn_error_create
868251881Speter          (SVN_ERR_SVNDIFF_BACKWARD_VIEW, NULL,
869251881Speter           _("Svndiff has backwards-sliding source views"));
870251881Speter
871251881Speter      /* Wait for more data if we don't have enough bytes for the
872251881Speter         whole window.  */
873251881Speter      if ((apr_size_t) (end - p) < inslen + newlen)
874251881Speter        return SVN_NO_ERROR;
875251881Speter
876251881Speter      /* Decode the window and send it off. */
877251881Speter      SVN_ERR(decode_window(&window, sview_offset, sview_len, tview_len,
878251881Speter                            inslen, newlen, p, db->subpool,
879251881Speter                            db->version));
880251881Speter      SVN_ERR(db->consumer_func(&window, db->consumer_baton));
881251881Speter
882251881Speter      /* Make a new subpool and buffer, saving aside the remaining
883251881Speter         data in the old buffer.  */
884251881Speter      newpool = svn_pool_create(db->pool);
885251881Speter      p += inslen + newlen;
886251881Speter      remaining = db->buffer->data + db->buffer->len - (const char *) p;
887251881Speter      db->buffer =
888251881Speter        svn_stringbuf_ncreate((const char *) p, remaining, newpool);
889251881Speter
890251881Speter      /* Remember the offset and length of the source view for next time.  */
891251881Speter      db->last_sview_offset = sview_offset;
892251881Speter      db->last_sview_len = sview_len;
893251881Speter
894251881Speter      /* We've copied stuff out of the old pool. Toss that pool and use
895251881Speter         our new pool.
896251881Speter         ### might be nice to avoid the copy and just use svn_pool_clear
897251881Speter         ### to get rid of whatever the "other stuff" is. future project...
898251881Speter      */
899251881Speter      svn_pool_destroy(db->subpool);
900251881Speter      db->subpool = newpool;
901251881Speter    }
902251881Speter
903251881Speter  /* NOTREACHED */
904251881Speter}
905251881Speter
906251881Speter/* Minimal svn_stream_t write handler, doing nothing */
907251881Speterstatic svn_error_t *
908251881Speternoop_write_handler(void *baton,
909251881Speter                   const char *buffer,
910251881Speter                   apr_size_t *len)
911251881Speter{
912251881Speter  return SVN_NO_ERROR;
913251881Speter}
914251881Speter
915251881Speterstatic svn_error_t *
916251881Speterclose_handler(void *baton)
917251881Speter{
918251881Speter  struct decode_baton *db = (struct decode_baton *) baton;
919251881Speter  svn_error_t *err;
920251881Speter
921251881Speter  /* Make sure that we're at a plausible end of stream, returning an
922251881Speter     error if we are expected to do so.  */
923251881Speter  if ((db->error_on_early_close)
924251881Speter      && (db->header_bytes < 4 || db->buffer->len != 0))
925251881Speter    return svn_error_create(SVN_ERR_SVNDIFF_UNEXPECTED_END, NULL,
926251881Speter                            _("Unexpected end of svndiff input"));
927251881Speter
928251881Speter  /* Tell the window consumer that we're done, and clean up.  */
929251881Speter  err = db->consumer_func(NULL, db->consumer_baton);
930251881Speter  svn_pool_destroy(db->pool);
931251881Speter  return err;
932251881Speter}
933251881Speter
934251881Speter
935251881Spetersvn_stream_t *
936251881Spetersvn_txdelta_parse_svndiff(svn_txdelta_window_handler_t handler,
937251881Speter                          void *handler_baton,
938251881Speter                          svn_boolean_t error_on_early_close,
939251881Speter                          apr_pool_t *pool)
940251881Speter{
941251881Speter  apr_pool_t *subpool = svn_pool_create(pool);
942251881Speter  struct decode_baton *db = apr_palloc(pool, sizeof(*db));
943251881Speter  svn_stream_t *stream;
944251881Speter
945251881Speter  db->consumer_func = handler;
946251881Speter  db->consumer_baton = handler_baton;
947251881Speter  db->pool = subpool;
948251881Speter  db->subpool = svn_pool_create(subpool);
949251881Speter  db->buffer = svn_stringbuf_create_empty(db->subpool);
950251881Speter  db->last_sview_offset = 0;
951251881Speter  db->last_sview_len = 0;
952251881Speter  db->header_bytes = 0;
953251881Speter  db->error_on_early_close = error_on_early_close;
954251881Speter  stream = svn_stream_create(db, pool);
955251881Speter
956251881Speter  if (handler != svn_delta_noop_window_handler)
957251881Speter    {
958251881Speter      svn_stream_set_write(stream, write_handler);
959251881Speter      svn_stream_set_close(stream, close_handler);
960251881Speter    }
961251881Speter  else
962251881Speter    {
963251881Speter      /* And else we just ignore everything as efficiently as we can.
964251881Speter         by only hooking a no-op handler */
965251881Speter      svn_stream_set_write(stream, noop_write_handler);
966251881Speter    }
967251881Speter  return stream;
968251881Speter}
969251881Speter
970251881Speter
971251881Speter/* Routines for reading one svndiff window at a time. */
972251881Speter
973251881Speter/* Read one byte from STREAM into *BYTE. */
974251881Speterstatic svn_error_t *
975251881Speterread_one_byte(unsigned char *byte, svn_stream_t *stream)
976251881Speter{
977251881Speter  char c;
978251881Speter  apr_size_t len = 1;
979251881Speter
980251881Speter  SVN_ERR(svn_stream_read(stream, &c, &len));
981251881Speter  if (len == 0)
982251881Speter    return svn_error_create(SVN_ERR_SVNDIFF_UNEXPECTED_END, NULL,
983251881Speter                            _("Unexpected end of svndiff input"));
984251881Speter  *byte = (unsigned char) c;
985251881Speter  return SVN_NO_ERROR;
986251881Speter}
987251881Speter
988251881Speter/* Read and decode one integer from STREAM into *SIZE. */
989251881Speterstatic svn_error_t *
990251881Speterread_one_size(apr_size_t *size, svn_stream_t *stream)
991251881Speter{
992251881Speter  unsigned char c;
993251881Speter
994251881Speter  *size = 0;
995251881Speter  while (1)
996251881Speter    {
997251881Speter      SVN_ERR(read_one_byte(&c, stream));
998251881Speter      *size = (*size << 7) | (c & 0x7f);
999251881Speter      if (!(c & 0x80))
1000251881Speter        break;
1001251881Speter    }
1002251881Speter  return SVN_NO_ERROR;
1003251881Speter}
1004251881Speter
1005251881Speter/* Read a window header from STREAM and check it for integer overflow. */
1006251881Speterstatic svn_error_t *
1007251881Speterread_window_header(svn_stream_t *stream, svn_filesize_t *sview_offset,
1008251881Speter                   apr_size_t *sview_len, apr_size_t *tview_len,
1009251881Speter                   apr_size_t *inslen, apr_size_t *newlen)
1010251881Speter{
1011251881Speter  unsigned char c;
1012251881Speter
1013251881Speter  /* Read the source view offset by hand, since it's not an apr_size_t. */
1014251881Speter  *sview_offset = 0;
1015251881Speter  while (1)
1016251881Speter    {
1017251881Speter      SVN_ERR(read_one_byte(&c, stream));
1018251881Speter      *sview_offset = (*sview_offset << 7) | (c & 0x7f);
1019251881Speter      if (!(c & 0x80))
1020251881Speter        break;
1021251881Speter    }
1022251881Speter
1023251881Speter  /* Read the four size fields. */
1024251881Speter  SVN_ERR(read_one_size(sview_len, stream));
1025251881Speter  SVN_ERR(read_one_size(tview_len, stream));
1026251881Speter  SVN_ERR(read_one_size(inslen, stream));
1027251881Speter  SVN_ERR(read_one_size(newlen, stream));
1028251881Speter
1029251881Speter  if (*tview_len > SVN_DELTA_WINDOW_SIZE ||
1030251881Speter      *sview_len > SVN_DELTA_WINDOW_SIZE ||
1031251881Speter      /* for svndiff1, newlen includes the original length */
1032251881Speter      *newlen > SVN_DELTA_WINDOW_SIZE + MAX_ENCODED_INT_LEN ||
1033251881Speter      *inslen > MAX_INSTRUCTION_SECTION_LEN)
1034251881Speter    return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL,
1035251881Speter                            _("Svndiff contains a too-large window"));
1036251881Speter
1037251881Speter  /* Check for integer overflow.  */
1038251881Speter  if (*sview_offset < 0 || *inslen + *newlen < *inslen
1039251881Speter      || *sview_len + *tview_len < *sview_len
1040251881Speter      || (apr_size_t)*sview_offset + *sview_len < (apr_size_t)*sview_offset)
1041251881Speter    return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL,
1042251881Speter                            _("Svndiff contains corrupt window header"));
1043251881Speter
1044251881Speter  return SVN_NO_ERROR;
1045251881Speter}
1046251881Speter
1047251881Spetersvn_error_t *
1048251881Spetersvn_txdelta_read_svndiff_window(svn_txdelta_window_t **window,
1049251881Speter                                svn_stream_t *stream,
1050251881Speter                                int svndiff_version,
1051251881Speter                                apr_pool_t *pool)
1052251881Speter{
1053251881Speter  svn_filesize_t sview_offset;
1054251881Speter  apr_size_t sview_len, tview_len, inslen, newlen, len;
1055251881Speter  unsigned char *buf;
1056251881Speter
1057251881Speter  SVN_ERR(read_window_header(stream, &sview_offset, &sview_len, &tview_len,
1058251881Speter                             &inslen, &newlen));
1059251881Speter  len = inslen + newlen;
1060251881Speter  buf = apr_palloc(pool, len);
1061251881Speter  SVN_ERR(svn_stream_read(stream, (char*)buf, &len));
1062251881Speter  if (len < inslen + newlen)
1063251881Speter    return svn_error_create(SVN_ERR_SVNDIFF_UNEXPECTED_END, NULL,
1064251881Speter                            _("Unexpected end of svndiff input"));
1065251881Speter  *window = apr_palloc(pool, sizeof(**window));
1066251881Speter  return decode_window(*window, sview_offset, sview_len, tview_len, inslen,
1067251881Speter                       newlen, buf, pool, svndiff_version);
1068251881Speter}
1069251881Speter
1070251881Speter
1071251881Spetersvn_error_t *
1072251881Spetersvn_txdelta_skip_svndiff_window(apr_file_t *file,
1073251881Speter                                int svndiff_version,
1074251881Speter                                apr_pool_t *pool)
1075251881Speter{
1076251881Speter  svn_stream_t *stream = svn_stream_from_aprfile2(file, TRUE, pool);
1077251881Speter  svn_filesize_t sview_offset;
1078251881Speter  apr_size_t sview_len, tview_len, inslen, newlen;
1079251881Speter  apr_off_t offset;
1080251881Speter
1081251881Speter  SVN_ERR(read_window_header(stream, &sview_offset, &sview_len, &tview_len,
1082251881Speter                             &inslen, &newlen));
1083251881Speter
1084251881Speter  offset = inslen + newlen;
1085251881Speter  return svn_io_file_seek(file, APR_CUR, &offset, pool);
1086251881Speter}
1087251881Speter
1088251881Speter
1089251881Spetersvn_error_t *
1090251881Spetersvn__compress(svn_string_t *in,
1091251881Speter              svn_stringbuf_t *out,
1092251881Speter              int compression_level)
1093251881Speter{
1094251881Speter  return zlib_encode(in->data, in->len, out, compression_level);
1095251881Speter}
1096251881Speter
1097251881Spetersvn_error_t *
1098251881Spetersvn__decompress(svn_string_t *in,
1099251881Speter                svn_stringbuf_t *out,
1100251881Speter                apr_size_t limit)
1101251881Speter{
1102251881Speter  return zlib_decode((const unsigned char*)in->data, in->len, out, limit);
1103251881Speter}
1104