GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/compression.cc
Date: 2024-04-21 02:33:16
Exec Total Coverage
Lines: 412 530 77.7%
Branches: 221 448 49.3%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 *
4 * This is a wrapper around zlib. It provides
5 * a set of functions to conveniently compress and decompress stuff.
6 * Almost all of the functions return true on success, otherwise false.
7 *
8 * TODO: think about code deduplication
9 */
10
11 #include "cvmfs_config.h"
12 #include "compression.h"
13
14 #include <alloca.h>
15 #include <stdlib.h>
16 #include <sys/stat.h>
17
18 #include <algorithm>
19 #include <cassert>
20 #include <cstring>
21
22 #include "crypto/hash.h"
23 #include "util/exception.h"
24 #include "util/logging.h"
25 #include "util/platform.h"
26 #include "util/posix.h"
27 #include "util/smalloc.h"
28
29 using namespace std; // NOLINT
30
31
32 8 static bool CopyFile2File(FILE *fsrc, FILE *fdest) {
33 unsigned char buf[1024];
34
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 rewind(fsrc);
35
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 rewind(fdest);
36
37 size_t have;
38 do {
39
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
23 have = fread(buf, 1, 1024, fsrc);
40
2/4
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
23 if (fwrite(buf, 1, have, fdest) != have)
41 return false;
42
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 8 times.
23 } while (have == 1024);
43 8 return true;
44 }
45
46 3 bool CopyPath2File(const std::string &src, FILE *fdest) {
47 3 int retval = -1;
48 platform_stat64 info;
49
50
1/2
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 FILE *fsrc = fopen(src.c_str(), "r");
51
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (!fsrc) goto file_copy_final;
52
53
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
3 if (!CopyFile2File(fsrc, fdest)) goto file_copy_final;
54 3 retval = platform_fstat(fileno(fsrc), &info);
55 3 retval |= fchmod(fileno(fdest), info.st_mode);
56
57 3 file_copy_final:
58
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
3 if (fsrc) fclose(fsrc);
59 3 return retval == 0;
60 }
61
62
63 5 bool CopyPath2Path(const string &src, const string &dest) {
64 5 FILE *fsrc = NULL;
65 5 FILE *fdest = NULL;
66 5 int retval = -1;
67 platform_stat64 info;
68
69
1/2
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 fsrc = fopen(src.c_str(), "r");
70
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (!fsrc) goto file_copy_final;
71
72
1/2
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 fdest = fopen(dest.c_str(), "w");
73
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (!fdest) goto file_copy_final;
74
75
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
5 if (!CopyFile2File(fsrc, fdest)) goto file_copy_final;
76 5 retval = platform_fstat(fileno(fsrc), &info);
77 5 retval |= fchmod(fileno(fdest), info.st_mode);
78
79 5 file_copy_final:
80
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
5 if (fsrc) fclose(fsrc);
81
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
5 if (fdest) fclose(fdest);
82 5 return retval == 0;
83 }
84
85
86 bool CopyMem2File(const unsigned char *buffer, const unsigned buffer_size,
87 FILE *fdest)
88 {
89 int written = fwrite(buffer, 1, buffer_size, fdest);
90 return (written >=0) && (unsigned(written) == buffer_size);
91 }
92
93
94 49 bool CopyMem2Path(const unsigned char *buffer, const unsigned buffer_size,
95 const string &path)
96 {
97 49 int fd = open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, kDefaultFileMode);
98
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 49 times.
49 if (fd < 0)
99 return false;
100
101 49 int written = write(fd, buffer, buffer_size);
102 49 close(fd);
103
104
2/4
✓ Branch 0 taken 49 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 49 times.
✗ Branch 3 not taken.
49 return (written >=0) && (unsigned(written) == buffer_size);
105 }
106
107
108 1 bool CopyPath2Mem(const string &path,
109 unsigned char **buffer, unsigned *buffer_size)
110 {
111 1 const int fd = open(path.c_str(), O_RDONLY);
112
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (fd < 0)
113 return false;
114
115 1 *buffer_size = 512;
116 1 *buffer = reinterpret_cast<unsigned char *>(smalloc(*buffer_size));
117 1 unsigned total_bytes = 0;
118 while (true) {
119 2 int num_bytes = read(fd, *buffer + total_bytes, *buffer_size - total_bytes);
120
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (num_bytes == 0)
121 1 break;
122
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (num_bytes < 0) {
123 close(fd);
124 free(*buffer);
125 *buffer_size = 0;
126 return false;
127 }
128 1 total_bytes += num_bytes;
129
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (total_bytes >= *buffer_size) {
130 *buffer_size *= 2;
131 *buffer =
132 reinterpret_cast<unsigned char *>(srealloc(*buffer, *buffer_size));
133 }
134 1 }
135
136 1 close(fd);
137 1 *buffer_size = total_bytes;
138 1 return true;
139 }
140
141 namespace zlib {
142
143 const unsigned kBufferSize = 32768;
144
145 /**
146 * Aborts if string doesn't match any of the algorithms.
147 */
148 Algorithms ParseCompressionAlgorithm(const std::string &algorithm_option) {
149 if ((algorithm_option == "default") || (algorithm_option == "zlib"))
150 return kZlibDefault;
151 if (algorithm_option == "none")
152 return kNoCompression;
153 PANIC(kLogStderr, "unknown compression algorithms: %s",
154 algorithm_option.c_str());
155 }
156
157
158 std::string AlgorithmName(const zlib::Algorithms alg) {
159 switch (alg) {
160 case kZlibDefault:
161 return "zlib";
162 break;
163 case kNoCompression:
164 return "none";
165 break;
166 // Purposely did not add a 'default' statement here: this will
167 // cause the compiler to generate a warning if a new algorithm
168 // is added but this function is not updated.
169 }
170 return "unknown";
171 }
172
173
174 227 void CompressInit(z_stream *strm) {
175 227 strm->zalloc = Z_NULL;
176 227 strm->zfree = Z_NULL;
177 227 strm->opaque = Z_NULL;
178 227 strm->next_in = Z_NULL;
179 227 strm->avail_in = 0;
180 227 int retval = deflateInit(strm, Z_DEFAULT_COMPRESSION);
181
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 227 times.
227 assert(retval == 0);
182 227 }
183
184
185 236 void DecompressInit(z_stream *strm) {
186 236 strm->zalloc = Z_NULL;
187 236 strm->zfree = Z_NULL;
188 236 strm->opaque = Z_NULL;
189 236 strm->avail_in = 0;
190 236 strm->next_in = Z_NULL;
191 236 int retval = inflateInit(strm);
192
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 236 times.
236 assert(retval == 0);
193 236 }
194
195
196 227 void CompressFini(z_stream *strm) {
197 227 (void)deflateEnd(strm);
198 227 }
199
200
201 230 void DecompressFini(z_stream *strm) {
202 230 (void)inflateEnd(strm);
203 230 }
204
205
206 4 StreamStates CompressZStream2Null(
207 const void *buf,
208 const int64_t size,
209 const bool eof,
210 z_stream *strm,
211 shash::ContextPtr *hash_context)
212 {
213 unsigned char out[kZChunk];
214 int z_ret;
215
216 4 strm->avail_in = size;
217 4 strm->next_in = static_cast<unsigned char *>(const_cast<void *>(buf));
218 // Run deflate() on input until output buffer not full, finish
219 // compression if all of source has been read in
220 do {
221 4 strm->avail_out = kZChunk;
222 4 strm->next_out = out;
223
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
4 z_ret = deflate(strm, eof ? Z_FINISH : Z_NO_FLUSH); // no bad return value
224
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (z_ret == Z_STREAM_ERROR)
225 return kStreamDataError;
226 4 size_t have = kZChunk - strm->avail_out;
227
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 shash::Update(out, have, *hash_context);
228
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 } while (strm->avail_out == 0);
229
230
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 return (z_ret == Z_STREAM_END ? kStreamEnd : kStreamContinue);
231 }
232
233
234 124 StreamStates DecompressZStream2Sink(
235 const void *buf,
236 const int64_t size,
237 z_stream *strm,
238 cvmfs::Sink *sink)
239 {
240 unsigned char out[kZChunk];
241 int z_ret;
242 124 int64_t pos = 0;
243
244 do {
245 124 strm->avail_in = (kZChunk > (size-pos)) ? size-pos : kZChunk;
246 124 strm->next_in = ((unsigned char *)buf)+pos;
247
248 // Run inflate() on input until output buffer not full
249 do {
250 201 strm->avail_out = kZChunk;
251 201 strm->next_out = out;
252
1/2
✓ Branch 1 taken 201 times.
✗ Branch 2 not taken.
201 z_ret = inflate(strm, Z_NO_FLUSH);
253
1/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 201 times.
201 switch (z_ret) {
254 case Z_NEED_DICT:
255 z_ret = Z_DATA_ERROR; // and fall through
256 case Z_STREAM_ERROR:
257 case Z_DATA_ERROR:
258 return kStreamDataError;
259 case Z_MEM_ERROR:
260 return kStreamIOError;
261 }
262 201 size_t have = kZChunk - strm->avail_out;
263
1/2
✓ Branch 1 taken 201 times.
✗ Branch 2 not taken.
201 int64_t written = sink->Write(out, have);
264
2/4
✓ Branch 0 taken 201 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 201 times.
201 if ((written < 0) || (static_cast<uint64_t>(written) != have))
265 return kStreamIOError;
266
2/2
✓ Branch 0 taken 77 times.
✓ Branch 1 taken 124 times.
201 } while (strm->avail_out == 0);
267
268 124 pos += kZChunk;
269
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 124 times.
124 } while (pos < size);
270
271
2/2
✓ Branch 0 taken 119 times.
✓ Branch 1 taken 5 times.
124 return (z_ret == Z_STREAM_END ? kStreamEnd : kStreamContinue);
272 }
273
274
275 8 StreamStates DecompressZStream2File(
276 const void *buf,
277 const int64_t size,
278 z_stream *strm,
279 FILE *f)
280 {
281 unsigned char out[kZChunk];
282 int z_ret;
283 8 int64_t pos = 0;
284
285 do {
286 8 strm->avail_in = (kZChunk > (size-pos)) ? size-pos : kZChunk;
287 8 strm->next_in = ((unsigned char *)buf)+pos;
288
289 // Run inflate() on input until output buffer not full
290 do {
291 11 strm->avail_out = kZChunk;
292 11 strm->next_out = out;
293
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 z_ret = inflate(strm, Z_NO_FLUSH);
294
1/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
11 switch (z_ret) {
295 case Z_NEED_DICT:
296 z_ret = Z_DATA_ERROR; // and fall through
297 case Z_STREAM_ERROR:
298 case Z_DATA_ERROR:
299 return kStreamDataError;
300 case Z_MEM_ERROR:
301 return kStreamIOError;
302 }
303 11 size_t have = kZChunk - strm->avail_out;
304
4/9
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 11 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 11 times.
11 if (fwrite(out, 1, have, f) != have || ferror(f)) {
305 LogCvmfs(kLogCompress, kLogDebug, "Inflate to file failed with %s "
306 "(errno=%d)", strerror(errno), errno);
307 return kStreamIOError;
308 }
309
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 8 times.
11 } while (strm->avail_out == 0);
310
311 8 pos += kZChunk;
312
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 } while (pos < size);
313
314
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 return (z_ret == Z_STREAM_END ? kStreamEnd : kStreamContinue);
315 }
316
317
318 44 bool CompressPath2Path(const string &src, const string &dest) {
319 44 FILE *fsrc = fopen(src.c_str(), "r");
320
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
44 if (!fsrc) {
321 LogCvmfs(kLogCompress, kLogDebug, "open %s as compression source failed",
322 src.c_str());
323 return false;
324 }
325
326 44 FILE *fdest = fopen(dest.c_str(), "w");
327
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
44 if (!fdest) {
328 LogCvmfs(kLogCompress, kLogDebug, "open %s as compression destination "
329 "failed with errno=%d", dest.c_str(), errno);
330 fclose(fsrc);
331 return false;
332 }
333
334 44 LogCvmfs(kLogCompress, kLogDebug, "opened %s and %s for compression",
335 src.c_str(), dest.c_str());
336 44 const bool result = CompressFile2File(fsrc, fdest);
337
338 44 fclose(fsrc);
339 44 fclose(fdest);
340 44 return result;
341 }
342
343
344 102 bool CompressPath2Path(const string &src, const string &dest,
345 shash::Any *compressed_hash)
346 {
347
1/2
✓ Branch 2 taken 102 times.
✗ Branch 3 not taken.
102 FILE *fsrc = fopen(src.c_str(), "r");
348
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
102 if (!fsrc) {
349 LogCvmfs(kLogCompress, kLogDebug, "open %s as compression source failed",
350 src.c_str());
351 return false;
352 }
353
354
1/2
✓ Branch 2 taken 102 times.
✗ Branch 3 not taken.
102 FILE *fdest = fopen(dest.c_str(), "w");
355
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
102 if (!fdest) {
356 LogCvmfs(kLogCompress, kLogDebug, "open %s as compression destination "
357 "failed with errno=%d", dest.c_str(), errno);
358 fclose(fsrc);
359 return false;
360 }
361
362
1/2
✓ Branch 3 taken 102 times.
✗ Branch 4 not taken.
102 LogCvmfs(kLogCompress, kLogDebug, "opened %s and %s for compression",
363 src.c_str(), dest.c_str());
364 102 bool result = false;
365
2/4
✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 102 times.
102 if (!CompressFile2File(fsrc, fdest, compressed_hash))
366 goto compress_path2path_final;
367 platform_stat64 info;
368
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 102 times.
102 if (platform_fstat(fileno(fsrc), &info) != 0) goto compress_path2path_final;
369 // TODO(jakob): open in the right mode from the beginning
370
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 102 times.
102 if (fchmod(fileno(fdest), info.st_mode) != 0) goto compress_path2path_final;
371
372 102 result = true;
373
374 102 compress_path2path_final:
375
1/2
✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
102 fclose(fsrc);
376
1/2
✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
102 fclose(fdest);
377 102 return result;
378 }
379
380
381 bool DecompressPath2Path(const string &src, const string &dest) {
382 FILE *fsrc = NULL;
383 FILE *fdest = NULL;
384 int result = false;
385
386 fsrc = fopen(src.c_str(), "r");
387 if (!fsrc) goto decompress_path2path_final;
388
389 fdest = fopen(dest.c_str(), "w");
390 if (!fdest) goto decompress_path2path_final;
391
392 result = DecompressFile2File(fsrc, fdest);
393
394 decompress_path2path_final:
395 if (fsrc) fclose(fsrc);
396 if (fdest) fclose(fdest);
397 return result;
398 }
399
400
401 44 bool CompressFile2Null(FILE *fsrc, shash::Any *compressed_hash) {
402 44 int z_ret = 0;
403 44 int flush = 0;
404 44 bool result = -1;
405 unsigned have;
406 z_stream strm;
407 unsigned char in[kZChunk];
408 unsigned char out[kZChunk];
409
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 shash::ContextPtr hash_context(compressed_hash->algorithm);
410
411
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 CompressInit(&strm);
412 44 hash_context.buffer = alloca(hash_context.size);
413
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 shash::Init(hash_context);
414
415 // Compress until end of file
416 do {
417
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 strm.avail_in = fread(in, 1, kZChunk, fsrc);
418
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 44 times.
44 if (ferror(fsrc)) goto compress_file2null_final;
419
420
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 flush = feof(fsrc) ? Z_FINISH : Z_NO_FLUSH;
421 44 strm.next_in = in;
422
423 // Run deflate() on input until output buffer not full, finish
424 // compression if all of source has been read in
425 do {
426 44 strm.avail_out = kZChunk;
427 44 strm.next_out = out;
428
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 z_ret = deflate(&strm, flush); // no bad return value
429
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
44 if (z_ret == Z_STREAM_ERROR)
430 goto compress_file2null_final; // state not clobbered
431 44 have = kZChunk - strm.avail_out;
432
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 shash::Update(out, have, hash_context);
433
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
44 } while (strm.avail_out == 0);
434
435 // Done when last data in file processed
436
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
44 } while (flush != Z_FINISH);
437
438 // stream will be complete
439
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
44 if (z_ret != Z_STREAM_END) goto compress_file2null_final;
440
441
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 shash::Final(hash_context, compressed_hash);
442 44 result = true;
443
444 // Clean up and return
445 44 compress_file2null_final:
446
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 CompressFini(&strm);
447
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 LogCvmfs(kLogCompress, kLogDebug, "file compression finished with result %d",
448 result);
449 44 return result;
450 }
451
452
453 2 bool CompressFd2Null(int fd_src, shash::Any *compressed_hash,
454 uint64_t *processed_bytes) {
455 2 int z_ret = 0;
456 2 int flush = 0;
457 2 bool result = false;
458 unsigned have;
459 z_stream strm;
460 unsigned char in[kZChunk];
461 unsigned char out[kZChunk];
462 2 off_t cksum_bytes = 0;
463
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 shash::ContextPtr hash_context(compressed_hash->algorithm);
464
465
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 CompressInit(&strm);
466 2 hash_context.buffer = alloca(hash_context.size);
467
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 shash::Init(hash_context);
468
469 // Compress until end of file
470 do {
471
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 ssize_t bytes_read = read(fd_src, in, kZChunk);
472
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (bytes_read < 0) {
473
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (errno == EINTR) {continue;}
474 1 goto compress_fd2null_final;
475 }
476 1 cksum_bytes += bytes_read;
477 1 strm.avail_in = bytes_read;
478
479
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 flush = (static_cast<size_t>(bytes_read) < kZChunk) ? Z_FINISH : Z_NO_FLUSH;
480 1 strm.next_in = in;
481
482 // Run deflate() on input until output buffer not full, finish
483 // compression if all of source has been read in
484 do {
485 1 strm.avail_out = kZChunk;
486 1 strm.next_out = out;
487
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 z_ret = deflate(&strm, flush); // no bad return value
488
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (z_ret == Z_STREAM_ERROR)
489 goto compress_fd2null_final; // state not clobbered
490 1 have = kZChunk - strm.avail_out;
491
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 shash::Update(out, have, hash_context);
492
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 } while (strm.avail_out == 0);
493
494 // Done when last data in file processed
495
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 } while (flush != Z_FINISH);
496
497 // stream will be complete
498
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (z_ret != Z_STREAM_END) goto compress_fd2null_final;
499
500
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 shash::Final(hash_context, compressed_hash);
501
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (processed_bytes) {
502 1 *processed_bytes = cksum_bytes;
503 }
504 1 result = true;
505
506 // Clean up and return
507 2 compress_fd2null_final:
508
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 CompressFini(&strm);
509
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 LogCvmfs(kLogCompress, kLogDebug, "file compression finished with result %d",
510 result);
511 2 return result;
512 }
513
514
515 44 bool CompressPath2Null(const string &src, shash::Any *compressed_hash) {
516 44 FILE *fsrc = fopen(src.c_str(), "r");
517
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
44 if (fsrc == NULL)
518 return false;
519 44 bool retval = CompressFile2Null(fsrc, compressed_hash);
520 44 fclose(fsrc);
521 44 return retval;
522 }
523
524
525 44 bool CompressFile2File(FILE *fsrc, FILE *fdest) {
526 44 int z_ret = 0;
527 44 int flush = 0;
528 44 bool result = false;
529 unsigned have;
530 z_stream strm;
531 unsigned char in[kZChunk];
532 unsigned char out[kZChunk];
533
534
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 CompressInit(&strm);
535
536 // Compress until end of file
537 do {
538
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 strm.avail_in = fread(in, 1, kZChunk, fsrc);
539
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 44 times.
44 if (ferror(fsrc)) goto compress_file2file_final;
540
541
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 flush = feof(fsrc) ? Z_FINISH : Z_NO_FLUSH;
542 44 strm.next_in = in;
543
544 // Run deflate() on input until output buffer not full, finish
545 // compression if all of source has been read in
546 do {
547 44 strm.avail_out = kZChunk;
548 44 strm.next_out = out;
549
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 z_ret = deflate(&strm, flush); // no bad return value
550
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
44 if (z_ret == Z_STREAM_ERROR)
551 goto compress_file2file_final; // state not clobbered
552 44 have = kZChunk - strm.avail_out;
553
4/9
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 44 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 44 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 44 times.
44 if (fwrite(out, 1, have, fdest) != have || ferror(fdest))
554 goto compress_file2file_final;
555
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
44 } while (strm.avail_out == 0);
556
557 // Done when last data in file processed
558
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
44 } while (flush != Z_FINISH);
559
560 // stream will be complete
561
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
44 if (z_ret != Z_STREAM_END) goto compress_file2file_final;
562
563 44 result = true;
564
565 // Clean up and return
566 44 compress_file2file_final:
567
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 CompressFini(&strm);
568
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 LogCvmfs(kLogCompress, kLogDebug, "file compression finished with result %d",
569 result);
570 44 return result;
571 }
572
573 bool CompressPath2File(const string &src, FILE *fdest,
574 shash::Any *compressed_hash)
575 {
576 FILE *fsrc = fopen(src.c_str(), "r");
577 if (!fsrc)
578 return false;
579
580 bool retval = CompressFile2File(fsrc, fdest, compressed_hash);
581 fclose(fsrc);
582 return retval;
583 }
584
585
586 102 bool CompressFile2File(FILE *fsrc, FILE *fdest, shash::Any *compressed_hash) {
587 102 int z_ret = 0;
588 102 int flush = 0;
589 102 bool result = false;
590 unsigned have;
591 z_stream strm;
592 unsigned char in[kZChunk];
593 unsigned char out[kZChunk];
594
1/2
✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
102 shash::ContextPtr hash_context(compressed_hash->algorithm);
595
596
1/2
✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
102 CompressInit(&strm);
597 102 hash_context.buffer = alloca(hash_context.size);
598
1/2
✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
102 shash::Init(hash_context);
599
600 // Compress until end of file
601 do {
602
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
144 strm.avail_in = fread(in, 1, kZChunk, fsrc);
603
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 144 times.
144 if (ferror(fsrc)) goto compress_file2file_hashed_final;
604
605
2/2
✓ Branch 1 taken 102 times.
✓ Branch 2 taken 42 times.
144 flush = feof(fsrc) ? Z_FINISH : Z_NO_FLUSH;
606 144 strm.next_in = in;
607
608 // Run deflate() on input until output buffer not full, finish
609 // compression if all of source has been read in
610 do {
611 144 strm.avail_out = kZChunk;
612 144 strm.next_out = out;
613
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
144 z_ret = deflate(&strm, flush); // no bad return value
614
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 144 times.
144 if (z_ret == Z_STREAM_ERROR)
615 goto compress_file2file_hashed_final; // state not clobbered
616 144 have = kZChunk - strm.avail_out;
617
4/9
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 144 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 144 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 144 times.
144 if (fwrite(out, 1, have, fdest) != have || ferror(fdest))
618 goto compress_file2file_hashed_final;
619
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
144 shash::Update(out, have, hash_context);
620
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 144 times.
144 } while (strm.avail_out == 0);
621
622 // Done when last data in file processed
623
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 102 times.
144 } while (flush != Z_FINISH);
624
625 // Stream will be complete
626
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
102 if (z_ret != Z_STREAM_END) goto compress_file2file_hashed_final;
627
628
1/2
✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
102 shash::Final(hash_context, compressed_hash);
629 102 result = true;
630
631 // Clean up and return
632 102 compress_file2file_hashed_final:
633
1/2
✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
102 CompressFini(&strm);
634
1/2
✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
102 LogCvmfs(kLogCompress, kLogDebug, "file compression finished with result %d",
635 result);
636 102 return result;
637 }
638
639
640 10 bool DecompressFile2File(FILE *fsrc, FILE *fdest) {
641 10 bool result = false;
642 10 StreamStates stream_state = kStreamIOError;
643 z_stream strm;
644 size_t have;
645 unsigned char buf[kBufferSize];
646
647
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 DecompressInit(&strm);
648
649
3/4
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 10 times.
18 while ((have = fread(buf, 1, kBufferSize, fsrc)) > 0) {
650
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 stream_state = DecompressZStream2File(buf, have, &strm, fdest);
651
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 if ((stream_state == kStreamDataError) || (stream_state == kStreamIOError))
652 goto decompress_file2file_final;
653 }
654
1/2
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 LogCvmfs(kLogCompress, kLogDebug, "end of decompression, state=%d, error=%d",
655 stream_state, ferror(fsrc));
656
5/6
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 8 times.
10 if ((stream_state != kStreamEnd) || ferror(fsrc))
657 2 goto decompress_file2file_final;
658
659 8 result = true;
660
661 10 decompress_file2file_final:
662
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 DecompressFini(&strm);
663 10 return result;
664 }
665
666
667 10 bool DecompressPath2File(const string &src, FILE *fdest) {
668 10 FILE *fsrc = fopen(src.c_str(), "r");
669
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (!fsrc)
670 return false;
671
672 10 bool retval = DecompressFile2File(fsrc, fdest);
673 10 fclose(fsrc);
674 10 return retval;
675 }
676
677
678 1 bool CompressMem2File(const unsigned char *buf, const size_t size,
679 FILE *fdest, shash::Any *compressed_hash) {
680 1 int z_ret = 0;
681 1 int flush = 0;
682 1 bool result = false;
683 unsigned have;
684 z_stream strm;
685 1 size_t offset = 0;
686 1 size_t used = 0;
687 unsigned char out[kZChunk];
688
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 shash::ContextPtr hash_context(compressed_hash->algorithm);
689
690
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 CompressInit(&strm);
691 1 hash_context.buffer = alloca(hash_context.size);
692
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 shash::Init(hash_context);
693
694 // Compress the given memory buffer
695 do {
696 5 used = min(static_cast<size_t>(kZChunk), size - offset);
697 5 strm.avail_in = used;
698
699
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
5 flush = (strm.avail_in < kZChunk) ? Z_FINISH : Z_NO_FLUSH;
700 5 strm.next_in = const_cast<unsigned char*>(buf + offset);
701
702 // Run deflate() on input until output buffer not full, finish
703 // compression if all of source has been read in
704 do {
705 8 strm.avail_out = kZChunk;
706 8 strm.next_out = out;
707
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 z_ret = deflate(&strm, flush); // no bad return value
708
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (z_ret == Z_STREAM_ERROR)
709 goto compress_file2file_hashed_final; // state not clobbered
710 8 have = kZChunk - strm.avail_out;
711
4/9
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 8 times.
8 if (fwrite(out, 1, have, fdest) != have || ferror(fdest))
712 goto compress_file2file_hashed_final;
713
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 shash::Update(out, have, hash_context);
714
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 5 times.
8 } while (strm.avail_out == 0);
715
716 5 offset += used;
717
718 // Done when last data in file processed
719
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
5 } while (flush != Z_FINISH);
720
721 // Stream will be complete
722
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (z_ret != Z_STREAM_END) goto compress_file2file_hashed_final;
723
724
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 shash::Final(hash_context, compressed_hash);
725 1 result = true;
726
727 // Clean up and return
728 1 compress_file2file_hashed_final:
729
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 CompressFini(&strm);
730
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 LogCvmfs(kLogCompress, kLogDebug, "file compression finished with result %d",
731 result);
732 1 return result;
733 }
734
735
736 /**
737 * User of this function has to free out_buf.
738 */
739 30 bool CompressMem2Mem(const void *buf, const int64_t size,
740 void **out_buf, uint64_t *out_size)
741 {
742 unsigned char out[kZChunk];
743 int z_ret;
744 int flush;
745 z_stream strm;
746 30 int64_t pos = 0;
747 30 uint64_t alloc_size = kZChunk;
748
749
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 CompressInit(&strm);
750 30 *out_buf = smalloc(alloc_size);
751 30 *out_size = 0;
752
753 do {
754 1053 strm.avail_in = (kZChunk > (size-pos)) ? size-pos : kZChunk;
755
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 1023 times.
1053 flush = (pos + kZChunk) >= size ? Z_FINISH : Z_NO_FLUSH;
756 1053 strm.next_in = ((unsigned char *)buf) + pos;
757
758 // Run deflate() on input until output buffer not full
759 do {
760 1069 strm.avail_out = kZChunk;
761 1069 strm.next_out = out;
762
1/2
✓ Branch 1 taken 1069 times.
✗ Branch 2 not taken.
1069 z_ret = deflate(&strm, flush);
763
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1069 times.
1069 if (z_ret == Z_STREAM_ERROR) {
764 CompressFini(&strm);
765 free(*out_buf);
766 *out_buf = NULL;
767 *out_size = 0;
768 return false;
769 }
770 1069 size_t have = kZChunk - strm.avail_out;
771
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1064 times.
1069 if (*out_size+have > alloc_size) {
772 5 alloc_size *= 2;
773 5 *out_buf = srealloc(*out_buf, alloc_size);
774 }
775 1069 memcpy(static_cast<unsigned char *>(*out_buf) + *out_size, out, have);
776 1069 *out_size += have;
777
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 1053 times.
1069 } while (strm.avail_out == 0);
778
779 1053 pos += kZChunk;
780
2/2
✓ Branch 0 taken 1023 times.
✓ Branch 1 taken 30 times.
1053 } while (flush != Z_FINISH);
781
782
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 CompressFini(&strm);
783
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 if (z_ret != Z_STREAM_END) {
784 free(*out_buf);
785 *out_buf = NULL;
786 *out_size = 0;
787 return false;
788 } else {
789 30 return true;
790 }
791 }
792
793
794 /**
795 * User of this function has to free out_buf.
796 */
797 91 bool DecompressMem2Mem(const void *buf, const int64_t size,
798 void **out_buf, uint64_t *out_size)
799 {
800 unsigned char out[kZChunk];
801 int z_ret;
802 z_stream strm;
803 91 int64_t pos = 0;
804 91 uint64_t alloc_size = kZChunk;
805
806
1/2
✓ Branch 1 taken 91 times.
✗ Branch 2 not taken.
91 DecompressInit(&strm);
807 91 *out_buf = smalloc(alloc_size);
808 91 *out_size = 0;
809
810 do {
811 92 strm.avail_in = (kZChunk > (size-pos)) ? size-pos : kZChunk;
812 92 strm.next_in = ((unsigned char *)buf)+pos;
813
814 // Run inflate() on input until output buffer not full
815 do {
816 1371 strm.avail_out = kZChunk;
817 1371 strm.next_out = out;
818
1/2
✓ Branch 1 taken 1371 times.
✗ Branch 2 not taken.
1371 z_ret = inflate(&strm, Z_NO_FLUSH);
819
1/3
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 1371 times.
1371 switch (z_ret) {
820 case Z_NEED_DICT:
821 z_ret = Z_DATA_ERROR; // and fall through
822 case Z_STREAM_ERROR:
823 case Z_DATA_ERROR:
824 case Z_MEM_ERROR:
825 DecompressFini(&strm);
826 free(*out_buf);
827 *out_buf = NULL;
828 *out_size = 0;
829 return false;
830 }
831 1371 size_t have = kZChunk - strm.avail_out;
832
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 1360 times.
1371 if (*out_size+have > alloc_size) {
833 11 alloc_size *= 2;
834 11 *out_buf = srealloc(*out_buf, alloc_size);
835 }
836 1371 memcpy(static_cast<unsigned char *>(*out_buf) + *out_size, out, have);
837 1371 *out_size += have;
838
2/2
✓ Branch 0 taken 1279 times.
✓ Branch 1 taken 92 times.
1371 } while (strm.avail_out == 0);
839
840 92 pos += kZChunk;
841
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 91 times.
92 } while (pos < size);
842
843
1/2
✓ Branch 1 taken 91 times.
✗ Branch 2 not taken.
91 DecompressFini(&strm);
844
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 91 times.
91 if (z_ret != Z_STREAM_END) {
845 free(*out_buf);
846 *out_buf = NULL;
847 *out_size = 0;
848 return false;
849 }
850
851 91 return true;
852 }
853
854
855 //------------------------------------------------------------------------------
856
857
858 1 void Compressor::RegisterPlugins() {
859 1 RegisterPlugin<ZlibCompressor>();
860 1 RegisterPlugin<EchoCompressor>();
861 1 }
862
863
864 //------------------------------------------------------------------------------
865
866
867 249737 bool ZlibCompressor::WillHandle(const zlib::Algorithms &alg) {
868 249737 return alg == kZlibDefault;
869 }
870
871
872 249717 ZlibCompressor::ZlibCompressor(const Algorithms &alg)
873 249717 : Compressor(alg)
874 {
875 249741 stream_.zalloc = Z_NULL;
876 249741 stream_.zfree = Z_NULL;
877 249741 stream_.opaque = Z_NULL;
878 249741 stream_.next_in = Z_NULL;
879 249741 stream_.avail_in = 0;
880
1/2
✓ Branch 1 taken 250446 times.
✗ Branch 2 not taken.
249741 const int zlib_retval = deflateInit(&stream_, Z_DEFAULT_COMPRESSION);
881
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 250446 times.
250446 assert(zlib_retval == 0);
882 250446 }
883
884
885 Compressor* ZlibCompressor::Clone() {
886 ZlibCompressor* other = new ZlibCompressor(zlib::kZlibDefault);
887 assert(stream_.avail_in == 0);
888 // Delete the other stream
889 int retcode = deflateEnd(&other->stream_);
890 assert(retcode == Z_OK);
891 retcode = deflateCopy(const_cast<z_streamp>(&other->stream_), &stream_);
892 assert(retcode == Z_OK);
893 return other;
894 }
895
896 649180 bool ZlibCompressor::Deflate(
897 const bool flush,
898 unsigned char **inbuf, size_t *inbufsize,
899 unsigned char **outbuf, size_t *outbufsize)
900 {
901 // Adding compression
902 649180 stream_.avail_in = *inbufsize;
903 649180 stream_.next_in = *inbuf;
904
2/2
✓ Branch 0 taken 250749 times.
✓ Branch 1 taken 398431 times.
649180 const int flush_int = (flush) ? Z_FINISH : Z_NO_FLUSH;
905 649180 int retcode = 0;
906
907 // TODO(jblomer) Figure out what exactly behaves differently with zlib 1.2.10
908 // if ((*inbufsize == 0) && !flush)
909 // return true;
910
911 649180 stream_.avail_out = *outbufsize;
912 649180 stream_.next_out = *outbuf;
913
914 // Deflate in zlib!
915 649180 retcode = deflate(&stream_, flush_int);
916
3/4
✓ Branch 0 taken 250423 times.
✓ Branch 1 taken 400108 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 250423 times.
650531 assert(retcode == Z_OK || retcode == Z_STREAM_END);
917
918 650531 *outbufsize -= stream_.avail_out;
919 650531 *inbuf = stream_.next_in;
920 650531 *inbufsize = stream_.avail_in;
921
922
3/4
✓ Branch 0 taken 399497 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 67565 times.
✓ Branch 3 taken 331932 times.
399491 return (flush_int == Z_NO_FLUSH && retcode == Z_OK && stream_.avail_in == 0)
923
6/6
✓ Branch 0 taken 399491 times.
✓ Branch 1 taken 251040 times.
✓ Branch 2 taken 251011 times.
✓ Branch 3 taken 67588 times.
✓ Branch 4 taken 250026 times.
✓ Branch 5 taken 985 times.
1050022 || (flush_int == Z_FINISH && retcode == Z_STREAM_END);
924 }
925
926
927 999650 ZlibCompressor::~ZlibCompressor() {
928 499794 int retcode = deflateEnd(&stream_);
929
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 250389 times.
500778 assert(retcode == Z_OK);
930 1000634 }
931
932
933 1 size_t ZlibCompressor::DeflateBound(const size_t bytes) {
934 // Call zlib's deflate bound
935 1 return deflateBound(&stream_, bytes);
936 }
937
938
939 //------------------------------------------------------------------------------
940
941
942 3 EchoCompressor::EchoCompressor(const zlib::Algorithms &alg):
943 3 Compressor(alg)
944 {
945 3 }
946
947
948 3 bool EchoCompressor::WillHandle(const zlib::Algorithms &alg) {
949 3 return alg == kNoCompression;
950 }
951
952
953 Compressor* EchoCompressor::Clone() {
954 return new EchoCompressor(zlib::kNoCompression);
955 }
956
957
958 209718 bool EchoCompressor::Deflate(
959 const bool flush,
960 unsigned char **inbuf, size_t *inbufsize,
961 unsigned char **outbuf, size_t *outbufsize)
962 {
963 209718 size_t bytes_to_copy = min(*outbufsize, *inbufsize);
964 209718 memcpy(*outbuf, *inbuf, bytes_to_copy);
965 209718 const bool done = (bytes_to_copy == *inbufsize);
966
967 // Update the return variables
968 209718 *inbuf += bytes_to_copy;
969 209718 *outbufsize = bytes_to_copy;
970 209718 *inbufsize -= bytes_to_copy;
971
972 209718 return done;
973 }
974
975
976 1 size_t EchoCompressor::DeflateBound(const size_t bytes) {
977 // zero bytes as an upper bound is no good because some callers want to
978 // allocate buffers according to this value
979
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 return (bytes == 0) ? 1 : bytes;
980 }
981
982 } // namespace zlib
983