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