2 * binary_diff.c: handling of git like binary diffs
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
26 #include "svn_pools.h"
27 #include "svn_error.h"
29 #include "svn_types.h"
31 /* Copies the data from ORIGINAL_STREAM to a temporary file, returning both
32 the original and compressed size. */
34 create_compressed(apr_file_t **result,
35 svn_filesize_t *full_size,
36 svn_filesize_t *compressed_size,
37 svn_stream_t *original_stream,
38 svn_cancel_func_t cancel_func,
40 apr_pool_t *result_pool,
41 apr_pool_t *scratch_pool)
43 svn_stream_t *compressed;
44 svn_filesize_t bytes_read = 0;
48 SVN_ERR(svn_io_open_uniquely_named(result, NULL, NULL, "diffgz",
49 NULL, svn_io_file_del_on_pool_cleanup,
50 result_pool, scratch_pool));
52 compressed = svn_stream_compressed(
53 svn_stream_from_aprfile2(*result, TRUE, scratch_pool),
59 char buffer[SVN_STREAM_CHUNK_SIZE];
63 SVN_ERR(cancel_func(cancel_baton));
65 SVN_ERR(svn_stream_read_full(original_stream, buffer, &rd));
68 SVN_ERR(svn_stream_write(compressed, buffer, &rd));
70 while(rd == SVN_STREAM_CHUNK_SIZE);
74 SVN_ERR(svn_stream_write(compressed, NULL, &zero));
77 SVN_ERR(svn_stream_close(compressed)); /* Flush compression */
79 *full_size = bytes_read;
80 SVN_ERR(svn_io_file_info_get(&finfo, APR_FINFO_SIZE, *result, scratch_pool));
81 *compressed_size = finfo.size;
86 #define GIT_BASE85_CHUNKSIZE 52
88 /* Git Base-85 table for write_literal */
89 static const char b85str[] =
91 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
92 "abcdefghijklmnopqrstuvwxyz"
93 "!#$%&()*+-;<=>?@^_`{|}~";
95 /* Git length encoding table for write_literal */
96 static const char b85lenstr[] =
97 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
98 "abcdefghijklmnopqrstuvwxyz";
100 /* Writes out a git-like literal output of the compressed data in
101 COMPRESSED_DATA to OUTPUT_STREAM, describing that its normal length is
102 UNCOMPRESSED_SIZE. */
104 write_literal(svn_filesize_t uncompressed_size,
105 svn_stream_t *compressed_data,
106 svn_stream_t *output_stream,
107 svn_cancel_func_t cancel_func,
109 apr_pool_t *scratch_pool)
112 SVN_ERR(svn_stream_seek(compressed_data, NULL)); /* Seek to start */
114 SVN_ERR(svn_stream_printf(output_stream, scratch_pool,
115 "literal %" SVN_FILESIZE_T_FMT APR_EOL_STR,
120 char chunk[GIT_BASE85_CHUNKSIZE];
121 const unsigned char *next;
127 SVN_ERR(cancel_func(cancel_baton));
129 SVN_ERR(svn_stream_read_full(compressed_data, chunk, &rd));
133 SVN_ERR(svn_stream_write(output_stream, &b85lenstr[rd-1], &one));
145 /* Push 4 bytes into the 32 bit info, when available */
146 for (n = 24; n >= 0 && left; n -= 8, next++, left--)
148 info |= (*next) << n;
151 /* Write out info as base85 */
152 for (n = 4; n >= 0; n--)
154 five[n] = b85str[info % 85];
159 SVN_ERR(svn_stream_write(output_stream, five, &five_sz));
162 SVN_ERR(svn_stream_puts(output_stream, APR_EOL_STR));
164 while (rd == GIT_BASE85_CHUNKSIZE);
170 svn_diff_output_binary(svn_stream_t *output_stream,
171 svn_stream_t *original,
172 svn_stream_t *latest,
173 svn_cancel_func_t cancel_func,
175 apr_pool_t *scratch_pool)
177 apr_file_t *original_apr;
178 svn_filesize_t original_full;
179 svn_filesize_t original_deflated;
180 apr_file_t *latest_apr;
181 svn_filesize_t latest_full;
182 svn_filesize_t latest_deflated;
183 apr_pool_t *subpool = svn_pool_create(scratch_pool);
185 SVN_ERR(create_compressed(&original_apr, &original_full, &original_deflated,
186 original, cancel_func, cancel_baton,
187 scratch_pool, subpool));
188 svn_pool_clear(subpool);
190 SVN_ERR(create_compressed(&latest_apr, &latest_full, &latest_deflated,
191 latest, cancel_func, cancel_baton,
192 scratch_pool, subpool));
193 svn_pool_clear(subpool);
195 SVN_ERR(svn_stream_puts(output_stream, "GIT binary patch" APR_EOL_STR));
197 /* ### git would first calculate if a git-delta original->latest would be
198 shorter than the zipped data. For now lets assume that it is not
199 and just dump the literal data */
200 SVN_ERR(write_literal(original_full,
201 svn_stream_from_aprfile2(original_apr, FALSE, subpool),
203 cancel_func, cancel_baton,
205 svn_pool_clear(subpool);
206 SVN_ERR(svn_stream_puts(output_stream, APR_EOL_STR));
208 /* ### git would first calculate if a git-delta latest->original would be
209 shorter than the zipped data. For now lets assume that it is not
210 and just dump the literal data */
211 SVN_ERR(write_literal(latest_full,
212 svn_stream_from_aprfile2(latest_apr, FALSE, subpool),
214 cancel_func, cancel_baton,
216 svn_pool_destroy(subpool);
218 SVN_ERR(svn_stream_puts(output_stream, APR_EOL_STR));