From 9daa35b843746208592168ac2ceefdbf2b21ebdc Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Wed, 3 Aug 2016 01:38:11 +0300 Subject: [PATCH] tinflate: Refactor to support stream mode. Now calling tinf_uncompress_dyn() will produce d.destSize uncompressed bytes in d->dest (autoincremented). Before using tinf_uncompress_dyn(), tinf_uncompress_dyn_init() should be called, with a buffer for sliding dictionary. --- src/tinf.h | 23 ++++- src/tinflate.c | 250 +++++++++++++++++++++++-------------------------- 2 files changed, 137 insertions(+), 136 deletions(-) diff --git a/src/tinf.h b/src/tinf.h index e9401f2..e42c8f2 100644 --- a/src/tinf.h +++ b/src/tinf.h @@ -26,7 +26,10 @@ extern "C" { #endif +/* ok status, more data produced */ #define TINF_OK 0 +/* end of compressed stream reached */ +#define TINF_DONE 1 #define TINF_DATA_ERROR (-3) #define TINF_DEST_OVERFLOW (-4) @@ -57,20 +60,32 @@ typedef struct TINF_DATA { fail again. */ int (*destGrow)(struct TINF_DATA *data, unsigned int lastAlloc); + int btype; + int bfinal; + unsigned int curlen; + int lzOff; + unsigned char *dict_ring; + unsigned int dict_size; + unsigned int dict_idx; + TINF_TREE ltree; /* dynamic length/symbol tree */ TINF_TREE dtree; /* dynamic distance tree */ } TINF_DATA; +#define TINF_PUT(d, c) \ + { *d->dest++ = c; d->dict_ring[d->dict_idx++] = c; if (d->dict_idx == d->dict_size) d->dict_idx = 0; } + /* low-level API */ /* Step 1: Allocate TINF_DATA structure */ -/* Step 2: Set destStart, destSize, and destGrow fields */ -/* Step 3: Set source field */ +/* Step 2: Set source field */ +/* Step 3: Call tinf_uncompress_dyn_init() */ /* Step 4: Call tinf_uncompress_dyn() */ /* Step 5: In response to destGrow callback, update destStart and destSize fields */ /* Step 6: When tinf_uncompress_dyn() returns, buf.dest points to a byte past last uncompressed byte */ +void tinf_uncompress_dyn_init(TINF_DATA *d, void *dict, unsigned int dictLen); int TINFCC tinf_uncompress_dyn(TINF_DATA *d); int TINFCC tinf_zlib_uncompress_dyn(TINF_DATA *d, unsigned int sourceLen); @@ -78,10 +93,10 @@ int TINFCC tinf_zlib_uncompress_dyn(TINF_DATA *d, unsigned int sourceLen); void TINFCC tinf_init(void); -int TINFCC tinf_uncompress(void *dest, unsigned int *destLen, +int TINFCC tinf_uncompress(TINF_DATA *d, void *dest, unsigned int *destLen, const void *source, unsigned int sourceLen); -int TINFCC tinf_gzip_uncompress(void *dest, unsigned int *destLen, +int TINFCC tinf_gzip_uncompress(TINF_DATA *d, void *dest, unsigned int *destLen, const void *source, unsigned int sourceLen); int TINFCC tinf_zlib_uncompress(void *dest, unsigned int *destLen, diff --git a/src/tinflate.c b/src/tinflate.c index f91b78e..11e05be 100644 --- a/src/tinflate.c +++ b/src/tinflate.c @@ -32,6 +32,7 @@ * any source distribution. */ +#include #include "tinf.h" /* --------------------------------------------------- * @@ -323,109 +324,74 @@ static void tinf_decode_trees(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt) /* given a stream and two trees, inflate a block of data */ static int tinf_inflate_block_data(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt) { - while (1) - { - int sym = tinf_decode_symbol(d, lt); + if (d->curlen == 0) { + unsigned int offs; + int dist; + int sym = tinf_decode_symbol(d, lt); + //printf("huff sym: %02x\n", sym); - /* check for end of block */ - if (sym == 256) - { - return TINF_OK; - } + /* literal byte */ + if (sym < 256) { + TINF_PUT(d, sym); + return TINF_OK; + } - if (sym < 256) - { - if (d->destRemaining == 0) - { - int res = tinf_grow_dest_buf(d, 1); - if (res) return res; - } + /* end of block */ + if (sym == 256) { + return TINF_DONE; + } - *d->dest++ = sym; - d->destRemaining--; + /* substring from sliding dictionary */ + sym -= 257; + /* possibly get more bits from length code */ + d->curlen = tinf_read_bits(d, length_bits[sym], length_base[sym]); - } else { + dist = tinf_decode_symbol(d, dt); + /* possibly get more bits from distance code */ + offs = tinf_read_bits(d, dist_bits[dist], dist_base[dist]); + d->lzOff = d->dict_idx - offs; + if (d->lzOff < 0) { + d->lzOff += d->dict_size; + } + } - unsigned int length, offs, i; - int dist; - - sym -= 257; - - /* possibly get more bits from length code */ - length = tinf_read_bits(d, length_bits[sym], length_base[sym]); - - dist = tinf_decode_symbol(d, dt); - - /* possibly get more bits from distance code */ - offs = tinf_read_bits(d, dist_bits[dist], dist_base[dist]); - - if (d->destRemaining < length) - { - int res = tinf_grow_dest_buf(d, length); - if (res) return res; - } - - /* copy match */ - for (i = 0; i < length; ++i) - { - d->dest[i] = d->dest[(int)(i - offs)]; - } - - d->dest += length; - d->destRemaining -= length; - } - } + /* copy next byte from dict substring */ + TINF_PUT(d, d->dict_ring[d->lzOff]); + if (++d->lzOff == d->dict_size) { + d->lzOff = 0; + } + d->curlen--; + return TINF_OK; } /* inflate an uncompressed block of data */ static int tinf_inflate_uncompressed_block(TINF_DATA *d) { - unsigned int length, invlength; - unsigned int i; + if (d->curlen == 0) { + unsigned int length, invlength; - /* get length */ - length = tinf_read_src_byte(d) + 256 * tinf_read_src_byte(d); + /* get length */ + length = tinf_read_src_byte(d) + 256 * tinf_read_src_byte(d); + /* get one's complement of length */ + invlength = tinf_read_src_byte(d) + 256 * tinf_read_src_byte(d); + /* check length */ + if (length != (~invlength & 0x0000ffff)) return TINF_DATA_ERROR; - /* get one's complement of length */ - invlength = tinf_read_src_byte(d) + 256 * tinf_read_src_byte(d); + /* increment length to properly return TINF_DONE below, without + producing data at the same time */ + d->curlen = length + 1; - /* check length */ - if (length != (~invlength & 0x0000ffff)) return TINF_DATA_ERROR; + /* make sure we start next block on a byte boundary */ + d->bitcount = 0; + } - if (d->destRemaining < length) - { - int res = tinf_grow_dest_buf(d, length); - if (res) return res; - } + if (--d->curlen == 0) { + return TINF_DONE; + } - /* copy block */ - for (i = length; i; --i) *d->dest++ = tinf_read_src_byte(d); - d->destRemaining -= length; - - /* make sure we start next block on a byte boundary */ - d->bitcount = 0; - - return TINF_OK; -} - -/* inflate a block of data compressed with fixed huffman trees */ -static int tinf_inflate_fixed_block(TINF_DATA *d) -{ - /* build fixed huffman trees */ - tinf_build_fixed_trees(&d->ltree, &d->dtree); - - /* decode block using fixed trees */ - return tinf_inflate_block_data(d, &d->ltree, &d->dtree); -} - -/* inflate a block of data compressed with dynamic huffman trees */ -static int tinf_inflate_dynamic_block(TINF_DATA *d) -{ - /* decode trees from stream */ - tinf_decode_trees(d, &d->ltree, &d->dtree); - - /* decode block using decoded trees */ - return tinf_inflate_block_data(d, &d->ltree, &d->dtree); + unsigned char c = tinf_read_src_byte(d); + TINF_PUT(d, c); + return TINF_OK; } /* ---------------------- * @@ -447,71 +413,91 @@ void tinf_init(void) } /* inflate stream from source to dest */ -int tinf_uncompress(void *dest, unsigned int *destLen, +int tinf_uncompress(TINF_DATA *d, void *dest, unsigned int *destLen, const void *source, unsigned int sourceLen) { (void)sourceLen; - TINF_DATA d; int res; /* initialise data */ - d.source = (const unsigned char *)source; + d->source = (const unsigned char *)source; - d.destStart = (unsigned char *)dest; - d.destRemaining = *destLen; - d.destSize = *destLen; + d->destStart = (unsigned char *)dest; + d->destRemaining = *destLen; + d->destSize = *destLen; - res = tinf_uncompress_dyn(&d); + res = tinf_uncompress_dyn(d); - *destLen = d.dest - d.destStart; +// *destLen = d.dest - d.destStart; return res; } -/* inflate stream from source to dest */ +/* initialize decompression structure */ +void tinf_uncompress_dyn_init(TINF_DATA *d, void *dict, unsigned int dictLen) +{ + d->bitcount = 0; + d->bfinal = 0; + d->btype = -1; + d->dict_size = dictLen; + d->dict_ring = dict; + d->dict_idx = 0; + d->curlen = 0; +} + +/* inflate next byte of compressed stream */ int tinf_uncompress_dyn(TINF_DATA *d) { - int bfinal; + do { + int res; - /* initialise data */ - d->bitcount = 0; + /* start a new block */ + if (d->btype == -1) { +next_blk: + /* read final block flag */ + d->bfinal = tinf_getbit(d); + /* read block type (2 bits) */ + d->btype = tinf_read_bits(d, 2, 0); - d->dest = d->destStart; - d->destRemaining = d->destSize; + //printf("Started new block: type=%d final=%d\n", d->btype, d->bfinal); - do { + if (d->btype == 1) { + /* build fixed huffman trees */ + tinf_build_fixed_trees(&d->ltree, &d->dtree); + } else if (d->btype == 2) { + /* decode trees from stream */ + tinf_decode_trees(d, &d->ltree, &d->dtree); + } + } - unsigned int btype; - int res; + /* process current block */ + switch (d->btype) + { + case 0: + /* decompress uncompressed block */ + res = tinf_inflate_uncompressed_block(d); + break; + case 1: + case 2: + /* decompress block with fixed/dyanamic huffman trees */ + /* trees were decoded previously, so it's the same routine for both */ + res = tinf_inflate_block_data(d, &d->ltree, &d->dtree); + break; + default: + return TINF_DATA_ERROR; + } - /* read final block flag */ - bfinal = tinf_getbit(d); + if (res == TINF_DONE && !d->bfinal) { + /* the block has ended (without producing more data), but we + can't return without data, so start procesing next block */ + goto next_blk; + } - /* read block type (2 bits) */ - btype = tinf_read_bits(d, 2, 0); + if (res != TINF_OK) { + return res; + } - /* decompress block */ - switch (btype) - { - case 0: - /* decompress uncompressed block */ - res = tinf_inflate_uncompressed_block(d); - break; - case 1: - /* decompress block with fixed huffman trees */ - res = tinf_inflate_fixed_block(d); - break; - case 2: - /* decompress block with dynamic huffman trees */ - res = tinf_inflate_dynamic_block(d); - break; - default: - return TINF_DATA_ERROR; - } + } while (--d->destSize); - if (res != TINF_OK) return TINF_DATA_ERROR; - - } while (!bfinal); - - return TINF_OK; + return TINF_OK; }