/*
nochump.util.zip.Inflater
Copyright (C) 2007 David Chang (dchang@nochump.com)
This file is part of nochump.util.zip.
nochump.util.zip is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
nochump.util.zip is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Foobar. If not, see .
*/
package nochump.util.zip {
import flash.utils.Endian;
import flash.utils.ByteArray;
/**
* Inflater is used to decompress data that has been compressed according
* to the "deflate" standard described in rfc1950.
*
* The usage is as following. First you have to set some input with
* setInput()
, then inflate() it.
*
* This implementation is a port of Puff by Mark Addler that comes with
* the zlip data compression library. It is not the fastest routine as
* he intended it for learning purposes, his actual optimized inflater code
* is very different. I went with this approach basically because I got a
* headache looking at the optimized inflater code and porting this
* was a breeze. The speed should be adequate but there is plenty of room
* for improvements here.
*
* @author dchang
*/
public class Inflater {
private static const MAXBITS:int = 15; // maximum bits in a code
private static const MAXLCODES:int = 286; // maximum number of literal/length codes
private static const MAXDCODES:int = 30; // maximum number of distance codes
private static const MAXCODES:int = MAXLCODES + MAXDCODES; // maximum codes lengths to read
private static const FIXLCODES:int = 288; // number of fixed literal/length codes
// Size base for length codes 257..285
private static const LENS:Array = [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258];
// Extra bits for length codes 257..285
private static const LEXT:Array = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0];
// Offset base for distance codes 0..29
private static const DISTS:Array = [1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577];
// Extra bits for distance codes 0..29
private static const DEXT:Array = [ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13];
private var inbuf:ByteArray; // input buffer
private var incnt:uint; // bytes read so far
private var bitbuf:int; // bit buffer
private var bitcnt:int; // number of bits in bit buffer
// Huffman code decoding tables
private var lencode:Object;
private var distcode:Object;
/**
* Sets the input.
*
* @param buf the input.
*/
public function setInput(buf:ByteArray):void {
inbuf = buf;
inbuf.endian = Endian.LITTLE_ENDIAN;
}
/**
* Inflates the compressed stream to the output buffer.
*
* @param buf the output buffer.
*/
public function inflate(buf:ByteArray):uint {
incnt = bitbuf = bitcnt = 0;
var err:int = 0;
do { // process blocks until last block or error
var last:int = bits(1); // one if last block
var type:int = bits(2); // block type 0..3
if(type == 0) stored(buf); // uncompressed block
else if(type == 3) throw new Error('invalid block type (type == 3)', -1);
else { // compressed block
lencode = {count:[], symbol:[]};
distcode = {count:[], symbol:[]};
if(type == 1) constructFixedTables();
else if(type == 2) err = constructDynamicTables();
if(err != 0) return err;
err = codes(buf); // decode data until end-of-block code
}
if(err != 0) break; // return with error
} while(!last);
return err;
}
private function bits(need:int):int {
// bit accumulator (can use up to 20 bits)
// load at least need bits into val
var val:int = bitbuf;
while(bitcnt < need) {
if (incnt == inbuf.length) throw new Error('available inflate data did not terminate', 2);
val |= inbuf[incnt++] << bitcnt; // load eight bits
bitcnt += 8;
}
// drop need bits and update buffer, always zero to seven bits left
bitbuf = val >> need;
bitcnt -= need;
// return need bits, zeroing the bits above that
return val & ((1 << need) - 1);
}
private function construct(h:Object, length:Array, n:int):int {
var offs:Array = []; // offsets in symbol table for each length
// count number of codes of each length
for(var len:int = 0; len <= MAXBITS; len++) h.count[len] = 0;
// assumes lengths are within bounds
for(var symbol:int = 0; symbol < n; symbol++) h.count[length[symbol]]++;
// no codes! complete, but decode() will fail
if(h.count[0] == n) return 0;
// check for an over-subscribed or incomplete set of lengths
var left:int = 1; // one possible code of zero length
for(len = 1; len <= MAXBITS; len++) {
left <<= 1; // one more bit, double codes left
left -= h.count[len]; // deduct count from possible codes
if(left < 0) return left; // over-subscribed--return negative
} // left > 0 means incomplete
// generate offsets into symbol table for each length for sorting
offs[1] = 0;
for(len = 1; len < MAXBITS; len++) offs[len + 1] = offs[len] + h.count[len];
// put symbols in table sorted by length, by symbol order within each length
for(symbol = 0; symbol < n; symbol++)
if(length[symbol] != 0) h.symbol[offs[length[symbol]]++] = symbol;
// return zero for complete set, positive for incomplete set
return left;
}
private function decode(h:Object):int {
var code:int = 0; // len bits being decoded
var first:int = 0; // first code of length len
var index:int = 0; // index of first code of length len in symbol table
for(var len:int = 1; len <= MAXBITS; len++) { // current number of bits in code
code |= bits(1); // get next bit
var count:int = h.count[len]; // number of codes of length len
// if length len, return symbol
if(code < first + count) return h.symbol[index + (code - first)];
index += count; // else update for next length
first += count;
first <<= 1;
code <<= 1;
}
return -9; // ran out of codes
}
private function codes(buf:ByteArray):int {
// decode literals and length/distance pairs
do {
var symbol:int = decode(lencode);
if(symbol < 0) return symbol; // invalid symbol
if(symbol < 256) buf[buf.length] = symbol; // literal: symbol is the byte
else if(symbol > 256) { // length
// get and compute length
symbol -= 257;
if(symbol >= 29) throw new Error("invalid literal/length or distance code in fixed or dynamic block", -9);
var len:int = LENS[symbol] + bits(LEXT[symbol]); // length for copy
// get and check distance
symbol = decode(distcode);
if(symbol < 0) return symbol; // invalid symbol
var dist:uint = DISTS[symbol] + bits(DEXT[symbol]); // distance for copy
if(dist > buf.length) throw new Error("distance is too far back in fixed or dynamic block", -10);
// copy length bytes from distance bytes back
while(len--) buf[buf.length] = buf[buf.length - dist];
}
} while (symbol != 256); // end of block symbol
return 0; // done with a valid fixed or dynamic block
}
private function stored(buf:ByteArray):void {
// discard leftover bits from current byte (assumes s->bitcnt < 8)
bitbuf = 0;
bitcnt = 0;
// get length and check against its one's complement
if(incnt + 4 > inbuf.length) throw new Error('available inflate data did not terminate', 2);
var len:uint = inbuf[incnt++]; // length of stored block
len |= inbuf[incnt++] << 8;
if(inbuf[incnt++] != (~len & 0xff) || inbuf[incnt++] != ((~len >> 8) & 0xff))
throw new Error("stored block length did not match one's complement", -2);
if(incnt + len > inbuf.length) throw new Error('available inflate data did not terminate', 2);
while(len--) buf[buf.length] = inbuf[incnt++]; // copy len bytes from in to out
}
private function constructFixedTables():void {
var lengths:Array = [];
// literal/length table
for(var symbol:int = 0; symbol < 144; symbol++) lengths[symbol] = 8;
for(; symbol < 256; symbol++) lengths[symbol] = 9;
for(; symbol < 280; symbol++) lengths[symbol] = 7;
for(; symbol < FIXLCODES; symbol++) lengths[symbol] = 8;
construct(lencode, lengths, FIXLCODES);
// distance table
for(symbol = 0; symbol < MAXDCODES; symbol++) lengths[symbol] = 5;
construct(distcode, lengths, MAXDCODES);
}
private function constructDynamicTables():int {
var lengths:Array = []; // descriptor code lengths
// permutation of code length codes
var order:Array = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
// get number of lengths in each table, check lengths
var nlen:int = bits(5) + 257;
var ndist:int = bits(5) + 1;
var ncode:int = bits(4) + 4; // number of lengths in descriptor
if(nlen > MAXLCODES || ndist > MAXDCODES) throw new Error("dynamic block code description: too many length or distance codes", -3);
// read code length code lengths (really), missing lengths are zero
for(var index:int = 0; index < ncode; index++) lengths[order[index]] = bits(3);
for(; index < 19; index++) lengths[order[index]] = 0;
// build huffman table for code lengths codes (use lencode temporarily)
var err:int = construct(lencode, lengths, 19);
if(err != 0) throw new Error("dynamic block code description: code lengths codes incomplete", -4);
// read length/literal and distance code length tables
index = 0;
while(index < nlen + ndist) {
var symbol:int; // decoded value
var len:int; // last length to repeat
symbol = decode(lencode);
if(symbol < 16) lengths[index++] = symbol; // length in 0..15
else { // repeat instruction
len = 0; // assume repeating zeros
if(symbol == 16) { // repeat last length 3..6 times
if(index == 0) throw new Error("dynamic block code description: repeat lengths with no first length", -5);
len = lengths[index - 1]; // last length
symbol = 3 + bits(2);
}
else if(symbol == 17) symbol = 3 + bits(3); // repeat zero 3..10 times
else symbol = 11 + bits(7); // == 18, repeat zero 11..138 times
if(index + symbol > nlen + ndist)
throw new Error("dynamic block code description: repeat more than specified lengths", -6);
while(symbol--) lengths[index++] = len; // repeat last or zero symbol times
}
}
// build huffman table for literal/length codes
err = construct(lencode, lengths, nlen);
// only allow incomplete codes if just one code
if(err < 0 || (err > 0 && nlen - lencode.count[0] != 1))
throw new Error("dynamic block code description: invalid literal/length code lengths", -7);
// build huffman table for distance codes
err = construct(distcode, lengths.slice(nlen), ndist);
// only allow incomplete codes if just one code
if(err < 0 || (err > 0 && ndist - distcode.count[0] != 1))
throw new Error("dynamic block code description: invalid distance code lengths", -8);
return err;
}
}
}