CF25 Extension GArR Issue #03

 CF25   extension   GArR   debug 

1バイト分、文字列情報が失われるバグを修正

引き続きユーザからの報告を元に動作検証。

デバッグに関するメモ、このバグフィクス後に rev1.0.1 → 1.0.4 →1.0.5 として公開。

1 byte 文の情報喪失

なぜ喪失するのか。計算を間違っている可能性が高い。ではどこが間違っているのか。前回修正した「改行」に関係がある。

Windows PC は改行が「\r\n」だが UNIX OS 系は「\n」である。これらはバイト数で比較すると「2バイト」と「1バイト」。この差分が「1バイト」であり喪失したデータも1バイト、共通項がある。

GArR の二次元配列化機能は本家の StringTokenizer とほぼ同じ機能を提供しているが実装方法はかなり違うはず。

StringTokenizer のソースコードが公開されているわけでもないから動作から想像するしかないが、本家版はソース文字列をエクステ内部でデリミタを使って一個一個文字列へ切り出して新しく配列を作って保存する実装になっていると思われる。

本家版はメモリを喰う。2015 年時に実験して気がついたのが本家版はアプリのフレーム再起動を繰り返すとメモリが開放されずどんどん蓄積されていく。大きめサイズの文字列( 例えば1メガとか ) 扱って、これを総要素数 30000 の二次元配列化しアプリフレーム再起動を 10 回位、するとデバッガで見る使用メモリサイズが 100 MB 超えて 400 MB すら超えたことがあった

単に開放されていないだけであってもメモリ使用の増加率は異常に大きい本家版は本当に文字列で二次元配列化してると感じた。

GArR はソースである文字列のポインタ情報を元にポインタ位置を記憶しているので、実際に文字列で 30000 のレコードを配列に保存はしない。二次元配列も一次元配列化して扱っているから、この結果メモリ使用量や処理速度がともに本家版より軽くて速くなる。

しかし GArR はポインタの位置情報をどこかで間違えてた場合、それ以降の文字データがすべて狂ってしまう可能性があるので計算結果の評価はシビアだ。実際に今回の 1 byte 喪失は計算ミスがあることを示唆しており、それが改行の扱いだった。

UNIX 用の改行は 1 バイトだが Windows PC 用の改行は 2 バイト。

デリミタとして利用される「,」や「;」「\t」は全て 1 バイト。つまり二次元配列で二つのデリミタのうち一つに改行がデリミタとして使われていてかつ OS が Windows PC の場合にはもうひとつのデリミタとバイト数を比較すれば等価ではなく 1 バイト差分が出る。

a\r\nb;b\r\nc

この文字列を 16 進文字列化すると

610d0a623b630d0a64

こうなって、0d0aがデリミタとして改行「\r\n」の 2 バイト、もうひとつのデリミタ「;」は3bで 1 バイト。

ポインタは数値として int 型と同じに扱うことができるからバイト数を計算すれば文字列におけるデリミタ位置を把握できる。速度向上のため二次元配列は一次元配列化して扱われるが、エクステでは二次元配列として扱われているという前提のお話で以下。

a は string[1] ( 終端 )

\r\n は string[2] ~ [3] ( 開始〜終端 )

b は string[4]

; は string[5]

b は string[6]

\r\n は string[7] ~ [8] ( 開始〜終端 )

c は string[9]

こうなる。

デリミタ「;」で最初にソース文字列は二つに分割される。

a\r\nb = string[0]

b\r\nc = string[1]

次にデリミタ「\r\n」で string[0] に検索をかけると一回目検索ヒット、デリミタの終端位置は string[0][3]となる。

二回目の検索にヒットは無いからデリミタ「;」の分を考慮して位置を算出する。

色々省いて書いたがこんな感じ。デリミタのバイト数から差分を考慮して分割する文字列の長さを決定してる。

注意点はエクステの内部では二次元配列として扱っていないので一次元配列内でのデリミタ位置を求める。要するに足し算の繰り返しなので、1 byte でもミスると全部狂う。だから間違わなければいい。

切り出した文字列を毎回バイト数で数えてその分を足す方法もある。ユニコードなので1文字は 1 〜 4 バイトと幅があり単純に文字数×バイト数で割り出すわけにはいかないし一工程増えるので大量のデータを扱う場合にはボトルネックになるかもなのでやらなかった。

文字位置を割り出してから扱っているデリミタのバイト数を比較して位置を算出する方法がたぶんいちばん速い。ただしちょっとソースは読み難い。

最終的に直ったけど

過去のソースを見ているともう一度書き直したい気分にはなる。やらないしやれないけど。

でも CF25 がここまで寿命を伸ばしたのは Fusion 3 が出なかったからで、本当は今頃 F3 がとっくに発売されていて CF25 はサポート切れ、こんな筋書きだったはずだ。

エクステを公開した時期もだいぶ遅かったのでだから本腰を入れて書いてない部分とかもあって、実装が面倒な部分は実際に省略した。

まともにビルドできるサンプルが当時からほとんど無くて、公開されているのは EDif 系と MMF2 の rSDK で作られたもの、そして多機能で高度な簡単に読めない時間かかりすぎるソースなど。間口が狭いすぎる。そして 3 年前と現在でまったく状況は変わってない。

エクステの製作と公開という経験を経て技術的に自信も付いたのでその後 GMS にはまったく不安なく移行できた。移行して分かったのは外部出力機能が段違いの性能で GMS の方が良いということ。あれは軽くショックだった。それ以来 GMS で作っていたので CF25 は触らなくなり、あるいは自作ツールを Windows PC 用として作る時に起動する程度になった。

F3 をずっと待っている。2015 年から今までずっと。MMF2 の頃から合わせればもっとずっと長く。




次へ

前へ