日記

9月 1日 (木)

1. 吉里吉里

相変わらず脱線しまくって、予定外のことばかりやってます。

プラグインにArray.map()とArray.grep()を追加しました。それぞれPerlのmap関数、grep関数そのまんま(+α)です。Array.map()の引数には関数オブジェクトを渡します。場合によっては、for文による配列イテレーションの代わりになります。Array.grep()の引数にはPerl同様、正規表現オブジェクトか関数オブジェクトを渡せるようにしたほか、判定条件を逆転させるフラグも追加しました。

2. HTML2XHTML

相変わらず脱線(以下略)

XHTML1.1への変換時に、A要素やMAP要素のNAME属性をid属性に変換する処理を追加しました。重複して指定された属性を削除する処理も追加しました。同一属性の重複指定はSGML的にダメです。

9月 2日 (金)

1. 吉里吉里

プラグイン。配列から重複する要素を取り除く関数Array.uniqueを実装しました。同一判定には"==="演算子と"=="演算子から選択することができます。

配列関係の仕様はこの辺でフィックスします。

9月 3日 (土)

1. 吉里吉里

プラグイン。SQLiteインターフェースを、本体2.25beta9用に書き直しました。似非ネイティブクラス実装だったのが、まっとうなネイティブクラスになりました。これで後ろ暗いところはなくなりました(←後ろ暗かったのか)。

さて、ドキュメントを整備しないと…

2. 地球防衛軍2

INFERNO「灼熱」でアイテム稼ぎをして、イズナーFFゲット。これでペイルウィングの武器は全て揃いました。残るは陸戦兵の武器ですが、ちっとも出ません。超級が殺虫スプレーのみというのは悲しすぎます。

9月 4日 (日)

1. 吉里吉里

プラグイン。SQLiteインターフェースの書き直しに伴って、仕様が一部変更になりました。なるべく仕様を変えないつもりだったのですが、アレな部分が気に入らなかったので。もし使ってる人がいたらごめんなさい。

  • SQLite3.columnNames 関数 → SQLite3.getColumnNames 関数
  • SQLite3Cursor.columnNum 関数 → SQLite3Cursor.columnCount プロパティ
  • SQLite3Cursor.columnNames 関数 → SQLite3Cursor.columnNames プロパティ
  • SQLite3Cursor.columnTypes 関数 → SQLite3Cursor.columnTypes プロパティ
  • SQLite3.version 関数 → SQLite3.version スタティックプロパティ
  • SQLite3Statement.queryString プロパティ追加

と書いてる矢先に2.25beta10がリリースされていることに気づきました。

9月 5日 (月)

1. 吉里吉里

プラグインでバグ発見。引数で受け取ったコールバック関数を実行する場合は、クロージャで実行してやらないとダメですね。iTJSDispatch2::FuncCallで実行する場合は、objthisをきちんと指定しないと、明示的にコンテキスト指定された関数が期待通りに実行されません。以後気をつけよう。

ほか、global.getClassInstanceInfo()を追加しました。使い道は…ない?

9月 8日 (木)

1. 吉里吉里

プラグイン。ネイティブクラスだけでなく、通常の関数も半自動生成させるようにしました。これで関数やクラスメンバの記述方法をほぼ統一することができました。

ついでに、TJS2オブジェクトのコンテナ(?)となる、C++のクラス名も自動生成させるようにしました。これでプログラマ(私)はC++のクラス名を意識せずに済みます。

ついでについでに、マクロ、メッセージ、例外処理周りを強化しました。作業中に、Layer.colorEllipse()でメモリリークするバグが見つかったり…orz

ついでについでについでに、ドキュメント自動生成ツールを、関数だけでなくプロパティにも対応できるようにしました。その過程で、Layer.equalize()とLayer.equalizeColor()のドキュメントを書き忘れていたことに気づいたり。なんだかなあ、もう。

9月 9日 (金)

