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:
parent
b4082097c7
commit
9daa35b843
2 changed files with 137 additions and 136 deletions
23
src/tinf.h
23
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,
|
||||
|
|
|
|||
250
src/tinflate.c
250
src/tinflate.c
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue