日記

9月 20日 (火)

1. 吉里吉里

1.1. オーバーラップI/O

  • LunaClientSocketクラスインスタンスは、メンバにソケットハンドルとオーバーラップデータを持つ。
  • インスタンスはWSASend/WSARecvで、オーバーラップI/Oを使用してリクエストを発行する。
  • このとき、オーバーラップデータへのポインタと、リクエスト処理完了時コールバック関数へのポインタも指定する。
  • このコールバック関数はソケットハンドルを受け取れない。
  • このコールバック関数は、通常の__stdcall。
  • 通常の関数ポインタを受け取る引数に、メンバ関数へのポインタを渡すのは危険 *1

つまり、このコールバック関数は、どのインスタンスが登録したものなのかを、自分で知ることができません。それどころか、どのソケットなのかすらもわかりません。

…これで昨日は悩んだわけですが、今日、HTTPサーバーからファイルを取り寄せるを見て、やっと解法がわかりました。LunaのOVERLAPEDDATA構造体の先頭にWSAOVERLAPPED構造体を配置し、その後ろに自分自身へのポインタを配置します。

struct OVERLAPEDDATA
{
  WSAOVERLAPPED Overlap;
  LunaClientSocket * Client;
  // …略
};

WSASend/WSARecvをコールするときは、OVERLAPEDDATA構造体へのポインタを、WSAOVERLAPPEDへのポインタにキャストして渡せばいいだけです。コールバック関数は、受け取ったWSAOVERLAPPEDへのポインタを、OVERLAPEDDATA構造体へのポインタにキャストし直せば、OVERLAPEDDATA構造体経由でインスタンスへのポインタを取得することができます。もちろん、渡したポインタと受け取ったポインタが同じ場所を指している、というのが大前提ですけどね。

ところで、まだ一度も動作確認してないんですが…

1.2. マネージャ

Winsock2そのものと、LunaClient(or Server)Socketを管理・制御するための専用クラスを作成しました。管理下にあるソケット全てを一斉にクローズしたり、Winsock2の開始とシャットダウンを管理したり、まあそんな感じのことをします。吉里吉里プラグインとして使用中のソケットを監視制御するのに必要となるでしょう。特に、プラグインのアンリンク時にはしっかり解放してやらないと。

  • *1: 関数ポインタとメンバ関数ポインタでは、ポインタのサイズが違う場合がある。これは処理系依存らしい。

2. 中国製ロボット、「国技・太極拳」などを一般公開

あれ?中華キャノンがないよ。ああそうか。剣を装備して近接格闘戦用になったから要らないのか(違)

9月 21日 (水)

1. 吉里吉里

ソースコード自動生成スクリプトmakekrnc.plを改良して、引数と戻り値についても記述できるようにしました。今までアレなマクロで書いていた部分が、さらにアレな記述になりました。

TVP_PLUGIN_FUNCCALL_DECL(tHogeFunction)
{
  TP_REQ_NUM_PARAMS(2)
  TP_GET_REQ_PARAM_INT(x)
  TP_CHK_PARAM_MUST_OBJ
  TP_GET_REQ_PARAM_CLO(cond)
  // 中略
  TP_SET_RETURN_VALUE_INT(rv)
  return TJS_S_OK;
}
@function hoge
  @params 2
  @par int x
  @must obj
  @par vclo cond
  // 中略
  @return int rv
  return TJS_S_OK;
@;

もはやスクリプトなしではコンパイルすらできませんが、もともとスクリプトに頼りきりだったので、今更依存部分が増えたところで大して変わりません。

9月 22日 (木)

1. 吉里吉里

1.1. libiconv

libiconvに関する良い資料が見つからず、手こずりました。以下、Borland C++ Compilerで使うときのメモ。

  1. GNU FTPミラーサーバ(core.ring.gr.jp)等からlibiconv-1.9.1.bin.woe32.zipをダウンロード。
  2. iconv.dllとcharset.dllを取り出す。charset.dllは要らないかも?
  3. implibでインポートライブラリを作成。implib -a iconv.lib iconv.dllimplib -a charset.lib charset.dll

