よく考えてみると今のままだとUNSIGNEDな値しか取り扱えないのだった。SIGNEDを取り扱うためには、マイナスの値を表現する必要がある。
そこで、先頭2バイトをヘッダ領域として扱って、そこに符号情報を格納するというのはどうだろう。
どうせヘッダ領域を設けるなら、サイズも一緒に格納してよさそうだ。符号はマイナスが付くかどうかだけ分かればいいので、1bitで充分。サイズも16bit、32bit、48bit、64bitの4種類しかないから2bitで表現できるだろう。今後文字列型を一緒に格納できるようにしたいということなら、少し考える必要が出てくるが、とりあえず置いておく。
さて、SIGNEDで思い出したが、SIGNED SHORT INTの範囲は-32,767〜32,768である。そういえば、UNICODE関数は32,768までの範囲なら特に問題は起こらないはずだ。だったら、32,768までの数字は1文字目の範囲に収めて、それを超えたら2文字目を使う、というのはどうだろうか。場合によってはコンパクトになるかもしれない。少なくとも32,768を超えない範囲では効果がありそうだ。もちろん32,768を超えているかどうかをどこかに保持しなきゃいけないが、ヘッダ領域にはまだ余裕がある。これはフラグを立てればいいだろう。16bit毎に分割するから、4bitでよさそう。最初の16bitを1bit目に、次を2bit目に、というふうにするわけだ。
ところで、32,768を超えたら2文字目を使うと書いたが、32,768を超えた場合、1文字目は常に32,768である。32,768を超えているかどうかはフラグで持つから、1文字目の32,768はもはや保持する必要がない、超えた分だけを保持しておけばいいことになる。そうすると、64bit整数をヘッダ領域の16bit+データ部16bit*4で表現できるから10byteで済む。前は16byteも必要だったからずいぶん小さく済むようになったと言えるだろう。というわけで実装してみる。
LOCAL:0 = 1, 32, 4000, -50000, 100000, 0, 8, -128
REPEAT 8
H = 0
SIF SIGN(LOCAL:COUNT) == -1
SETBIT H, 7
SELECTCASE LOCAL:COUNT
CASE IS <= 0xFFFF
L = 1
; ビットを立てない
CASE IS <= 0xFFFFFFFF
L = 2
SETBIT H, 5
CASE IS <= 0xFFFFFFFFFFFF
L = 3
SETBIT H, 6
CASEELSE
L = 4
SETBIT H, 5
SETBIT H, 6
ENDSELECT
LOCALS:1 = %""%
COUNT:1 = COUNT
FOR COUNT, 0, L
A = ABS(LOCAL:(COUNT:1)) >> (16 * COUNT) & 0xFFFF
IF A > 0x7FFF
A &= 0x7FFF
SETBIT H, COUNT
ENDIF
LOCALS:1 = %LOCALS:1%%UNICODE(A)%
NEXT
COUNT = COUNT:1
LOCALS = %LOCALS%%UNICODE(H)%%LOCALS:1%
REND
ENCODETOUNI %LOCALS%
REPEAT RESULT:0
VARSET LOCAL
H = RESULT:(++COUNT)
IF GETBIT(H, 5) && GETBIT(H, 6)
L = 3
ELSEIF GETBIT(H, 6)
L = 2
ELSEIF GETBIT(H, 5)
L = 1
ELSE
L = 0
ENDIF
COUNT:1 = COUNT
FOR COUNT, 0, L
IF GETBIT(H, COUNT)
LOCAL |= (0x8000 + RESULT:(++COUNT:1)) << (16 * COUNT)
ELSE
LOCAL |= RESULT:(++COUNT:1) << (16 * COUNT)
ENDIF
NEXT
COUNT = COUNT:1
IF GETBIT(H, L)
LOCAL |= 0x8000 + RESULT:(COUNT+1) << (16 * L)
ELSE
LOCAL |= RESULT:(COUNT+1) << (16 * L)
ENDIF
LOCAL *= GETBIT(H, 7) ? -1 # 1
PRINTFORM %TOSTR(LOCAL)%,
REND
実装できた。相変わらず力技だが、そのあたりはおいおいリファクタリングすればいい。処理は大きく複雑になったが、ヘッダに情報を保持できるようになって、柔軟にデータを格納できるようになった。前回よりもサイズも小さくなっている。トレードオフといったところだろう。
ここまで来ればひとまず数値配列とバイナリ列の相互変換ができる。現実的に運用するなら専用のユーティリティ関数を用意する必要があるだろうが、とりあえずはこんな感じで。