/** * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose * * ported from https://github.com/photopea/UZIP.js/blob/master/UZIP.js * MIT License, Copyright (c) 2018 Photopea */ import { NumberArray } from '../type-helpers'; import { U, makeCodes, codes2map } from './util'; import { RuntimeContext } from '../../mol-task'; function InflateContext(data: Uint8Array, buf?: Uint8Array) { const noBuf = buf === undefined; if(buf === undefined) buf = new Uint8Array((data.length >>> 2) << 3); return { data, buf, noBuf, BFINAL: 0, off: 0, pos: 0 }; } type InflateContext = ReturnType function inflateBlocks(ctx: InflateContext, count: number) { const { data, noBuf } = ctx; let { buf, BFINAL, off, pos } = ctx; let iBlock = 0; while(BFINAL === 0 && iBlock < count) { let lmap, dmap; let ML = 0, MD = 0; BFINAL = _bitsF(data, pos, 1); iBlock += 1; const BTYPE = _bitsF(data, pos + 1, 2); pos += 3; if(BTYPE === 0) { // uncompressed block if((pos & 7) !== 0) pos += 8 - (pos & 7); const p8 = (pos >>> 3) + 4; const len = data[p8 - 4] | (data[p8 - 3] << 8); if(noBuf) buf = _check(buf, off + len); buf.set(new Uint8Array(data.buffer, data.byteOffset + p8, len), off); pos = ((p8 + len) << 3); off += len; continue; } // grow output buffer if not provided if(noBuf) buf = _check(buf, off + (1 << 17)); if(BTYPE === 1) { // block compressed with fixed Huffman codes lmap = U.flmap; dmap = U.fdmap; ML = (1 << 9) - 1; MD = (1 << 5) - 1; } else if(BTYPE === 2) { // block compressed with dynamic Huffman codes const HLIT = _bitsE(data, pos, 5) + 257; const HDIST = _bitsE(data, pos + 5, 5) + 1; const HCLEN = _bitsE(data, pos + 10, 4) + 4; pos += 14; for(let i = 0; i < 38; i += 2) { U.itree[i] = 0; U.itree[i + 1] = 0; } let tl = 1; for(let i = 0; i < HCLEN; i++) { const l = _bitsE(data, pos + i * 3, 3); U.itree[(U.ordr[i] << 1) + 1] = l; if(l > tl) tl = l; } pos += 3 * HCLEN; makeCodes(U.itree, tl); codes2map(U.itree, tl, U.imap); lmap = U.lmap; dmap = U.dmap; pos = _decodeTiny(U.imap, (1 << tl) - 1, HLIT + HDIST, data, pos, U.ttree); const mx0 = _copyOut(U.ttree, 0, HLIT, U.ltree); ML = (1 << mx0) - 1; const mx1 = _copyOut(U.ttree, HLIT, HDIST, U.dtree); MD = (1 << mx1) - 1; makeCodes(U.ltree, mx0); codes2map(U.ltree, mx0, lmap); makeCodes(U.dtree, mx1); codes2map(U.dtree, mx1, dmap); } else { throw new Error(`unknown BTYPE ${BTYPE}`); } while(true) { const code = lmap[_get17(data, pos) & ML]; pos += code & 15; const lit = code >>> 4; if((lit >>> 8) === 0) { buf[off++] = lit; } else if(lit === 256) { break; } else { let end = off + lit - 254; if(lit > 264) { const ebs = U.ldef[lit - 257]; end = off + (ebs >>> 3) + _bitsE(data, pos, ebs & 7); pos += ebs & 7; } const dcode = dmap[_get17(data, pos) & MD]; pos += dcode & 15; const dlit = dcode >>> 4; const dbs = U.ddef[dlit]; const dst = (dbs >>> 4) + _bitsF(data, pos, dbs & 15); pos += dbs & 15; if(noBuf) buf = _check(buf, off + (1 << 17)); while(off < end) { buf[off] = buf[off++ - dst]; buf[off] = buf[off++ - dst]; buf[off] = buf[off++ - dst]; buf[off] = buf[off++ - dst]; } off = end; } } } ctx.buf = buf; ctx.BFINAL = BFINAL; ctx.off = off; ctx.pos = pos; } // https://tools.ietf.org/html/rfc1951 export async function _inflate(runtime: RuntimeContext, data: Uint8Array, buf?: Uint8Array) { if(data[0] === 3 && data[1] === 0) return (buf ? buf : new Uint8Array(0)); const ctx = InflateContext(data, buf); while(ctx.BFINAL === 0) { if (runtime.shouldUpdate) { await runtime.update({ message: 'Inflating blocks...', current: ctx.pos, max: data.length }); } inflateBlocks(ctx, 100); } return ctx.buf.length === ctx.off ? ctx.buf : ctx.buf.slice(0, ctx.off); } function _check(buf: Uint8Array, len: number) { const bl = buf.length; if(len <= bl) return buf; const nbuf = new Uint8Array(Math.max(bl << 1, len)); nbuf.set(buf, 0); return nbuf; } function _decodeTiny(lmap: NumberArray, LL: number, len: number, data: Uint8Array, pos: number, tree: number[]) { let i = 0; while(i < len) { const code = lmap[_get17(data, pos) & LL]; pos += code & 15; const lit = code >>> 4; if(lit <= 15) { tree[i] = lit; i++; } else { let ll = 0, n = 0; if(lit === 16) { n = (3 + _bitsE(data, pos, 2)); pos += 2; ll = tree[i - 1]; } else if(lit === 17) { n = (3 + _bitsE(data, pos, 3)); pos += 3; } else if(lit === 18) { n = (11 + _bitsE(data, pos, 7)); pos += 7; } const ni = i + n; while(i < ni) { tree[i] = ll; i++; } } } return pos; } function _copyOut(src: number[], off: number, len: number, tree: number[]) { let mx = 0, i = 0; const tl = tree.length >>> 1; while(i < len) { let v = src[i + off]; tree[(i << 1)] = 0; tree[(i << 1) + 1] = v; if(v > mx)mx = v; i++; } while(i < tl) { tree[(i << 1)] = 0; tree[(i << 1) + 1] = 0; i++; } return mx; } function _bitsE(dt: NumberArray, pos: number, length: number) { return ((dt[pos >>> 3] | (dt[(pos >>> 3) + 1] << 8)) >>> (pos & 7)) & ((1 << length) - 1); } function _bitsF(dt: NumberArray, pos: number, length: number) { return ((dt[pos >>> 3] | (dt[(pos >>> 3) + 1] << 8) | (dt[(pos >>> 3) + 2] << 16)) >>> (pos & 7)) & ((1 << length) - 1); } function _get17(dt: NumberArray, pos: number) { // return at least 17 meaningful bytes return (dt[pos >>> 3] | (dt[(pos >>> 3) + 1] << 8) | (dt[(pos >>> 3) + 2] << 16) ) >>> (pos & 7); }