var Buffer = require("buffer").Buffer; | |
function JSInflater(/*Buffer*/input) { | |
var WSIZE = 0x8000, | |
slide = new Buffer(0x10000), | |
windowPos = 0, | |
fixedTableList = null, | |
fixedTableDist, | |
fixedLookup, | |
bitBuf = 0, | |
bitLen = 0, | |
method = -1, | |
eof = false, | |
copyLen = 0, | |
copyDist = 0, | |
tblList, tblDist, bitList, bitdist, | |
inputPosition = 0, | |
MASK_BITS = [0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff], | |
LENS = [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, 0, 0], | |
LEXT = [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, 99, 99], | |
DISTS = [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], | |
DEXT = [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], | |
BITORDER = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; | |
function HuffTable(clen, cnum, cval, blist, elist, lookupm) { | |
this.status = 0; | |
this.root = null; | |
this.maxbit = 0; | |
var el, f, tail, | |
offsets = [], | |
countTbl = [], | |
sTbl = [], | |
values = [], | |
tentry = {extra: 0, bitcnt: 0, lbase: 0, next: null}; | |
tail = this.root = null; | |
for(var i = 0; i < 0x11; i++) { countTbl[i] = 0; sTbl[i] = 0; offsets[i] = 0; } | |
for(i = 0; i < 0x120; i++) values[i] = 0; | |
el = cnum > 256 ? clen[256] : 16; | |
var pidx = -1; | |
while (++pidx < cnum) countTbl[clen[pidx]]++; | |
if(countTbl[0] == cnum) return; | |
for(var j = 1; j <= 16; j++) if(countTbl[j] != 0) break; | |
var bitLen = j; | |
for(i = 16; i != 0; i--) if(countTbl[i] != 0) break; | |
var maxLen = i; | |
lookupm < j && (lookupm = j); | |
var dCodes = 1 << j; | |
for(; j < i; j++, dCodes <<= 1) | |
if((dCodes -= countTbl[j]) < 0) { | |
this.status = 2; | |
this.maxbit = lookupm; | |
return; | |
} | |
if((dCodes -= countTbl[i]) < 0) { | |
this.status = 2; | |
this.maxbit = lookupm; | |
return; | |
} | |
countTbl[i] += dCodes; | |
offsets[1] = j = 0; | |
pidx = 1; | |
var xp = 2; | |
while(--i > 0) offsets[xp++] = (j += countTbl[pidx++]); | |
pidx = 0; | |
i = 0; | |
do { | |
(j = clen[pidx++]) && (values[offsets[j]++] = i); | |
} while(++i < cnum); | |
cnum = offsets[maxLen]; | |
offsets[0] = i = 0; | |
pidx = 0; | |
var level = -1, | |
w = sTbl[0] = 0, | |
cnode = null, | |
tblCnt = 0, | |
tblStack = []; | |
for(; bitLen <= maxLen; bitLen++) { | |
var kccnt = countTbl[bitLen]; | |
while(kccnt-- > 0) { | |
while(bitLen > w + sTbl[1 + level]) { | |
w += sTbl[1 + level]; | |
level++; | |
tblCnt = (tblCnt = maxLen - w) > lookupm ? lookupm : tblCnt; | |
if((f = 1 << (j = bitLen - w)) > kccnt + 1) { | |
f -= kccnt + 1; | |
xp = bitLen; | |
while(++j < tblCnt) { | |
if((f <<= 1) <= countTbl[++xp]) break; | |
f -= countTbl[xp]; | |
} | |
} | |
if(w + j > el && w < el) j = el - w; | |
tblCnt = 1 << j; | |
sTbl[1 + level] = j; | |
cnode = []; | |
while (cnode.length < tblCnt) cnode.push({extra: 0, bitcnt: 0, lbase: 0, next: null}); | |
if (tail == null) { | |
tail = this.root = {next:null, list:null}; | |
} else { | |
tail = tail.next = {next:null, list:null} | |
} | |
tail.next = null; | |
tail.list = cnode; | |
tblStack[level] = cnode; | |
if(level > 0) { | |
offsets[level] = i; | |
tentry.bitcnt = sTbl[level]; | |
tentry.extra = 16 + j; | |
tentry.next = cnode; | |
j = (i & ((1 << w) - 1)) >> (w - sTbl[level]); | |
tblStack[level-1][j].extra = tentry.extra; | |
tblStack[level-1][j].bitcnt = tentry.bitcnt; | |
tblStack[level-1][j].lbase = tentry.lbase; | |
tblStack[level-1][j].next = tentry.next; | |
} | |
} | |
tentry.bitcnt = bitLen - w; | |
if(pidx >= cnum) | |
tentry.extra = 99; | |
else if(values[pidx] < cval) { | |
tentry.extra = (values[pidx] < 256 ? 16 : 15); | |
tentry.lbase = values[pidx++]; | |
} else { | |
tentry.extra = elist[values[pidx] - cval]; | |
tentry.lbase = blist[values[pidx++] - cval]; | |
} | |
f = 1 << (bitLen - w); | |
for(j = i >> w; j < tblCnt; j += f) { | |
cnode[j].extra = tentry.extra; | |
cnode[j].bitcnt = tentry.bitcnt; | |
cnode[j].lbase = tentry.lbase; | |
cnode[j].next = tentry.next; | |
} | |
for(j = 1 << (bitLen - 1); (i & j) != 0; j >>= 1) | |
i ^= j; | |
i ^= j; | |
while((i & ((1 << w) - 1)) != offsets[level]) { | |
w -= sTbl[level]; | |
level--; | |
} | |
} | |
} | |
this.maxbit = sTbl[1]; | |
this.status = ((dCodes != 0 && maxLen != 1) ? 1 : 0); | |
} | |
function addBits(n) { | |
while(bitLen < n) { | |
bitBuf |= input[inputPosition++] << bitLen; | |
bitLen += 8; | |
} | |
return bitBuf; | |
} | |
function cutBits(n) { | |
bitLen -= n; | |
return bitBuf >>= n; | |
} | |
function maskBits(n) { | |
while(bitLen < n) { | |
bitBuf |= input[inputPosition++] << bitLen; | |
bitLen += 8; | |
} | |
var res = bitBuf & MASK_BITS[n]; | |
bitBuf >>= n; | |
bitLen -= n; | |
return res; | |
} | |
function codes(buff, off, size) { | |
var e, t; | |
if(size == 0) return 0; | |
var n = 0; | |
for(;;) { | |
t = tblList.list[addBits(bitList) & MASK_BITS[bitList]]; | |
e = t.extra; | |
while(e > 16) { | |
if(e == 99) return -1; | |
cutBits(t.bitcnt); | |
e -= 16; | |
t = t.next[addBits(e) & MASK_BITS[e]]; | |
e = t.extra; | |
} | |
cutBits(t.bitcnt); | |
if(e == 16) { | |
windowPos &= WSIZE - 1; | |
buff[off + n++] = slide[windowPos++] = t.lbase; | |
if(n == size) return size; | |
continue; | |
} | |
if(e == 15) break; | |
copyLen = t.lbase + maskBits(e); | |
t = tblDist.list[addBits(bitdist) & MASK_BITS[bitdist]]; | |
e = t.extra; | |
while(e > 16) { | |
if(e == 99) return -1; | |
cutBits(t.bitcnt); | |
e -= 16; | |
t = t.next[addBits(e) & MASK_BITS[e]]; | |
e = t.extra | |
} | |
cutBits(t.bitcnt); | |
copyDist = windowPos - t.lbase - maskBits(e); | |
while(copyLen > 0 && n < size) { | |
copyLen--; | |
copyDist &= WSIZE - 1; | |
windowPos &= WSIZE - 1; | |
buff[off + n++] = slide[windowPos++] = slide[copyDist++]; | |
} | |
if(n == size) return size; | |
} | |
method = -1; // done | |
return n; | |
} | |
function stored(buff, off, size) { | |
cutBits(bitLen & 7); | |
var n = maskBits(0x10); | |
if(n != ((~maskBits(0x10)) & 0xffff)) return -1; | |
copyLen = n; | |
n = 0; | |
while(copyLen > 0 && n < size) { | |
copyLen--; | |
windowPos &= WSIZE - 1; | |
buff[off + n++] = slide[windowPos++] = maskBits(8); | |
} | |
if(copyLen == 0) method = -1; | |
return n; | |
} | |
function fixed(buff, off, size) { | |
var fixed_bd = 0; | |
if(fixedTableList == null) { | |
var lengths = []; | |
for(var symbol = 0; symbol < 144; symbol++) lengths[symbol] = 8; | |
for(; symbol < 256; symbol++) lengths[symbol] = 9; | |
for(; symbol < 280; symbol++) lengths[symbol] = 7; | |
for(; symbol < 288; symbol++) lengths[symbol] = 8; | |
fixedLookup = 7; | |
var htbl = new HuffTable(lengths, 288, 257, LENS, LEXT, fixedLookup); | |
if(htbl.status != 0) return -1; | |
fixedTableList = htbl.root; | |
fixedLookup = htbl.maxbit; | |
for(symbol = 0; symbol < 30; symbol++) lengths[symbol] = 5; | |
fixed_bd = 5; | |
htbl = new HuffTable(lengths, 30, 0, DISTS, DEXT, fixed_bd); | |
if(htbl.status > 1) { | |
fixedTableList = null; | |
return -1; | |
} | |
fixedTableDist = htbl.root; | |
fixed_bd = htbl.maxbit; | |
} | |
tblList = fixedTableList; | |
tblDist = fixedTableDist; | |
bitList = fixedLookup; | |
bitdist = fixed_bd; | |
return codes(buff, off, size); | |
} | |
function dynamic(buff, off, size) { | |
var ll = new Array(0x023C); | |
for (var m = 0; m < 0x023C; m++) ll[m] = 0; | |
var llencnt = 257 + maskBits(5), | |
dcodescnt = 1 + maskBits(5), | |
bitlencnt = 4 + maskBits(4); | |
if(llencnt > 286 || dcodescnt > 30) return -1; | |
for(var j = 0; j < bitlencnt; j++) ll[BITORDER[j]] = maskBits(3); | |
for(; j < 19; j++) ll[BITORDER[j]] = 0; | |
// build decoding table for trees--single level, 7 bit lookup | |
bitList = 7; | |
var hufTable = new HuffTable(ll, 19, 19, null, null, bitList); | |
if(hufTable.status != 0) | |
return -1; // incomplete code set | |
tblList = hufTable.root; | |
bitList = hufTable.maxbit; | |
var lencnt = llencnt + dcodescnt, | |
i = 0, | |
lastLen = 0; | |
while(i < lencnt) { | |
var hufLcode = tblList.list[addBits(bitList) & MASK_BITS[bitList]]; | |
j = hufLcode.bitcnt; | |
cutBits(j); | |
j = hufLcode.lbase; | |
if(j < 16) | |
ll[i++] = lastLen = j; | |
else if(j == 16) { | |
j = 3 + maskBits(2); | |
if(i + j > lencnt) return -1; | |
while(j-- > 0) ll[i++] = lastLen; | |
} else if(j == 17) { | |
j = 3 + maskBits(3); | |
if(i + j > lencnt) return -1; | |
while(j-- > 0) ll[i++] = 0; | |
lastLen = 0; | |
} else { | |
j = 11 + maskBits(7); | |
if(i + j > lencnt) return -1; | |
while(j-- > 0) ll[i++] = 0; | |
lastLen = 0; | |
} | |
} | |
bitList = 9; | |
hufTable = new HuffTable(ll, llencnt, 257, LENS, LEXT, bitList); | |
bitList == 0 && (hufTable.status = 1); | |
if (hufTable.status != 0) return -1; | |
tblList = hufTable.root; | |
bitList = hufTable.maxbit; | |
for(i = 0; i < dcodescnt; i++) ll[i] = ll[i + llencnt]; | |
bitdist = 6; | |
hufTable = new HuffTable(ll, dcodescnt, 0, DISTS, DEXT, bitdist); | |
tblDist = hufTable.root; | |
bitdist = hufTable.maxbit; | |
if((bitdist == 0 && llencnt > 257) || hufTable.status != 0) return -1; | |
return codes(buff, off, size); | |
} | |
return { | |
inflate : function(/*Buffer*/outputBuffer) { | |
tblList = null; | |
var size = outputBuffer.length, | |
offset = 0, i; | |
while(offset < size) { | |
if(eof && method == -1) return; | |
if(copyLen > 0) { | |
if(method != 0) { | |
while(copyLen > 0 && offset < size) { | |
copyLen--; | |
copyDist &= WSIZE - 1; | |
windowPos &= WSIZE - 1; | |
outputBuffer[offset++] = (slide[windowPos++] = slide[copyDist++]); | |
} | |
} else { | |
while(copyLen > 0 && offset < size) { | |
copyLen--; | |
windowPos &= WSIZE - 1; | |
outputBuffer[offset++] = (slide[windowPos++] = maskBits(8)); | |
} | |
copyLen == 0 && (method = -1); // done | |
} | |
if (offset == size) return; | |
} | |
if(method == -1) { | |
if(eof) break; | |
eof = maskBits(1) != 0; | |
method = maskBits(2); | |
tblList = null; | |
copyLen = 0; | |
} | |
switch(method) { | |
case 0: i = stored(outputBuffer, offset, size - offset); break; | |
case 1: i = tblList != null ? codes(outputBuffer, offset, size - offset) : fixed(outputBuffer, offset, size - offset); break; | |
case 2: i = tblList != null ? codes(outputBuffer, offset, size - offset) : dynamic(outputBuffer, offset, size - offset); break; | |
default: i = -1; break; | |
} | |
if(i == -1) return; | |
offset += i; | |
} | |
} | |
}; | |
} | |
module.exports = function(/*Buffer*/inbuf) { | |
var zlib = require("zlib"); | |
return { | |
inflateAsync : function(/*Function*/callback) { | |
var tmp = zlib.createInflateRaw(), | |
parts = [], total = 0; | |
tmp.on('data', function(data) { | |
parts.push(data); | |
total += data.length; | |
}); | |
tmp.on('end', function() { | |
var buf = new Buffer(total), written = 0; | |
buf.fill(0); | |
for (var i = 0; i < parts.length; i++) { | |
var part = parts[i]; | |
part.copy(buf, written); | |
written += part.length; | |
} | |
callback && callback(buf); | |
}); | |
tmp.end(inbuf) | |
}, | |
inflate : function(/*Buffer*/outputBuffer) { | |
var x = { | |
x: new JSInflater(inbuf) | |
}; | |
x.x.inflate(outputBuffer); | |
delete(x.x); | |
} | |
} | |
}; |