Upgrade to libarchive 2.0.25 which gives us a nice speed boost along with
[dragonfly/port-amd64.git] / contrib / libarchive-1.3.1 / libarchive / archive_write_set_compression_bzip2.c
blob7dd43c42331605ed20704c339150daee7f272e84
1 /*-
2 * Copyright (c) 2003-2004 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 * in this position and unchanged.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "archive_platform.h"
29 /* Don't compile this if we don't have bzlib. */
30 #if HAVE_BZLIB_H
32 __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_bzip2.c,v 1.8 2005/06/01 15:52:39 kientzle Exp $");
34 #include <errno.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <bzlib.h>
40 #include "archive.h"
41 #include "archive_private.h"
43 struct private_data {
44 bz_stream stream;
45 int64_t total_in;
46 char *compressed;
47 size_t compressed_buffer_size;
52 * Yuck. bzlib.h is not const-correct, so I need this one bit
53 * of ugly hackery to convert a const * pointer to a non-const pointer.
55 #define SET_NEXT_IN(st,src) \
56 (st)->stream.next_in = (void *)(uintptr_t)(const void *)(src)
58 static int archive_compressor_bzip2_finish(struct archive *);
59 static int archive_compressor_bzip2_init(struct archive *);
60 static int archive_compressor_bzip2_write(struct archive *, const void *,
61 size_t);
62 static int drive_compressor(struct archive *, struct private_data *,
63 int finishing);
66 * Allocate, initialize and return an archive object.
68 int
69 archive_write_set_compression_bzip2(struct archive *a)
71 __archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_compression_bzip2");
72 a->compression_init = &archive_compressor_bzip2_init;
73 a->compression_code = ARCHIVE_COMPRESSION_BZIP2;
74 a->compression_name = "bzip2";
75 return (ARCHIVE_OK);
79 * Setup callback.
81 static int
82 archive_compressor_bzip2_init(struct archive *a)
84 int ret;
85 struct private_data *state;
87 a->compression_code = ARCHIVE_COMPRESSION_BZIP2;
88 a->compression_name = "bzip2";
90 if (a->client_opener != NULL) {
91 ret = (a->client_opener)(a, a->client_data);
92 if (ret != 0)
93 return (ret);
96 state = malloc(sizeof(*state));
97 if (state == NULL) {
98 archive_set_error(a, ENOMEM,
99 "Can't allocate data for compression");
100 return (ARCHIVE_FATAL);
102 memset(state, 0, sizeof(*state));
104 state->compressed_buffer_size = a->bytes_per_block;
105 state->compressed = malloc(state->compressed_buffer_size);
107 if (state->compressed == NULL) {
108 archive_set_error(a, ENOMEM,
109 "Can't allocate data for compression buffer");
110 free(state);
111 return (ARCHIVE_FATAL);
114 state->stream.next_out = state->compressed;
115 state->stream.avail_out = state->compressed_buffer_size;
116 a->compression_write = archive_compressor_bzip2_write;
117 a->compression_finish = archive_compressor_bzip2_finish;
119 /* Initialize compression library */
120 ret = BZ2_bzCompressInit(&(state->stream), 9, 0, 30);
121 if (ret == BZ_OK) {
122 a->compression_data = state;
123 return (ARCHIVE_OK);
126 /* Library setup failed: clean up. */
127 archive_set_error(a, ARCHIVE_ERRNO_MISC,
128 "Internal error initializing compression library");
129 free(state->compressed);
130 free(state);
132 /* Override the error message if we know what really went wrong. */
133 switch (ret) {
134 case BZ_PARAM_ERROR:
135 archive_set_error(a, ARCHIVE_ERRNO_MISC,
136 "Internal error initializing compression library: "
137 "invalid setup parameter");
138 break;
139 case BZ_MEM_ERROR:
140 archive_set_error(a, ENOMEM,
141 "Internal error initializing compression library: "
142 "out of memory");
143 break;
144 case BZ_CONFIG_ERROR:
145 archive_set_error(a, ARCHIVE_ERRNO_MISC,
146 "Internal error initializing compression library: "
147 "mis-compiled library");
148 break;
151 return (ARCHIVE_FATAL);
156 * Write data to the compressed stream.
158 * Returns ARCHIVE_OK if all data written, error otherwise.
160 static int
161 archive_compressor_bzip2_write(struct archive *a, const void *buff,
162 size_t length)
164 struct private_data *state;
166 state = a->compression_data;
167 if (a->client_writer == NULL) {
168 archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
169 "No write callback is registered? "
170 "This is probably an internal programming error.");
171 return (ARCHIVE_FATAL);
174 /* Update statistics */
175 state->total_in += length;
177 /* Compress input data to output buffer */
178 SET_NEXT_IN(state, buff);
179 state->stream.avail_in = length;
180 if (drive_compressor(a, state, 0))
181 return (ARCHIVE_FATAL);
182 a->file_position += length;
183 return (ARCHIVE_OK);
188 * Finish the compression.
190 static int
191 archive_compressor_bzip2_finish(struct archive *a)
193 ssize_t block_length;
194 int ret;
195 struct private_data *state;
196 ssize_t target_block_length;
197 ssize_t bytes_written;
198 unsigned tocopy;
200 state = a->compression_data;
201 ret = ARCHIVE_OK;
202 if (a->client_writer == NULL) {
203 archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
204 "No write callback is registered?\n"
205 "This is probably an internal programming error.");
206 ret = ARCHIVE_FATAL;
207 goto cleanup;
210 /* By default, always pad the uncompressed data. */
211 if (a->pad_uncompressed) {
212 tocopy = a->bytes_per_block -
213 (state->total_in % a->bytes_per_block);
214 while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) {
215 SET_NEXT_IN(state, a->nulls);
216 state->stream.avail_in = tocopy < a->null_length ?
217 tocopy : a->null_length;
218 state->total_in += state->stream.avail_in;
219 tocopy -= state->stream.avail_in;
220 ret = drive_compressor(a, state, 0);
221 if (ret != ARCHIVE_OK)
222 goto cleanup;
226 /* Finish compression cycle. */
227 if ((ret = drive_compressor(a, state, 1)))
228 goto cleanup;
230 /* Optionally, pad the final compressed block. */
231 block_length = state->stream.next_out - state->compressed;
234 /* Tricky calculation to determine size of last block. */
235 target_block_length = block_length;
236 if (a->bytes_in_last_block <= 0)
237 /* Default or Zero: pad to full block */
238 target_block_length = a->bytes_per_block;
239 else
240 /* Round length to next multiple of bytes_in_last_block. */
241 target_block_length = a->bytes_in_last_block *
242 ( (block_length + a->bytes_in_last_block - 1) /
243 a->bytes_in_last_block);
244 if (target_block_length > a->bytes_per_block)
245 target_block_length = a->bytes_per_block;
246 if (block_length < target_block_length) {
247 memset(state->stream.next_out, 0,
248 target_block_length - block_length);
249 block_length = target_block_length;
252 /* Write the last block */
253 bytes_written = (a->client_writer)(a, a->client_data,
254 state->compressed, block_length);
256 /* TODO: Handle short write of final block. */
257 if (bytes_written <= 0)
258 ret = ARCHIVE_FATAL;
259 else {
260 a->raw_position += ret;
261 ret = ARCHIVE_OK;
264 /* Cleanup: shut down compressor, release memory, etc. */
265 cleanup:
266 switch (BZ2_bzCompressEnd(&(state->stream))) {
267 case BZ_OK:
268 break;
269 default:
270 archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
271 "Failed to clean up compressor");
272 ret = ARCHIVE_FATAL;
275 free(state->compressed);
276 free(state);
278 /* Close the output */
279 if (a->client_closer != NULL)
280 (a->client_closer)(a, a->client_data);
282 return (ret);
286 * Utility function to push input data through compressor, writing
287 * full output blocks as necessary.
289 * Note that this handles both the regular write case (finishing ==
290 * false) and the end-of-archive case (finishing == true).
292 static int
293 drive_compressor(struct archive *a, struct private_data *state, int finishing)
295 ssize_t bytes_written;
296 int ret;
298 for (;;) {
299 if (state->stream.avail_out == 0) {
300 bytes_written = (a->client_writer)(a, a->client_data,
301 state->compressed, state->compressed_buffer_size);
302 if (bytes_written <= 0) {
303 /* TODO: Handle this write failure */
304 return (ARCHIVE_FATAL);
305 } else if ((size_t)bytes_written < state->compressed_buffer_size) {
306 /* Short write: Move remainder to
307 * front and keep filling */
308 memmove(state->compressed,
309 state->compressed + bytes_written,
310 state->compressed_buffer_size - bytes_written);
313 a->raw_position += bytes_written;
314 state->stream.next_out = state->compressed +
315 state->compressed_buffer_size - bytes_written;
316 state->stream.avail_out = bytes_written;
319 ret = BZ2_bzCompress(&(state->stream),
320 finishing ? BZ_FINISH : BZ_RUN);
322 switch (ret) {
323 case BZ_RUN_OK:
324 /* In non-finishing case, did compressor
325 * consume everything? */
326 if (!finishing && state->stream.avail_in == 0)
327 return (ARCHIVE_OK);
328 break;
329 case BZ_FINISH_OK: /* Finishing: There's more work to do */
330 break;
331 case BZ_STREAM_END: /* Finishing: all done */
332 /* Only occurs in finishing case */
333 return (ARCHIVE_OK);
334 default:
335 /* Any other return value indicates an error */
336 archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
337 "Bzip2 compression failed");
338 return (ARCHIVE_FATAL);
343 #endif /* HAVE_BZLIB_H */