プログラムする上での注意点。

  • iconv_openの引数の順序に注意。
  • iconv_openで取得した変換ディスクリプタは、cd == (iconv_t)(-1)で比較して必ずエラーチェックすること。
  • iconvの引数 char ** inbuf と char ** outbuf は、ポインタへのポインタ。変換が進むと、進んだ分だけポインタ inbuf と outbuf がインクリメントされる。どれだけ変換された(ポインタが進んだ)かは、それぞれ inbytesleft、outbytesleft でわかる。これらも書き換えられる。
  • つまり、iconv関数を実行すると、cd以外の引数は全て書き換わっている可能性があるということ。
  • iconvの戻り値は、非可逆変換(一方通行の変換---戻せない)した文字数。完全な一対一の変換なら0が返る。
  • errnoにセットされる可能性のあるEILSEQマクロは、環境によっては定義されていない。定義されていない場合、iconv.hはEILSEQを@EILSEQ@ でdefineしてしまうので、EILSEQを使用したコードはコンパイル時にエラーとなる。
  • 用が済んだらiconv_closeで変換ディスクリプタを解放すること。

1.2. util_io

なぜiconvなんかを弄っているかというと、文字の符号化変換モジュールを作成するためです。このモジュールは、吉里吉里が内部で持っているワイド文字列(16ビット体系かな?)と、符号化されたオクテット列との相互変換を行います。

前々から欲しいと思っていた *1 、というのもありますが、ソケットを使う上でいずれ必要になる、というのが大きいです。例えば、文字列の送受信等で、通信相手が、自分とは異なる特定の符号化方法しかサポートしていない場合、符号化方法を変換してあげる必要がありますから。

…いや、そもそもソケットプラグインではオクテット列の送受信しか対応しない予定 *2 なので、文字列送受信には必須になるかもしれません。

1.3. LGPL

libiconvって、LGPLなんですよね。吉里吉里のプラグインは、できれば吉里吉里独自のライセンスか、BSDライセンスか、あるいは「好きにしてくれ状態」で公開したいと考えているので、気をつけて扱わないとGPL汚染されてしまいます。まあ、動的リンクだから問題ないか。

  • *1: 吉里吉里でEUC-JPのファイルを扱いたい、とか。
  • *2: 簡単な文字列送受信機能くらいなら付けてもいいかな。

9月 23日 (金)

1. 吉里吉里

1.1. Encoder

文字の符号化方法を変換する機構を作成しました。perlみたいに、Encoder.encode(encname, text)Encoder.decode(encname, octet)で変換できます。EUC-JPでもMacArabicでも何でも来やがれ、て感じですかね。

1.2. BinaryFileStream

バイナリファイルを読み書きするためのクラスを作成しました。普通にdata = stream.read(SIZE)とかwritten = stream.write(octet, SIZE)みたいに扱えます。

こういうのを作っておけば、ソケットのテストが多少は楽になるでしょう。

2. 電波少女 TV

serial experiments lain TV-BOXが届きました。暇を見つけて鑑賞しましょう。

9月 24日 (土)

1. 吉里吉里

1.1. BinaryFileStream

なんか、バイナリファイルストリームにかかりっきりで、ソケットの作業が全然進んでません。連休中にクライアントくらいは、と思っていたのですが。うーむ。

9月 25日 (日)

1. 吉里吉里

1.1. Socket

lainを鑑賞しながらコーディング。奇妙なアニメですね、これ。奇妙なコードになったらこのアニメのせいにしよう。

TJS2文字列(UTF-16LE) → オクテット列(CP932) → クライアントソケット →SSPが反応。ここまではOK。

さて、送信終了後のCompleteCallbackSendをどうするか。うまくいかないんだ、これが(涙)

9月 26日 (月)

1. 吉里吉里

1.1. Socket

送信完了時コールバック関数自体は実行されているのですが、内部でキックしているはずのTJS関数が期待通りに動いてくれていない様子。FuncCall()自体は例外も出さず、戻り値も0なのですが、実行結果が反映されていません。うーむ。

9月 27日 (火)

1. 吉里吉里

3x3メジアンフィルタ。符号なし8ビット、要素数9の配列から、メジアンを取得するロジックが浮かんだので、実装してみました。今まではクイックソートで並べてから真ん中の要素を抽出していましたが、これを基数ソート+バブルソート+クイックソートの変態複合型(?)に変えました。真面目にソートしないのがポイントです。

他の方法と比較したところ、当社(独自実装のクイックソート)比で約二倍、C標準のqsort関数の約四倍の速度になりました。ただ、これは値が平均的にばらついていた場合の話で、配列中の要素の値が極端に偏っている(エントロピーが小さい)場合は却って遅くなります。メジアンフィルタに適用した場合、ベタ塗りのアニメ調の画像では重くなりやすい、ということです。

9月 28日 (水)

1. 吉里吉里

あまりにアレなカラーイコライゼーションの実装を改善しました。

あと、イメージバッファ比較関数を書きました。これを使えば、リグレッションテストが多少は楽になるかな。