gitrev.sh: search harder for the git dir
[dragonfly.git] / contrib / libarchive / libarchive / archive_write_set_compression_gzip.c
bloba8b1c33360de8ba0bf7f811b5920357c2a18d9ac
1 /*-
2 * Copyright (c) 2003-2007 Tim Kientzle
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #include "archive_platform.h"
28 __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_gzip.c,v 1.16 2008/02/21 03:21:50 kientzle Exp $");
30 #ifdef HAVE_ERRNO_H
31 #include <errno.h>
32 #endif
33 #ifdef HAVE_STDLIB_H
34 #include <stdlib.h>
35 #endif
36 #ifdef HAVE_STRING_H
37 #include <string.h>
38 #endif
39 #include <time.h>
40 #ifdef HAVE_ZLIB_H
41 #include <zlib.h>
42 #endif
44 #include "archive.h"
45 #include "archive_private.h"
46 #include "archive_write_private.h"
48 #ifndef HAVE_ZLIB_H
49 int
50 archive_write_set_compression_gzip(struct archive *a)
52 archive_set_error(a, ARCHIVE_ERRNO_MISC,
53 "gzip compression not supported on this platform");
54 return (ARCHIVE_FATAL);
56 #else
57 /* Don't compile this if we don't have zlib. */
59 struct private_data {
60 z_stream stream;
61 int64_t total_in;
62 unsigned char *compressed;
63 size_t compressed_buffer_size;
64 unsigned long crc;
67 struct private_config {
68 int compression_level;
73 * Yuck. zlib.h is not const-correct, so I need this one bit
74 * of ugly hackery to convert a const * pointer to a non-const pointer.
76 #define SET_NEXT_IN(st,src) \
77 (st)->stream.next_in = (Bytef *)(uintptr_t)(const void *)(src)
79 static int archive_compressor_gzip_finish(struct archive_write *);
80 static int archive_compressor_gzip_init(struct archive_write *);
81 static int archive_compressor_gzip_options(struct archive_write *,
82 const char *, const char *);
83 static int archive_compressor_gzip_write(struct archive_write *,
84 const void *, size_t);
85 static int drive_compressor(struct archive_write *, struct private_data *,
86 int finishing);
90 * Allocate, initialize and return a archive object.
92 int
93 archive_write_set_compression_gzip(struct archive *_a)
95 struct archive_write *a = (struct archive_write *)_a;
96 struct private_config *config;
97 __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
98 ARCHIVE_STATE_NEW, "archive_write_set_compression_gzip");
99 config = malloc(sizeof(*config));
100 if (config == NULL) {
101 archive_set_error(&a->archive, ENOMEM, "Out of memory");
102 return (ARCHIVE_FATAL);
104 a->compressor.config = config;
105 a->compressor.finish = &archive_compressor_gzip_finish;
106 config->compression_level = Z_DEFAULT_COMPRESSION;
107 a->compressor.init = &archive_compressor_gzip_init;
108 a->compressor.options = &archive_compressor_gzip_options;
109 a->archive.compression_code = ARCHIVE_COMPRESSION_GZIP;
110 a->archive.compression_name = "gzip";
111 return (ARCHIVE_OK);
115 * Setup callback.
117 static int
118 archive_compressor_gzip_init(struct archive_write *a)
120 int ret;
121 struct private_data *state;
122 struct private_config *config;
123 time_t t;
125 config = (struct private_config *)a->compressor.config;
127 if (a->client_opener != NULL) {
128 ret = (a->client_opener)(&a->archive, a->client_data);
129 if (ret != ARCHIVE_OK)
130 return (ret);
134 * The next check is a temporary workaround until the gzip
135 * code can be overhauled some. The code should not require
136 * that compressed_buffer_size == bytes_per_block. Removing
137 * this assumption will allow us to compress larger chunks at
138 * a time, which should improve overall performance
139 * marginally. As a minor side-effect, such a cleanup would
140 * allow us to support truly arbitrary block sizes.
142 if (a->bytes_per_block < 10) {
143 archive_set_error(&a->archive, EINVAL,
144 "GZip compressor requires a minimum 10 byte block size");
145 return (ARCHIVE_FATAL);
148 state = (struct private_data *)malloc(sizeof(*state));
149 if (state == NULL) {
150 archive_set_error(&a->archive, ENOMEM,
151 "Can't allocate data for compression");
152 return (ARCHIVE_FATAL);
154 memset(state, 0, sizeof(*state));
157 * See comment above. We should set compressed_buffer_size to
158 * max(bytes_per_block, 65536), but the code can't handle that yet.
160 state->compressed_buffer_size = a->bytes_per_block;
161 state->compressed = (unsigned char *)malloc(state->compressed_buffer_size);
162 state->crc = crc32(0L, NULL, 0);
164 if (state->compressed == NULL) {
165 archive_set_error(&a->archive, ENOMEM,
166 "Can't allocate data for compression buffer");
167 free(state);
168 return (ARCHIVE_FATAL);
171 state->stream.next_out = state->compressed;
172 state->stream.avail_out = state->compressed_buffer_size;
174 /* Prime output buffer with a gzip header. */
175 t = time(NULL);
176 state->compressed[0] = 0x1f; /* GZip signature bytes */
177 state->compressed[1] = 0x8b;
178 state->compressed[2] = 0x08; /* "Deflate" compression */
179 state->compressed[3] = 0; /* No options */
180 state->compressed[4] = (t)&0xff; /* Timestamp */
181 state->compressed[5] = (t>>8)&0xff;
182 state->compressed[6] = (t>>16)&0xff;
183 state->compressed[7] = (t>>24)&0xff;
184 state->compressed[8] = 0; /* No deflate options */
185 state->compressed[9] = 3; /* OS=Unix */
186 state->stream.next_out += 10;
187 state->stream.avail_out -= 10;
189 a->compressor.write = archive_compressor_gzip_write;
191 /* Initialize compression library. */
192 ret = deflateInit2(&(state->stream),
193 config->compression_level,
194 Z_DEFLATED,
195 -15 /* < 0 to suppress zlib header */,
197 Z_DEFAULT_STRATEGY);
199 if (ret == Z_OK) {
200 a->compressor.data = state;
201 return (0);
204 /* Library setup failed: clean up. */
205 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal error "
206 "initializing compression library");
207 free(state->compressed);
208 free(state);
210 /* Override the error message if we know what really went wrong. */
211 switch (ret) {
212 case Z_STREAM_ERROR:
213 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
214 "Internal error initializing "
215 "compression library: invalid setup parameter");
216 break;
217 case Z_MEM_ERROR:
218 archive_set_error(&a->archive, ENOMEM, "Internal error initializing "
219 "compression library");
220 break;
221 case Z_VERSION_ERROR:
222 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
223 "Internal error initializing "
224 "compression library: invalid library version");
225 break;
228 return (ARCHIVE_FATAL);
232 * Set write options.
234 static int
235 archive_compressor_gzip_options(struct archive_write *a, const char *key,
236 const char *value)
238 struct private_config *config;
240 config = (struct private_config *)a->compressor.config;
241 if (strcmp(key, "compression-level") == 0) {
242 if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
243 value[1] != '\0')
244 return (ARCHIVE_WARN);
245 config->compression_level = value[0] - '0';
246 return (ARCHIVE_OK);
249 return (ARCHIVE_WARN);
253 * Write data to the compressed stream.
255 static int
256 archive_compressor_gzip_write(struct archive_write *a, const void *buff,
257 size_t length)
259 struct private_data *state;
260 int ret;
262 state = (struct private_data *)a->compressor.data;
263 if (a->client_writer == NULL) {
264 archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
265 "No write callback is registered? "
266 "This is probably an internal programming error.");
267 return (ARCHIVE_FATAL);
270 /* Update statistics */
271 state->crc = crc32(state->crc, (const Bytef *)buff, length);
272 state->total_in += length;
274 /* Compress input data to output buffer */
275 SET_NEXT_IN(state, buff);
276 state->stream.avail_in = length;
277 if ((ret = drive_compressor(a, state, 0)) != ARCHIVE_OK)
278 return (ret);
280 a->archive.file_position += length;
281 return (ARCHIVE_OK);
285 * Finish the compression...
287 static int
288 archive_compressor_gzip_finish(struct archive_write *a)
290 ssize_t block_length, target_block_length, bytes_written;
291 int ret;
292 struct private_data *state;
293 unsigned tocopy;
294 unsigned char trailer[8];
296 state = (struct private_data *)a->compressor.data;
297 ret = 0;
298 if (state != NULL) {
299 if (a->client_writer == NULL) {
300 archive_set_error(&a->archive,
301 ARCHIVE_ERRNO_PROGRAMMER,
302 "No write callback is registered? "
303 "This is probably an internal programming error.");
304 ret = ARCHIVE_FATAL;
305 goto cleanup;
308 /* By default, always pad the uncompressed data. */
309 if (a->pad_uncompressed) {
310 tocopy = a->bytes_per_block -
311 (state->total_in % a->bytes_per_block);
312 while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) {
313 SET_NEXT_IN(state, a->nulls);
314 state->stream.avail_in = tocopy < a->null_length ?
315 tocopy : a->null_length;
316 state->crc = crc32(state->crc, a->nulls,
317 state->stream.avail_in);
318 state->total_in += state->stream.avail_in;
319 tocopy -= state->stream.avail_in;
320 ret = drive_compressor(a, state, 0);
321 if (ret != ARCHIVE_OK)
322 goto cleanup;
326 /* Finish compression cycle */
327 if (((ret = drive_compressor(a, state, 1))) != ARCHIVE_OK)
328 goto cleanup;
330 /* Build trailer: 4-byte CRC and 4-byte length. */
331 trailer[0] = (state->crc)&0xff;
332 trailer[1] = (state->crc >> 8)&0xff;
333 trailer[2] = (state->crc >> 16)&0xff;
334 trailer[3] = (state->crc >> 24)&0xff;
335 trailer[4] = (state->total_in)&0xff;
336 trailer[5] = (state->total_in >> 8)&0xff;
337 trailer[6] = (state->total_in >> 16)&0xff;
338 trailer[7] = (state->total_in >> 24)&0xff;
340 /* Add trailer to current block. */
341 tocopy = 8;
342 if (tocopy > state->stream.avail_out)
343 tocopy = state->stream.avail_out;
344 memcpy(state->stream.next_out, trailer, tocopy);
345 state->stream.next_out += tocopy;
346 state->stream.avail_out -= tocopy;
348 /* If it overflowed, flush and start a new block. */
349 if (tocopy < 8) {
350 bytes_written = (a->client_writer)(&a->archive, a->client_data,
351 state->compressed, state->compressed_buffer_size);
352 if (bytes_written <= 0) {
353 ret = ARCHIVE_FATAL;
354 goto cleanup;
356 a->archive.raw_position += bytes_written;
357 state->stream.next_out = state->compressed;
358 state->stream.avail_out = state->compressed_buffer_size;
359 memcpy(state->stream.next_out, trailer + tocopy, 8-tocopy);
360 state->stream.next_out += 8-tocopy;
361 state->stream.avail_out -= 8-tocopy;
364 /* Optionally, pad the final compressed block. */
365 block_length = state->stream.next_out - state->compressed;
367 /* Tricky calculation to determine size of last block. */
368 target_block_length = block_length;
369 if (a->bytes_in_last_block <= 0)
370 /* Default or Zero: pad to full block */
371 target_block_length = a->bytes_per_block;
372 else
373 /* Round length to next multiple of bytes_in_last_block. */
374 target_block_length = a->bytes_in_last_block *
375 ( (block_length + a->bytes_in_last_block - 1) /
376 a->bytes_in_last_block);
377 if (target_block_length > a->bytes_per_block)
378 target_block_length = a->bytes_per_block;
379 if (block_length < target_block_length) {
380 memset(state->stream.next_out, 0,
381 target_block_length - block_length);
382 block_length = target_block_length;
385 /* Write the last block */
386 bytes_written = (a->client_writer)(&a->archive, a->client_data,
387 state->compressed, block_length);
388 if (bytes_written <= 0) {
389 ret = ARCHIVE_FATAL;
390 goto cleanup;
392 a->archive.raw_position += bytes_written;
394 /* Cleanup: shut down compressor, release memory, etc. */
395 cleanup:
396 switch (deflateEnd(&(state->stream))) {
397 case Z_OK:
398 break;
399 default:
400 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
401 "Failed to clean up compressor");
402 ret = ARCHIVE_FATAL;
404 free(state->compressed);
405 free(state);
407 /* Clean up config area even if we never initialized. */
408 free(a->compressor.config);
409 a->compressor.config = NULL;
410 return (ret);
414 * Utility function to push input data through compressor,
415 * writing full output blocks as necessary.
417 * Note that this handles both the regular write case (finishing ==
418 * false) and the end-of-archive case (finishing == true).
420 static int
421 drive_compressor(struct archive_write *a, struct private_data *state, int finishing)
423 ssize_t bytes_written;
424 int ret;
426 for (;;) {
427 if (state->stream.avail_out == 0) {
428 bytes_written = (a->client_writer)(&a->archive,
429 a->client_data, state->compressed,
430 state->compressed_buffer_size);
431 if (bytes_written <= 0) {
432 /* TODO: Handle this write failure */
433 return (ARCHIVE_FATAL);
434 } else if ((size_t)bytes_written < state->compressed_buffer_size) {
435 /* Short write: Move remaining to
436 * front of block and keep filling */
437 memmove(state->compressed,
438 state->compressed + bytes_written,
439 state->compressed_buffer_size - bytes_written);
441 a->archive.raw_position += bytes_written;
442 state->stream.next_out
443 = state->compressed +
444 state->compressed_buffer_size - bytes_written;
445 state->stream.avail_out = bytes_written;
448 /* If there's nothing to do, we're done. */
449 if (!finishing && state->stream.avail_in == 0)
450 return (ARCHIVE_OK);
452 ret = deflate(&(state->stream),
453 finishing ? Z_FINISH : Z_NO_FLUSH );
455 switch (ret) {
456 case Z_OK:
457 /* In non-finishing case, check if compressor
458 * consumed everything */
459 if (!finishing && state->stream.avail_in == 0)
460 return (ARCHIVE_OK);
461 /* In finishing case, this return always means
462 * there's more work */
463 break;
464 case Z_STREAM_END:
465 /* This return can only occur in finishing case. */
466 return (ARCHIVE_OK);
467 default:
468 /* Any other return value indicates an error. */
469 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
470 "GZip compression failed:"
471 " deflate() call returned status %d",
472 ret);
473 return (ARCHIVE_FATAL);
478 #endif /* HAVE_ZLIB_H */