/** * 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 { _hufTree } from './huffman'; import { U, revCodes, makeCodes } from './util'; export function _deflateRaw(data: Uint8Array, out: Uint8Array, opos: number, lvl: number) { const opts = [ /* ush good_length; /* reduce lazy search above this match length ush max_lazy; /* do not perform lazy search above this match length ush nice_length; /* quit search above this match length */ /* good lazy nice chain */ /* 0 */ [ 0, 0, 0, 0, 0], /* store only */ /* 1 */ [ 4, 4, 8, 4, 0], /* max speed, no lazy matches */ /* 2 */ [ 4, 5, 16, 8, 0], /* 3 */ [ 4, 6, 16, 16, 0], /* 4 */ [ 4, 10, 16, 32, 0], /* lazy matches */ /* 5 */ [ 8, 16, 32, 32, 0], /* 6 */ [ 8, 16, 128, 128, 0], /* 7 */ [ 8, 32, 128, 256, 0], /* 8 */ [32, 128, 258, 1024, 1], /* 9 */ [32, 258, 258, 4096, 1] /* max compression */ ]; const opt = opts[lvl]; let i = 0, pos = opos << 3, cvrd = 0; const dlen = data.length; if(lvl === 0) { while(i < dlen) { const len = Math.min(0xffff, dlen - i); _putsE(out, pos, (i + len === dlen ? 1 : 0)); pos = _copyExact(data, i, len, out, pos + 8); i += len; } return pos >>> 3; } const { lits, strt, prev } = U; let li = 0, lc = 0, bs = 0, ebits = 0, c = 0, nc = 0; // last_item, literal_count, block_start if(dlen > 2) { nc = _hash(data, 0); strt[nc] = 0; } // let nmch = 0 // let nmci = 0 for(i = 0; i < dlen; i++) { c = nc; //* if(i + 1 < dlen - 2) { nc = _hash(data, i + 1); const ii = ((i + 1) & 0x7fff); prev[ii] = strt[nc]; strt[nc] = ii; } // */ if(cvrd <= i) { if((li > 14000 || lc > 26697) && (dlen - i) > 100) { if(cvrd < i) { lits[li] = i - cvrd; li += 2; cvrd = i; } pos = _writeBlock(((i === dlen - 1) || (cvrd === dlen)) ? 1 : 0, lits, li, ebits, data, bs, i - bs, out, pos); li = lc = ebits = 0; bs = i; } let mch = 0; // if(nmci==i) mch= nmch; else if(i < dlen - 2) { mch = _bestMatch(data, i, prev, c, Math.min(opt[2], dlen - i), opt[3]); } /* if(mch!=0 && opt[4]==1 && (mch>>>16)>>16)>(mch>>>16)) mch=0; }//*/ // const len = mch>>>16, dst = mch & 0xffff; // if(i-dst<0) throw "e"; if(mch !== 0) { const len = mch >>> 16, dst = mch & 0xffff; // if(i-dst<0) throw "e"; const lgi = _goodIndex(len, U.of0); U.lhst[257 + lgi]++; const dgi = _goodIndex(dst, U.df0); U.dhst[ dgi]++; ebits += U.exb[lgi] + U.dxb[dgi]; lits[li] = (len << 23) | (i - cvrd); lits[li + 1] = (dst << 16) | (lgi << 8) | dgi; li += 2; cvrd = i + len; } else { U.lhst[data[i]]++; } lc++; } } if(bs !== i || data.length === 0) { if(cvrd < i) { lits[li] = i - cvrd; li += 2; cvrd = i; } pos = _writeBlock(1, lits, li, ebits, data, bs, i - bs, out, pos); li = 0; lc = 0; li = lc = ebits = 0; bs = i; } while((pos & 7) !== 0) pos++; return pos >>> 3; } function _bestMatch(data: Uint8Array, i: number, prev: Uint16Array, c: number, nice: number, chain: number) { let ci = (i & 0x7fff), pi = prev[ci]; // console.log("----", i); let dif = ((ci - pi + (1 << 15)) & 0x7fff); if(pi === ci || c !== _hash(data, i - dif)) return 0; let tl = 0, td = 0; // top length, top distance const dlim = Math.min(0x7fff, i); while(dif <= dlim && --chain !== 0 && pi !== ci /* && c==UZIP.F._hash(data,i-dif)*/) { if(tl === 0 || (data[i + tl] === data[i + tl - dif])) { let cl = _howLong(data, i, dif); if(cl > tl) { tl = cl; td = dif; if(tl >= nice) break; //* if(dif + 2 < cl) cl = dif + 2; let maxd = 0; // pi does not point to the start of the word for(let j = 0; j < cl - 2; j++) { const ei = (i - dif + j + (1 << 15)) & 0x7fff; const li = prev[ei]; const curd = (ei - li + (1 << 15)) & 0x7fff; if(curd > maxd) { maxd = curd; pi = ei; } } } } ci = pi; pi = prev[ci]; dif += ((ci - pi + (1 << 15)) & 0x7fff); } return (tl << 16) | td; } function _howLong(data: Uint8Array, i: number, dif: number) { if(data[i] !== data[i - dif] || data[i + 1] !== data[i + 1 - dif] || data[i + 2] !== data[i + 2 - dif]) return 0; const oi = i, l = Math.min(data.length, i + 258); i += 3; // while(i+4>> 23), end = off + (qb & ((1 << 23) - 1)); while(off < end) pos = _writeLit(data[off++], ltree, out, pos); if(len !== 0) { const qc = lits[si + 1], dst = (qc >> 16), lgi = (qc >> 8) & 255, dgi = (qc & 255); pos = _writeLit(257 + lgi, ltree, out, pos); _putsE(out, pos, len - U.of0[lgi]); pos += U.exb[lgi]; pos = _writeLit(dgi, dtree, out, pos); _putsF(out, pos, dst - U.df0[dgi]); pos += U.dxb[dgi]; off += len; } } pos = _writeLit(256, ltree, out, pos); } // console.log(pos-opos, fxdSize, dynSize, cstSize); return pos; } function _copyExact(data: Uint8Array, off: number, len: number, out: Uint8Array, pos: number) { let p8 = (pos >>> 3); out[p8] = (len); out[p8 + 1] = (len >>> 8); out[p8 + 2] = 255 - out[p8]; out[p8 + 3] = 255 - out[p8 + 1]; p8 += 4; out.set(new Uint8Array(data.buffer, off, len), p8); // for(var i=0; i 4 && U.itree[(U.ordr[numh - 1] << 1) + 1] === 0) numh--; return [ML, MD, MH, numl, numd, numh, lset, dset] as const; } function contSize(tree: number[], hst: NumberArray) { let s = 0; for(let i = 0; i < hst.length; i++) s += hst[i] * tree[(i << 1) + 1]; return s; } function _codeTiny(set: number[], tree: number[], out: Uint8Array, pos: number) { for(let i = 0; i < set.length; i += 2) { const l = set[i], rst = set[i + 1]; // console.log(l, pos, tree[(l<<1)+1]); pos = _writeLit(l, tree, out, pos); const rsl = l === 16 ? 2 : (l === 17 ? 3 : 7); if(l > 15) { _putsE(out, pos, rst); pos += rsl; } } return pos; } function _lenCodes(tree: number[], set: number[]) { let len = tree.length; while(len !== 2 && tree[len - 1] === 0) len -= 2; // when no distances, keep one code with length 0 for(let i = 0; i < len; i += 2) { const l = tree[i + 1], nxt = (i + 3 < len ? tree[i + 3] : -1), nnxt = (i + 5 < len ? tree[i + 5] : -1), prv = (i === 0 ? -1 : tree[i - 1]); if(l === 0 && nxt === l && nnxt === l) { let lz = i + 5; while(lz + 2 < len && tree[lz + 2] === l) lz += 2; const zc = Math.min((lz + 1 - i) >>> 1, 138); if(zc < 11) set.push(17, zc - 3); else set.push(18, zc - 11); i += zc * 2 - 2; } else if(l === prv && nxt === l && nnxt === l) { let lz = i + 5; while(lz + 2 < len && tree[lz + 2] === l) lz += 2; const zc = Math.min((lz + 1 - i) >>> 1, 6); set.push(16, zc - 3); i += zc * 2 - 2; } else { set.push(l, 0); } } return len >>> 1; } function _goodIndex(v: number, arr: number[]) { let i = 0; if(arr[i | 16] <= v) i |= 16; if(arr[i | 8] <= v) i |= 8; if(arr[i | 4] <= v) i |= 4; if(arr[i | 2] <= v) i |= 2; if(arr[i | 1] <= v) i |= 1; return i; } function _writeLit(ch: number, ltree: number[], out: Uint8Array, pos: number) { _putsF(out, pos, ltree[ch << 1]); return pos + ltree[(ch << 1) + 1]; } function _putsE(dt: NumberArray, pos: number, val: number) { val = val << (pos & 7); const o = (pos >>> 3); dt[o] |= val; dt[o + 1] |= (val >>> 8); } function _putsF(dt: NumberArray, pos: number, val: number) { val = val << (pos & 7); const o = (pos >>> 3); dt[o] |= val; dt[o + 1] |= (val >>> 8); dt[o + 2] |= (val >>> 16); }