amd64: Change the way I/O APIC's are mapped.
[dragonfly.git] / contrib / libarchive-2 / libarchive / archive_write_set_compression_bzip2.c
1 /*-
2  * Copyright (c) 2003-2007 Tim Kientzle
3  * All rights reserved.
4  *
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.
13  *
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.
24  */
25
26 #include "archive_platform.h"
27
28 /* Don't compile this if we don't have bzlib. */
29 #if HAVE_BZLIB_H
30
31 __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_bzip2.c,v 1.13 2007/12/30 04:58:21 kientzle Exp $");
32
33 #ifdef HAVE_ERRNO_H
34 #include <errno.h>
35 #endif
36 #include <stdio.h>
37 #ifdef HAVE_STDLIB_H
38 #include <stdlib.h>
39 #endif
40 #ifdef HAVE_STRING_H
41 #include <string.h>
42 #endif
43 #ifdef HAVE_BZLIB_H
44 #include <bzlib.h>
45 #endif
46
47 #include "archive.h"
48 #include "archive_private.h"
49 #include "archive_write_private.h"
50
51 struct private_data {
52         bz_stream        stream;
53         int64_t          total_in;
54         char            *compressed;
55         size_t           compressed_buffer_size;
56 };
57
58
59 /*
60  * Yuck.  bzlib.h is not const-correct, so I need this one bit
61  * of ugly hackery to convert a const * pointer to a non-const pointer.
62  */
63 #define SET_NEXT_IN(st,src)                                     \
64         (st)->stream.next_in = (char *)(uintptr_t)(const void *)(src)
65
66 static int      archive_compressor_bzip2_finish(struct archive_write *);
67 static int      archive_compressor_bzip2_init(struct archive_write *);
68 static int      archive_compressor_bzip2_write(struct archive_write *,
69                     const void *, size_t);
70 static int      drive_compressor(struct archive_write *, struct private_data *,
71                     int finishing);
72
73 /*
74  * Allocate, initialize and return an archive object.
75  */
76 int
77 archive_write_set_compression_bzip2(struct archive *_a)
78 {
79         struct archive_write *a = (struct archive_write *)_a;
80         __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
81             ARCHIVE_STATE_NEW, "archive_write_set_compression_bzip2");
82         a->compressor.init = &archive_compressor_bzip2_init;
83         return (ARCHIVE_OK);
84 }
85
86 /*
87  * Setup callback.
88  */
89 static int
90 archive_compressor_bzip2_init(struct archive_write *a)
91 {
92         int ret;
93         struct private_data *state;
94
95         a->archive.compression_code = ARCHIVE_COMPRESSION_BZIP2;
96         a->archive.compression_name = "bzip2";
97
98         if (a->client_opener != NULL) {
99                 ret = (a->client_opener)(&a->archive, a->client_data);
100                 if (ret != 0)
101                         return (ret);
102         }
103
104         state = (struct private_data *)malloc(sizeof(*state));
105         if (state == NULL) {
106                 archive_set_error(&a->archive, ENOMEM,
107                     "Can't allocate data for compression");
108                 return (ARCHIVE_FATAL);
109         }
110         memset(state, 0, sizeof(*state));
111
112         state->compressed_buffer_size = a->bytes_per_block;
113         state->compressed = (char *)malloc(state->compressed_buffer_size);
114
115         if (state->compressed == NULL) {
116                 archive_set_error(&a->archive, ENOMEM,
117                     "Can't allocate data for compression buffer");
118                 free(state);
119                 return (ARCHIVE_FATAL);
120         }
121
122         state->stream.next_out = state->compressed;
123         state->stream.avail_out = state->compressed_buffer_size;
124         a->compressor.write = archive_compressor_bzip2_write;
125         a->compressor.finish = archive_compressor_bzip2_finish;
126
127         /* Initialize compression library */
128         ret = BZ2_bzCompressInit(&(state->stream), 9, 0, 30);
129         if (ret == BZ_OK) {
130                 a->compressor.data = state;
131                 return (ARCHIVE_OK);
132         }
133
134         /* Library setup failed: clean up. */
135         archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
136             "Internal error initializing compression library");
137         free(state->compressed);
138         free(state);
139
140         /* Override the error message if we know what really went wrong. */
141         switch (ret) {
142         case BZ_PARAM_ERROR:
143                 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
144                     "Internal error initializing compression library: "
145                     "invalid setup parameter");
146                 break;
147         case BZ_MEM_ERROR:
148                 archive_set_error(&a->archive, ENOMEM,
149                     "Internal error initializing compression library: "
150                     "out of memory");
151                 break;
152         case BZ_CONFIG_ERROR:
153                 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
154                     "Internal error initializing compression library: "
155                     "mis-compiled library");
156                 break;
157         }
158
159         return (ARCHIVE_FATAL);
160
161 }
162
163 /*
164  * Write data to the compressed stream.
165  *
166  * Returns ARCHIVE_OK if all data written, error otherwise.
167  */
168 static int
169 archive_compressor_bzip2_write(struct archive_write *a, const void *buff,
170     size_t length)
171 {
172         struct private_data *state;
173
174         state = (struct private_data *)a->compressor.data;
175         if (a->client_writer == NULL) {
176                 archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
177                     "No write callback is registered?  "
178                     "This is probably an internal programming error.");
179                 return (ARCHIVE_FATAL);
180         }
181
182         /* Update statistics */
183         state->total_in += length;
184
185         /* Compress input data to output buffer */
186         SET_NEXT_IN(state, buff);
187         state->stream.avail_in = length;
188         if (drive_compressor(a, state, 0))
189                 return (ARCHIVE_FATAL);
190         a->archive.file_position += length;
191         return (ARCHIVE_OK);
192 }
193
194
195 /*
196  * Finish the compression.
197  */
198 static int
199 archive_compressor_bzip2_finish(struct archive_write *a)
200 {
201         ssize_t block_length;
202         int ret;
203         struct private_data *state;
204         ssize_t target_block_length;
205         ssize_t bytes_written;
206         unsigned tocopy;
207
208         state = (struct private_data *)a->compressor.data;
209         ret = ARCHIVE_OK;
210         if (a->client_writer == NULL) {
211                 archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
212                     "No write callback is registered?\n"
213                     "This is probably an internal programming error.");
214                 ret = ARCHIVE_FATAL;
215                 goto cleanup;
216         }
217
218         /* By default, always pad the uncompressed data. */
219         if (a->pad_uncompressed) {
220                 tocopy = a->bytes_per_block -
221                     (state->total_in % a->bytes_per_block);
222                 while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) {
223                         SET_NEXT_IN(state, a->nulls);
224                         state->stream.avail_in = tocopy < a->null_length ?
225                             tocopy : a->null_length;
226                         state->total_in += state->stream.avail_in;
227                         tocopy -= state->stream.avail_in;
228                         ret = drive_compressor(a, state, 0);
229                         if (ret != ARCHIVE_OK)
230                                 goto cleanup;
231                 }
232         }
233
234         /* Finish compression cycle. */
235         if ((ret = drive_compressor(a, state, 1)))
236                 goto cleanup;
237
238         /* Optionally, pad the final compressed block. */
239         block_length = state->stream.next_out - state->compressed;
240
241
242         /* Tricky calculation to determine size of last block. */
243         target_block_length = block_length;
244         if (a->bytes_in_last_block <= 0)
245                 /* Default or Zero: pad to full block */
246                 target_block_length = a->bytes_per_block;
247         else
248                 /* Round length to next multiple of bytes_in_last_block. */
249                 target_block_length = a->bytes_in_last_block *
250                     ( (block_length + a->bytes_in_last_block - 1) /
251                         a->bytes_in_last_block);
252         if (target_block_length > a->bytes_per_block)
253                 target_block_length = a->bytes_per_block;
254         if (block_length < target_block_length) {
255                 memset(state->stream.next_out, 0,
256                     target_block_length - block_length);
257                 block_length = target_block_length;
258         }
259
260         /* Write the last block */
261         bytes_written = (a->client_writer)(&a->archive, a->client_data,
262             state->compressed, block_length);
263
264         /* TODO: Handle short write of final block. */
265         if (bytes_written <= 0)
266                 ret = ARCHIVE_FATAL;
267         else {
268                 a->archive.raw_position += ret;
269                 ret = ARCHIVE_OK;
270         }
271
272         /* Cleanup: shut down compressor, release memory, etc. */
273 cleanup:
274         switch (BZ2_bzCompressEnd(&(state->stream))) {
275         case BZ_OK:
276                 break;
277         default:
278                 archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
279                     "Failed to clean up compressor");
280                 ret = ARCHIVE_FATAL;
281         }
282
283         free(state->compressed);
284         free(state);
285         return (ret);
286 }
287
288 /*
289  * Utility function to push input data through compressor, writing
290  * full output blocks as necessary.
291  *
292  * Note that this handles both the regular write case (finishing ==
293  * false) and the end-of-archive case (finishing == true).
294  */
295 static int
296 drive_compressor(struct archive_write *a, struct private_data *state, int finishing)
297 {
298         ssize_t bytes_written;
299         int ret;
300
301         for (;;) {
302                 if (state->stream.avail_out == 0) {
303                         bytes_written = (a->client_writer)(&a->archive,
304                             a->client_data, state->compressed,
305                             state->compressed_buffer_size);
306                         if (bytes_written <= 0) {
307                                 /* TODO: Handle this write failure */
308                                 return (ARCHIVE_FATAL);
309                         } else if ((size_t)bytes_written < state->compressed_buffer_size) {
310                                 /* Short write: Move remainder to
311                                  * front and keep filling */
312                                 memmove(state->compressed,
313                                     state->compressed + bytes_written,
314                                     state->compressed_buffer_size - bytes_written);
315                         }
316
317                         a->archive.raw_position += bytes_written;
318                         state->stream.next_out = state->compressed +
319                             state->compressed_buffer_size - bytes_written;
320                         state->stream.avail_out = bytes_written;
321                 }
322
323                 /* If there's nothing to do, we're done. */
324                 if (!finishing && state->stream.avail_in == 0)
325                         return (ARCHIVE_OK);
326
327                 ret = BZ2_bzCompress(&(state->stream),
328                     finishing ? BZ_FINISH : BZ_RUN);
329
330                 switch (ret) {
331                 case BZ_RUN_OK:
332                         /* In non-finishing case, did compressor
333                          * consume everything? */
334                         if (!finishing && state->stream.avail_in == 0)
335                                 return (ARCHIVE_OK);
336                         break;
337                 case BZ_FINISH_OK:  /* Finishing: There's more work to do */
338                         break;
339                 case BZ_STREAM_END: /* Finishing: all done */
340                         /* Only occurs in finishing case */
341                         return (ARCHIVE_OK);
342                 default:
343                         /* Any other return value indicates an error */
344                         archive_set_error(&a->archive,
345                             ARCHIVE_ERRNO_PROGRAMMER,
346                             "Bzip2 compression failed;"
347                             " BZ2_bzCompress() returned %d",
348                             ret);
349                         return (ARCHIVE_FATAL);
350                 }
351         }
352 }
353
354 #endif /* HAVE_BZLIB_H */