0
0

かつて圧縮業界で最強を誇った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もどきは本家と互換性は皆無です。本家に忠実な移植して下さる方は大歓迎です

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0