主にhtmlやjsを圧縮する企画です。超高速に展開できる優れ物。圧縮率も悪くありません。
圧縮手法はLZを採用。binary tree match finderで速度、optimal parsingで圧縮力を稼ぎます。
[\0-\x7f]
以外の文字列は特殊変換するため、性能が悪くなります。
設計図が薄汚いのは仕様です。気にしてはいけません。
/*
A2LZe(T,ws,ES6,IE,fix,type)
@T :[\0-\x7f]からなる文字列. それ以外は特殊変換される
@ws :最長一致.範囲は0~127. 大きくすると辞書が小さくなる
@ES6 :ECMAScriptのversionが5か6+かの真偽
@IE :InternetExplorer対応かどうかの真偽
@fix :[\x80-\uffff]の変換法.
0はUnicode番号に応じて桁数調整するが復号器が複雑.
1だと4桁固定で復号器が単純
@type: 復号法. 圧縮率が変わる
0:for loop
1:再帰(巨大文字列苦手)
2:for loop & substr未使用
3:for loop & longest match>255. 巨大文字列向き
*/
function A2LZe(T,ws,ES6,IE,fix,type){
var e=(fix?/\1/:/[\1-\3]/).test(T),f=/[\x80-\uffff]/.test(T)&&[],pf=fix?["\x0100","\x010","\1"]:["\1","\2","\3"];
e&&f?T=T.replace(fix?/\1/g:/[\1-\3]/g,function(a){return a+a}):e=0;
T=(f?T.replace(/[\x80-\uffff]/g,function(a,b){
a=a.charCodeAt();b=a.toString(16);
return(a<256?f[0]=pf[0]:a<4096?f[1]=pf[1]:f[2]=pf[2])+b
}):T).split("");
var MIN=3,H=[],N=[],O=[],a=0,b=65308,c,d,h,i,k=0,l=32655,m,n,o=0,p,r,s,v,w,x,z=T.length;
if(!z)return"";
for(ws+=2;ws>b/--l>>0;);ws=l;
var Max=b/ws|0,M2=Max+1,M3=Max,ex=ES6?{92:1,96:1}:{10:1,13:1,34:+/"/.test(T),39:+/'/.test(T),92:1};
for(type>2&&(Max+=0xd7f1,M2=Max+1);Max>>++k;);
// Pass 1: Find all matches
for(x=(1<<k)-1;a<z;a++){
v=0,m=z-a,p=o;
if(m>2){
r=a%ws,l=r+ws,b=c=0,w=a<ws?-1:a-ws,s=H[h=T[a]+T[a+1]+T[a+2]];
m>M2&&(m=M2);
for(H[h]=a;s>w;){
i=b<c?b:c;
if(T[s+i]===T[a+i]){
for(;i++<m&&T[s+i]===T[a+i];);
if(i>v){
if(s+i<a||type>1)O[++o]=~s+a<<k|i;
else{
for(n=m,h=s,v=a;h>w&&T[h-1]===T[--v]&&n--;)--h;
for(v=0;T[h+v]===T[a+v]&&h+v<a&&v<m;)++v;
if(v<a-s)O[++o]=~s+a<<k|a-s;
else if(v>2)O[++o]=~h+a<<k|v
}v=i;
if(i>m){N[r]=N[s%=ws];N[l]=N[s+ws];m=0;break}}
}
if(T[s+i]<T[a+i])N[r]=s,s=N[r=s%ws+ws],c=i;
else N[l]=s,s=N[l=s%ws],b=i}
if(m)N[l]=N[r]=-1}
O[p]=o++-p}
// Pass 2: Build the shortest path
a=p=N[0]=N.length=H.length=0;
for(v=M3+MIN-1;a<z;h>=N[a]||(N[a]=h,H[a]=c)){
if(n=O[p++])for(c=N[a],h=MIN-1;n--;){
l=O[p++];d=l>>>k;l&=x;r=d*M3;
b=c+2;r-=MIN;d<<=k;
if(type<3)for(;h<l;s>=N[a+h]||(N[a+h]=s,H[a+h]=d+h))m=++h+r,s=m<1923?b:m<55296||57343<m?b+1:b+4;
else for(;h<l;s>=N[a+h]||(N[a+h]=s,H[a+h]=d+h))m=++h<v?h+r:v+r,s=m<1923?b:m<55296||57343<m?b+1:b+4,h<v||(s++,s+=ex[h-v+14]||0)
}
h=N[a]+1,c=T[a++]
}
for(o=p=O.length=0;c=N[p++]=H[a],a-=c.big?1:c&x;);
// Pass 3: Output the codes
H=String.fromCharCode;
if(type<3)for(;l=N[--p];)
O[o++]=l.big?l:H((l>>>k)*M3+(l&x)+125);
else for(M2=M3+MIN-1;l=N[--p];)
if(l.big)O[o++]=l;
else O[o++]=H((l>>>k)*M3+(l&=x,l<M2?l:M2)+125),l<M2||(O[o++]=H(l-M2+14));
// build decoder
for(b=c=d=h=i=0;o;)s=O[--o],b+=s==="\n"||s==="\r",c+=s==='"',d+=s==="'",h+=s==="$"&&O[o+1]==="{",i+=s==="`";
O=O.join("").replace(/\\/g,'\\\\').replace(/\0/g,'\\0').replace(/[\ud800-\udfff]/g,function(a){return"\\u"+a.charCodeAt().toString(16)}); // avoid '\' '\0' and surrogate pair
if(ES6&&b+c+d>h+i)r="`",O=O.replace(/\$\{/g,"\\${").replace(/`/g,'\\`');
else O=O.replace(/\n/g,'\\n').replace(/\r/g,'\\r').replace(RegExp(r=d>c?'"':"'","g"),"\\"+r).replace(/[\u2028\u2029]/g,function(a){return"\\u"+a.charCodeAt().toString(16)}); // LineTerminator can't be included
if(f){
if(fix)f=["\1([\\da-f]{4})"];
else f[a=0]&&(f[a++]=f[0]+"([\\da-f]{2})"),f[1]&&(f[a++]=f[1]+"([\\da-f]{3})"),f[2]&&(f[a++]=f[2]+"([\\da-f]{4})"),f.length=a;
s="String.fromCharCode('0x'+"+(a>1?"a.slice(1)":"b")+")",b=ES6?"(a,b)=>"+s:"function(a,b){return "+s+"}";
}
f=f?".replace(/"+f.join("|").replace(a>1?/[()]/g:"","")+"/g,"+b+")":"";
e=e?".replace(/("+(fix?"\1":"[\1-\3]")+"){2}/g,"+(ES6?"(a,b)=>b":"function(a,b){return b}")+")"+f:f;
if(!type){
// for loop. it can decode big text
a="{for(;c=i.charCodeAt(++p);)o+=c<128?i"+(IE?".charAt(p)":"[p]")+":o.substr(~((c-=128)/"+Max+")"+(IE?"+o.length":"")+",c%"+Max+"+3);return o}"+(ES6?")":"");
return(ES6?"((i,o,p,c)=>":"function(i,o,p,c)")+a+"("+r+O+r+","+r+r+",-1)"+e
}
if(type<2){
// recursive loop. best for small text
a="(c=i.charCodeAt(p))?f(i,o+(c<128?i"+(IE?".charAt(p)":"[p]")+":o.substr(~((c-=128)/"+Max+")"+(IE?"+o.length":"")+",c%"+Max+"+3)),++p):o";
return ES6?"(f=(i,o,p,c)=>"+a+")("+r+O+r+","+r+r+",0)"+e:"function f(i,o,p,c){return"+a+"}("+r+O+r+","+r+r+",0)"+e
}
// for loop & longest match>255. best for big text
a="{for(;c=i[h](++p);)if(c<128)o[n++]=i"+(IE?".charAt(p)":"[p]")+";else for(a=~((c-=128)/"+M3+")+n,c%="+M3+(type<3?"":","+--M3+">c||(c+=i[h](++p)-14)")+";--c+4;)o[n++]=o[a++];return o.join('')}"+(ES6?")":"");
return(ES6?"((i,o,p,n,h,c,a)=>":"function(i,o,p,n,h,c,a)")+a+"("+r+O+r+",[],-1,0,'charCodeAt')"+e
}
出力文字列は即時関数に囲まれているので、eval()
やdocument.write()
に放り込んだり、変数に代入する等して下さい。ついでに言うとUTF8形式です。以下codepenによる実演。
See the Pen A2LZ by xezz (@xezz) on CodePen.
関数の説明的解説
-
T
は入力文字列。圧縮されます -
ws
は最長一致。範囲は0~127。大きくすると辞書が小さくなる(ws=0だと32640、ws=127だと506) -
ES6
はECMAScriptのversionが5か6+かの真偽。trueの方が復号器が小さい -
IE
はInternet Explorer対応かどうかの真偽。具体的にはsubstr
の仕様対策 -
fix
は[\x80-\uffff]
の変換法。0はUnicode番号に応じて桁数調整するが復号器が複雑。1だと4桁固定で復号器が単純 -
type
は復号法- 0: for loop
- 1: 再帰。巨大文字列は復号できない
- 2: for loop。0より圧縮しやすいが、復号器は大きくなる
- 3: for loop。
ws
の値に関係なく最長一致無制限になる。巨大文字列に向いている