const IOStream = require("./iostream");
const ByteModel = require("./byte_model");
const RangeCoder = require("./arith_sh");
function read_array(src, tab, size) {
var j = 0; var z = 0; var last = -1;
var R = new Array(1024) while (z < size) {
var run = src.ReadByte()
R[j++] = run
z += run
if (run == last) {
var copy = src.ReadByte()
z += run * copy
while (copy--)
R[j++] = run
}
last = run
}
var i = 0
j = 0
z = 0
while (z < size) {
var run_len = 0
do {
var part = R[j++]
run_len += part
} while (part == 255)
while (run_len--)
tab[z++] = i;
i++
}
}
const QMAX = 256
const FLAG_DEDUP = 2
const FLAG_FLEN = 4
const FLAG_SEL = 8 const FLAG_QMAP = 16
const FLAG_PTAB = 32
const FLAG_DTAB = 64
const FLAG_QTAB = 128
const GFLAG_MULTI_PARAM = 1
const GFLAG_HAVE_STAB = 2
const GFLAG_DO_REV = 4
function fqz_update_ctx(params, state, q) {
var last = params.context
state.qctx = ((state.qctx << params.qshift) + params.qtab[q]); last += ((state.qctx & ((1<<params.qbits)-1)) << params.qloc);
if (params.do_pos)
last += params.ptab[Math.min(state.p, 1023)] << params.ploc
if (params.do_delta) {
last += params.dtab[Math.min(state.delta, 255)] << params.dloc
state.delta += (state.prevq != q) ? 1 : 0
state.prevq = q
}
if (params.do_sel)
last += state.s << params.sloc
state.p--
return last & 0xffff
}
function decode_fqz_single_param(src) {
var p = {}
p.context = src.ReadUint16()
p.pflags = src.ReadByte()
p.do_dedup = p.pflags & FLAG_DEDUP
p.fixed_len = p.pflags & FLAG_FLEN
p.do_sel = p.pflags & FLAG_SEL
p.do_qmap = p.pflags & FLAG_QMAP
p.do_pos = p.pflags & FLAG_PTAB
p.do_delta = p.pflags & FLAG_DTAB
p.do_qtab = p.pflags & FLAG_QTAB
p.max_sym = src.ReadByte()
var x = src.ReadByte()
p.qbits = x>>4
p.qshift = x&15
x = src.ReadByte()
p.qloc = x>>4
p.sloc = x&15
x = src.ReadByte()
p.ploc = x>>4
p.dloc = x&15
p.qmap = new Array(256);
if (p.pflags & FLAG_QMAP) {
for (var i = 0; i < p.max_sym; i++)
p.qmap[i] = src.ReadByte()
} else {
for (var i = 0; i < 256; i++)
p.qmap[i] = i; }
p.qtab = new Array(1024);
if (p.qbits > 0 && (p.pflags & FLAG_QTAB)) {
read_array(src, p.qtab, 256)
} else {
for (var i = 0; i < 256; i++)
p.qtab[i] = i; }
p.ptab = new Array(1024);
if (p.pflags & FLAG_PTAB)
read_array(src, p.ptab, 1024);
p.dtab = new Array(256);
if (p.pflags & FLAG_DTAB)
read_array(src, p.dtab, 256);
return p
}
function decode_fqz_params(src) {
var gparams = {
max_sym: 0
}
var vers = src.ReadByte()
if (vers != 5) {
console.error("Invalid FQZComp version number");
return;
}
var gflags = src.ReadByte()
var nparam = (gflags & GFLAG_MULTI_PARAM) ? src.ReadByte() : 1
var max_sel = gflags.nparam > 1 ? gflags.nparam-1 : 0
var stab = new Array(256);
if (gflags & GFLAG_HAVE_STAB) {
max_sel = src.ReadByte()
read_array(src, stab, 256);
} else {
for (var i = 0; i < nparam; i++)
stab[i] = i;
for (; i < 256; i++)
stab[i] = nparam-1;
}
gparams.do_rev = (gflags & GFLAG_DO_REV)
gparams.stab = stab
gparams.max_sel = max_sel
gparams.params = new Array(gparams.nparam)
for (var p = 0; p < nparam; p++) {
gparams.params[p] = decode_fqz_single_param(src)
if (gparams.max_sym < gparams.params[p].max_sym)
gparams.max_sym = gparams.params[p].max_sym
}
return gparams
}
function fqz_create_models(gparams) {
var model = {}
model.qual = new Array(1<<16)
for (var i = 0; i < (1<<16); i++)
model.qual[i] = new ByteModel(gparams.max_sym+1)
model.len = new Array(4)
for (var i = 0; i < 4; i++)
model.len[i] = new ByteModel(256)
model.rev = new ByteModel(2)
model.dup = new ByteModel(2)
if (gparams.max_sel > 0)
model.sel = new ByteModel(gparams.max_sel+1)
return model
}
function decode_fqz_new_record(src, rc, gparams, model, state, rev) {
if (gparams.max_sel > 0) {
state.s = model.sel.ModelDecode(src, rc)
} else {
state.s = 0;
}
state.x = gparams.stab[state.s]
var params = gparams.params[state.x]
if (params.fixed_len >= 0) {
var len = model.len[0].ModelDecode(src, rc)
len |= model.len[1].ModelDecode(src, rc) << 8
len |= model.len[2].ModelDecode(src, rc) << 16
len |= model.len[3].ModelDecode(src, rc) << 24
if (params.fixed_len > 0)
params.fixed_len = -len
} else {
len = -params.fixed_len
}
state.len = len
if (gparams.do_rev)
rev[state.rec] = model.rev.ModelDecode(src, rc)
state.is_dup = 0
if (params.pflags & FLAG_DEDUP) {
if (model.dup.ModelDecode(src, rc))
state.is_dup = 1
}
state.p = len; state.delta = 0
state.qctx = 0
state.prevq = 0
state.rec++
}
function decode_fqz(src, q_lens) {
var n_out = src.ReadUint7()
var gparams = decode_fqz_params(src)
if (!gparams) return
var params = gparams.params
var rev = new Array(q_lens.length)
var model = fqz_create_models(gparams)
var rc = new RangeCoder(src)
rc.RangeStartDecode(src)
var output = new Buffer.allocUnsafe(n_out)
var state = {
qctx:0, prevq:0, delta:0, p:0, s:0, x:0, len:0, is_dup:0, rec:0 }
var i = 0 while (i < n_out) {
if (state.p == 0) {
decode_fqz_new_record(src, rc, gparams, model, state, rev)
if (state.is_dup > 0) {
if (model.dup.ModelDecode(src, rc)) {
for (var x = 0; x < len; x++)
output[i+x] = output[i+x-state.len]
i += state.len
state.p = 0
continue
}
}
q_lens.push(state.len)
var params = gparams.params[state.x]
var last = params.context
}
var Q = model.qual[last].ModelDecode(src, rc)
output[i++] = params.qmap[Q]; last = fqz_update_ctx(params, state, Q)
}
if (gparams.do_rev)
reverse_qualities(output, n_out, rev, q_lens)
return output;
}
function reverse_qualities(qual, qual_len, rev, len) {
var rec = 0
var i = 0
while (i < qual_len) {
if (rev[rec]) {
var j = 0
var k = len[rec]-1
while (j < k) {
var tmp = qual[i+j]
qual[i+j] = qual[i+k]
qual[i+k] = tmp
j++
k--
}
}
i += len[rec++];
}
}
function decode(src, q_lens) {
var stream = new IOStream(src);
return decode_fqz(stream, q_lens);
}
function pick_fqz_params(src, q_lens, q_dirs, qhist) {
var qd_last = q_dirs[0];
for (var i = 0; i < q_dirs.length; i++)
if (q_dirs[i] != qd_last)
break;
var qd_fixed = (i == q_dirs.length) ? 1 : 0
var nsym = 0
var max_sym = 0
for (var i = 0; i < 256; i++)
qhist[0][i] = 0;
var rec = 0;
var len = 0
for (var i = 0; i < src.length; i++) {
if (len == 0) {
len = q_lens[rec < q_lens.length-1 ? rec++ : rec]
}
qhist[0][src[i]]++;
len--;
}
for (var i = 0; i < 256; i++) {
if (!qhist[0][i])
continue;
if (max_sym < i)
max_sym = i;
nsym++;
}
var qshift = 5
var do_qmap = 0
if (nsym <= 16) {
do_qmap = 1 if (nsym <= 2)
qshift = 1
else if (nsym <= 4)
qshift = 2
else if (nsym <= 8)
qshift = 3
else
qshift = 4
}
return [{qbits: 8+(qshift>4),
qshift: qshift,
qloc: 7,
pbits: 7,
pshift: q_lens[0] > 128 ? 1 : 0,
ploc: 0,
dbits: qshift>4 ? 0 : 1,
dshift: 3,
dloc: 15,
sbits: 0,
sloc: 15,
do_stab: 0,
context: (0<<15),
max_sym: max_sym,
nsym: nsym,
do_qmap: do_qmap,
do_dedup: 0,
fixed_len: (q_lens.length == 1) ? 1 : 0,
do_sel: 0,
do_rev: 0,
do_pos: 1,
do_delta: (qshift <= 4) ? 1 : 0,
do_qtab: 0,
qbits: 8+(qshift>4)-(qd_fixed==0),
sbits: 1,
sloc: 15-(qshift<=4), do_stab: 1,
do_sel: 1,
}]
}
function store_array(out, tab, size) {
var i = 0; var j = 0;
var tmp1 = new Array(size*2);
var sz1 = 0;
while (i < size) {
var i_start = i
while (i < size && tab[i] == j)
i++;
var run_len = i - i_start
do {
var r = Math.min(255, run_len)
tmp1[sz1++] = r
run_len -= r
} while (r == 255)
j++;
}
var last = -1
var tmp2 = new Array(size*2)
var sz2 = 0
i = 0 while (i < sz1) {
var curr = tmp1[i++];
tmp2[sz2++] = curr
if (curr == last) {
var i_start = i;
while (i < sz1 && tmp1[i] == last && i - i_start < 255)
i++;
tmp2[sz2++] = i - i_start;
} else {
last = curr
}
}
out.WriteData(tmp2, sz2)
}
function encode_fqz_params(out, params, qhist, qtab, ptab, dtab, stab) {
var dsqr = [
0, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
]
for (var i = 0; i < params.length; i++)
stab[i] = i; for (; i < 256; i++)
stab[i] = params.length-1;
out.WriteByte(5); var gflags = ((params.length > 1) ? GFLAG_MULTI_PARAM : 0)
| ((params[0].do_stab) ? GFLAG_HAVE_STAB : 0)
out.WriteByte(gflags)
if (gflags & GFLAG_MULTI_PARAM)
out.WriteByte(params.length)
if (gflags & GFLAG_HAVE_STAB) {
var max_sel = 1<<params[0].sbits;
if (max_sel > 0) max_sel--;
out.WriteByte(max_sel)
store_array(out, stab, 256)
}
for (var p = 0; p < params.length; p++) {
out.WriteUint16(params[p].context)
out.WriteByte((params[p].do_qtab ? FLAG_QTAB : 0) | (params[p].do_delta ? FLAG_DTAB : 0) |
(params[p].do_pos ? FLAG_PTAB : 0) |
(params[p].do_qmap ? FLAG_QMAP : 0) |
(params[p].do_sel ? FLAG_SEL : 0) |
(params[p].fixed_len? FLAG_FLEN : 0) |
(params[p].do_dedup ? FLAG_DEDUP : 0))
if (params[p].do_qmap)
out.WriteByte(params[p].nsym)
else
out.WriteByte(params[p].max_sym)
out.WriteByte((params[p].qbits << 4) | (params[p].qshift))
out.WriteByte((params[p].qloc << 4) | (params[p].sloc))
out.WriteByte((params[p].ploc << 4) | (params[p].dloc))
if (params[p].do_qmap) {
params[p].max_sym = params[p].nsym
var n = 0;
for (var i = 0; i < 256; i++) {
if (qhist[p][i]) {
out.WriteByte(i)
qhist[p][i] = n++;
}
}
for (; n < params[p].nsym; n++)
out.WriteByte(0)
} else {
for (var i = 0; i < 256; i++)
qhist[p][i] = i; }
if (params[p].qbits > 0) {
for (var i = 0; i < 256; i++)
qtab[p][i] = i;
if (params[p].do_qtab)
store_array(out, qtab[p], 256)
}
if (params[p].pbits > 0) {
for (var i = 0; i < 1024; i++)
ptab[p][i] = Math.min((1<<params[p].pbits)-1, i >> params[p].pshift)
store_array(out, ptab[p], 1024)
}
if (params[p].dbits > 0) {
for (var i = 0; i < 256; i++)
if (dsqr[i] > (1<<params[p].dbits) - 1)
dsqr[i] = (1<<params[p].dbits) - 1
for (var i = 0; i < 256; i++)
dtab[p][i] = dsqr[Math.min(dsqr.length-1, i >> params[p].dshift)]
store_array(out, dtab[p], 256)
}
}
return out
}
function encode_fqz(out, src, q_lens, q_dirs, params, qhist, qtab, ptab, dtab, stab) {
var max_sel = 1<<params[0].sbits
if (max_sel > 0) max_sel--
var n_in = src.length
var max_sym = 0;
for (var p = 0; p < params.length; p++)
if (max_sym < params[p].max_sym)
max_sym = params[p].max_sym;
var model_qual = new Array(1<<16)
for (var i = 0; i < (1<<16); i++)
model_qual[i] = new ByteModel(max_sym+1)
var model_len = new Array(4)
for (var i = 0; i < 4; i++)
model_len[i] = new ByteModel(256)
var model_rev = new ByteModel(2)
var model_dup = new ByteModel(2)
var model_sel = new ByteModel(max_sel+1)
var rc = new RangeCoder(src)
var p = 0; var i = 0; var rec = 0;
while (i < n_in) {
if (p == 0) {
var s = q_dirs[rec]
if (params[0].sbits > 0) { model_sel.ModelEncode(out, rc, s)
}
var x = stab[s]
var len = q_lens[Math.min(q_lens.length-1, rec++)]
if (params[x].fixed_len) {
if (params[x].fixed_len > 0) { model_len[0].ModelEncode(out, rc, len & 0xff)
model_len[1].ModelEncode(out, rc, (len>>8) & 0xff)
model_len[2].ModelEncode(out, rc, (len>>16) & 0xff)
model_len[3].ModelEncode(out, rc, (len>>24) & 0xff)
params[x].fixed_len = -1; }
} else {
model_len[0].ModelEncode(out, rc, len & 0xff)
model_len[1].ModelEncode(out, rc, (len>>8) & 0xff)
model_len[2].ModelEncode(out, rc, (len>>16) & 0xff)
model_len[3].ModelEncode(out, rc, (len>>24) & 0xff)
}
if (params[x].do_dedup)
process.exit(1)
p = len
var delta = 0
var last = params[x].context
var qlast = 0
var q1 = 0
}
var q = src[i++]
var Q = qhist[x][q]
model_qual[last].ModelEncode(out, rc, Q)
qlast = (qlast << params[x].qshift) + qtab[x][Q]
last = params[x].context
last += (qlast & ((1<<params[x].qbits)-1)) << params[x].qloc
if (params[x].pbits > 0)
last += ptab[x][Math.min(p, 1023)] << params[x].ploc
if (params[x].dbits > 0) {
last += dtab[x][Math.min(delta, 255)] << params[x].dloc
delta += (q1 != Q) ? 1 : 0
q1 = Q
}
if (params[x].do_sel)
last += s << params[x].sloc
last = (last & 0xffff)
p--
}
rc.RangeFinishEncode(out)
return out.buf.slice(0, out.pos)
}
function encode(src, q_lens, q_dirs) {
var qhist = new Array(2)
var qtab = new Array(2)
var ptab = new Array(2)
var dtab = new Array(2)
var stab = new Array(256)
for (var s = 0; s < 2; s++) {
qhist[s] = new Array(256)
qtab[s] = new Array(256)
ptab[s] = new Array(1024)
dtab[s] = new Array(256)
}
var out = new IOStream("", 0, src.length*1.1 + 100);
out.WriteUint7(src.length);
var params = pick_fqz_params(src, q_lens, q_dirs, qhist)
var out = encode_fqz_params(out, params, qhist, qtab, ptab, dtab, stab)
return encode_fqz(out, src, q_lens, q_dirs, params, qhist, qtab, ptab, dtab, stab)
}
module.exports = { decode, encode }