/* * bzip2 is written by Julian Seward <jseward@bzip.org>. * Adapted for busybox by Denys Vlasenko <vda.linux@googlemail.com>. * See README and LICENSE files in this directory for more information. */ /*-------------------------------------------------------------*/ /*--- Library top-level functions. ---*/ /*--- bzlib.c ---*/ /*-------------------------------------------------------------*/ /* ------------------------------------------------------------------ This file is part of bzip2/libbzip2, a program and library for lossless, block-sorting data compression. bzip2/libbzip2 version 1.0.4 of 20 December 2006 Copyright (C) 1996-2006 Julian Seward <jseward@bzip.org> Please read the WARNING, DISCLAIMER and PATENTS sections in the README file. This program is released under the terms of the license contained in the file LICENSE. ------------------------------------------------------------------ */ /* CHANGES * 0.9.0 -- original version. * 0.9.0a/b -- no changes in this file. * 0.9.0c -- made zero-length BZ_FLUSH work correctly in bzCompress(). * fixed bzWrite/bzRead to ignore zero-length requests. * fixed bzread to correctly handle read requests after EOF. * wrong parameter order in call to bzDecompressInit in * bzBuffToBuffDecompress. Fixed. */ /* #include "bzlib_private.h" */ /*---------------------------------------------------*/ /*--- Compression stuff ---*/ /*---------------------------------------------------*/ /*---------------------------------------------------*/ #if BZ_LIGHT_DEBUG static void bz_assert_fail(int errcode) { /* if (errcode == 1007) bb_error_msg_and_die("probably bad RAM"); */ bb_error_msg_and_die("internal error %d", errcode); } #endif /*---------------------------------------------------*/ static void prepare_new_block(EState* s) { int i; s->nblock = 0; s->numZ = 0; s->state_out_pos = 0; BZ_INITIALISE_CRC(s->blockCRC); /* inlined memset would be nice to have here */ for (i = 0; i < 256; i++) s->inUse[i] = 0; s->blockNo++; } /*---------------------------------------------------*/ static ALWAYS_INLINE void init_RL(EState* s) { s->state_in_ch = 256; s->state_in_len = 0; } static int isempty_RL(EState* s) { return (s->state_in_ch >= 256 || s->state_in_len <= 0); } /*---------------------------------------------------*/ static void BZ2_bzCompressInit(bz_stream *strm, int blockSize100k) { int32_t n; EState* s; s = xzalloc(sizeof(EState)); s->strm = strm; n = 100000 * blockSize100k; s->arr1 = xmalloc(n * sizeof(uint32_t)); s->mtfv = (uint16_t*)s->arr1; s->ptr = (uint32_t*)s->arr1; s->arr2 = xmalloc((n + BZ_N_OVERSHOOT) * sizeof(uint32_t)); s->block = (uint8_t*)s->arr2; s->ftab = xmalloc(65537 * sizeof(uint32_t)); s->crc32table = crc32_filltable(NULL, 1); s->state = BZ_S_INPUT; s->mode = BZ_M_RUNNING; s->blockSize100k = blockSize100k; s->nblockMAX = n - 19; strm->state = s; /*strm->total_in = 0;*/ strm->total_out = 0; init_RL(s); prepare_new_block(s); } /*---------------------------------------------------*/ static void add_pair_to_block(EState* s) { int32_t i; uint8_t ch = (uint8_t)(s->state_in_ch); for (i = 0; i < s->state_in_len; i++) { BZ_UPDATE_CRC(s, s->blockCRC, ch); } s->inUse[s->state_in_ch] = 1; switch (s->state_in_len) { case 3: s->block[s->nblock] = (uint8_t)ch; s->nblock++; /* fall through */ case 2: s->block[s->nblock] = (uint8_t)ch; s->nblock++; /* fall through */ case 1: s->block[s->nblock] = (uint8_t)ch; s->nblock++; break; default: s->inUse[s->state_in_len - 4] = 1; s->block[s->nblock] = (uint8_t)ch; s->nblock++; s->block[s->nblock] = (uint8_t)ch; s->nblock++; s->block[s->nblock] = (uint8_t)ch; s->nblock++; s->block[s->nblock] = (uint8_t)ch; s->nblock++; s->block[s->nblock] = (uint8_t)(s->state_in_len - 4); s->nblock++; break; } } /*---------------------------------------------------*/ static void flush_RL(EState* s) { if (s->state_in_ch < 256) add_pair_to_block(s); init_RL(s); } /*---------------------------------------------------*/ #define ADD_CHAR_TO_BLOCK(zs, zchh0) \ { \ uint32_t zchh = (uint32_t)(zchh0); \ /*-- fast track the common case --*/ \ if (zchh != zs->state_in_ch && zs->state_in_len == 1) { \ uint8_t ch = (uint8_t)(zs->state_in_ch); \ BZ_UPDATE_CRC(zs, zs->blockCRC, ch); \ zs->inUse[zs->state_in_ch] = 1; \ zs->block[zs->nblock] = (uint8_t)ch; \ zs->nblock++; \ zs->state_in_ch = zchh; \ } \ else \ /*-- general, uncommon cases --*/ \ if (zchh != zs->state_in_ch || zs->state_in_len == 255) { \ if (zs->state_in_ch < 256) \ add_pair_to_block(zs); \ zs->state_in_ch = zchh; \ zs->state_in_len = 1; \ } else { \ zs->state_in_len++; \ } \ } /*---------------------------------------------------*/ static void /*Bool*/ copy_input_until_stop(EState* s) { /*Bool progress_in = False;*/ #ifdef SAME_CODE_AS_BELOW if (s->mode == BZ_M_RUNNING) { /*-- fast track the common case --*/ while (1) { /*-- no input? --*/ if (s->strm->avail_in == 0) break; /*-- block full? --*/ if (s->nblock >= s->nblockMAX) break; /*progress_in = True;*/ ADD_CHAR_TO_BLOCK(s, (uint32_t)(*(uint8_t*)(s->strm->next_in))); s->strm->next_in++; s->strm->avail_in--; /*s->strm->total_in++;*/ } } else #endif { /*-- general, uncommon case --*/ while (1) { /*-- no input? --*/ if (s->strm->avail_in == 0) break; /*-- block full? --*/ if (s->nblock >= s->nblockMAX) break; //# /*-- flush/finish end? --*/ //# if (s->avail_in_expect == 0) break; /*progress_in = True;*/ ADD_CHAR_TO_BLOCK(s, *(uint8_t*)(s->strm->next_in)); s->strm->next_in++; s->strm->avail_in--; /*s->strm->total_in++;*/ //# s->avail_in_expect--; } } /*return progress_in;*/ } /*---------------------------------------------------*/ static void /*Bool*/ copy_output_until_stop(EState* s) { /*Bool progress_out = False;*/ while (1) { /*-- no output space? --*/ if (s->strm->avail_out == 0) break; /*-- block done? --*/ if (s->state_out_pos >= s->numZ) break; /*progress_out = True;*/ *(s->strm->next_out) = s->zbits[s->state_out_pos]; s->state_out_pos++; s->strm->avail_out--; s->strm->next_out++; s->strm->total_out++; } /*return progress_out;*/ } /*---------------------------------------------------*/ static void /*Bool*/ handle_compress(bz_stream *strm) { /*Bool progress_in = False;*/ /*Bool progress_out = False;*/ EState* s = strm->state; while (1) { if (s->state == BZ_S_OUTPUT) { /*progress_out |=*/ copy_output_until_stop(s); if (s->state_out_pos < s->numZ) break; if (s->mode == BZ_M_FINISHING //# && s->avail_in_expect == 0 && s->strm->avail_in == 0 && isempty_RL(s)) break; prepare_new_block(s); s->state = BZ_S_INPUT; #ifdef FLUSH_IS_UNUSED if (s->mode == BZ_M_FLUSHING && s->avail_in_expect == 0 && isempty_RL(s)) break; #endif } if (s->state == BZ_S_INPUT) { /*progress_in |=*/ copy_input_until_stop(s); //#if (s->mode != BZ_M_RUNNING && s->avail_in_expect == 0) { if (s->mode != BZ_M_RUNNING && s->strm->avail_in == 0) { flush_RL(s); BZ2_compressBlock(s, (s->mode == BZ_M_FINISHING)); s->state = BZ_S_OUTPUT; } else if (s->nblock >= s->nblockMAX) { BZ2_compressBlock(s, 0); s->state = BZ_S_OUTPUT; } else if (s->strm->avail_in == 0) { break; } } } /*return progress_in || progress_out;*/ } /*---------------------------------------------------*/ static int BZ2_bzCompress(bz_stream *strm, int action) { /*Bool progress;*/ EState* s; s = strm->state; switch (s->mode) { case BZ_M_RUNNING: if (action == BZ_RUN) { /*progress =*/ handle_compress(strm); /*return progress ? BZ_RUN_OK : BZ_PARAM_ERROR;*/ return BZ_RUN_OK; } #ifdef FLUSH_IS_UNUSED else if (action == BZ_FLUSH) { //#s->avail_in_expect = strm->avail_in; s->mode = BZ_M_FLUSHING; goto case_BZ_M_FLUSHING; } #endif else /*if (action == BZ_FINISH)*/ { //#s->avail_in_expect = strm->avail_in; s->mode = BZ_M_FINISHING; goto case_BZ_M_FINISHING; } #ifdef FLUSH_IS_UNUSED case_BZ_M_FLUSHING: case BZ_M_FLUSHING: /*if (s->avail_in_expect != s->strm->avail_in) return BZ_SEQUENCE_ERROR;*/ /*progress =*/ handle_compress(strm); if (s->avail_in_expect > 0 || !isempty_RL(s) || s->state_out_pos < s->numZ) return BZ_FLUSH_OK; s->mode = BZ_M_RUNNING; return BZ_RUN_OK; #endif case_BZ_M_FINISHING: /*case BZ_M_FINISHING:*/ default: /*if (s->avail_in_expect != s->strm->avail_in) return BZ_SEQUENCE_ERROR;*/ /*progress =*/ handle_compress(strm); /*if (!progress) return BZ_SEQUENCE_ERROR;*/ //#if (s->avail_in_expect > 0 || !isempty_RL(s) || s->state_out_pos < s->numZ) //# return BZ_FINISH_OK; if (s->strm->avail_in > 0 || !isempty_RL(s) || s->state_out_pos < s->numZ) return BZ_FINISH_OK; /*s->mode = BZ_M_IDLE;*/ return BZ_STREAM_END; } /* return BZ_OK; --not reached--*/ } /*---------------------------------------------------*/ #if ENABLE_FEATURE_CLEAN_UP static void BZ2_bzCompressEnd(bz_stream *strm) { EState* s; s = strm->state; free(s->arr1); free(s->arr2); free(s->ftab); free(s->crc32table); free(strm->state); } #endif /*---------------------------------------------------*/ /*--- Misc convenience stuff ---*/ /*---------------------------------------------------*/ /*---------------------------------------------------*/ #ifdef EXAMPLE_CODE_FOR_MEM_TO_MEM_COMPRESSION static int BZ2_bzBuffToBuffCompress(char* dest, unsigned int* destLen, char* source, unsigned int sourceLen, int blockSize100k) { bz_stream strm; int ret; if (dest == NULL || destLen == NULL || source == NULL || blockSize100k < 1 || blockSize100k > 9) return BZ_PARAM_ERROR; BZ2_bzCompressInit(&strm, blockSize100k); strm.next_in = source; strm.next_out = dest; strm.avail_in = sourceLen; strm.avail_out = *destLen; ret = BZ2_bzCompress(&strm, BZ_FINISH); if (ret == BZ_FINISH_OK) goto output_overflow; if (ret != BZ_STREAM_END) goto errhandler; /* normal termination */ *destLen -= strm.avail_out; BZ2_bzCompressEnd(&strm); return BZ_OK; output_overflow: BZ2_bzCompressEnd(&strm); return BZ_OUTBUFF_FULL; errhandler: BZ2_bzCompressEnd(&strm); return ret; } #endif /*-------------------------------------------------------------*/ /*--- end bzlib.c ---*/ /*-------------------------------------------------------------*/