1. UUID枯渇問題

  • UUIDは128ビットなので、約3.4×1038通りのIDを表現できます。
  • 一人が一秒間に百億(=1.0×1010)のUUIDを消費すると仮定します。
  • 世界人口を百億人(=1.0×1010)とします。
  • 一年は60×60×24×365(≒3.2×107)秒です。

よって、このケースでは

3.4×1038÷(1.0×1010×1.0×1010×3.2×107) ≒ 1.1×1011

ですから、わずか一千億年 *1 程度で全てのUUIDを使い尽くしてしまうことになります。

  • *1: 有効桁数二桁で計算しているので、百億年程度の誤差が見込まれます。が、桁が桁だけに大した問題ではありませんね。ちなみに、太陽の寿命は残り五十億年程度と言われています。

2. UUID衝突問題

UUIDが衝突する確率を計算してみました。先程と同様、百億の人間が各々一秒間に百億のUUIDを消費すると仮定します。千年後、生成されたある一つのUUIDが、過去千年間に生成されたUUIDのいずれかと衝突する確率は、なんと0.00000093%にも上ります。

これは、ある個人が一秒間に生成した百億のUUIDのうち、約百個ものUUIDが、過去に生成されたUUIDのいずれかと衝突することを意味します。つまり、世界中で毎秒一兆もの衝突が発生することになるのです! *1

  • *1: 前提が馬鹿げているので真に受けないように。

9月 10日 (土)

1. 地球防衛軍2

マグマ火炎砲を所持していたことに気づきました。いつの間に入手していたんでしょう?

今日もINFERNO「灼熱」で武器稼ぎをしましたが、何も出ませんでした。俺の「THE 地球防衛軍2」には、超級武器のデータが入ってないんじゃないかと。(←単に運が悪いだけです)

2. 吉里吉里

2.1. 文書レイヤ

TeXを参考にしているせいか、文字の配置、ルビの振り方等、かなり美しく組版されています。これ、そのまま頂こうかな。

9月 11日 (日)

1. 吉里吉里

SQLitePlus 3.0.7に付属していたisqliteを、一部日本語化しました。ついでに、インタラクティブモードのコマンドにエイリアスindexesを追加しました。index という単語の複数形はindicesindexes がありますが、isqliteにはデフォルトでindicesしかなかったので。

isqliteはもともとシンプルに作られているので、折を見ていろいろ改造してみましょうか。

9月 13日 (火)

1. 吉里吉里

プラグイン。util_sqliteのmakeにはsqlite3.libとsqlite3.dllが必要なのに、makeコマンドだけではこの二つが生成されません。予め別に作成しておく必要があります。前から思っていたんですが、これ、面倒です。だったら対処しろよ、との心の叫びに従って、Makefileを改良しました。これでようやく真の「make一発」になりましたよ、ああ楽ちん。

ちなみに、make release一発で、全ライブラリ再作成、全ドキュメント再作成、全リリース用アーカイブファイル作成までやってくれるので、私は楽ちんです。作者(私)にしか恩恵がないですけど。

9月 15日 (木)

1. 吉里吉里

プラグイン。使い道が不明な関数、global.getClassInstanceInfo(obj)のドキュメントを書き忘れた気がします。はい、忘れてました。

9月 17日 (土)

1. 吉里吉里

DXライブラリLunaから、ソケットライブラリであるLunaSocket、LunaSocketAsyncIO、LunaSocketServerAsyncを引っこ抜いて、これらだけでコンパイルが通るようにしました。

…した矢先、Luna本家ではすでに、LunaSocket系がLunaNetworkに移行されていることに気づいてがっかり。

2. 地球防衛軍2

INFERNO「灼熱」。相変わらず超級武器が出ません。「殺虫スプレー」はもういいから、ハーキュリーとかUMXA-V2を出してくれ。

3. スプライツ

放置気味だったティンクルスタースプライツをプレイ。キャラクタモードの最後の空欄、ドラゴンスター リアリー・ティルがなかなか出てこず、ストーリーモードを何周もしました。ほとんど「作業」でした。運が悪かったのでしょうか。

