| /* | 
 |  * THIS FILE IS NOT IDENTICAL TO THE ORIGINAL | 
 |  * FROM THE BZIP2 DISTRIBUTION. | 
 |  * | 
 |  * It has been modified, mainly to break the library | 
 |  * into smaller pieces. | 
 |  * | 
 |  * Russ Cox | 
 |  * rsc@plan9.bell-labs.com | 
 |  * July 2000 | 
 |  */ | 
 |  | 
 | /*-------------------------------------------------------------*/ | 
 | /*--- Library top-level functions.                          ---*/ | 
 | /*---                                               bzlib.c ---*/ | 
 | /*-------------------------------------------------------------*/ | 
 |  | 
 | /*-- | 
 |   This file is a part of bzip2 and/or libbzip2, a program and | 
 |   library for lossless, block-sorting data compression. | 
 |  | 
 |   Copyright (C) 1996-2000 Julian R Seward.  All rights reserved. | 
 |  | 
 |   Redistribution and use in source and binary forms, with or without | 
 |   modification, are permitted provided that the following conditions | 
 |   are met: | 
 |  | 
 |   1. Redistributions of source code must retain the above copyright | 
 |      notice, this list of conditions and the following disclaimer. | 
 |  | 
 |   2. The origin of this software must not be misrepresented; you must  | 
 |      not claim that you wrote the original software.  If you use this  | 
 |      software in a product, an acknowledgment in the product  | 
 |      documentation would be appreciated but is not required. | 
 |  | 
 |   3. Altered source versions must be plainly marked as such, and must | 
 |      not be misrepresented as being the original software. | 
 |  | 
 |   4. The name of the author may not be used to endorse or promote  | 
 |      products derived from this software without specific prior written  | 
 |      permission. | 
 |  | 
 |   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS | 
 |   OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | 
 |   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
 |   ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | 
 |   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
 |   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE | 
 |   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | 
 |   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | 
 |   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | 
 |   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | 
 |   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
 |  | 
 |   Julian Seward, Cambridge, UK. | 
 |   jseward@acm.org | 
 |   bzip2/libbzip2 version 1.0 of 21 March 2000 | 
 |  | 
 |   This program is based on (at least) the work of: | 
 |      Mike Burrows | 
 |      David Wheeler | 
 |      Peter Fenwick | 
 |      Alistair Moffat | 
 |      Radford Neal | 
 |      Ian H. Witten | 
 |      Robert Sedgewick | 
 |      Jon L. Bentley | 
 |  | 
 |   For more information on these sources, see the manual. | 
 | --*/ | 
 |  | 
 | /*-- | 
 |    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 "os.h" | 
 | #include "bzlib.h" | 
 | #include "bzlib_private.h" | 
 |  | 
 | /*---------------------------------------------------*/ | 
 | /*--- Decompression stuff                         ---*/ | 
 | /*---------------------------------------------------*/ | 
 |  | 
 | /*---------------------------------------------------*/ | 
 | int BZ_API(BZ2_bzDecompressInit)  | 
 |                      ( bz_stream* strm,  | 
 |                        int        verbosity, | 
 |                        int        small ) | 
 | { | 
 |    DState* s; | 
 |  | 
 |    if (!bz_config_ok()) return BZ_CONFIG_ERROR; | 
 |  | 
 |    if (strm == NULL) return BZ_PARAM_ERROR; | 
 |    if (small != 0 && small != 1) return BZ_PARAM_ERROR; | 
 |    if (verbosity < 0 || verbosity > 4) return BZ_PARAM_ERROR; | 
 |  | 
 |    if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc; | 
 |    if (strm->bzfree == NULL) strm->bzfree = default_bzfree; | 
 |  | 
 |    s = BZALLOC( sizeof(DState) ); | 
 |    if (s == NULL) return BZ_MEM_ERROR; | 
 |    s->strm                  = strm; | 
 |    strm->state              = s; | 
 |    s->state                 = BZ_X_MAGIC_1; | 
 |    s->bsLive                = 0; | 
 |    s->bsBuff                = 0; | 
 |    s->calculatedCombinedCRC = 0; | 
 |    strm->total_in_lo32      = 0; | 
 |    strm->total_in_hi32      = 0; | 
 |    strm->total_out_lo32     = 0; | 
 |    strm->total_out_hi32     = 0; | 
 |    s->smallDecompress       = (Bool)small; | 
 |    s->ll4                   = NULL; | 
 |    s->ll16                  = NULL; | 
 |    s->tt                    = NULL; | 
 |    s->currBlockNo           = 0; | 
 |    s->verbosity             = verbosity; | 
 |  | 
 |    return BZ_OK; | 
 | } | 
 |  | 
 |  | 
 | /*---------------------------------------------------*/ | 
 | static | 
 | void unRLE_obuf_to_output_FAST ( DState* s ) | 
 | { | 
 |    UChar k1; | 
 |  | 
 |    if (s->blockRandomised) { | 
 |  | 
 |       while (True) { | 
 |          /* try to finish existing run */ | 
 |          while (True) { | 
 |             if (s->strm->avail_out == 0) return; | 
 |             if (s->state_out_len == 0) break; | 
 |             *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; | 
 |             BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); | 
 |             s->state_out_len--; | 
 |             s->strm->next_out++; | 
 |             s->strm->avail_out--; | 
 |             s->strm->total_out_lo32++; | 
 |             if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; | 
 |          } | 
 |     | 
 |          /* can a new run be started? */ | 
 |          if (s->nblock_used == s->save_nblock+1) return; | 
 |                 | 
 |     | 
 |          s->state_out_len = 1; | 
 |          s->state_out_ch = s->k0; | 
 |          BZ_GET_FAST(k1); BZ_RAND_UPD_MASK;  | 
 |          k1 ^= BZ_RAND_MASK; s->nblock_used++; | 
 |          if (s->nblock_used == s->save_nblock+1) continue; | 
 |          if (k1 != s->k0) { s->k0 = k1; continue; }; | 
 |     | 
 |          s->state_out_len = 2; | 
 |          BZ_GET_FAST(k1); BZ_RAND_UPD_MASK;  | 
 |          k1 ^= BZ_RAND_MASK; s->nblock_used++; | 
 |          if (s->nblock_used == s->save_nblock+1) continue; | 
 |          if (k1 != s->k0) { s->k0 = k1; continue; }; | 
 |     | 
 |          s->state_out_len = 3; | 
 |          BZ_GET_FAST(k1); BZ_RAND_UPD_MASK;  | 
 |          k1 ^= BZ_RAND_MASK; s->nblock_used++; | 
 |          if (s->nblock_used == s->save_nblock+1) continue; | 
 |          if (k1 != s->k0) { s->k0 = k1; continue; }; | 
 |     | 
 |          BZ_GET_FAST(k1); BZ_RAND_UPD_MASK;  | 
 |          k1 ^= BZ_RAND_MASK; s->nblock_used++; | 
 |          s->state_out_len = ((Int32)k1) + 4; | 
 |          BZ_GET_FAST(s->k0); BZ_RAND_UPD_MASK;  | 
 |          s->k0 ^= BZ_RAND_MASK; s->nblock_used++; | 
 |       } | 
 |  | 
 |    } else { | 
 |  | 
 |       /* restore */ | 
 |       UInt32        c_calculatedBlockCRC = s->calculatedBlockCRC; | 
 |       UChar         c_state_out_ch       = s->state_out_ch; | 
 |       Int32         c_state_out_len      = s->state_out_len; | 
 |       Int32         c_nblock_used        = s->nblock_used; | 
 |       Int32         c_k0                 = s->k0; | 
 |       UInt32*       c_tt                 = s->tt; | 
 |       UInt32        c_tPos               = s->tPos; | 
 |       char*         cs_next_out          = s->strm->next_out; | 
 |       unsigned int  cs_avail_out         = s->strm->avail_out; | 
 |       /* end restore */ | 
 |  | 
 |       UInt32       avail_out_INIT = cs_avail_out; | 
 |       Int32        s_save_nblockPP = s->save_nblock+1; | 
 |       unsigned int total_out_lo32_old; | 
 |  | 
 |       while (True) { | 
 |  | 
 |          /* try to finish existing run */ | 
 |          if (c_state_out_len > 0) { | 
 |             while (True) { | 
 |                if (cs_avail_out == 0) goto return_notr; | 
 |                if (c_state_out_len == 1) break; | 
 |                *( (UChar*)(cs_next_out) ) = c_state_out_ch; | 
 |                BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch ); | 
 |                c_state_out_len--; | 
 |                cs_next_out++; | 
 |                cs_avail_out--; | 
 |             } | 
 |             s_state_out_len_eq_one: | 
 |             { | 
 |                if (cs_avail_out == 0) {  | 
 |                   c_state_out_len = 1; goto return_notr; | 
 |                }; | 
 |                *( (UChar*)(cs_next_out) ) = c_state_out_ch; | 
 |                BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch ); | 
 |                cs_next_out++; | 
 |                cs_avail_out--; | 
 |             } | 
 |          }    | 
 |          /* can a new run be started? */ | 
 |          if (c_nblock_used == s_save_nblockPP) { | 
 |             c_state_out_len = 0; goto return_notr; | 
 |          };    | 
 |          c_state_out_ch = c_k0; | 
 |          BZ_GET_FAST_C(k1); c_nblock_used++; | 
 |          if (k1 != c_k0) {  | 
 |             c_k0 = k1; goto s_state_out_len_eq_one;  | 
 |          }; | 
 |          if (c_nblock_used == s_save_nblockPP)  | 
 |             goto s_state_out_len_eq_one; | 
 |     | 
 |          c_state_out_len = 2; | 
 |          BZ_GET_FAST_C(k1); c_nblock_used++; | 
 |          if (c_nblock_used == s_save_nblockPP) continue; | 
 |          if (k1 != c_k0) { c_k0 = k1; continue; }; | 
 |     | 
 |          c_state_out_len = 3; | 
 |          BZ_GET_FAST_C(k1); c_nblock_used++; | 
 |          if (c_nblock_used == s_save_nblockPP) continue; | 
 |          if (k1 != c_k0) { c_k0 = k1; continue; }; | 
 |     | 
 |          BZ_GET_FAST_C(k1); c_nblock_used++; | 
 |          c_state_out_len = ((Int32)k1) + 4; | 
 |          BZ_GET_FAST_C(c_k0); c_nblock_used++; | 
 |       } | 
 |  | 
 |       return_notr: | 
 |       total_out_lo32_old = s->strm->total_out_lo32; | 
 |       s->strm->total_out_lo32 += (avail_out_INIT - cs_avail_out); | 
 |       if (s->strm->total_out_lo32 < total_out_lo32_old) | 
 |          s->strm->total_out_hi32++; | 
 |  | 
 |       /* save */ | 
 |       s->calculatedBlockCRC = c_calculatedBlockCRC; | 
 |       s->state_out_ch       = c_state_out_ch; | 
 |       s->state_out_len      = c_state_out_len; | 
 |       s->nblock_used        = c_nblock_used; | 
 |       s->k0                 = c_k0; | 
 |       s->tt                 = c_tt; | 
 |       s->tPos               = c_tPos; | 
 |       s->strm->next_out     = cs_next_out; | 
 |       s->strm->avail_out    = cs_avail_out; | 
 |       /* end save */ | 
 |    } | 
 | } | 
 |  | 
 |  | 
 |  | 
 | /*---------------------------------------------------*/ | 
 | __inline__ Int32 BZ2_indexIntoF ( Int32 indx, Int32 *cftab ) | 
 | { | 
 |    Int32 nb, na, mid; | 
 |    nb = 0; | 
 |    na = 256; | 
 |    do { | 
 |       mid = (nb + na) >> 1; | 
 |       if (indx >= cftab[mid]) nb = mid; else na = mid; | 
 |    } | 
 |    while (na - nb != 1); | 
 |    return nb; | 
 | } | 
 |  | 
 |  | 
 | /*---------------------------------------------------*/ | 
 | static | 
 | void unRLE_obuf_to_output_SMALL ( DState* s ) | 
 | { | 
 |    UChar k1; | 
 |  | 
 |    if (s->blockRandomised) { | 
 |  | 
 |       while (True) { | 
 |          /* try to finish existing run */ | 
 |          while (True) { | 
 |             if (s->strm->avail_out == 0) return; | 
 |             if (s->state_out_len == 0) break; | 
 |             *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; | 
 |             BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); | 
 |             s->state_out_len--; | 
 |             s->strm->next_out++; | 
 |             s->strm->avail_out--; | 
 |             s->strm->total_out_lo32++; | 
 |             if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; | 
 |          } | 
 |     | 
 |          /* can a new run be started? */ | 
 |          if (s->nblock_used == s->save_nblock+1) return; | 
 |                 | 
 |     | 
 |          s->state_out_len = 1; | 
 |          s->state_out_ch = s->k0; | 
 |          BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK;  | 
 |          k1 ^= BZ_RAND_MASK; s->nblock_used++; | 
 |          if (s->nblock_used == s->save_nblock+1) continue; | 
 |          if (k1 != s->k0) { s->k0 = k1; continue; }; | 
 |     | 
 |          s->state_out_len = 2; | 
 |          BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK;  | 
 |          k1 ^= BZ_RAND_MASK; s->nblock_used++; | 
 |          if (s->nblock_used == s->save_nblock+1) continue; | 
 |          if (k1 != s->k0) { s->k0 = k1; continue; }; | 
 |     | 
 |          s->state_out_len = 3; | 
 |          BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK;  | 
 |          k1 ^= BZ_RAND_MASK; s->nblock_used++; | 
 |          if (s->nblock_used == s->save_nblock+1) continue; | 
 |          if (k1 != s->k0) { s->k0 = k1; continue; }; | 
 |     | 
 |          BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK;  | 
 |          k1 ^= BZ_RAND_MASK; s->nblock_used++; | 
 |          s->state_out_len = ((Int32)k1) + 4; | 
 |          BZ_GET_SMALL(s->k0); BZ_RAND_UPD_MASK;  | 
 |          s->k0 ^= BZ_RAND_MASK; s->nblock_used++; | 
 |       } | 
 |  | 
 |    } else { | 
 |  | 
 |       while (True) { | 
 |          /* try to finish existing run */ | 
 |          while (True) { | 
 |             if (s->strm->avail_out == 0) return; | 
 |             if (s->state_out_len == 0) break; | 
 |             *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; | 
 |             BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); | 
 |             s->state_out_len--; | 
 |             s->strm->next_out++; | 
 |             s->strm->avail_out--; | 
 |             s->strm->total_out_lo32++; | 
 |             if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; | 
 |          } | 
 |     | 
 |          /* can a new run be started? */ | 
 |          if (s->nblock_used == s->save_nblock+1) return; | 
 |     | 
 |          s->state_out_len = 1; | 
 |          s->state_out_ch = s->k0; | 
 |          BZ_GET_SMALL(k1); s->nblock_used++; | 
 |          if (s->nblock_used == s->save_nblock+1) continue; | 
 |          if (k1 != s->k0) { s->k0 = k1; continue; }; | 
 |     | 
 |          s->state_out_len = 2; | 
 |          BZ_GET_SMALL(k1); s->nblock_used++; | 
 |          if (s->nblock_used == s->save_nblock+1) continue; | 
 |          if (k1 != s->k0) { s->k0 = k1; continue; }; | 
 |     | 
 |          s->state_out_len = 3; | 
 |          BZ_GET_SMALL(k1); s->nblock_used++; | 
 |          if (s->nblock_used == s->save_nblock+1) continue; | 
 |          if (k1 != s->k0) { s->k0 = k1; continue; }; | 
 |     | 
 |          BZ_GET_SMALL(k1); s->nblock_used++; | 
 |          s->state_out_len = ((Int32)k1) + 4; | 
 |          BZ_GET_SMALL(s->k0); s->nblock_used++; | 
 |       } | 
 |  | 
 |    } | 
 | } | 
 |  | 
 |  | 
 | /*---------------------------------------------------*/ | 
 | int BZ_API(BZ2_bzDecompress) ( bz_stream *strm ) | 
 | { | 
 |    DState* s; | 
 |    if (strm == NULL) return BZ_PARAM_ERROR; | 
 |    s = strm->state; | 
 |    if (s == NULL) return BZ_PARAM_ERROR; | 
 |    if (s->strm != strm) return BZ_PARAM_ERROR; | 
 |  | 
 |    while (True) { | 
 |       if (s->state == BZ_X_IDLE) return BZ_SEQUENCE_ERROR; | 
 |       if (s->state == BZ_X_OUTPUT) { | 
 |          if (s->smallDecompress) | 
 |             unRLE_obuf_to_output_SMALL ( s ); else | 
 |             unRLE_obuf_to_output_FAST  ( s ); | 
 |          if (s->nblock_used == s->save_nblock+1 && s->state_out_len == 0) { | 
 |             BZ_FINALISE_CRC ( s->calculatedBlockCRC ); | 
 |             if (s->verbosity >= 3)  | 
 |                VPrintf2 ( " {0x%x, 0x%x}", s->storedBlockCRC,  | 
 |                           s->calculatedBlockCRC ); | 
 |             if (s->verbosity >= 2) VPrintf0 ( "]" ); | 
 |             if (s->calculatedBlockCRC != s->storedBlockCRC) | 
 |                return BZ_DATA_ERROR; | 
 |             s->calculatedCombinedCRC  | 
 |                = (s->calculatedCombinedCRC << 1) |  | 
 |                     (s->calculatedCombinedCRC >> 31); | 
 |             s->calculatedCombinedCRC ^= s->calculatedBlockCRC; | 
 |             s->state = BZ_X_BLKHDR_1; | 
 |          } else { | 
 |             return BZ_OK; | 
 |          } | 
 |       } | 
 |       if (s->state >= BZ_X_MAGIC_1) { | 
 |          Int32 r = BZ2_decompress ( s ); | 
 |          if (r == BZ_STREAM_END) { | 
 |             if (s->verbosity >= 3) | 
 |                VPrintf2 ( "\n    combined CRCs: stored = 0x%x, computed = 0x%x",  | 
 |                           s->storedCombinedCRC, s->calculatedCombinedCRC ); | 
 |             if (s->calculatedCombinedCRC != s->storedCombinedCRC) | 
 |                return BZ_DATA_ERROR; | 
 |             return r; | 
 |          } | 
 |          if (s->state != BZ_X_OUTPUT) return r; | 
 |       } | 
 |    } | 
 |  | 
 | /* | 
 |    AssertH ( 0, 6001 ); | 
 |  | 
 |    return 0;   | 
 | */ | 
 | } | 
 |  | 
 |  | 
 | /*---------------------------------------------------*/ | 
 | int BZ_API(BZ2_bzDecompressEnd)  ( bz_stream *strm ) | 
 | { | 
 |    DState* s; | 
 |    if (strm == NULL) return BZ_PARAM_ERROR; | 
 |    s = strm->state; | 
 |    if (s == NULL) return BZ_PARAM_ERROR; | 
 |    if (s->strm != strm) return BZ_PARAM_ERROR; | 
 |  | 
 |    if (s->tt   != NULL) BZFREE(s->tt); | 
 |    if (s->ll16 != NULL) BZFREE(s->ll16); | 
 |    if (s->ll4  != NULL) BZFREE(s->ll4); | 
 |  | 
 |    BZFREE(strm->state); | 
 |    strm->state = NULL; | 
 |  | 
 |    return BZ_OK; | 
 | } | 
 |  |