1/* 2 * Copyright (C) 2007 Denys Vlasenko <vda.linux@googlemail.com> 3 * 4 * This file uses bzip2 library code which is written 5 * by Julian Seward <jseward@bzip.org>. 6 * See README and LICENSE files in bz/ directory for more information 7 * about bzip2 library code. 8 */ 9 10//config:config BZIP2 11//config: bool "bzip2" 12//config: default y 13//config: help 14//config: bzip2 is a compression utility using the Burrows-Wheeler block 15//config: sorting text compression algorithm, and Huffman coding. Compression 16//config: is generally considerably better than that achieved by more 17//config: conventional LZ77/LZ78-based compressors, and approaches the 18//config: performance of the PPM family of statistical compressors. 19//config: 20//config: Unless you have a specific application which requires bzip2, you 21//config: should probably say N here. 22 23//applet:IF_BZIP2(APPLET(bzip2, BB_DIR_USR_BIN, BB_SUID_DROP)) 24//kbuild:lib-$(CONFIG_BZIP2) += bzip2.o 25 26//usage:#define bzip2_trivial_usage 27//usage: "[OPTIONS] [FILE]..." 28//usage:#define bzip2_full_usage "\n\n" 29//usage: "Compress FILEs (or stdin) with bzip2 algorithm\n" 30//usage: "\n -1..9 Compression level" 31//usage: "\n -d Decompress" 32//usage: "\n -c Write to stdout" 33//usage: "\n -f Force" 34 35#include "libbb.h" 36#include "bb_archive.h" 37 38#define CONFIG_BZIP2_FAST 1 39 40/* Speed test: 41 * Compiled with gcc 4.2.1, run on Athlon 64 1800 MHz (512K L2 cache). 42 * Stock bzip2 is 26.4% slower than bbox bzip2 at SPEED 1 43 * (time to compress gcc-4.2.1.tar is 126.4% compared to bbox). 44 * At SPEED 5 difference is 32.7%. 45 * 46 * Test run of all CONFIG_BZIP2_FAST values on a 11Mb text file: 47 * Size Time (3 runs) 48 * 0: 10828 4.145 4.146 4.148 49 * 1: 11097 3.845 3.860 3.861 50 * 2: 11392 3.763 3.767 3.768 51 * 3: 11892 3.722 3.724 3.727 52 * 4: 12740 3.637 3.640 3.644 53 * 5: 17273 3.497 3.509 3.509 54 */ 55 56 57#define BZ_DEBUG 0 58/* Takes ~300 bytes, detects corruption caused by bad RAM etc */ 59#define BZ_LIGHT_DEBUG 0 60 61#include "libarchive/bz/bzlib.h" 62 63#include "libarchive/bz/bzlib_private.h" 64 65#include "libarchive/bz/blocksort.c" 66#include "libarchive/bz/bzlib.c" 67#include "libarchive/bz/compress.c" 68#include "libarchive/bz/huffman.c" 69 70/* No point in being shy and having very small buffer here. 71 * bzip2 internal buffers are much bigger anyway, hundreds of kbytes. 72 * If iobuf is several pages long, malloc() may use mmap, 73 * making iobuf is page aligned and thus (maybe) have one memcpy less 74 * if kernel is clever enough. 75 */ 76enum { 77 IOBUF_SIZE = 8 * 1024 78}; 79 80static uint8_t level; 81 82/* NB: compressStream() has to return -1 on errors, not die. 83 * bbunpack() will correctly clean up in this case 84 * (delete incomplete .bz2 file) 85 */ 86 87/* Returns: 88 * -1 on errors 89 * total written bytes so far otherwise 90 */ 91static 92IF_DESKTOP(long long) int bz_write(bz_stream *strm, void* rbuf, ssize_t rlen, void *wbuf) 93{ 94 int n, n2, ret; 95 96 strm->avail_in = rlen; 97 strm->next_in = rbuf; 98 while (1) { 99 strm->avail_out = IOBUF_SIZE; 100 strm->next_out = wbuf; 101 102 ret = BZ2_bzCompress(strm, rlen ? BZ_RUN : BZ_FINISH); 103 if (ret != BZ_RUN_OK /* BZ_RUNning */ 104 && ret != BZ_FINISH_OK /* BZ_FINISHing, but not done yet */ 105 && ret != BZ_STREAM_END /* BZ_FINISHed */ 106 ) { 107 bb_error_msg_and_die("internal error %d", ret); 108 } 109 110 n = IOBUF_SIZE - strm->avail_out; 111 if (n) { 112 n2 = full_write(STDOUT_FILENO, wbuf, n); 113 if (n2 != n) { 114 if (n2 >= 0) 115 errno = 0; /* prevent bogus error message */ 116 bb_perror_msg(n2 >= 0 ? "short write" : bb_msg_write_error); 117 return -1; 118 } 119 } 120 121 if (ret == BZ_STREAM_END) 122 break; 123 if (rlen && strm->avail_in == 0) 124 break; 125 } 126 return 0 IF_DESKTOP( + strm->total_out ); 127} 128 129static 130IF_DESKTOP(long long) int FAST_FUNC compressStream(transformer_state_t *xstate UNUSED_PARAM) 131{ 132 IF_DESKTOP(long long) int total; 133 ssize_t count; 134 bz_stream bzs; /* it's small */ 135#define strm (&bzs) 136 char *iobuf; 137#define rbuf iobuf 138#define wbuf (iobuf + IOBUF_SIZE) 139 140 iobuf = xmalloc(2 * IOBUF_SIZE); 141 BZ2_bzCompressInit(strm, level); 142 143 while (1) { 144 count = full_read(STDIN_FILENO, rbuf, IOBUF_SIZE); 145 if (count < 0) { 146 bb_perror_msg(bb_msg_read_error); 147 total = -1; 148 break; 149 } 150 /* if count == 0, bz_write finalizes compression */ 151 total = bz_write(strm, rbuf, count, wbuf); 152 if (count == 0 || total < 0) 153 break; 154 } 155 156 /* Can't be conditional on ENABLE_FEATURE_CLEAN_UP - 157 * we are called repeatedly 158 */ 159 BZ2_bzCompressEnd(strm); 160 free(iobuf); 161 162 return total; 163} 164 165int bzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 166int bzip2_main(int argc UNUSED_PARAM, char **argv) 167{ 168 unsigned opt; 169 170 /* standard bzip2 flags 171 * -d --decompress force decompression 172 * -z --compress force compression 173 * -k --keep keep (don't delete) input files 174 * -f --force overwrite existing output files 175 * -t --test test compressed file integrity 176 * -c --stdout output to standard out 177 * -q --quiet suppress noncritical error messages 178 * -v --verbose be verbose (a 2nd -v gives more) 179 * -s --small use less memory (at most 2500k) 180 * -1 .. -9 set block size to 100k .. 900k 181 * --fast alias for -1 182 * --best alias for -9 183 */ 184 185 opt_complementary = "s2"; /* -s means -2 (compatibility) */ 186 /* Must match bbunzip's constants OPT_STDOUT, OPT_FORCE! */ 187 opt = getopt32(argv, "cfv" IF_BUNZIP2("dt") "123456789qzs"); 188#if ENABLE_BUNZIP2 /* bunzip2_main may not be visible... */ 189 if (opt & 0x18) // -d and/or -t 190 return bunzip2_main(argc, argv); 191 opt >>= 5; 192#else 193 opt >>= 3; 194#endif 195 opt = (uint8_t)opt; /* isolate bits for -1..-8 */ 196 opt |= 0x100; /* if nothing else, assume -9 */ 197 level = 1; 198 while (!(opt & 1)) { 199 level++; 200 opt >>= 1; 201 } 202 203 argv += optind; 204 option_mask32 &= 0x7; /* ignore all except -cfv */ 205 return bbunpack(argv, compressStream, append_ext, "bz2"); 206} 207