UWSCでBase128を作ってみる

先日、Base128を考えたので、スクリプトを作ってみた。


符号化は8bitデータ7つを7bitデータ8つと見て、0x00〜0x7fを0x2?〜0x6?,0xB?〜0xD?にマッピング
復号化はその逆。



スクリプト

Base128.uws

OPTION EXPLICIT

IFB GET_UWSC_NAME = "Base128.uws" THEN
	DIM l, buf, i, enc, dec, res = TRUE
	FOR l = 0 TO 256
		buf = SAFEARRAY(0, l)
		FOR i = 0 TO LENGTH(buf) - 1
			buf[i] = RANDOM(256)
		NEXT
		enc = Base128.Encode(buf)
		PRINT LENGTH(enc) + " : " + enc
		dec = Base128.Decode(enc)
		FOR i = 0 TO LENGTH(buf) - 1
			IFB buf[i] <> dec[i] THEN
				PRINT "NG: " + i + " " + buf[i] + " <> " + dec[i]
				res = FALSE
			ENDIF
		NEXT
		PRINT l + " : " + LENGTH(buf) + ", " + LENGTH(dec)
		IF LENGTH(buf) <> LENGTH(dec) THEN res = FALSE
	NEXT
	PRINT res
ENDIF


MODULE Base128
	DIM _b[] = 1, 2, 4, 8, 16, 32, 64, 128

	FUNCTION ConvSub(i, encode)
		IFB encode THEN
			IFB i < $50 THEN
				RESULT = CHRB(i + $20)
			ELSE
				RESULT = CHRB(i + $60)
			ENDIF
		ELSE
			IFB i < $80 THEN
				RESULT = i - $20
			ELSE
				RESULT = i - $60
			ENDIF
		ENDIF
		//PRINT "ConvSub(" + i + ", " + encode + ") " + RESULT
	FEND
	FUNCTION Conv(i, b, b0, base="", off=0)
		DIM t = VARTYPE(base)
		IFB t = VAR_BSTR THEN
			RESULT = base
			DIM ng = b < 0
			IF ng THEN b = 0
			// iが0なら単体で算出。それ以外はbとb0(上i桁)から算出。i-6ではb単体でも算出
			DIM j = b MOD _b[8 - i - 1]
			IF i THEN j = INT(b0 / _b[8 - i]) + j * _b[i]
			RESULT = RESULT + ConvSub(j, TRUE)
			IF i = 6 AND !ng THEN RESULT = RESULT + ConvSub(INT(b / _b[8 - i - 1]), TRUE)
			//PRINT "Conv(" + i + ", " + b + ", " + b0 + ")=" + COPY(RESULT, LENGTH(base) + 1)
		ELSE
			DIM l = 0
			IFB t >= VAR_ARRAY THEN
				RESULT = base
				IF off >= LENGTH(base) THEN RESIZE(RESULT, off)
			ELSE
				RESULT = SAFEARRAY(0, off)
			ENDIF
			// iとi-1から算出する。bは下i桁、b0は上8-i桁
			IFB i THEN
				RESULT[off] = INT(ConvSub(b0,FALSE) / _b[i-1]) + (ConvSub(b,FALSE) MOD _b[i]) * _b[8-i]
				//PRINT "Conv(" + i + ", " + b + ", " + b0 + ")=" + RESULT[off]
			ENDIF
		ENDIF
	FEND

	FUNCTION Encoding(var b, var idx, data, len=-1, base="")
		RESULT = base
		IFB VARTYPE(data) >= VAR_ARRAY THEN
			IF len = -1 THEN len = LENGTH(data)
			DIM i
			FOR i = 0 TO len - 1
				RESULT = Conv(idx, data[i], b, RESULT)
				b = data[i]
				idx = (idx + 1) MOD 7
			NEXT
		ELSE
			IF len = -1 THEN len = 1
			RESULT = Conv(idx, data, b, RESULT)
			b = data
			idx = (idx + 1) MOD 7
		ENDIF
	FEND
	FUNCTION Encode(data, len=-1, base="")
		DIM b = 0, idx = 0
		RESULT = Encoding(b, idx, data, len, base)
		IF idx THEN RESULT = Encoding(b, idx, -1, 1, RESULT)
	FEND

	FUNCTION Decoding(var b, var idx, data, var off, base=NULL)
		RESULT = base
		DIM i, tar
		FOR i = 1 TO LENGTH(data)
			tar = ASCB(COPY(data, i, 1))
			IFB (tar > $1F AND tar < $70) OR (tar > $AF AND tar < $E0) THEN
				RESULT = Conv(idx, tar, b, RESULT, off)
				b = tar
				IF idx THEN off = off + 1
				idx = (idx + 1) MOD 8
			ENDIF
		NEXT
	FEND
	FUNCTION Decode(data, base=NULL)
		DIM b = 0, idx = 0, off = 0
		IF base = NULL THEN base = SAFEARRAY(0, INT(LENGTH(data) * 7 / 8) - 1)
		RESULT = Decoding(b, idx, data, off, base)
	FEND

ENDMODULE

テストスクリプトは、1〜257長のバイト配列を、符号化・復号化し元と同じかチェックする。
どこかでNGがあれば最後のPrintはFalse。全てOKならTrueを表示。


半角カナ等の領域にマッピングしているので、CHRBで半角カナが表示されるようにしています。
埋める記号は、いらんか、と思ってつけてません。

使い方

符号化

バイト配列を符号化する。
Encode関数に、バイト配列を渡すだけ。
LENGTH関数で、第一引数の長さが取れない場合は、第二引数に長さを指定してください。


柔軟に符号化したい場合は、Encoding関数を呼ぶ必要があります。
 第一引数:前データ(第二引数が0の場合は、参照されない)。出力:現データ(第三引数)
 第二引数:インデックス0〜6。この値によって前データの参照量が決まる。出力:次インデックス
 第三引数:現データ
 第四引数:データ長さ(LENGTH関数で長さが取れるなら省略可)
 第五引数:ベース文字列(省略可)
第一引数・第二引数は、呼ぶたびに変わるため、初期値0,0の変数を用意し、変更せずに呼び続ければOK。
Encode関数にあるように、ちょうど7バイト単位で処理できてない場合は、ダミーデータでもう一回呼ぶ必要があります。
途中から復号化する場合を想定して用意しています。

復号化

文字列をバイト配列に復号する。
Decode関数に、文字列を渡すだけ。


柔軟に復号化したい場合は、Decoding関数を呼ぶ必要があります。
 第一引数:前データ(第二引数が0の場合は、参照されない)。出力:現データ
 第二引数:インデックス0〜7。この値によって前データの参照量が決まる。出力:次インデックス
 第三引数:現データ
 第四引数:オフセット
 第五引数:ベース配列(省略可。省略時はなし)
符号化と基本的に同じ思想です。


独り言

特に記事とは関係ない独り言。
SetWindowLong/GetWindowLongにANSIUnicode版があるのは、GWL_WNDPROCあたりのせい。
WndProcは、WM_GETTEXT等文字列を扱うのですよ。