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。この値によって前データの参照量が決まる。出力:次インデックス
第三引数:現データ
第四引数:オフセット
第五引数:ベース配列(省略可。省略時はなし)
符号化と基本的に同じ思想です。