かつて圧縮業界で最強を誇ったPAQ8を軽量化したfp8
の紹介。例によって文脈混合法により圧縮しまくっていきます。
実装
本家よりとんでもなく手抜きしまくっています。jpegとかexeとか色々対応が欠落。全部実装すると莫大な量になります。ちなみに超低速です。
function hash(a,b,c){
c=((a>>>16)*52643+(c=a&65535)*3051<<16)+c*52643;
c+=((b>>>16)*55539+(c=b&65535)*457<<16)+c*55539+4064955751;
return c^c>>>9^a>>>2^b>>>3^201326591
}
function hash3(a,b,c,d){
d=((a>>>16)*52643+(d=a&65535)*3051<<16)+d*52643;
d+=((b>>>16)*55539+(d=b&65535)*457<<16)+d*55539;
d+=((c>>>16)*271+(d=c&65535)*763<<16)+d*271+4114959990;
return d^d>>>9^a>>>2^b>>>3^c>>>4^67108864
}
function squash(d,w){
if(d>2047)return 4095;
if(d<-2047)return 0;
w=d&127;
return SQ[d=(d>>7)+16]*(128-w)+SQ[d+1]*w+64>>7
}
const MAXLEN=0xfffe,
SQ=new Int16Array([1,2,3,6,10,16,27,45,73,120,194,310,488,747,1101,1546,2047,2549,2994,3348,3607,3785,3901,3975,4022,4050,4068,4079,4085,4089,4092,4093,4094]),
ST=new Uint8Array([1,2,0,0,3,5,1,0,4,6,0,1,7,10,2,0,8,12,1,1,9,13,1,1,11,14,0,2,15,19,3,0,16,23,2,1,17,24,2,1,18,25,2,1,20,27,1,2,21,28,1,2,22,29,1,2,26,30,0,3,31,33,4,0,32,35,3,1,32,35,3,1,32,35,3,1,32,35,3,1,34,37,2,2,34,37,2,2,34,37,2,2,34,37,2,2,34,37,2,2,34,37,2,2,36,39,1,3,36,39,1,3,36,39,1,3,36,39,1,3,38,40,0,4,41,43,5,0,42,45,4,1,42,45,4,1,44,47,3,2,44,47,3,2,46,49,2,3,46,49,2,3,48,51,1,4,48,51,1,4,50,52,0,5,53,43,6,0,54,57,5,1,54,57,5,1,56,59,4,2,56,59,4,2,58,61,3,3,58,61,3,3,60,63,2,4,60,63,2,4,62,65,1,5,62,65,1,5,50,66,0,6,67,55,7,0,68,57,6,1,68,57,6,1,70,73,5,2,70,73,5,2,72,75,4,3,72,75,4,3,74,77,3,4,74,77,3,4,76,79,2,5,76,79,2,5,62,81,1,6,62,81,1,6,64,82,0,7,83,69,8,0,84,71,7,1,84,71,7,1,86,73,6,2,86,73,6,2,44,59,5,3,44,59,5,3,58,61,4,4,58,61,4,4,60,49,3,5,60,49,3,5,76,89,2,6,76,89,2,6,78,91,1,7,78,91,1,7,80,92,0,8,93,69,9,0,94,87,8,1,94,87,8,1,96,45,7,2,96,45,7,2,48,99,2,7,48,99,2,7,88,101,1,8,88,101,1,8,80,102,0,9,103,69,10,0,104,87,9,1,104,87,9,1,106,57,8,2,106,57,8,2,62,109,2,8,62,109,2,8,88,111,1,9,88,111,1,9,80,112,0,10,113,85,11,0,114,87,10,1,114,87,10,1,116,57,9,2,116,57,9,2,62,119,2,9,62,119,2,9,88,121,1,10,88,121,1,10,90,122,0,11,123,85,12,0,124,97,11,1,124,97,11,1,126,57,10,2,126,57,10,2,62,129,2,10,62,129,2,10,98,131,1,11,98,131,1,11,90,132,0,12,133,85,13,0,134,97,12,1,134,97,12,1,136,57,11,2,136,57,11,2,62,139,2,11,62,139,2,11,98,141,1,12,98,141,1,12,90,142,0,13,143,95,14,0,144,97,13,1,144,97,13,1,68,57,12,2,68,57,12,2,62,81,2,12,62,81,2,12,98,147,1,13,98,147,1,13,100,148,0,14,149,95,15,0,150,107,14,1,150,107,14,1,108,151,1,14,108,151,1,14,100,152,0,15,153,95,16,0,154,107,15,1,108,155,1,15,100,156,0,16,157,95,17,0,158,107,16,1,108,159,1,16,100,160,0,17,161,105,18,0,162,107,17,1,108,163,1,17,110,164,0,18,165,105,19,0,166,117,18,1,118,167,1,18,110,168,0,19,169,105,20,0,170,117,19,1,118,171,1,19,110,172,0,20,173,105,21,0,174,117,20,1,118,175,1,20,110,176,0,21,177,105,22,0,178,117,21,1,118,179,1,21,110,180,0,22,181,115,23,0,182,117,22,1,118,183,1,22,120,184,0,23,185,115,24,0,186,127,23,1,128,187,1,23,120,188,0,24,189,115,25,0,190,127,24,1,128,191,1,24,120,192,0,25,193,115,26,0,194,127,25,1,128,195,1,25,120,196,0,26,197,115,27,0,198,127,26,1,128,199,1,26,120,200,0,27,201,115,28,0,202,127,27,1,128,203,1,27,120,204,0,28,205,115,29,0,206,127,28,1,128,207,1,28,120,208,0,29,209,125,30,0,210,127,29,1,128,211,1,29,130,212,0,30,213,125,31,0,214,137,30,1,138,215,1,30,130,216,0,31,217,125,32,0,218,137,31,1,138,219,1,31,130,220,0,32,221,125,33,0,222,137,32,1,138,223,1,32,130,224,0,33,225,125,34,0,226,137,33,1,138,227,1,33,130,228,0,34,229,125,35,0,230,137,34,1,138,231,1,34,130,232,0,35,233,125,36,0,234,137,35,1,138,235,1,35,130,236,0,36,237,125,37,0,238,137,36,1,138,239,1,36,130,240,0,37,241,125,38,0,242,137,37,1,138,243,1,37,130,244,0,38,245,135,39,0,246,137,38,1,138,247,1,38,140,248,0,39,249,135,40,0,250,69,39,1,80,251,1,39,140,252,0,40,249,135,41,0,250,69,40,1,80,251,1,40,140,252,0,41]),
DT=function(a,b){for(;b;)a[--b]=16384/(b+b+3)|0;return a}(new Int16Array(1024),1024),
ctype=new Uint8Array([0,5,5,5,5,5,5,5,5,3,3,3,3,3,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,6,6,6,6,6,6,6,6,6,6,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,4]),
//Logarithm
ilog=function(a,b,c){for(;b<65536;a[b++]=c>>>24)c+=774541002/(b*2-1)|0;return a}(new Uint8Array(65536),2,14155776),
stretch=function(a,b,c,d){for(;++c<2048;)for(d=squash(c);b<d;)a[++b]=c;a[++b]=c-1;return a}(new Int16Array(4096),-1,-2048);
function rnd(){
var A=new Uint32Array(65),a=0;
for(A[0]=123456789,A[1]=987654321;a<62;)A[a+2]=(A[a++]*23>>>4)+A[a]*11;return A
}
function dotProduct(t,w,n,p){
for(var s=0;n>1;)s+=t[--n]*w[n+p]+t[--n]*w[n+p]>>8;
return s
}
//Mixer-combines models using neural networks
function Mixer(n,m,s,w){
this.tx=new Int16Array(this.N=n+7&-8),this.wx=new Int16Array(m*=this.N);
this.cxt=new Int32Array(s),this.pr=new Int32Array(s);
if(s>1)this.mp=new Mixer(s,1,1);
for(;s;)this.pr[--s]=2048;
if(w)for(;m;)this.wx[--m]=w
}
Mixer.prototype={
base:0,ncxt:0,nx:0,
update:function(y,i,e,n,p,w){
for(i=this.ncxt,y<<=12;i;)
if(e=(y-this.pr[--i])*14)
for(n=this.nx,p=this.cxt[i]*this.N+n;n;this.wx[p]=w<-32768?-32768:32767<w?32767:w)
w=this.wx[--p]+(1+(this.tx[--n]*e>>16)>>1);
this.nx=this.base=this.ncxt=0
},
add:function(x){this.tx[this.nx++]=x},
set:function(cx,range){
this.cxt[this.ncxt++]=this.base+cx;this.base+=range
},
p:function(y){
for(;this.nx&7;)this.tx[this.nx++]=0;
if(this.mp){
this.mp.update(y);
for(var i=this.ncxt;i;)
this.mp.add(stretch[this.pr[--i]=squash(dotProduct(this.tx,this.wx,this.nx,this.cxt[i]*this.N)>>5)]);
this.mp.set(0,1);
return this.mp.p()
}return this.pr[0]=squash(dotProduct(this.tx,this.wx,this.nx,0)>>8)
}};
//////////////////////////// StateMap, APM //////////////////////////
function StateMap(i){
var A=new Uint32Array(i);
if(A.fill)A.fill(1<<31);
else for(;i;)A[--i]=1<<31;A.cxt=0;return A}
function APM1(n){
for(var i=33,p=i,A=new Uint32Array(n*=i);i;)A[--i]=squash(i-16<<7)*16;
for(A.index=0;p<n;)A[p++]=A[i++];
return A
}
function APM1p(t,y,pr,cxt){
pr=stretch[pr];
var g=(y<<16|y<<7)-y-y,i=t.index;
t[i]+=g-t[i++]>>7;t[i]+=g-t[i]>>7;g=pr&127;
return t[i=t.index=(pr+2048>>7)+cxt*33]*(128-g)+t[i+1]*g>>11
}
/*function APM(n){
for(var i=24,p=i,A=new Uint32Array(n*=i);i;)A[--i]=squash(((i*2+1<<12)/48|0)-2048)<<20|6;
for(A.cxt=0;p<n;)A[p++]=A[i++];
return A
}
function APMp(A,y,pr,cx){
SMup(A,y,255),
pr=(stretch[pr]+2048)*23,
cx=cx*24+(pr>>12),
A.cxt=cx+((pr&=4095)>>11);
return(A[cx]>>>13)*(4096-pr)+(A[cx+1]>>>13)*pr>>19
}*/
function SMup(A,y,limit){
var c=A.cxt,i=A[c],n=i&1023,p=i>>>10;
A[c]+=((y<<22)-p>>3)*DT[n]&-1024|n<limit
}
function SMp(A,y,cx){
SMup(A,y,1023);
return A[A.cxt=cx]>>>20
}
/////////////////////////// ContextMap /////////////////////////
function SmallStatesMap(m){
var t=new Uint16Array(m>>=1);t.m=m-256;
if(t.fill)t.fill(32768);
else for(;m;)t[--m]=32768;
t.cp=t.cxt=0;return t
}
function scmset(t,cx){t.cxt=cx*256&t.m}
function scmix(t,m,y,c0,rate){
t[t.cp]+=(y<<16)-t[t.cp]+(1<<rate-1)>>rate;
m.add(stretch[t[t.cp=t.cxt+c0]>>4])
}
function ContextMap(m,c,f){
this.cxt=new Uint32Array(c);
this.runp=new Uint32Array(c);
this.cp=new Uint32Array(c);
this.cp0=new Uint32Array(c);
for(this.sm=[],this.fast=f;c;this.sm[c]=StateMap(256))this.runp[--c]=3;
this.last=new Uint8Array(m>>=6);
this.chk=new Uint16Array(m*7);
this.bh=new Uint8Array(m*49);
this.Rnd=rnd(this.m=m-1)
}
ContextMap.prototype={cn:0,
set:function(c,n){
var i=this.cn++;i&=n||-1;
c=((c>>>16)*26803+(c&=65535)*15070<<16)+c*26803+i;c=c<<16|c>>>16;
this.cxt[i]=((c>>>16)*52503+(c&=65535)*1883<<16)+c*52503+i>>>0},
get:function(bh,chk,ch,o){
var last=this.last,l=last[o],m=o*7,n=o*49;
if(chk[m+(l&15)]===ch)return(l&15)*7+n;
for(var b=65535,bi=0,i=7,p;i;){
if(chk[m+--i]===ch)return last[o]=l<<4|i,i*7+n;
p=bh[i*7+n];
if(p<b&&15&l^i&&l>>4^i)b=p,bi=i
}last[o]=240|bi,chk[m+bi]=ch;
for(p=7,n+=bi*7;p;)bh[n+--p]=0;
return n},
mix:function(m,cc,bp,c1,y){
for(var i=this.cn,c,h,p,x,bh=this.bh,cp=this.cp,cp0=this.cp0,chk=this.chk,runp=this.runp,cxt=this.cxt,Rnd=this.Rnd,result=0;i;){
if(h=cp[--i]){
c=ST[bh[h]<<2|y];
if(c>203&&(p=++Rnd[64],Rnd[p&63]=Rnd[p-24&63]^Rnd[p-55&63])<<(452-c>>3))c-=4;
bh[h]=c
}
//Update context pointers
if(bp>1&&!bh[runp[i]])cp[i]=0;
else{x=cxt[i]>>>16;
switch(bp){
case 1:case 3:case 6:cp[i]=cp0[i]+1+(cc&1);break;
case 4:case 7:cp[i]=cp0[i]+3+(cc&3);break;
case 2:case 5:cp[i]=cp0[i]=this.get(bh,chk,x,cxt[i]+cc&this.m);break;
default:
h=cp[i]=cp0[i]=this.get(bh,chk,x,cxt[i]+cc&this.m);
//Update pending bit histories for bits 2-7
if(bh[h+=3]===2)
c=bh[h+1]+256,
bh[p=this.get(bh,chk,x,cxt[i]+(c>>6)&this.m)]=1+(c>>5&1),
bh[p+1+(c>>5&1)]=1+(c>>4&1),
bh[p+3+(c>>4&3)]=1+(c>>3&1),
bh[p=this.get(bh,chk,x,cxt[i]+(c>>3)&this.m)]=1+(c>>2&1),
bh[p+1+(c>>2&1)]=1+(c>>1&1),
bh[p+3+(c>>1&3)]=1+(c&1),
bh[h+3]=0;
//Update run count of previous context
if(!bh[p=runp[i]])bh[p]=2,bh[p+1]=c1;//new context
else if(bh[p+1]^c1)bh[p]=1,bh[p+1]=c1;//different byte in context
else if(bh[p]<254)bh[p]+=2;//same byte in context
runp[i]=h
}
}
if(!this.fast)
//predict from last byte in context
if(bh[p=runp[i]+1]+256>>8-bp^cc)m.add(0);
else c=bh[p-1],//count*2,+1 if 2 different bytes seen
p=(bh[p]>>7-bp&1)*2-1,//predicted bit+for 1,-for 0
m.add(p*ilog[c+1]<<2+(~c&1));
//predict from bit context
if(p=cp[i])p=bh[p],p&&result++;
m.add(stretch[SMp(this.sm[i],y,p)])
}
if(bp>6)this.cn=0;
return result}
};
/////////////////////////// dmc model /////////////////////////
function dmcModel(m){
if(m<65536)m=65536;
this.state=new Uint8Array(this.top=this.size=m*=2);
this.nx=new Uint32Array(m*2);
this.c=new Uint16Array(m*2);
this.sm=StateMap(256)
}
dmcModel.prototype={
curr:0,th:256,
add:function(m,y){
//clone next state
var N=this.nx,C=this.c,S=this.state,a=this.curr,p=this.top,s=this.size,x=a+y*s;
if(p<s){
var n=N[x],i=C[x],j=C[n]+C[n+s];
if(i>=this.th*2&&j-i>=this.th*3){
i=i*4096/j|0;
C[n]-=C[p]=C[n]*i>>12;
C[n+s]-=C[p+s]=C[n+s]*i>>12;
N[p]=N[n];N[p+s]=N[n+s];
S[N[x]=p++]=S[n];
// if(top==MEM*2)this.th=512;if(top==MEM*3)this.th=768
}
}
//Initialize to a bytewise order 1 model at startup or when flushing memory
if(p>=s)for(i=this.th=256,p=i*i,a=0,x=y*s;i--;)
for(j=256;j;i<127?(N[n]=n+i+1,N[n+s]=n+i+2):(N[n]=i-127<<8,N[n+s]=i+1<<8))
C[n=--j<<8|i]=C[n+s]=192;
this.top=p;
//update count,state
C[x]<3800&&(C[x]+=256);
S[a]=ST[S[a]<<2|y];
//predict
m.add(stretch[SMp(this.sm,y,S[x=this.curr=N[x]])]);n=C[x+s];
m.add(stretch[(n+5)*4096/(C[x]+n+10)|0])
}};
function Predictor(m){
if(m>9)m=9;m=1<<m+16;
this.buf=new Uint8Array(m*8);this.bm=m*8-1;
this.dmc=new dmcModel(m);
this.a=APM1(256);this.a1=APM1(1<<16);this.a2=APM1(1<<16);
// match model
this.H=new Int32Array(m);this.hm=m-1;
this.scm1=SmallStatesMap(1<<17);
this.scm2=SmallStatesMap(1<<17);
// contextModel2
this.cm=new ContextMap(m*32,22);
this.m=new Mixer(76,1288,5);
this.cxt=new Uint32Array(16);//order 0-11 contexts
// normal model
this.t1=new Uint32Array(256);
this.t2=new Uint16Array(1<<16)
}
Predictor.prototype={
c0:1,c4:0,bpos:0,
// match model
h:0,ptr:0,len:0,result:0,posnl:-1,pos:-1,
match:function(m,y,bp){
var B=this.buf,l=this.len,c=B[this.pos&this.bm],p,a=0,b=0;
if(!bp){
this.h=this.h*7976+c+1&this.hm;//update context hash
if(l)++l,++this.ptr;
else{
p=this.ptr=this.H[this.h];
if(p&&this.pos-p<this.bm)
for(;B[this.pos-l&this.bm]===B[p+~l&this.bm]&&l<MAXLEN;)++l;
}
scmset(this.scm1,this.H[this.h]=this.pos+1);
this.result=l;
if(c===255||c===13||c===10)this.posnl=this.pos;
p=this.pos-this.posnl,scmset(this.scm2,p<255?p:255)
}
if(l)
if(c===B[this.ptr-1&this.bm]&&this.c0===(p=B[this.ptr&this.bm])+256>>8-bp){
if(l>MAXLEN)l=MAXLEN;
a=ilog[l]<<2;b=l<32?l<<6:2048;
p>>7-bp&1||(a=-a,b=-b)
}else l=0;
m.add(a),m.add(b);
this.len=l;scmix(this.scm1,m,y,this.c0,7);scmix(this.scm2,m,y,this.c0,7);
return this.result},
word0:1,word1:0,mask:0,mask2:0,
contextModel2:function(y,bp){
this.m.update(y);
var im=ilog[this.match(this.m,y,bp)],B=this.buf,c1=B[this.pos&this.bm],c2=B[this.pos-1&this.bm];//Length of longest matching context+4
this.dmc.add(this.m,y);
//Normal model
if(!bp){
for(var i=14,c4=this.c4,c3=c4&255,cxt=this.cxt;i;)cxt[i]=hash(cxt[--i],c3); //update order 0-11 context hashes
this.cm.set(0);
this.cm.set(c3);
this.cm.set(c4&65535);
this.cm.set(c4&0xffffff);
this.cm.set(c4);
this.cm.set(cxt[5]);
this.cm.set(cxt[6]);
this.cm.set(cxt[14]);
//sparse model
this.cm.set(c4&0xf8f8c0ff);
this.cm.set(c4&0xe0e0e0);
this.cm.set(c4&0xffc0ff80);
this.cm.set(im|c4&-65536);
this.cm.set(im|c4&0xff00);
this.cm.set(im|c4&0xff0000);
this.cm.set(this.mask=this.mask<<3|ctype[c3]);
this.mask2=this.mask2<<3|this.mask>>27&7;
this.cm.set(hash(this.mask<<5,this.mask2<<2));
//indirect model
c3=B[this.pos-2&this.bm]<<8|c2;
this.t1[c2]=this.t1[c2]<<8|c1;
this.t2[c3]=this.t2[c3]<<8|c1;
c3=c2<<8|c1|this.t2[c2<<8|c1]<<16,c1|=this.t1[c1]<<8;
this.cm.set(c1>>8&65535),this.cm.set(c3>>16&255),
this.cm.set(c1&65535),this.cm.set(c3&0xffffff);
//word/exe model
// if(filetype==EXE)this.cm.set(execxt(4)),this.cm.set(execxt(5));
// else{
c3=c1&=255;
if(c3>64&&c3<91)c3+=32;
if(c3>96&&c3<123||c3>127)this.word0=hash(this.word0,c3);
else if(this.word0)this.word1=this.word0,this.word0=0;
this.cm.set(this.word0);
this.cm.set(hash(this.word0,this.word1))
// }
}
i=this.cm.mix(this.m,this.c0,bp,c1,y);
this.m.set(c1+8,264);
this.m.set(this.c0,256);
this.m.set(i+16*(c1>32)+32*!bp+64*(c1===c2)/*+128*(filetype==EXE)*/,256);
this.m.set(c2,256);
this.m.set(im,256);
return this.m.p(y)},
update:function(y){
if((this.c0+=this.c0+y)>255)
this.buf[++this.pos&this.bm]=this.c0&=255,
this.c4=this.c4<<8|this.c0,
this.c0=1;
var p0=this.contextModel2(y,this.bpos=this.bpos+1&7),p=APM1p(this.a,y,p0,this.c0),p2=this.buf[this.pos&this.bm],p1=APM1p(this.a1,y,p0,this.c0|p2<<8);
return p*5+p1*15+APM1p(this.a2,y,p0,65535&hash3(this.bpos,p2,this.buf[this.pos-1&this.bm]))*12+16>>5
}};
//setImmediateもどき
(function(a){
var F=[],hit="\0";
a.wait0=function(f){
F[F.length]=f;a.postMessage(hit,"*")
};
a.onmessage=function(e){
if(e.source===a&&e.data===hit)
e.stopPropagation(),F[0]&&F.shift()()
}
})(this);
/* FP8enc(A,m,done,rate): compressor
@A: input(Array / Uint8Array)
@m: memory usage(0-9). (65536<<m)*44 bytes
@done: call back of last process.
done(A,a,z)
@A: compressed array of input.
@a: last position
@z: compressed size
@rate: call back of progress
rate(a,z)
@a: current position
@z: last position
@return:
call with await: compressed array of @In
without await: Promise object
*/
async function FP8enc(A,m,done=a=>a,rate){
var a=A.length,b,z=a,w=0,x=-1,o=1,p=2048,
O=[m&=15],P=new Predictor(m),
pass="function"!=typeof rate?-1:1023,fn=b=>wait0(c=>b(rate(a,z))),st=+new Date;
//write size
for(;a;a>>>=8)O[o++]=a&255,O[0]+=16;
//main loop
for(;a<z;a&pass||Date.now()-st<200||await new Promise(fn,st=+new Date))
for(let i=8,s=A[a++];i;){
p+=p<2048;m=w+(x-w>>>12)*p+((x-w&4095)*p>>12);
p=P.update(b=s>>--i&1);
for(b?x=m:w=m+1;!((w^x)>>24);x=x<<8|255)
O[o++]=x>>>24,w<<=8
}
O[o++]=w>>>24;done(O,z,o);
return O
}
/* FP8dec(A,done,rate): decompresser
@A: input(Array / Uint8Array)
@done: call back of last process
done(A,a,z)
@A: compressed array of input
@a: input size
@z: compressed size
@rate: call back of progress
rate(a,z)
@a: current position
@z: last position
@return:
call with await: decompressed array of @A
without await: Promise object
*/
async function FP8dec(A,done,rate){
var a=1,b,c,l=0,m,o=0,p=2048,v=A[0],w=0,x,y=v>>4,z=A.length,
O=[],P=new Predictor(v&15),
pass="function"!=typeof rate?-1:1023,fn=b=>wait0(c=>b(rate(a,z))),st=+new Date;
for(;y--;w+=8)l+=A[a++]<<w;
for(v=w=0;o<l;o&pass||Date.now()-st<200||await new Promise(fn,st=+new Date)){
for(c=1;c<256;c+=c+b){
for(p+=p<2048;!((w^x)>>24);x=x<<8|255)
v=(v<<8|(a<z?A[a++]:255))>>>0,w=w<<8>>>0;
m=w+(x-w>>>12)*p+((x-w&4095)*p>>12);
p=P.update(b=v<=m|0);b?x=m:w=m+1
}
O[o++]=255&c
}
done(O,z,o);return O
}
使用例
(async()=>{
let A=Array.from("That that is is that that is not is not is that it it is",a=>a.charCodeAt());
let rate=(a,b)=>{console.log((a/b*1e4|0)/100,"%")}, done=(a,b,c)=>{console.log(b,"->",c,a)};
let e=await FP8enc(A,5,done,rate);
let d=await FP8dec(e,done,rate);
console.log(e.length, String.fromCharCode(...d))
})()
file爆縮実験
See the Pen fp8 compressor by xezz (@xezz) on CodePen.
参考文献
発信地
Large Text Compression Benchmark
補足
本記事のfp8もどきは本家と互換性は皆無です。本家に忠実な移植して下さる方は大歓迎です