1289177Speter/* 2289177Speter * binary_diff.c: handling of git like binary diffs 3289177Speter * 4289177Speter * ==================================================================== 5289177Speter * Licensed to the Apache Software Foundation (ASF) under one 6289177Speter * or more contributor license agreements. See the NOTICE file 7289177Speter * distributed with this work for additional information 8289177Speter * regarding copyright ownership. The ASF licenses this file 9289177Speter * to you under the Apache License, Version 2.0 (the 10289177Speter * "License"); you may not use this file except in compliance 11289177Speter * with the License. You may obtain a copy of the License at 12289177Speter * 13289177Speter * http://www.apache.org/licenses/LICENSE-2.0 14289177Speter * 15289177Speter * Unless required by applicable law or agreed to in writing, 16289177Speter * software distributed under the License is distributed on an 17289177Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18289177Speter * KIND, either express or implied. See the License for the 19289177Speter * specific language governing permissions and limitations 20289177Speter * under the License. 21289177Speter * ==================================================================== 22289177Speter */ 23289177Speter 24289177Speter#include <apr.h> 25289177Speter 26289177Speter#include "svn_pools.h" 27289177Speter#include "svn_error.h" 28289177Speter#include "svn_diff.h" 29289177Speter#include "svn_types.h" 30289177Speter 31289177Speter/* Copies the data from ORIGINAL_STREAM to a temporary file, returning both 32289177Speter the original and compressed size. */ 33289177Speterstatic svn_error_t * 34289177Spetercreate_compressed(apr_file_t **result, 35289177Speter svn_filesize_t *full_size, 36289177Speter svn_filesize_t *compressed_size, 37289177Speter svn_stream_t *original_stream, 38289177Speter svn_cancel_func_t cancel_func, 39289177Speter void *cancel_baton, 40289177Speter apr_pool_t *result_pool, 41289177Speter apr_pool_t *scratch_pool) 42289177Speter{ 43289177Speter svn_stream_t *compressed; 44289177Speter svn_filesize_t bytes_read = 0; 45289177Speter apr_finfo_t finfo; 46289177Speter apr_size_t rd; 47289177Speter 48289177Speter SVN_ERR(svn_io_open_uniquely_named(result, NULL, NULL, "diffgz", 49289177Speter NULL, svn_io_file_del_on_pool_cleanup, 50289177Speter result_pool, scratch_pool)); 51289177Speter 52289177Speter compressed = svn_stream_compressed( 53289177Speter svn_stream_from_aprfile2(*result, TRUE, scratch_pool), 54289177Speter scratch_pool); 55289177Speter 56289177Speter if (original_stream) 57289177Speter do 58289177Speter { 59289177Speter char buffer[SVN_STREAM_CHUNK_SIZE]; 60289177Speter rd = sizeof(buffer); 61289177Speter 62289177Speter if (cancel_func) 63289177Speter SVN_ERR(cancel_func(cancel_baton)); 64289177Speter 65289177Speter SVN_ERR(svn_stream_read_full(original_stream, buffer, &rd)); 66289177Speter 67289177Speter bytes_read += rd; 68289177Speter SVN_ERR(svn_stream_write(compressed, buffer, &rd)); 69289177Speter } 70289177Speter while(rd == SVN_STREAM_CHUNK_SIZE); 71289177Speter else 72289177Speter { 73289177Speter apr_size_t zero = 0; 74289177Speter SVN_ERR(svn_stream_write(compressed, NULL, &zero)); 75289177Speter } 76289177Speter 77289177Speter SVN_ERR(svn_stream_close(compressed)); /* Flush compression */ 78289177Speter 79289177Speter *full_size = bytes_read; 80289177Speter SVN_ERR(svn_io_file_info_get(&finfo, APR_FINFO_SIZE, *result, scratch_pool)); 81289177Speter *compressed_size = finfo.size; 82289177Speter 83289177Speter return SVN_NO_ERROR; 84289177Speter} 85289177Speter 86289177Speter#define GIT_BASE85_CHUNKSIZE 52 87289177Speter 88289177Speter/* Git Base-85 table for write_literal */ 89289177Speterstatic const char b85str[] = 90289177Speter "0123456789" 91289177Speter "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 92289177Speter "abcdefghijklmnopqrstuvwxyz" 93289177Speter "!#$%&()*+-;<=>?@^_`{|}~"; 94289177Speter 95289177Speter/* Git length encoding table for write_literal */ 96289177Speterstatic const char b85lenstr[] = 97289177Speter "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 98289177Speter "abcdefghijklmnopqrstuvwxyz"; 99289177Speter 100289177Speter/* Writes out a git-like literal output of the compressed data in 101289177Speter COMPRESSED_DATA to OUTPUT_STREAM, describing that its normal length is 102289177Speter UNCOMPRESSED_SIZE. */ 103289177Speterstatic svn_error_t * 104289177Speterwrite_literal(svn_filesize_t uncompressed_size, 105289177Speter svn_stream_t *compressed_data, 106289177Speter svn_stream_t *output_stream, 107289177Speter svn_cancel_func_t cancel_func, 108289177Speter void *cancel_baton, 109289177Speter apr_pool_t *scratch_pool) 110289177Speter{ 111289177Speter apr_size_t rd; 112289177Speter SVN_ERR(svn_stream_seek(compressed_data, NULL)); /* Seek to start */ 113289177Speter 114289177Speter SVN_ERR(svn_stream_printf(output_stream, scratch_pool, 115289177Speter "literal %" SVN_FILESIZE_T_FMT APR_EOL_STR, 116289177Speter uncompressed_size)); 117289177Speter 118289177Speter do 119289177Speter { 120289177Speter char chunk[GIT_BASE85_CHUNKSIZE]; 121289177Speter const unsigned char *next; 122289177Speter apr_size_t left; 123289177Speter 124289177Speter rd = sizeof(chunk); 125289177Speter 126289177Speter if (cancel_func) 127289177Speter SVN_ERR(cancel_func(cancel_baton)); 128289177Speter 129289177Speter SVN_ERR(svn_stream_read_full(compressed_data, chunk, &rd)); 130289177Speter 131289177Speter { 132289177Speter apr_size_t one = 1; 133289177Speter SVN_ERR(svn_stream_write(output_stream, &b85lenstr[rd-1], &one)); 134289177Speter } 135289177Speter 136289177Speter left = rd; 137289177Speter next = (void*)chunk; 138289177Speter while (left) 139289177Speter { 140289177Speter char five[5]; 141289177Speter unsigned info = 0; 142289177Speter int n; 143289177Speter apr_size_t five_sz; 144289177Speter 145289177Speter /* Push 4 bytes into the 32 bit info, when available */ 146289177Speter for (n = 24; n >= 0 && left; n -= 8, next++, left--) 147289177Speter { 148289177Speter info |= (*next) << n; 149289177Speter } 150289177Speter 151289177Speter /* Write out info as base85 */ 152289177Speter for (n = 4; n >= 0; n--) 153289177Speter { 154289177Speter five[n] = b85str[info % 85]; 155289177Speter info /= 85; 156289177Speter } 157289177Speter 158289177Speter five_sz = 5; 159289177Speter SVN_ERR(svn_stream_write(output_stream, five, &five_sz)); 160289177Speter } 161289177Speter 162289177Speter SVN_ERR(svn_stream_puts(output_stream, APR_EOL_STR)); 163289177Speter } 164289177Speter while (rd == GIT_BASE85_CHUNKSIZE); 165289177Speter 166289177Speter return SVN_NO_ERROR; 167289177Speter} 168289177Speter 169289177Spetersvn_error_t * 170289177Spetersvn_diff_output_binary(svn_stream_t *output_stream, 171289177Speter svn_stream_t *original, 172289177Speter svn_stream_t *latest, 173289177Speter svn_cancel_func_t cancel_func, 174289177Speter void *cancel_baton, 175289177Speter apr_pool_t *scratch_pool) 176289177Speter{ 177289177Speter apr_file_t *original_apr; 178289177Speter svn_filesize_t original_full; 179289177Speter svn_filesize_t original_deflated; 180289177Speter apr_file_t *latest_apr; 181289177Speter svn_filesize_t latest_full; 182289177Speter svn_filesize_t latest_deflated; 183289177Speter apr_pool_t *subpool = svn_pool_create(scratch_pool); 184289177Speter 185289177Speter SVN_ERR(create_compressed(&original_apr, &original_full, &original_deflated, 186289177Speter original, cancel_func, cancel_baton, 187289177Speter scratch_pool, subpool)); 188289177Speter svn_pool_clear(subpool); 189289177Speter 190289177Speter SVN_ERR(create_compressed(&latest_apr, &latest_full, &latest_deflated, 191289177Speter latest, cancel_func, cancel_baton, 192289177Speter scratch_pool, subpool)); 193289177Speter svn_pool_clear(subpool); 194289177Speter 195289177Speter SVN_ERR(svn_stream_puts(output_stream, "GIT binary patch" APR_EOL_STR)); 196289177Speter 197299742Sdim /* ### git would first calculate if a git-delta latest->original would be 198289177Speter shorter than the zipped data. For now lets assume that it is not 199289177Speter and just dump the literal data */ 200299742Sdim SVN_ERR(write_literal(latest_full, 201299742Sdim svn_stream_from_aprfile2(latest_apr, FALSE, subpool), 202289177Speter output_stream, 203289177Speter cancel_func, cancel_baton, 204289177Speter scratch_pool)); 205289177Speter svn_pool_clear(subpool); 206289177Speter SVN_ERR(svn_stream_puts(output_stream, APR_EOL_STR)); 207289177Speter 208299742Sdim /* ### git would first calculate if a git-delta original->latest would be 209289177Speter shorter than the zipped data. For now lets assume that it is not 210289177Speter and just dump the literal data */ 211299742Sdim SVN_ERR(write_literal(original_full, 212299742Sdim svn_stream_from_aprfile2(original_apr, FALSE, subpool), 213289177Speter output_stream, 214289177Speter cancel_func, cancel_baton, 215289177Speter scratch_pool)); 216289177Speter svn_pool_destroy(subpool); 217289177Speter 218289177Speter SVN_ERR(svn_stream_puts(output_stream, APR_EOL_STR)); 219289177Speter 220289177Speter return SVN_NO_ERROR; 221289177Speter} 222