0
0

binary dataを文字列で表現する手法を紹介します。base64はあまりに有名だし、組み込み関数でも実装されているので除外…しようと思いましたが、btoa()atob()も使い勝手が悪過ぎるので、自作関数を用意しました。

  • base85

    以下の85種類の文字で表現。膨張率約25%

    !&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz
    
  • base91

    以下の91種類の文字で表現。膨張率約23%

    !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}
    
  • base122

    Unicode番号0, 10, 13, 34, 38, 92以外の7bit文字と[\x80-\xFF\u0180-\u01FF\u0280-\u02FF\u0380-\u03FF\u0480-\u04FF\u0580-\u05FF]で表現。出力文字はUTF8形式が適します。膨張率約13%

    !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}
    
  • base32768

    [\u3400-\uC1FF]で表現。出力文字はUTF16形式が適します。膨張率約6%。lacalStrageに詰め込む場合に役立ちます

実装

global領域にBaseというobjectを生成しており、符号化関数はBase.e64Base.e85Base.e91Base.e122Base.e32768で引数はArrayUint8Array
復号関数はBase.d64Base.d85Base.d91Base.d122Base.d32768で引数は符号化関数で得られた文字列。

{const S=String.fromCharCode, n2cjk=n=>S(n+=n<6400?13312:n<27136?13568:16896),
Base={
e64(A){
	var a=62,b=0,c,z=A.length,B=new Uint8Array((z*4+2)/3),C=new Uint8Array(64);
	for(C[a]=43;a;)C[--a]=a>51?a-4:a>25?a+71:a+65;
	for(C[63]=47;a<z;B[b++]=C[63&c])c=A[a++]<<16|A[a++]<<8|A[a++],B[b++]=C[c>>18],B[b++]=C[63&c>>12],B[b++]=C[63&c>>6];
	return new TextDecoder().decode(B)
},
d64(A){
	if(typeof A=="string")A=new TextEncoder().encode(A);
	var a=62,b=0,c,z=A.length,B=new Uint8Array((z+2>>>2)*3-"0021"[z&3]),C=new Uint8Array(123);
	for(C[43]=a;C[--a>51?a-4:a>25?a+71:a+65]=a;);
	for(C[47]=63;a<z;B[b++]=255&c)c=C[A[a++]]<<18|C[A[a++]]<<12|C[A[a++]]<<6|C[A[a++]],B[b++]=c>>16,B[b++]=255&c>>8;
	return B
},
e85(B){
	var a=85,b,i,o=0,z=B.length,C=new Uint8Array(124),A=new Uint8Array((z*5+3)/4);
	for(;a;)C[--a]=a+38;
	for(C[54]=33;a<z;){
		b=(B[a++]|B[a++]<<8|B[a++]<<16|B[a++]<<24)>>>0;
		for(i=5;i--;b=b/85|0)A[o++]=C[b%85]
	}
	return new TextDecoder().decode(A)
},
d85(A){
	if(typeof A=="string")A=new TextEncoder().encode(A);
	var a=85,o=0,z=A.length,b=z%5,C=new Uint8Array(124),B=new Uint8Array((z-b)/5*4+(b&&b-1));
	for(;C[--a+38]=a;);
	for(C[33]=54,C[0..a]=0;a<z;B[o++]=b>>>24)
		b=52200625*C[A[a+=4]]+614125*C[A[a-1]]+7225*C[A[a-2]]+85*C[A[a-3]]+C[A[a++-4]],
		B[o++]=b,B[o++]=b>>8,B[o++]=b>>16;
	return B
},
e91(B){
	var a=91,b=0,c,z=B.length,n,o=0,A=new Uint8Array(z*1.24+2),C=new Uint8Array(a);
	for(;a;)C[--a]=(a>57)+!!a+a+33;
	for(;a<z;){
		c|=B[a++]<<b;b+=8;
		if(b>13){n=c&8191;
			if(n>88)c>>=13,b-=13;
			else n=c&16383,c>>=14,b-=14;
			A[o++]=C[n%91];A[o++]=C[n/91|0]
		}
	}
	if(b){
		A[o++]=C[c%91];
		if(b>7||c>90)A[o++]=C[c/91|0]
	}
	return new TextDecoder().decode(A.subarray(0,o))
},
d91(A){
	if(typeof A=="string")A=new TextEncoder().encode(A);
	for(var a=91,b=0,c=-1,d,e,o=0,C=new Uint8Array(126);C[(--a>57)+!!a+a+33]=a;);
	for(;d=A[a++];)
		if(d=C[d],~c)for(c+=d*91,e|=c<<b,b+=(c&8191)>88?13:14,c=-1;A[o++]=e&255,e>>=8,b-=8,b>7;);
		else c=d;
	if(~c)A[o++]=e|c<<b;
	return A.subarray(0,o)
},
e122(A){
	function g(c){
		if(a>=z)return-1;
		c=(254>>>b&A[a])<<b;c>>=1,b+=7;
		if(b<8||(b-=8,++a>=z))return c;
		return(65280>>b&A[a])>>8-b|c
	}
	var a=0,b=0,c,e=8,n,o=0,z=A.length,E=new Int8Array(256),B=new Uint8Array(z*1.15+3);
	for(n of[0,10,13,34,38,92])E[n]=e++;
	for(;c=g(),~c;B[o++]=c)
		if(e=E[c])n=g(),e=~n?194|28&e<<2:(n=c,222),B[o++]=e|n>>6,c=128|63&n;
	return new TextDecoder().decode(B.subarray(0,o))
},
d122(B){
	function p(n){n<<=1,c|=n>>b,b+=7;if(b>7)A[o++]=c,b-=8,c=n<<7-b&255}
	for(var a=0,b=0,c,d,e,o=0,z=B.length,E=[0,10,13,34,38,92],A=new Uint8Array(z*1.75);a<z;p(e))
		if(e=B.charCodeAt(a++),127<e)
			d=e>>8&7,7>d&&p(E[d]),e&=127;
	return A.subarray(0,o)
},
e32768(A){
	for(var a=0,b,c,i=0,n=0,z=A.length,s=[];a<z;)
		if(c=A[a++],n<8)b|=c<<7-n,n+=8;
		else n-=7,s[i++]=n2cjk(b|=c>>n),b=c<<15-n&32767;
	if(0<n)if(s[i++]=n2cjk(b),8<n)s[i++]=n2cjk(32768);
	return s.join("")
},
d32768(s){
	if(!s)return new Uint8Array(0);
	var a=0,b,c,i=0,n=s.length-1,z=15*(n>>3)+n%8*2,A=s[n]==n2cjk(32768);
	n%8<1&&!A&&z++,n%8&&A&&z--;
	A=new Uint8Array(z);
	for(n=0;a<z;n-=8,A[a++]=b>>n&255)
		if(n<8)c=s.charCodeAt(i++),b=b<<15|(c-=c<19712?13312:c<40704?13568:16896),n+=15;
	return A
}};
if(!this.Base)this.Base=Base}
使用例
const A=Array.from("That that is is that that is not is not is that it it is",a=>a.charCodeAt());
const z=A.length;

let e=Base.e64(A);
let d=Base.d64(e);
console.log(z,"->",e.length,e,d)

e=Base.e85(A);
d=Base.d85(e);
console.log(z,"->",e.length,e,d)

e=Base.e91(A);
d=Base.d91(e);
console.log(z,"->",e.length,e,d)

e=Base.e122(A);
d=Base.d122(e);
console.log(z,"->",e.replace(/[^\0-\x7f]/g,11).length,e,d)

e=Base.e32768(A);
d=Base.d32768(e);
console.log(z,"->",e.length*2,e,d)

簡単お気楽お気軽お手軽足軽尻軽動作検証。入力文字列はUTF8形式とみなしています

See the Pen Base64,85,91,122,32768 encode by xezz (@xezz) on CodePen.

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