0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

BWTと文脈混合法、どちらも高圧縮な手法として有名です。それらをうまく組み合わせると、当然スンバラスィー性能になります。
その代表格としてbcmを紹介します。高速高圧縮、ついでに単純設計で移植も容易と文句の付け所がありません。
開発者はIlya Muravyov氏。他にも多数の圧縮programを公開されています。

実装編

BWTの処理についてはこの記事で触れているものと同じになります。符号化にはBinary Range符号を利用。圧縮と伸長は以下の通り。
ちなみに本家とは仕様が異なるので互換性は皆無

//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);
/* compressor
@In: input(Uint8Array)
@bs: block size(32768 - 0x7fffffff)
@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
*/
function BCMenc(In,bs,done=a=>a,rate){
	function encBit(b){
		var m=l+(h-l>>>0)*32768/262144>>>0;
		for(b?h=m:l=m+1;!((l^h)>>24);h=h<<8|255)O[o++]=l>>>24,l<<=8
	}
	function body(){
		var b,c,step=goal,f,n,ctx,p,p0,p1,p2,i,x1,x2,ssep,m,C,d=Date.now();
		for(rate(a,z);a<size;c1=c){
			if(!--step&&Date.now(step=goal)-d>200)return wait(body);
			c=In[a++],run=c1===c2&&++run,f=run>2&&256,ctx=1;
			for(n=8;n;ctx+=ctx+b){
				// prediction
				p0=C0[ctx];
				p1=C1[ctx|c1<<8];
				p2=C1[ctx|c2<<8];
				p=p0+p0+p0+p0+p1+p1+p1+p2>>3;
				C=C2[f+ctx];x1=C[i=p>>12];x2=C[i+1];
				ssep=x1+((x2-x1)*(p&4095)>>12);
				p+=ssep+ssep+ssep;
				m=l+(h-l>>>0)*p/262144>>>0;
				(b=c>>--n&1)?h=m:l=m+1,m=b<<16,
				C0[ctx]-=(p0-m>>2)+b,
				C1[ctx|c1<<8]-=(p1-m>>4)+b,
				C[i]-=(x1-m>>6)+b;
				// encoding normalize
				for(C[i+1]-=(x2-m>>6)+b;!((l^h)>>24);h=h<<8|255)O[o++]=l>>>24,l<<=8
			}c2=c1
		}return wait(head);
	}
	function head(){
		var U=In.subarray(a,a+bs),n=U.length,i=31,bp;
		if(a&&n==bs)encBit(0);
		else{if(a)for(i=0,encBit(1);bm>>++i;);
			for(;i;)encBit(n>>--i&1)
		}if(!n){for(;l;l<<=8)O[o++]=l>>>24;done(O,z,o);return O}
		bp=BWTe(U.slice(),U)-1;size+=bm=n;
		if(n>2){
			for(i=0;n-1>>++i;);
			for(;i;)encBit(bp>>--i&1);
		}return wait(body)
	}
	In instanceof Uint8Array||(In=new Uint8Array(In));
	var goal="function"!=typeof rate?0:8192,wait=wait0,bm=-1>>>1,
		l=0,h=-1>>>0,a,o=512,c1=0,c2=0,run=0,size=1<<15,z=In.length,
		C,C0=new Uint16Array(65792).fill(32768),C1=C0.subarray(256),C2=[],O=[];

	bs&=-1>>>1;if(bs<size)bs=size;if(bs>z)bs=z;size=0;
	if(!goal)wait=a=>a(),rate=a=>a;// not async

	// initialize model
	for(;o;)for(C=C2[--o]=new Uint16Array(a=17);a;)
		C[--a]=a-(a>15)<<12;
	return head()
}
/* decompressor
@In: input (Uint8Array)
@done: call back of last process.
	done(A,a,z)
	@A: decompressed array of input.
	@a: input size
	@z: decompressed size
@rate: call back of progress.
	rate(a,z)
	@a: current position
	@z: last position
*/
function BCMdec(In,done=a=>a,rate){
	function decBit(){
		for(;!((l^h)>>24);h=h<<8|255)v=(v<<8|In[a++])>>>0,l<<=8;
		var m=l+(h-l>>>0)*32768/262144>>>0,b=v<=m;b?h=m:l=m+1;
		return b
	}
	function body(){
		var b,c,step=goal,f,ctx,p,p0,p1,p2,i,x1,x2,ssep,m,C,d=Date.now();
		for(rate(a,z);u<size;P[u]=F[U[u++]=c1=255&ctx]++){
			if(!--step&&Date.now(step=goal)-d>200)return wait(body);
			run=c1===c2&&++run,f=run>2&&256;
			for(ctx=1;ctx<256;ctx+=ctx+b){
				// decoding normalize
				for(;!((l^h)>>24);h=h<<8|255)v=(v<<8|In[a++])>>>0,l<<=8;
				// prediction
				p0=C0[ctx];
				p1=C1[ctx|c1<<8];
				p2=C1[ctx|c2<<8];
				p=p0+p0+p0+p0+p1+p1+p1+p2>>3;
				C=C2[f+ctx];x1=C[i=p>>12];x2=C[i+1];
				ssep=x1+((x2-x1)*(p&4095)>>12);
				p+=ssep+ssep+ssep;
				m=l+(h-l>>>0)*p/262144>>>0,b=0;
				v>m?l=m+1:(h=m,b=1),m=b<<16;
				C0[ctx]-=(p0-m>>2)+b,
				C1[ctx|c1<<8]-=(p1-m>>4)+b,
				C[i]-=(x1-m>>6)+b,C[i+1]-=(x2-m>>6)+b
			}c2=c1
		}
		if(size==2)bp=U[1]<U[0]?1:2;
		for(b=f=0;b<u;b+=c)c=F[f],F[f++]=b;
		for(c=0;u;c<bp&&c++)c=P[c]+F[O[o+--u]=U[c]];
		o+=b;F.fill(0);return wait(head)
	}
	function head(i){
		if(!o||decBit()){
			if(o)for(i=0;size>>++i;);else i=31;
			for(size=0;i;)size|=decBit()<<--i;
			if(!size)return done(O,z,o),O;
			if(!o)U=new Uint8Array(size),P=new Uint32Array(size);
		}bp=1;
		if(size>2){
			for(i=0;size-1>>++i;);
			for(;i;)bp+=decBit()<<--i;
		}
		return wait(body)
	}
	var goal="function"!=typeof rate?0:8192,l,h,v,a,o=512,u=0,size,z=In.length,bp,c1=0,c2=0,run=0,
		C,C0=new Uint16Array(65792).fill(32768),C1=C0.subarray(256),C2=[],O=[],P,F=new Uint32Array(256),U,wait=wait0;

	if(!goal)wait=a=>a(),rate=a=>a;// not async
	// initialize model
	for(;o;)for(C=C2[--o]=new Uint16Array(a=17);a;)
		C[--a]=a-(a>15)<<12;
	return head()
}

BCMencの第2引数には圧縮元配列の分割区間幅を指定。通常大きい方が圧縮率が高くなり、空間消費量が増え、速度が犠牲になります。
doneとrateはcallback関数ですが、指定するほどのものではありません。面倒臭いだけです。非同期関数のように振る舞い、CPU負荷が若干減るかもしれない程度のもの。

使用例
let A=Array.from("That that is is that that is not is not is that it it is",a=>a.charCodeAt());
let e=BCMenc(A,A.length), d=BCMdec(e);
console.log(A.length,"->",e.length, String.fromCharCode(...d))

具現化系codepen

See the Pen BCM compressor by xezz (@xezz) on CodePen.

突っ込みなど

本家は元々divsufsortを使ってBWTをしていましたが、この記事では実装していません。pointer乱舞のprogramなんてJavaScriptで書いてられるかってんだ! まあ書きはしましたが、長くなる上に一般的なfileの処理では今回の実装の方が高速という結末に…

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?