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.
This commit is contained in:
Paul Sokolovsky 2016-08-03 01:38:11 +03:00
parent b4082097c7
commit 9daa35b843
2 changed files with 137 additions and 136 deletions

View file

@ -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,

View file

@ -32,6 +32,7 @@
* any source distribution.
*/
#include <assert.h>
#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;
}