ギャラリーにサターン版OPが追加されたので、鑑賞。この下手くそな歌はほとんど音波攻撃ですね。この歌い方はとても独創的ですね。PS2ということもあって、画質はかなり向上しています。まあ、サターン版ではハードの制約上、画質を落とさざるを得なかったわけで、PS2版で本来の画質になった、というところでしょう。ただ、画質は向上しても歌唱力は向上しなか…うわ、何をす…

で、NEOGEO版。ラスボスが凶悪ですね。特にエキストラアタック。PS2版ではずいぶん弱体化しているように感じますが、やっぱり歳のせ…うわ、何(略)

9月 18日 (日)

1. 吉里吉里

1.1. Smalltalk

怪しげな関数を思いつきました。Javaでは表現しにくかった、Smalltalk風の表記法も、TJS2でなら実現できる!

Array.timesRepeat = function (lambda)
{
  if (this.count > 0) {
    var cnt = this[0];
    if (cnt > 0) {
      while (cnt--) {
        lambda();
      }
    }
  }
};

[ 3 ].timesRepeat(function () {println("hoge");});
Array.ifTrue = function (lambda)
{
  if (this.count > 0) {
    lambda() if (this[0]);
  }
};

Array.ifFalse = function (lambda)
{
  if (this.count > 0) {
    lambda() if (!this[0]);
  }
};

var foo = 5;
var bar = 3;

[ foo > bar ].ifTrue(function () {println("fuga");});
[ foo > bar ].ifFalse(function () {println("xyzzy");});

アホです。配列をこんなことに使うなよ…

1.2. 関数が持つメンバ

冗談はさておき。TJS2では、オブジェクトはプロパティを持つことができます。関数もオブジェクトですから、関数もまたプロパティを持つことができます。では、その関数内部から、その関数オブジェクトが持つプロパティにアクセスする方法は?

var func = function()
{
  println(prop1);
};
func.prop1 = "hoge";
func();

現状のTJS2では、このコードは実行時エラーになります。

メンバ "prop1" が見つかりません at test/f2.tjs(3)[(function expression) (anonymous)]

func.prop1と明示すればアクセスできますが、これは美しくありません。また、thisはglobalを指すので、this.prop1でもダメです。まあ、これができたからといって、どうということもないのですが。

1.3. Array.range

より高度なクロージャとは?にあったrange関数の改悪版。

Array.range = function(lambda)
{
  if (this.count > 1) {
    var begin = this[0];
    var end = this[1];
    var step = (this.count > 2) ? this[2] : 1;

    if (begin <= end) {
      step = -step if (step < 0);
      for (var i = begin; i <= end; i += step) {
        lambda(i);
      }
    } else {
      step = -step if (step > 0);
      for (var i = begin; i >= end; i += step) {
        lambda(i);
      }
    }
  }
};

[ 1, 3 ].range(function (n) {println(n);});
[ 5, 1, 2 ].range(function (n) {println(n);});

だから、配列をこういうことに(略)

全然関係ないですが、ラムダ関数で検索するラムダ関数たん…ハァハァ(内容は真面目です)がトップにくるGoogleは素敵だと思いました。

1.4. Socket

ネットワーク関係。構想中。こんなイメージで進めてみましょうか。

