svndiff.c revision 362181
11573Srgrimes/* 21573Srgrimes * svndiff.c -- Encoding and decoding svndiff-format deltas. 31573Srgrimes * 41573Srgrimes * ==================================================================== 51573Srgrimes * Licensed to the Apache Software Foundation (ASF) under one 61573Srgrimes * or more contributor license agreements. See the NOTICE file 71573Srgrimes * distributed with this work for additional information 81573Srgrimes * regarding copyright ownership. The ASF licenses this file 91573Srgrimes * to you under the Apache License, Version 2.0 (the 101573Srgrimes * "License"); you may not use this file except in compliance 111573Srgrimes * with the License. You may obtain a copy of the License at 121573Srgrimes * 131573Srgrimes * http://www.apache.org/licenses/LICENSE-2.0 141573Srgrimes * 151573Srgrimes * Unless required by applicable law or agreed to in writing, 161573Srgrimes * software distributed under the License is distributed on an 171573Srgrimes * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 181573Srgrimes * KIND, either express or implied. See the License for the 191573Srgrimes * specific language governing permissions and limitations 201573Srgrimes * under the License. 211573Srgrimes * ==================================================================== 221573Srgrimes */ 231573Srgrimes 241573Srgrimes 251573Srgrimes#include <assert.h> 261573Srgrimes#include <string.h> 271573Srgrimes#include "svn_delta.h" 281573Srgrimes#include "svn_io.h" 291573Srgrimes#include "delta.h" 301573Srgrimes#include "svn_pools.h" 311573Srgrimes#include "svn_private_config.h" 321573Srgrimes 331573Srgrimes#include "private/svn_error_private.h" 3423668Speter#include "private/svn_delta_private.h" 351573Srgrimes#include "private/svn_subr_private.h" 3690041Sobrien#include "private/svn_string_private.h" 3790041Sobrien#include "private/svn_dep_compat.h" 381573Srgrimes 39108626Stjrstatic const char SVNDIFF_V0[] = { 'S', 'V', 'N', 0 }; 401573Srgrimesstatic const char SVNDIFF_V1[] = { 'S', 'V', 'N', 1 }; 417615Swpaulstatic const char SVNDIFF_V2[] = { 'S', 'V', 'N', 2 }; 4295459Sdes 437615Swpaul#define SVNDIFF_HEADER_SIZE (sizeof(SVNDIFF_V0)) 441573Srgrimes 457615Swpaulstatic const char * 469978Swpaulget_svndiff_header(int version) 479978Swpaul{ 489978Swpaul if (version == 2) 499978Swpaul return SVNDIFF_V2; 509978Swpaul else if (version == 1) 519978Swpaul return SVNDIFF_V1; 529978Swpaul else 539978Swpaul return SVNDIFF_V0; 549978Swpaul} 559978Swpaul 569978Swpaul/* ----- Text delta to svndiff ----- */ 579978Swpaul 589978Swpaul/* We make one of these and get it passed back to us in calls to the 599978Swpaul window handler. We only use it to record the write function and 609978Swpaul baton passed to svn_txdelta_to_svndiff3(). */ 619978Swpaulstruct encoder_baton { 629978Swpaul svn_stream_t *output; 639978Swpaul svn_boolean_t header_done; 649978Swpaul int version; 659978Swpaul int compression_level; 669978Swpaul /* Pool for temporary allocations, will be cleared periodically. */ 679978Swpaul apr_pool_t *scratch_pool; 689978Swpaul}; 699978Swpaul 709978Swpaul/* This is at least as big as the largest size for a single instruction. */ 719978Swpaul#define MAX_INSTRUCTION_LEN (2*SVN__MAX_ENCODED_UINT_LEN+1) 729978Swpaul/* This is at least as big as the largest possible instructions 739978Swpaul section: in theory, the instructions could be SVN_DELTA_WINDOW_SIZE 749978Swpaul 1-byte copy-from-source instructions (though this is very unlikely). */ 759978Swpaul#define MAX_INSTRUCTION_SECTION_LEN (SVN_DELTA_WINDOW_SIZE*MAX_INSTRUCTION_LEN) 769978Swpaul 779978Swpaul 789978Swpaul/* Append an encoded integer to a string. */ 799978Swpaulstatic void 809978Swpaulappend_encoded_int(svn_stringbuf_t *header, svn_filesize_t val) 819978Swpaul{ 829978Swpaul unsigned char buf[SVN__MAX_ENCODED_UINT_LEN], *p; 839978Swpaul 8430288Swpaul SVN_ERR_ASSERT_NO_RETURN(val >= 0); 857615Swpaul p = svn__encode_uint(buf, (apr_uint64_t)val); 867615Swpaul svn_stringbuf_appendbytes(header, (const char *)buf, p - buf); 877615Swpaul} 889978Swpaul 899978Swpaulstatic svn_error_t * 909978Swpaulsend_simple_insertion_window(svn_txdelta_window_t *window, 919978Swpaul struct encoder_baton *eb) 929978Swpaul{ 9310521Swpaul unsigned char headers[SVNDIFF_HEADER_SIZE + 5 * SVN__MAX_ENCODED_UINT_LEN 947615Swpaul + MAX_INSTRUCTION_LEN]; 959978Swpaul unsigned char ibuf[MAX_INSTRUCTION_LEN]; 967615Swpaul unsigned char *header_current; 977615Swpaul apr_size_t header_len; 989978Swpaul apr_size_t ip_len, i; 991573Srgrimes apr_size_t len = window->new_data->len; 1009978Swpaul 1011573Srgrimes /* there is only one target copy op. It must span the whole window */ 1021573Srgrimes assert(window->ops[0].action_code == svn_txdelta_new); 1031573Srgrimes assert(window->ops[0].length == window->tview_len); 1041573Srgrimes assert(window->ops[0].offset == 0); 1051573Srgrimes 1061573Srgrimes /* write stream header if necessary */ 1071573Srgrimes if (!eb->header_done) 1081573Srgrimes { 1091573Srgrimes eb->header_done = TRUE; 1101573Srgrimes memcpy(headers, get_svndiff_header(eb->version), SVNDIFF_HEADER_SIZE); 1111573Srgrimes header_current = headers + SVNDIFF_HEADER_SIZE; 1121573Srgrimes } 1131573Srgrimes else 1141573Srgrimes { 1151573Srgrimes header_current = headers; 1161573Srgrimes } 1171573Srgrimes 1181573Srgrimes /* Encode the action code and length. */ 1191573Srgrimes if (window->tview_len >> 6 == 0) 1201573Srgrimes { 1211573Srgrimes ibuf[0] = (unsigned char)(window->tview_len + (0x2 << 6)); 1221573Srgrimes ip_len = 1; 1231573Srgrimes } 1241573Srgrimes else 1251573Srgrimes { 1261573Srgrimes ibuf[0] = (0x2 << 6); 1271573Srgrimes ip_len = svn__encode_uint(ibuf + 1, window->tview_len) - ibuf; 1281573Srgrimes } 1291573Srgrimes 1301573Srgrimes /* encode the window header. Please note that the source window may 1311573Srgrimes * have content despite not being used for deltification. */ 1321573Srgrimes header_current = svn__encode_uint(header_current, 1331573Srgrimes (apr_uint64_t)window->sview_offset); 1341573Srgrimes header_current = svn__encode_uint(header_current, window->sview_len); 1351573Srgrimes header_current = svn__encode_uint(header_current, window->tview_len); 136132793Sdes header_current[0] = (unsigned char)ip_len; /* 1 instruction */ 137132793Sdes header_current = svn__encode_uint(&header_current[1], len); 138132793Sdes 139132793Sdes /* append instructions (1 to a handful of bytes) */ 140132793Sdes for (i = 0; i < ip_len; ++i) 141132793Sdes header_current[i] = ibuf[i]; 142132793Sdes 1431573Srgrimes header_len = header_current - headers + ip_len; 1441573Srgrimes 1451573Srgrimes /* Write out the window. */ 1461573Srgrimes SVN_ERR(svn_stream_write(eb->output, (const char *)headers, &header_len)); 1471573Srgrimes if (len) 1481573Srgrimes SVN_ERR(svn_stream_write(eb->output, window->new_data->data, &len)); 1491573Srgrimes 1501573Srgrimes return SVN_NO_ERROR; 1511573Srgrimes} 152132793Sdes 1531573Srgrimes/* Encodes delta window WINDOW to svndiff-format. 1549978Swpaul The svndiff version is VERSION. COMPRESSION_LEVEL is the 1559978Swpaul compression level to use. 1569978Swpaul Returned values will be allocated in POOL or refer to *WINDOW 1579978Swpaul fields. */ 1589978Swpaulstatic svn_error_t * 1597336Swpaulencode_window(svn_stringbuf_t **instructions_p, 1607289Swpaul svn_stringbuf_t **header_p, 1617336Swpaul const svn_string_t **newdata_p, 1627289Swpaul svn_txdelta_window_t *window, 1637336Swpaul int version, 164237160Skib int compression_level, 1651573Srgrimes apr_pool_t *pool) 1669978Swpaul{ 16710521Swpaul svn_stringbuf_t *instructions; 16810521Swpaul svn_stringbuf_t *header; 1699978Swpaul const svn_string_t *newdata; 17015264Swpaul unsigned char ibuf[MAX_INSTRUCTION_LEN], *ip; 1719978Swpaul const svn_txdelta_op_t *op; 1729978Swpaul 1739978Swpaul /* create the necessary data buffers */ 174237160Skib instructions = svn_stringbuf_create_empty(pool); 1759978Swpaul header = svn_stringbuf_create_empty(pool); 176244092Sjilles 1779978Swpaul /* Encode the instructions. */ 1789978Swpaul for (op = window->ops; op < window->ops + window->num_ops; op++) 1799978Swpaul { 1809978Swpaul /* Encode the action code and length. */ 1819978Swpaul ip = ibuf; 1829978Swpaul switch (op->action_code) 1839978Swpaul { 1849978Swpaul case svn_txdelta_source: *ip = 0; break; 1859978Swpaul case svn_txdelta_target: *ip = (0x1 << 6); break; 1869978Swpaul case svn_txdelta_new: *ip = (0x2 << 6); break; 1879978Swpaul } 1889978Swpaul if (op->length >> 6 == 0) 1899978Swpaul *ip++ |= (unsigned char)op->length; 1909978Swpaul else 1919978Swpaul ip = svn__encode_uint(ip + 1, op->length); 1929978Swpaul if (op->action_code != svn_txdelta_new) 19310521Swpaul ip = svn__encode_uint(ip, op->offset); 19410521Swpaul svn_stringbuf_appendbytes(instructions, (const char *)ibuf, ip - ibuf); 19512585Swpaul } 19612585Swpaul 1979978Swpaul /* Encode the header. */ 19810521Swpaul append_encoded_int(header, window->sview_offset); 1999978Swpaul append_encoded_int(header, window->sview_len); 200244092Sjilles append_encoded_int(header, window->tview_len); 2019978Swpaul if (version == 2) 2021573Srgrimes { 2031573Srgrimes svn_stringbuf_t *compressed_instructions; 2041573Srgrimes compressed_instructions = svn_stringbuf_create_empty(pool); 205235740Sghelmer SVN_ERR(svn__compress_lz4(instructions->data, instructions->len, 2061573Srgrimes compressed_instructions)); 2079978Swpaul instructions = compressed_instructions; 2089978Swpaul } 2091573Srgrimes else if (version == 1) 2101573Srgrimes { 2111573Srgrimes svn_stringbuf_t *compressed_instructions; 2121573Srgrimes compressed_instructions = svn_stringbuf_create_empty(pool); 2131573Srgrimes SVN_ERR(svn__compress_zlib(instructions->data, instructions->len, 2141573Srgrimes compressed_instructions, compression_level)); 2151573Srgrimes instructions = compressed_instructions; 2161573Srgrimes } 2171573Srgrimes append_encoded_int(header, instructions->len); 218132793Sdes 2191573Srgrimes /* Encode the data. */ 2209978Swpaul if (version == 2) 2219978Swpaul { 2229978Swpaul svn_stringbuf_t *compressed = svn_stringbuf_create_empty(pool); 2231573Srgrimes 2241573Srgrimes SVN_ERR(svn__compress_lz4(window->new_data->data, window->new_data->len, 2251573Srgrimes compressed)); 2261573Srgrimes newdata = svn_stringbuf__morph_into_string(compressed); 2271573Srgrimes } 2281573Srgrimes else if (version == 1) 2291573Srgrimes { 2301573Srgrimes svn_stringbuf_t *compressed = svn_stringbuf_create_empty(pool); 2311573Srgrimes 2321573Srgrimes SVN_ERR(svn__compress_zlib(window->new_data->data, window->new_data->len, 2331573Srgrimes compressed, compression_level)); 2341573Srgrimes newdata = svn_stringbuf__morph_into_string(compressed); 2351573Srgrimes } 2361573Srgrimes else 2371573Srgrimes newdata = window->new_data; 238132793Sdes 2391573Srgrimes append_encoded_int(header, newdata->len); 24090041Sobrien 24190041Sobrien *instructions_p = instructions; 2421573Srgrimes *header_p = header; 2431573Srgrimes *newdata_p = newdata; 2441573Srgrimes 2451573Srgrimes return SVN_NO_ERROR; 2461573Srgrimes} 2471573Srgrimes 2481573Srgrimes/* Note: When changing things here, check the related comment in 249237160Skib the svn_txdelta_to_svndiff_stream() function. */ 2501573Srgrimesstatic svn_error_t * 251237160Skibwindow_handler(svn_txdelta_window_t *window, void *baton) 2521573Srgrimes{ 2531573Srgrimes struct encoder_baton *eb = baton; 254237160Skib apr_size_t len; 2551573Srgrimes svn_stringbuf_t *instructions; 2561573Srgrimes svn_stringbuf_t *header; 2571573Srgrimes const svn_string_t *newdata; 2581573Srgrimes 2591573Srgrimes /* use specialized code if there is no source */ 260237160Skib if (window && !window->src_ops && window->num_ops == 1 && !eb->version) 261237160Skib return svn_error_trace(send_simple_insertion_window(window, eb)); 262237160Skib 263237160Skib /* Make sure we write the header. */ 2641573Srgrimes if (!eb->header_done) 265237160Skib { 266237160Skib len = SVNDIFF_HEADER_SIZE; 2677149Swpaul SVN_ERR(svn_stream_write(eb->output, get_svndiff_header(eb->version), 2687223Swpaul &len)); 2697149Swpaul eb->header_done = TRUE; 2701573Srgrimes } 2711573Srgrimes 2729978Swpaul if (window == NULL) 273132793Sdes { 274132793Sdes /* We're done; clean up. */ 2759978Swpaul SVN_ERR(svn_stream_close(eb->output)); 276132793Sdes 277132793Sdes svn_pool_destroy(eb->scratch_pool); 27831180Swpaul 2799978Swpaul return SVN_NO_ERROR; 28031180Swpaul } 281237160Skib 28231180Swpaul svn_pool_clear(eb->scratch_pool); 28315264Swpaul 28433950Ssteve SVN_ERR(encode_window(&instructions, &header, &newdata, window, 28533950Ssteve eb->version, eb->compression_level, 28652856Sache eb->scratch_pool)); 28733950Ssteve 28833950Ssteve /* Write out the window. */ 289235739Sghelmer len = header->len; 290237160Skib SVN_ERR(svn_stream_write(eb->output, header->data, &len)); 29133950Ssteve if (instructions->len > 0) 29215839Swpaul { 29315839Swpaul len = instructions->len; 294235739Sghelmer SVN_ERR(svn_stream_write(eb->output, instructions->data, &len)); 2959978Swpaul } 2969978Swpaul if (newdata->len > 0) 297132793Sdes { 298148317Sjon len = newdata->len; 299148317Sjon SVN_ERR(svn_stream_write(eb->output, newdata->data, &len)); 3009978Swpaul } 301148317Sjon 302148317Sjon return SVN_NO_ERROR; 303148317Sjon} 304148317Sjon 305148317Sjonvoid 306148317Sjonsvn_txdelta_to_svndiff3(svn_txdelta_window_handler_t *handler, 307148317Sjon void **handler_baton, 308236402Sghelmer svn_stream_t *output, 309235739Sghelmer int svndiff_version, 310235739Sghelmer int compression_level, 311235739Sghelmer apr_pool_t *pool) 312236402Sghelmer{ 313235739Sghelmer struct encoder_baton *eb; 314235739Sghelmer 315236402Sghelmer eb = apr_palloc(pool, sizeof(*eb)); 316235739Sghelmer eb->output = output; 317235739Sghelmer eb->header_done = FALSE; 318235739Sghelmer eb->scratch_pool = svn_pool_create(pool); 319236402Sghelmer eb->version = svndiff_version; 320235739Sghelmer eb->compression_level = compression_level; 321235739Sghelmer 322236402Sghelmer *handler = window_handler; 323236402Sghelmer *handler_baton = eb; 324148317Sjon} 325148317Sjon 326236402Sghelmervoid 327148317Sjonsvn_txdelta_to_svndiff2(svn_txdelta_window_handler_t *handler, 328148317Sjon void **handler_baton, 329148317Sjon svn_stream_t *output, 330236402Sghelmer int svndiff_version, 331236402Sghelmer apr_pool_t *pool) 332148317Sjon{ 333148317Sjon svn_txdelta_to_svndiff3(handler, handler_baton, output, svndiff_version, 334148317Sjon SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool); 335148317Sjon} 336148317Sjon 337148317Sjonvoid 338235739Sghelmersvn_txdelta_to_svndiff(svn_stream_t *output, 339148317Sjon apr_pool_t *pool, 3409978Swpaul svn_txdelta_window_handler_t *handler, 3419978Swpaul void **handler_baton) 3429978Swpaul{ 3439978Swpaul svn_txdelta_to_svndiff3(handler, handler_baton, output, 0, 3441573Srgrimes SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool); 3451573Srgrimes} 3461573Srgrimes 3471573Srgrimes 348132793Sdes/* ----- svndiff to text delta ----- */ 3491573Srgrimes 3501573Srgrimes/* An svndiff parser object. */ 3517336Swpaulstruct decode_baton 3529287Swpaul{ 3537336Swpaul /* Once the svndiff parser has enough data buffered to create a 3547336Swpaul "window", it passes this window to the caller's consumer routine. */ 3557336Swpaul svn_txdelta_window_handler_t consumer_func; 3569978Swpaul void *consumer_baton; 3579978Swpaul 3589978Swpaul /* Pool to create subpools from; each developing window will be a 3591573Srgrimes subpool. */ 3609978Swpaul apr_pool_t *pool; 36115264Swpaul 3629978Swpaul /* The current subpool which contains our current window-buffer. */ 3639978Swpaul apr_pool_t *subpool; 3649978Swpaul 365148317Sjon /* The actual svndiff data buffer, living within subpool. */ 366148317Sjon svn_stringbuf_t *buffer; 367148317Sjon 368148317Sjon /* The offset and size of the last source view, so that we can check 369148317Sjon to make sure the next one isn't sliding backwards. */ 370148317Sjon svn_filesize_t last_sview_offset; 371148317Sjon apr_size_t last_sview_len; 372148317Sjon 373148317Sjon /* We have to discard four bytes at the beginning for the header. 374148317Sjon This field keeps track of how many of those bytes we have read. */ 375148317Sjon apr_size_t header_bytes; 376148317Sjon 377148317Sjon /* Do we want an error to occur when we close the stream that 378148317Sjon indicates we didn't send the whole svndiff data? If you plan to 379148317Sjon not transmit the whole svndiff data stream, you will want this to 380148317Sjon be FALSE. */ 381148317Sjon svn_boolean_t error_on_early_close; 382148317Sjon 383148317Sjon /* svndiff version in use by delta. */ 3849978Swpaul unsigned char version; 385148317Sjon 386148317Sjon /* Length of parsed delta window header. 0 if window is not parsed yet. */ 3879978Swpaul apr_size_t window_header_len; 388235739Sghelmer 389148317Sjon /* Five integer fields of parsed delta window header. Valid only if 390148317Sjon WINDOW_HEADER_LEN > 0 */ 391148317Sjon svn_filesize_t sview_offset; 392148317Sjon apr_size_t sview_len; 393235739Sghelmer apr_size_t tview_len; 394148317Sjon apr_size_t inslen; 395235739Sghelmer apr_size_t newlen; 3969978Swpaul}; 39730390Swpaul 3989978Swpaul 3999978Swpaul/* Wrapper aroung svn__deencode_uint taking a file size as *VAL. */ 40030390Swpaulstatic const unsigned char * 4011573Srgrimesdecode_file_offset(svn_filesize_t *val, 4029978Swpaul const unsigned char *p, 4039978Swpaul const unsigned char *end) 4049978Swpaul{ 4051573Srgrimes apr_uint64_t temp = 0; 4061573Srgrimes const unsigned char *result = svn__decode_uint(&temp, p, end); 4071573Srgrimes *val = (svn_filesize_t)temp; 4081573Srgrimes 4091573Srgrimes return result; 4101573Srgrimes} 4111573Srgrimes 4121573Srgrimes/* Same as above, only decode into a size variable. */ 4131573Srgrimesstatic const unsigned char * 4141573Srgrimesdecode_size(apr_size_t *val, 4151573Srgrimes const unsigned char *p, 416132793Sdes const unsigned char *end) 4171573Srgrimes{ 418236402Sghelmer apr_uint64_t temp = 0; 419236402Sghelmer const unsigned char *result = svn__decode_uint(&temp, p, end); 420236402Sghelmer if (temp > APR_SIZE_MAX) 421236402Sghelmer return NULL; 422236402Sghelmer 4239287Swpaul *val = (apr_size_t)temp; 42490041Sobrien return result; 4259287Swpaul} 4261573Srgrimes 4271573Srgrimes/* Decode an instruction into OP, returning a pointer to the text 4281573Srgrimes after the instruction. Note that if the action code is 4291573Srgrimes svn_txdelta_new, the offset field of *OP will not be set. */ 4301573Srgrimesstatic const unsigned char * 4311573Srgrimesdecode_instruction(svn_txdelta_op_t *op, 4321573Srgrimes const unsigned char *p, 4331573Srgrimes const unsigned char *end) 4341573Srgrimes{ 435237160Skib apr_size_t c; 4361573Srgrimes apr_size_t action; 4371573Srgrimes 4389287Swpaul if (p == end) 4399287Swpaul return NULL; 4409287Swpaul 4419287Swpaul /* We need this more than once */ 4429287Swpaul c = *p++; 4439287Swpaul 4449287Swpaul /* Decode the instruction selector. */ 4451573Srgrimes action = (c >> 6) & 0x3; 4469287Swpaul if (action >= 0x3) 4471573Srgrimes return NULL; 4481573Srgrimes 4491573Srgrimes /* This relies on enum svn_delta_action values to match and never to be 4501573Srgrimes redefined. */ 4517149Swpaul op->action_code = (enum svn_delta_action)(action); 4527149Swpaul 4531573Srgrimes /* Decode the length and offset. */ 454236402Sghelmer op->length = c & 0x3f; 455235740Sghelmer if (op->length == 0) 456235740Sghelmer { 457236402Sghelmer p = decode_size(&op->length, p, end); 458236402Sghelmer if (p == NULL) 4591573Srgrimes return NULL; 4601573Srgrimes } 4619287Swpaul if (action != svn_txdelta_new) 4629287Swpaul { 4639287Swpaul p = decode_size(&op->offset, p, end); 4641573Srgrimes if (p == NULL) 465236402Sghelmer return NULL; 4669287Swpaul } 4679287Swpaul 4689287Swpaul return p; 4699287Swpaul} 4709287Swpaul 4719287Swpaul/* Count the instructions in the range [P..END-1] and make sure they 472236402Sghelmer are valid for the given window lengths. Return an error if the 473236402Sghelmer instructions are invalid; otherwise set *NINST to the number of 4749287Swpaul instructions. */ 475236402Sghelmerstatic svn_error_t * 476236402Sghelmercount_and_verify_instructions(int *ninst, 477236402Sghelmer const unsigned char *p, 478236402Sghelmer const unsigned char *end, 479236402Sghelmer apr_size_t sview_len, 480236402Sghelmer apr_size_t tview_len, 481236402Sghelmer apr_size_t new_len) 482236402Sghelmer{ 483236402Sghelmer int n = 0; 484236402Sghelmer svn_txdelta_op_t op; 485236402Sghelmer apr_size_t tpos = 0, npos = 0; 486236402Sghelmer 487236402Sghelmer while (p < end) 488236402Sghelmer { 489236402Sghelmer p = decode_instruction(&op, p, end); 490236402Sghelmer 491236402Sghelmer /* Detect any malformed operations from the instruction stream. */ 492236402Sghelmer if (p == NULL) 493236402Sghelmer return svn_error_createf 494236402Sghelmer (SVN_ERR_SVNDIFF_INVALID_OPS, NULL, 495236402Sghelmer _("Invalid diff stream: insn %d cannot be decoded"), n); 4961573Srgrimes else if (op.length == 0) 497235740Sghelmer return svn_error_createf 498235740Sghelmer (SVN_ERR_SVNDIFF_INVALID_OPS, NULL, 4999287Swpaul _("Invalid diff stream: insn %d has length zero"), n); 5009287Swpaul else if (op.length > tview_len - tpos) 5019287Swpaul return svn_error_createf 5029287Swpaul (SVN_ERR_SVNDIFF_INVALID_OPS, NULL, 5039287Swpaul _("Invalid diff stream: insn %d overflows the target view"), n); 5049287Swpaul 5059287Swpaul switch (op.action_code) 506236402Sghelmer { 507236402Sghelmer case svn_txdelta_source: 508236402Sghelmer if (op.length > sview_len - op.offset || 509236402Sghelmer op.offset > sview_len) 510236402Sghelmer return svn_error_createf 511236402Sghelmer (SVN_ERR_SVNDIFF_INVALID_OPS, NULL, 512236402Sghelmer _("Invalid diff stream: " 513236402Sghelmer "[src] insn %d overflows the source view"), n); 514236402Sghelmer break; 515292138Sngie case svn_txdelta_target: 5169287Swpaul if (op.offset >= tpos) 5171573Srgrimes return svn_error_createf 5181573Srgrimes (SVN_ERR_SVNDIFF_INVALID_OPS, NULL, 5191573Srgrimes _("Invalid diff stream: " 5207175Swpaul "[tgt] insn %d starts beyond the target view position"), n); 5211573Srgrimes break; 52223668Speter case svn_txdelta_new: 52323668Speter if (op.length > new_len - npos) 52423668Speter return svn_error_createf 52523668Speter (SVN_ERR_SVNDIFF_INVALID_OPS, NULL, 5261573Srgrimes _("Invalid diff stream: " 5271573Srgrimes "[new] insn %d overflows the new data section"), n); 5281573Srgrimes npos += op.length; 5291573Srgrimes break; 5301573Srgrimes } 5311573Srgrimes tpos += op.length; 5321573Srgrimes n++; 5331573Srgrimes } 5341573Srgrimes if (tpos != tview_len) 535132793Sdes return svn_error_create(SVN_ERR_SVNDIFF_INVALID_OPS, NULL, 5361573Srgrimes _("Delta does not fill the target window")); 537237159Skib if (npos != new_len) 53890041Sobrien return svn_error_create(SVN_ERR_SVNDIFF_INVALID_OPS, NULL, 5391573Srgrimes _("Delta does not contain enough new data")); 5401573Srgrimes 54120957Swpaul *ninst = n; 5427149Swpaul return SVN_NO_ERROR; 5437149Swpaul} 5447149Swpaul 545235740Sghelmer/* Given the five integer fields of a window header and a pointer to 5461573Srgrimes the remainder of the window contents, fill in a delta window 5477149Swpaul structure *WINDOW. New allocations will be performed in POOL; 5487149Swpaul the new_data field of *WINDOW will refer directly to memory pointed 5497149Swpaul to by DATA. */ 5507223Swpaulstatic svn_error_t * 5517149Swpauldecode_window(svn_txdelta_window_t *window, svn_filesize_t sview_offset, 5527223Swpaul apr_size_t sview_len, apr_size_t tview_len, apr_size_t inslen, 553236402Sghelmer apr_size_t newlen, const unsigned char *data, apr_pool_t *pool, 5547223Swpaul unsigned int version) 5559978Swpaul{ 5569978Swpaul const unsigned char *insend; 5579978Swpaul int ninst; 5589978Swpaul apr_size_t npos; 5599978Swpaul svn_txdelta_op_t *ops, *op; 5609978Swpaul svn_string_t *new_data; 5617149Swpaul 56220957Swpaul window->sview_offset = sview_offset; 5637149Swpaul window->sview_len = sview_len; 5647149Swpaul window->tview_len = tview_len; 5657149Swpaul 566235740Sghelmer insend = data + inslen; 5671573Srgrimes 5687149Swpaul if (version == 2) 5697149Swpaul { 5707149Swpaul svn_stringbuf_t *instout = svn_stringbuf_create_empty(pool); 5717149Swpaul svn_stringbuf_t *ndout = svn_stringbuf_create_empty(pool); 5727149Swpaul 5737149Swpaul SVN_ERR(svn__decompress_lz4(insend, newlen, ndout, 5747149Swpaul SVN_DELTA_WINDOW_SIZE)); 5757149Swpaul SVN_ERR(svn__decompress_lz4(data, insend - data, instout, 5761573Srgrimes MAX_INSTRUCTION_SECTION_LEN)); 5771573Srgrimes 5781573Srgrimes newlen = ndout->len; 5791573Srgrimes data = (unsigned char *)instout->data; 5801573Srgrimes insend = (unsigned char *)instout->data + instout->len; 5811573Srgrimes 5821573Srgrimes new_data = svn_stringbuf__morph_into_string(ndout); 5831573Srgrimes } 5841573Srgrimes else if (version == 1) 5851573Srgrimes { 5861573Srgrimes svn_stringbuf_t *instout = svn_stringbuf_create_empty(pool); 5871573Srgrimes svn_stringbuf_t *ndout = svn_stringbuf_create_empty(pool); 5881573Srgrimes 589235740Sghelmer SVN_ERR(svn__decompress_zlib(insend, newlen, ndout, 590235740Sghelmer SVN_DELTA_WINDOW_SIZE)); 5911573Srgrimes SVN_ERR(svn__decompress_zlib(data, insend - data, instout, 5921573Srgrimes MAX_INSTRUCTION_SECTION_LEN)); 593235740Sghelmer 594235740Sghelmer newlen = ndout->len; 595235740Sghelmer data = (unsigned char *)instout->data; 596235740Sghelmer insend = (unsigned char *)instout->data + instout->len; 5971573Srgrimes 5981573Srgrimes new_data = svn_stringbuf__morph_into_string(ndout); 5991573Srgrimes } 6001573Srgrimes else 6011573Srgrimes { 6021573Srgrimes /* Copy the data because an svn_string_t must have the invariant 6031573Srgrimes data[len]=='\0'. */ 6041573Srgrimes new_data = svn_string_ncreate((const char*)insend, newlen, pool); 6051573Srgrimes } 6061573Srgrimes 6071573Srgrimes /* Count the instructions and make sure they are all valid. */ 6081573Srgrimes SVN_ERR(count_and_verify_instructions(&ninst, data, insend, 6091573Srgrimes sview_len, tview_len, newlen)); 6101573Srgrimes 6111573Srgrimes /* Allocate a buffer for the instructions and decode them. */ 6121573Srgrimes ops = apr_palloc(pool, ninst * sizeof(*ops)); 6131573Srgrimes npos = 0; 614237159Skib window->src_ops = 0; 615235740Sghelmer for (op = ops; op < ops + ninst; op++) 616235740Sghelmer { 617235740Sghelmer data = decode_instruction(op, data, insend); 618235740Sghelmer if (op->action_code == svn_txdelta_source) 6191573Srgrimes ++window->src_ops; 620237159Skib else if (op->action_code == svn_txdelta_new) 621237159Skib { 622237159Skib op->offset = npos; 623237159Skib npos += op->length; 6241573Srgrimes } 6251573Srgrimes } 6261573Srgrimes SVN_ERR_ASSERT(data == insend); 627237159Skib 6281573Srgrimes window->ops = ops; 6291573Srgrimes window->num_ops = ninst; 6301573Srgrimes window->new_data = new_data; 6311573Srgrimes 6321573Srgrimes return SVN_NO_ERROR; 6331573Srgrimes} 6341573Srgrimes 6351573Srgrimesstatic svn_error_t * 6361573Srgrimeswrite_handler(void *baton, 6371573Srgrimes const char *buffer, 6381573Srgrimes apr_size_t *len) 6391573Srgrimes{ 6401573Srgrimes struct decode_baton *db = (struct decode_baton *) baton; 6411573Srgrimes const unsigned char *p, *end; 6421573Srgrimes apr_size_t buflen = *len; 6431573Srgrimes 6441573Srgrimes /* Chew up four bytes at the beginning for the header. */ 6451573Srgrimes if (db->header_bytes < SVNDIFF_HEADER_SIZE) 6461573Srgrimes { 6471573Srgrimes apr_size_t nheader = SVNDIFF_HEADER_SIZE - db->header_bytes; 6489978Swpaul if (nheader > buflen) 6499978Swpaul nheader = buflen; 6509978Swpaul if (memcmp(buffer, SVNDIFF_V0 + db->header_bytes, nheader) == 0) 6519978Swpaul db->version = 0; 6529978Swpaul else if (memcmp(buffer, SVNDIFF_V1 + db->header_bytes, nheader) == 0) 6539978Swpaul db->version = 1; 6549978Swpaul else if (memcmp(buffer, SVNDIFF_V2 + db->header_bytes, nheader) == 0) 6559978Swpaul db->version = 2; 6569978Swpaul else 6579978Swpaul return svn_error_create(SVN_ERR_SVNDIFF_INVALID_HEADER, NULL, 658235740Sghelmer _("Svndiff has invalid header")); 6591573Srgrimes buflen -= nheader; 660 buffer += nheader; 661 db->header_bytes += nheader; 662 } 663 664 /* Concatenate the old with the new. */ 665 svn_stringbuf_appendbytes(db->buffer, buffer, buflen); 666 667 /* We have a buffer of svndiff data that might be good for: 668 669 a) an integral number of windows' worth of data - this is a 670 trivial case. Make windows from our data and ship them off. 671 672 b) a non-integral number of windows' worth of data - we shall 673 consume the integral portion of the window data, and then 674 somewhere in the following loop the decoding of the svndiff 675 data will run out of stuff to decode, and will simply return 676 SVN_NO_ERROR, anxiously awaiting more data. 677 */ 678 679 while (1) 680 { 681 svn_txdelta_window_t window; 682 683 /* Read the header, if we have enough bytes for that. */ 684 p = (const unsigned char *) db->buffer->data; 685 end = (const unsigned char *) db->buffer->data + db->buffer->len; 686 687 if (db->window_header_len == 0) 688 { 689 svn_filesize_t sview_offset; 690 apr_size_t sview_len, tview_len, inslen, newlen; 691 const unsigned char *hdr_start = p; 692 693 p = decode_file_offset(&sview_offset, p, end); 694 if (p == NULL) 695 break; 696 697 p = decode_size(&sview_len, p, end); 698 if (p == NULL) 699 break; 700 701 p = decode_size(&tview_len, p, end); 702 if (p == NULL) 703 break; 704 705 p = decode_size(&inslen, p, end); 706 if (p == NULL) 707 break; 708 709 p = decode_size(&newlen, p, end); 710 if (p == NULL) 711 break; 712 713 if (tview_len > SVN_DELTA_WINDOW_SIZE || 714 sview_len > SVN_DELTA_WINDOW_SIZE || 715 /* for svndiff1, newlen includes the original length */ 716 newlen > SVN_DELTA_WINDOW_SIZE + SVN__MAX_ENCODED_UINT_LEN || 717 inslen > MAX_INSTRUCTION_SECTION_LEN) 718 return svn_error_create( 719 SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL, 720 _("Svndiff contains a too-large window")); 721 722 /* Check for integer overflow. */ 723 if (sview_offset < 0 || inslen + newlen < inslen 724 || sview_len + tview_len < sview_len 725 || (apr_size_t)sview_offset + sview_len < (apr_size_t)sview_offset) 726 return svn_error_create( 727 SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL, 728 _("Svndiff contains corrupt window header")); 729 730 /* Check for source windows which slide backwards. */ 731 if (sview_len > 0 732 && (sview_offset < db->last_sview_offset 733 || (sview_offset + sview_len 734 < db->last_sview_offset + db->last_sview_len))) 735 return svn_error_create( 736 SVN_ERR_SVNDIFF_BACKWARD_VIEW, NULL, 737 _("Svndiff has backwards-sliding source views")); 738 739 /* Remember parsed window header. */ 740 db->window_header_len = p - hdr_start; 741 db->sview_offset = sview_offset; 742 db->sview_len = sview_len; 743 db->tview_len = tview_len; 744 db->inslen = inslen; 745 db->newlen = newlen; 746 } 747 else 748 { 749 /* Skip already parsed window header. */ 750 p += db->window_header_len; 751 } 752 753 /* Wait for more data if we don't have enough bytes for the 754 whole window. */ 755 if ((apr_size_t) (end - p) < db->inslen + db->newlen) 756 return SVN_NO_ERROR; 757 758 /* Decode the window and send it off. */ 759 SVN_ERR(decode_window(&window, db->sview_offset, db->sview_len, 760 db->tview_len, db->inslen, db->newlen, p, 761 db->subpool, db->version)); 762 SVN_ERR(db->consumer_func(&window, db->consumer_baton)); 763 764 p += db->inslen + db->newlen; 765 766 /* Remove processed data from the buffer. */ 767 svn_stringbuf_remove(db->buffer, 0, db->buffer->len - (end - p)); 768 769 /* Reset window header length. */ 770 db->window_header_len = 0; 771 772 /* Remember the offset and length of the source view for next time. */ 773 db->last_sview_offset = db->sview_offset; 774 db->last_sview_len = db->sview_len; 775 776 /* Clear subpool. */ 777 svn_pool_clear(db->subpool); 778 } 779 780 /* At this point we processed all integral windows and DB->BUFFER is empty 781 or contains partially read window header. 782 Check that unprocessed data is not larger than theoretical maximum 783 window header size. */ 784 if (db->buffer->len > 5 * SVN__MAX_ENCODED_UINT_LEN) 785 return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL, 786 _("Svndiff contains a too-large window header")); 787 788 return SVN_NO_ERROR; 789} 790 791/* Minimal svn_stream_t write handler, doing nothing */ 792static svn_error_t * 793noop_write_handler(void *baton, 794 const char *buffer, 795 apr_size_t *len) 796{ 797 return SVN_NO_ERROR; 798} 799 800static svn_error_t * 801close_handler(void *baton) 802{ 803 struct decode_baton *db = (struct decode_baton *) baton; 804 svn_error_t *err; 805 806 /* Make sure that we're at a plausible end of stream, returning an 807 error if we are expected to do so. */ 808 if ((db->error_on_early_close) 809 && (db->header_bytes < 4 || db->buffer->len != 0)) 810 return svn_error_create(SVN_ERR_SVNDIFF_UNEXPECTED_END, NULL, 811 _("Unexpected end of svndiff input")); 812 813 /* Tell the window consumer that we're done, and clean up. */ 814 err = db->consumer_func(NULL, db->consumer_baton); 815 svn_pool_destroy(db->pool); 816 return err; 817} 818 819 820svn_stream_t * 821svn_txdelta_parse_svndiff(svn_txdelta_window_handler_t handler, 822 void *handler_baton, 823 svn_boolean_t error_on_early_close, 824 apr_pool_t *pool) 825{ 826 svn_stream_t *stream; 827 828 if (handler != svn_delta_noop_window_handler) 829 { 830 apr_pool_t *subpool = svn_pool_create(pool); 831 struct decode_baton *db = apr_palloc(pool, sizeof(*db)); 832 833 db->consumer_func = handler; 834 db->consumer_baton = handler_baton; 835 db->pool = subpool; 836 db->subpool = svn_pool_create(subpool); 837 db->buffer = svn_stringbuf_create_empty(db->pool); 838 db->last_sview_offset = 0; 839 db->last_sview_len = 0; 840 db->header_bytes = 0; 841 db->error_on_early_close = error_on_early_close; 842 db->window_header_len = 0; 843 stream = svn_stream_create(db, pool); 844 845 svn_stream_set_write(stream, write_handler); 846 svn_stream_set_close(stream, close_handler); 847 } 848 else 849 { 850 /* And else we just ignore everything as efficiently as we can. 851 by only hooking a no-op handler */ 852 stream = svn_stream_create(NULL, pool); 853 svn_stream_set_write(stream, noop_write_handler); 854 } 855 return stream; 856} 857 858 859/* Routines for reading one svndiff window at a time. */ 860 861/* Read one byte from STREAM into *BYTE. */ 862static svn_error_t * 863read_one_byte(unsigned char *byte, svn_stream_t *stream) 864{ 865 char c; 866 apr_size_t len = 1; 867 868 SVN_ERR(svn_stream_read_full(stream, &c, &len)); 869 if (len == 0) 870 return svn_error_create(SVN_ERR_SVNDIFF_UNEXPECTED_END, NULL, 871 _("Unexpected end of svndiff input")); 872 *byte = (unsigned char) c; 873 return SVN_NO_ERROR; 874} 875 876/* Read and decode one integer from STREAM into *SIZE. 877 Increment *BYTE_COUNTER by the number of chars we have read. */ 878static svn_error_t * 879read_one_size(apr_size_t *size, 880 apr_size_t *byte_counter, 881 svn_stream_t *stream) 882{ 883 unsigned char c; 884 885 *size = 0; 886 while (1) 887 { 888 SVN_ERR(read_one_byte(&c, stream)); 889 ++*byte_counter; 890 *size = (*size << 7) | (c & 0x7f); 891 if (!(c & 0x80)) 892 break; 893 } 894 return SVN_NO_ERROR; 895} 896 897/* Read a window header from STREAM and check it for integer overflow. */ 898static svn_error_t * 899read_window_header(svn_stream_t *stream, svn_filesize_t *sview_offset, 900 apr_size_t *sview_len, apr_size_t *tview_len, 901 apr_size_t *inslen, apr_size_t *newlen, 902 apr_size_t *header_len) 903{ 904 unsigned char c; 905 906 /* Read the source view offset by hand, since it's not an apr_size_t. */ 907 *header_len = 0; 908 *sview_offset = 0; 909 while (1) 910 { 911 SVN_ERR(read_one_byte(&c, stream)); 912 ++*header_len; 913 *sview_offset = (*sview_offset << 7) | (c & 0x7f); 914 if (!(c & 0x80)) 915 break; 916 } 917 918 /* Read the four size fields. */ 919 SVN_ERR(read_one_size(sview_len, header_len, stream)); 920 SVN_ERR(read_one_size(tview_len, header_len, stream)); 921 SVN_ERR(read_one_size(inslen, header_len, stream)); 922 SVN_ERR(read_one_size(newlen, header_len, stream)); 923 924 if (*tview_len > SVN_DELTA_WINDOW_SIZE || 925 *sview_len > SVN_DELTA_WINDOW_SIZE || 926 /* for svndiff1, newlen includes the original length */ 927 *newlen > SVN_DELTA_WINDOW_SIZE + SVN__MAX_ENCODED_UINT_LEN || 928 *inslen > MAX_INSTRUCTION_SECTION_LEN) 929 return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL, 930 _("Svndiff contains a too-large window")); 931 932 /* Check for integer overflow. */ 933 if (*sview_offset < 0 || *inslen + *newlen < *inslen 934 || *sview_len + *tview_len < *sview_len 935 || (apr_size_t)*sview_offset + *sview_len < (apr_size_t)*sview_offset) 936 return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL, 937 _("Svndiff contains corrupt window header")); 938 939 return SVN_NO_ERROR; 940} 941 942svn_error_t * 943svn_txdelta_read_svndiff_window(svn_txdelta_window_t **window, 944 svn_stream_t *stream, 945 int svndiff_version, 946 apr_pool_t *pool) 947{ 948 svn_filesize_t sview_offset; 949 apr_size_t sview_len, tview_len, inslen, newlen, len, header_len; 950 unsigned char *buf; 951 952 SVN_ERR(read_window_header(stream, &sview_offset, &sview_len, &tview_len, 953 &inslen, &newlen, &header_len)); 954 len = inslen + newlen; 955 buf = apr_palloc(pool, len); 956 SVN_ERR(svn_stream_read_full(stream, (char*)buf, &len)); 957 if (len < inslen + newlen) 958 return svn_error_create(SVN_ERR_SVNDIFF_UNEXPECTED_END, NULL, 959 _("Unexpected end of svndiff input")); 960 *window = apr_palloc(pool, sizeof(**window)); 961 return decode_window(*window, sview_offset, sview_len, tview_len, inslen, 962 newlen, buf, pool, svndiff_version); 963} 964 965 966svn_error_t * 967svn_txdelta_skip_svndiff_window(apr_file_t *file, 968 int svndiff_version, 969 apr_pool_t *pool) 970{ 971 svn_stream_t *stream = svn_stream_from_aprfile2(file, TRUE, pool); 972 svn_filesize_t sview_offset; 973 apr_size_t sview_len, tview_len, inslen, newlen, header_len; 974 apr_off_t offset; 975 976 SVN_ERR(read_window_header(stream, &sview_offset, &sview_len, &tview_len, 977 &inslen, &newlen, &header_len)); 978 979 offset = inslen + newlen; 980 return svn_io_file_seek(file, APR_CUR, &offset, pool); 981} 982 983svn_error_t * 984svn_txdelta__read_raw_window_len(apr_size_t *window_len, 985 svn_stream_t *stream, 986 apr_pool_t *pool) 987{ 988 svn_filesize_t sview_offset; 989 apr_size_t sview_len, tview_len, inslen, newlen, header_len; 990 991 SVN_ERR(read_window_header(stream, &sview_offset, &sview_len, &tview_len, 992 &inslen, &newlen, &header_len)); 993 994 *window_len = inslen + newlen + header_len; 995 return SVN_NO_ERROR; 996} 997 998typedef struct svndiff_stream_baton_t 999{ 1000 apr_pool_t *scratch_pool; 1001 svn_txdelta_stream_t *txstream; 1002 svn_txdelta_window_handler_t handler; 1003 void *handler_baton; 1004 svn_stringbuf_t *window_buffer; 1005 apr_size_t read_pos; 1006 svn_boolean_t hit_eof; 1007} svndiff_stream_baton_t; 1008 1009static svn_error_t * 1010svndiff_stream_write_fn(void *baton, const char *data, apr_size_t *len) 1011{ 1012 svndiff_stream_baton_t *b = baton; 1013 1014 /* The memory usage here is limited, as this buffer doesn't grow 1015 beyond the (header size + max window size in svndiff format). 1016 See the comment in svn_txdelta_to_svndiff_stream(). */ 1017 svn_stringbuf_appendbytes(b->window_buffer, data, *len); 1018 1019 return SVN_NO_ERROR; 1020} 1021 1022static svn_error_t * 1023svndiff_stream_read_fn(void *baton, char *buffer, apr_size_t *len) 1024{ 1025 svndiff_stream_baton_t *b = baton; 1026 apr_size_t left = *len; 1027 apr_size_t read = 0; 1028 1029 while (left) 1030 { 1031 apr_size_t chunk_size; 1032 1033 if (b->read_pos == b->window_buffer->len && !b->hit_eof) 1034 { 1035 svn_txdelta_window_t *window; 1036 1037 svn_pool_clear(b->scratch_pool); 1038 svn_stringbuf_setempty(b->window_buffer); 1039 SVN_ERR(svn_txdelta_next_window(&window, b->txstream, 1040 b->scratch_pool)); 1041 SVN_ERR(b->handler(window, b->handler_baton)); 1042 b->read_pos = 0; 1043 1044 if (!window) 1045 b->hit_eof = TRUE; 1046 } 1047 1048 if (left > b->window_buffer->len - b->read_pos) 1049 chunk_size = b->window_buffer->len - b->read_pos; 1050 else 1051 chunk_size = left; 1052 1053 if (!chunk_size) 1054 break; 1055 1056 memcpy(buffer, b->window_buffer->data + b->read_pos, chunk_size); 1057 b->read_pos += chunk_size; 1058 buffer += chunk_size; 1059 read += chunk_size; 1060 left -= chunk_size; 1061 } 1062 1063 *len = read; 1064 return SVN_NO_ERROR; 1065} 1066 1067svn_stream_t * 1068svn_txdelta_to_svndiff_stream(svn_txdelta_stream_t *txstream, 1069 int svndiff_version, 1070 int compression_level, 1071 apr_pool_t *pool) 1072{ 1073 svndiff_stream_baton_t *baton; 1074 svn_stream_t *push_stream; 1075 svn_stream_t *pull_stream; 1076 1077 baton = apr_pcalloc(pool, sizeof(*baton)); 1078 baton->scratch_pool = svn_pool_create(pool); 1079 baton->txstream = txstream; 1080 baton->window_buffer = svn_stringbuf_create_empty(pool); 1081 baton->hit_eof = FALSE; 1082 baton->read_pos = 0; 1083 1084 push_stream = svn_stream_create(baton, pool); 1085 svn_stream_set_write(push_stream, svndiff_stream_write_fn); 1086 1087 /* We rely on the implementation detail of the svn_txdelta_to_svndiff3() 1088 function, namely, on how the window_handler() function behaves. 1089 As long as it writes one svndiff window at a time to the target 1090 stream, the memory usage of this function (in other words, how 1091 much data can be accumulated in the internal 'window_buffer') 1092 is limited. */ 1093 svn_txdelta_to_svndiff3(&baton->handler, &baton->handler_baton, 1094 push_stream, svndiff_version, 1095 compression_level, pool); 1096 1097 pull_stream = svn_stream_create(baton, pool); 1098 svn_stream_set_read2(pull_stream, NULL, svndiff_stream_read_fn); 1099 1100 return pull_stream; 1101} 1102