diff options
Diffstat (limited to 'lib/deflate.c')
-rw-r--r-- | lib/deflate.c | 70 |
1 files changed, 49 insertions, 21 deletions
diff --git a/lib/deflate.c b/lib/deflate.c index cdc4f8d3..a418c21c 100644 --- a/lib/deflate.c +++ b/lib/deflate.c @@ -17,7 +17,7 @@ struct deflate { void *fixdisthuff, *fixlithuff; // CRC - void (*crcfunc)(struct deflate *dd, char *data, int len); + void (*crcfunc)(struct deflate *dd, char *data, unsigned len); unsigned crctable[256], crc; @@ -49,16 +49,21 @@ static struct bitbuf *bitbuf_init(int fd, int size) // Advance bitpos without the overhead of recording bits // Loads more data when input buffer empty -static void bitbuf_skip(struct bitbuf *bb, int bits) +// call with 0 to just load data, returns 0 at EOF +static int bitbuf_skip(struct bitbuf *bb, int bits) { - int pos = bb->bitpos + bits, len = bb->len << 3; + int pos = bb->bitpos + bits + (bits<0), len; - while (pos >= len) { + while (pos >= (len = bb->len<<3)) { pos -= len; - len = (bb->len = read(bb->fd, bb->buf, bb->max)) << 3; - if (bb->len < 1) perror_exit("inflate EOF"); + if (1 > (bb->len = read(bb->fd, bb->buf, bb->max))) { + if (!bb->len && !bits) break; + error_exit("inflate EOF"); + } } bb->bitpos = pos; + + return pos<len; } // Optimized single bit inlined version @@ -67,7 +72,7 @@ static inline int bitbuf_bit(struct bitbuf *bb) int bufpos = bb->bitpos>>3; if (bufpos == bb->len) { - bitbuf_skip(bb, 0); + bitbuf_skip(bb, -1); bufpos = 0; } @@ -83,7 +88,10 @@ static unsigned bitbuf_get(struct bitbuf *bb, int bits) int click = bb->bitpos >> 3, blow, blen; // Load more data if buffer empty - if (click == bb->len) bitbuf_skip(bb, click = 0); + if (click == bb->len) { + bitbuf_skip(bb, -1); + click = 0; + } // grab bits from next byte blow = bb->bitpos & 7; @@ -194,6 +202,7 @@ static unsigned huff_and_puff(struct bitbuf *bb, struct huff *huff) static void inflate(struct deflate *dd, struct bitbuf *bb) { dd->crc = ~0; + // repeat until spanked for (;;) { int final, type; @@ -322,7 +331,7 @@ static void deflate(struct deflate *dd, struct bitbuf *bb) while (!final) { // Read next half-window of data if we haven't hit EOF yet. len = readall(dd->infd, data+(dd->len&32768), 32768); - if (len < 0) perror_exit("read"); // todo: add filename + if (len < 0) perror_exit("read"); // TODO: add filename if (len != 32768) final++; if (dd->crcfunc) dd->crcfunc(dd, data+(dd->len&32768), len); // dd->len += len; crcfunc advances len TODO @@ -411,17 +420,33 @@ static int is_gzip(struct bitbuf *bb) return 1; } -static void gzip_crc(struct deflate *dd, char *data, int len) +static void gzip_crc(struct deflate *dd, char *data, unsigned len) { int i; unsigned crc, *crc_table = dd->crctable; crc = dd->crc; - for (i=0; i<len; i++) crc = crc_table[(crc^data[i])&0xff] ^ (crc>>8); + for (i = 0; i<len; i++) crc = crc_table[(crc^data[i])&0xff] ^ (crc>>8); dd->crc = crc; dd->len += len; } +/* +// Start with crc = 1, or pass in last crc to append more data +// Deferred modulus good for paged size inputs (can't overflow for ~5500 bytes) +unsigned adler32(char *buf, unsigned len, unsigned crc) +{ + unsigned aa = crc&((1<<16)-1), bb = crc>>16; + + while (len--) { + aa += *buf++; + bb += aa; + } + + return ((bb%65521)<<16)+aa%65521; +} +*/ + long long gzip_fd(int infd, int outfd) { struct bitbuf *bb = bitbuf_init(outfd, 4096); @@ -460,24 +485,27 @@ long long gunzip_fd(int infd, int outfd) { struct bitbuf *bb = bitbuf_init(infd, 4096); struct deflate *dd = init_deflate(0); - long long rc; - - if (!is_gzip(bb)) error_exit("not gzip"); - dd->outfd = outfd; + long long rc = 0; // Little endian crc table crc_init(dd->crctable, 1); dd->crcfunc = gzip_crc; + dd->outfd = outfd; - inflate(dd, bb); + do { + if (!is_gzip(bb)) error_exit("not gzip"); - // tail: crc32, len32 + inflate(dd, bb); - bitbuf_skip(bb, (8-bb->bitpos)&7); - if (~dd->crc != bitbuf_get(bb, 32) || dd->len != bitbuf_get(bb, 32)) - error_exit("bad crc"); + // tail: crc32, len32 + bitbuf_skip(bb, (8-bb->bitpos)&7); + if (~dd->crc != bitbuf_get(bb, 32) || dd->len != bitbuf_get(bb, 32)) + error_exit("bad crc"); + rc += dd->len; - rc = dd->len; + bitbuf_skip(bb, (8-bb->bitpos)&7); + dd->pos = dd->len = 0; + } while (bitbuf_skip(bb, 0)); free(bb); free(dd); |