static function Socket.getResourceViaHTTP(hostname, URI)
HTTP経由でデータを取得(GET)する。
static property Socket.lastError
最後に発生したエラー情報。
class ClientSocket
クライアント用ソケットクラス。原則、非同期モード。
function ClientSocket.ClientSocket()
コンストラクタ。
function ClientSocket.connect(serverHostname, serverPort)
サーバに接続する。
function ClientSocket.close()
クローズする。
property ClientSocket.isEndRequestSend
送信処理終了チェック。
property ClientSocket.isEndRequestReceive
受信処理終了チェック。
function ClientSocket.requestSend(data)
データ(文字列またはオクテット列)を送信する。
function ClientSocket.requestReceiveAsString(size)
文字列を受信する。(要求を出す)
function ClientSocket.requestReceiveAsOctet(size)
オクテット列を受信する。(要求を出す)
property ClientSocket.requestSendResult
リクエスト結果コード
property ClientSocket.requestReceiveResult
リクエスト結果コード
function ServerSocket.ServerSocket()
コンストラクタ
function ServerSocket.createServer(port, async, callbackAccept, callbackConnect, callbackClose, callbackRead, callbackWrite)
サーバソケットを生成する。
function ServerSocket.startServer()
サーバソケットを開始する。
function ServerSocket.accept()
接続受け入れ待ち
property ServerSocket.clientCount
接続中のクライアント数。
function ServerSocket.send(data)
データ(文字列またはオクテット列)を送信する。
function ServerSocket.receiveAsString(size)
文字列を受信する。
function ServerSocket.receiveAsOctet(size)
オクテット列を受信する。
property Socket.aliases
ホスト名エイリアス(配列)。
property Socket.addresses
アドレスエイリアス(配列)。
function Socket.getHostInfo(hostname)
ホスト情報を取得する。

9月 19日 (月)

1. アキバのエロゲ店内でナイフを使った傷害事件

秋葉原のエロゲ取り扱いをしているソフマップ14号店の店内で、お客さん同士の口論から、ナイフで背後から斬りつけるという傷害事件が発生した。

真偽のほどは不明だが、2chへの書き込みを紹介されているブログ(エントリー下部の【関連リンク】)には、『「キモイ」発言が発端で口論。刺した方は高校2年生らしい』となっているのも見られる。

未成年がエロゲショップ?入店だけなら問題ないか。

斬ったの?刺したの?【秋葉原】 ソフマップ14号店でヲタ同士による傷害事件発生によると、刺傷事件のようですが。

「キモい」「キモくない」で刺されちゃ堪らんな。

2. 吉里吉里

昨日、LunaNetworkのバイナリ作成をしていたときから嫌な予感はしていたのですが、今日ソースを追ってみて、予感が当たっていたことがわかりました。これ、複数のソケットを制御できません。クライアントソケット、サーバソケットがそれぞれ一つずつ、ソケットハンドルをクラス変数として持っているためです。LunaNetwork::Client::ConnectServerには接続中の場合は切断とはっきり書かれていますし。

こいつは手術が必要ですね。それとも、LunaSocket系に戻すか?

3. 誰も「本物のハイビジョン」を知らない

面白いことが書かれています。視聴者だけでなく、制作サイドもまた規格に振り回されているようですね。

3.1. 私見

今のテレビ放送事情って、HDTVだのデジタル放送だの、画質がどうの双方向性がどうの、なんだかもう、視聴者置いてけぼりです。まあ、正直言ってどうでもいいんですけどね。そこそこ綺麗に映ればいいんですよ、テレビなんて。本気で画質云々言ってるのは、一部の映像マニアだけじゃないんですか?

バジリスクと、十月から放映開始予定のリリカルなのはA'sがそこそこ綺麗に映ればそれで良し。画質に疎い俺にとっては、テレビなんてそんなもの。

あ、いや、待て。ゲームの画面は綺麗に映って欲しいな。

4. 吉里吉里

クライアントソケットとサーバソケット、それぞれインスタンスを作成できるように、staticの呪縛から解き放ちました。LunaNetwork::Clientクラス中のWSA Overlapped Completion Routine *1 中で、どのソケットなのかを知る方法がわからず(ソケットハンドルを受け取れないっぽい?)、どうしてくれようかと悩みましたが、結局この機能は切り捨てました。代わりに、関連する部分はWSAGetOverlappedResult()関数を使って何とかすることにしました。

とりあえずバイナリは作成できた *2 ので、あとは吉里吉里でこいつをどう扱うか。まあ、ゆっくり考えるとしましょう。

  • *1: リクエスト処理終了時にコールバックされる。winsock2.h中でLPWSAOVERLAPPED_COMPLETION_ROUTINEでtypedefされている。
  • *2: 正しく動作するかは別ですが。

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. 吉里吉里

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

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