The Man Who Fell From The Wrong Side Of The Sky:2007年4月分

2007/4/1(Sun)

[NetBSD] fgetwc(3)

バグってたんで直した、__sgetc()を呼んでるせいで
fwide(3)のorientationが正しくない値で上書きされてしまうんだよね。
つか__sgetc()使って1byteづつmbrtowc(3)に食わせるより
FILE構造体のバッファ(fp->_p)分まとめて食わせたほうが性能もいいでそ。

2007/4/2(Mon)

[BTS] Scarab

0.21がリリースされてたみたい、とりあえず b20用の不具合解消patchは全部使えそうな予感。
つーわけで ScarabBundle_ja.properties
だけ更新しておいた、b20->b21では項目の追加だけなのでどっちのバージョンでも使える。

[NetBSD] vfwscanf(3)

vfscanf(3)のwchar_tサポートも入ったな。
これFreeBSD由来のコードなんだろうけど vfwscanf(3)とか バグが多いんだよね。
ちなみに↑でVisualC++はMSDN読む限りSolarisと同じとか書いちゃったけど
実際にはglibcと同じ動きなんで、仕様どおり全く持って正しいです、はい。

2007/4/3(Tue)

[C言語] scanf(3)

scanf/wscanfファミリーを使うときのちょっとした注意おば。

@ scanf + "%c" + field width

これは大丈夫。

#include <stdio.h>

int main(void)
{
	char buf[3];
	int n;

	scanf("%3c%n", &buf[0], &n);
	printf("%.*s", n, buf);

	return 0;
}
width = byte数(char)

の計算ですので、bufの長さはwidthと同じだけ用意すればOK。
stdinからは(EOFに達しない限り)最大width=3byte読み込まれ、bufに書き込まれます。

@ wscanf + "%lc" + field width

これも大丈夫。

#include <locale.h>
#include <stdio.h>
#include <wchar.h>

int main(void)
{
	wchar_t buf[3];
	int n;

	setlocale(LC_CTYPE, "ja_JP.eucJP");
	wscanf(L"%3lc%n", &buf[0], &n);
	wprintf(L"%.*ls\n", n, buf);
	return 0;
}
width == ワイド文字数(wchar_t)

の計算で、 bufも同じ長さだけ確保。
stdinからはfgetwc(3)相当を用いて(WEOFが返されない限り)
最大width=3ワイド文字分読み込まれ、bufに書き込まれます。

@ scanf + "%lc" + field width

こいつは曲者です、実装により差があります。

  • FreeBSDと(その実装を先日取り込んだ)NetBSDの場合
    test.c
    ----------------------------------------
    #include <locale.h>
    #include <stdio.h>
    #include <string.h>
    #include <wchar.h>
    
    int main(void)
    {
    	wchar_t buf[3];
    	int n;
    
    	setlocale(LC_CTYPE, "");
    	memset(buf, 0, sizeof(buf));
    	scanf("%3lc%n", buf, &n);
    	wprintf(L"%d:%3ls\n", n, buf);
    
    	return 0;
    }
    ----------------------------------------
    $ make test
    
    としてできたバイナリを実行すると
    $ LC_CTYPE=ja_JP.eucJP echo "あいうえお" | ./test
    3:あ
    
    との結果が出力されます。
    width = 読み込むワイド文字数 ではないことに注意してください。
    あくまでstdinからは(EOFにならない限り)width=3byteだけ読み込むという意味になります。
    読み込まれたマルチバイト文字はmbrtowc(3)によってwchar_tに変換を試み、成功すればbufに書き込まれます。
    bufの長さはmb_cur_min=1を仮定してwidthと同じだけ確保が必要。

  • glibc2やVisualC/C++の場合
    [VisualC/C++]
    あいうえお
    6:あいう
    Press any key to continue
    
    [glibc2(FedoraCore6)]
    $ LC_CTYPE=ja_JP.eucJP echo "あいうえお" | ./test
    あいうえお
    6:あいう??????????
    
    glibc2のwprintf(3)バグってね?というのは置いといて(printfの方も%lsの動作がおかしい)
    width = 読み込むワイド文字数で、6byte stdinから読み込まれたことが判ります。

さてどちらが正しい実装なのでしょうか?
SUSv3を読む限り、前者の実装の方が正しいように私には思えます。

@ wscanf + "%c" + field width

こっちも困ったことに実装により差があります。
詳しくは 昨日のメモ以前のメモを参照してください。
どちらが正しいかといわれると、 SUSv3読む限りでは、今度は逆にglibc2とVisualC++の方が正しい実装のように思えます。
不幸なことに実装によって必要とされるbufの長さがまちまちなので
FreeBSDで書いたコードがLinuxではオーバーランなんて事態が起きる可能性があります。

@ 結論

C99拡張を取り込んでないOpenBSD最強
以上の通り実装によっててんでバラバラなんで
scanf + %lc、wscanf + %c をfield width付きで使うのは避けた方がいい鴨。
そもそもどちらもbufに何byte、何文字書かれたか判らないという欠点もあることだし。
その他のscanf(3)を使うときに注意する点は C FAQでも読んで頂戴。

2007/4/4(Wed)

[NetBSD] vfprintf(3)

塩崎さんとこより。

以前のことですがFreeBSDのvfprintf(3)に比べてNetBSDの実装が異常に遅かったんで
gprof(1)で統計取ったんですけど、やっぱりmbrtowc(3)はボトルネックになってましたね。
(intmax_t使いまくりなのが最大のボトルネックで、それ直すだけでだいぶ改善したんですが)

先日mbrtowc(3)呼ばないと困るなぁとは書いたんですが、以前のvfprintf(3)の実装では

という方法だったので、ja_JP.ISO2022-JP localeの場合
これとか これがあって、どのみち問題ありだったのを思い出しました。

というわけで

  1. singlebyte locale用
  2. stateless multibyte locale〃
  3. stateful multibyte locale〃

の3つくらいを用意するべきなんでしょうかね。
今の実装でも頑張ればMB_CUR_MAXとmbrtowc(NULL, NULL, 0, ps)の戻り値のチェックで
ある程度は最適化できるかもしれないですけど。

[C言語] 続 scanf(3)

昨日の続き。

@ scanf + %s + field width

これは大丈夫。

#include <stdio.h>

int main(void)
{
	char buf[4];
	int n;

	scanf("%3s%n", buf, &n);
	printf("%d:%s\n", n, buf);
	return 0;
}

stdinから(EOFに達しない限り)width=3byte読み込まれ、bufに書き込まれます。
bufは必ず'\0'で終端されますので、width + 1だけ確保するように。
ただし、この'\0'はstdinから読み込まれたものではないので
%n(現在までstdinから読み込んだbyte数を返す)にセットされる値はあくまで3です。

@ wscanf + %ls + field width

これも大丈夫。

#include <locale.h>
#include <stdio.h>
#include <wchar.h>

int main(void)
{
	wchar_t buf[4];
	int n;

	setlocale(LC_CTYPE, "");
	wscanf(L"%3ls%n", buf, &n);
	wprintf(L"%d:%ls\n", n, buf);
	return 0;
}

stdinからはfgetwc(3)相当を用いて(WEOFが返されない限り)
最大width=3ワイド文字分読み込まれ、bufに書き込まれます。
bufは必ずL'\0'で終端されますので、width + 1だけ確保するように。
ただし、このL'\0'はstdinから読み込まれたものではないので
%n(現在までstdinから読み込んだワイド文字数を返す)にセットされる値はあくまで3です。

@ scanf + %ls + field width

これも scanf + %lc + field widthと同様に、実装によって差があります。

#include <locale.h>
#include <stdio.h>
#include <wchar.h>

int main(void)
{
	wchar_t buf[4];
	int n;

	setlocale(LC_CTYPE, "");
	scanf("%3ls%n", buf, &n);
	printf("%d:%ls\n", n, buf);
	return 0;
}

以上のコードを実行した結果

  • FreeBSDと(その実装を先日取り込んだ)NetBSDの場合
    $ LC_CTYPE=ja_JP.eucJP echo "あいうえお" | ./test
    3:あ
    
    stdinからは(EOFにならない限り)width=3byteだけ読み込み
    これをmbrtowc(3)によってwchar_tに変換を試みて、成功した分だけbufに書きこんでいます。
    bufはmb_cur_min=1を仮定し、width + 1(L'\0'による終端分)を確保する必要があります。

  • glibc2やVisualC/C++の場合
    [VisualC/C++]
    あいうえお
    6:あいう
    Press any key to continue
    
    [glibc2(FedoraCore6)]
    $ LC_CTYPE=ja_JP.eucJP echo "あいうえお" | ./test
    あいうえお
    6:あいう
    
    widthを読む込むべきワイド文字の数と解釈し、6byte読み込んでいます。

どちらの仕様が正しいかといわれると、私はFreeBSDとNetBSDだと思います。

@ wscanf + %s + field width

ウンザリしてきましたが orz
wscanf + %c + field widthと同様、実装によって差があります。 以上のコードを実行した結果

  • FreeBSDと(その実装を先日取り込んだ)NetBSDの場合
    #include <locale.h>
    #include <stdio.h>
    #include <wchar.h>
    
    int main(void)
    {
    	char buf[4];
    	int n;
    
    	setlocale(LC_CTYPE, "");
    	wscanf(L"%3s%n", buf, &n);
    	wprintf(L"%d:%s\n", n, buf);
    	wprintf(L"%lc\n", getwchar());
    
    	return 0;
    }
    
    $ LC_CTYPE=ja_JP.eucJP echo "あいうえお" | ./test
    1:あ
    い
    
    widthはbyte数を表します、width + 1('\0'による終端分)の確保が必要です。
    stdinからはfgetwc(3)相当を用いて(WEOFが返されない限り) ワイド文字を読み込みます。
    これをwcrtomb(3)によってmultibyteに変換を試みて、成功かつ長さがwidth=3byteならbufに書き込んでます。
    上の例の場合、L'あ'だけではmultybyteに変換すると2byteなので、次のL'い'をstdinから読み込みます。
    しかしL'い'まで変換すると合計4byteになり、widthを超過してしまいます。
    ですので、L'い'はungetwc(3)相当を用いてstdinに戻されます。bufに書かれるのはL'あ'を変換した2byteだけです。

  • glibc2やVisualC/C++の場合
    #include <locale.h>
    #include <limits.h>
    #include <stdio.h>
    #include <wchar.h>
    
    int main(void)
    {
    	char buf[MB_LEN_MAX * 4];
    	int n;
    
    	setlocale(LC_CTYPE, "");
    	wscanf(L"%3s%n", buf, &n);
    	wprintf(L"%d:%s\n", n, buf);
    	wprintf(L"%lc\n", getwchar());
    
    	return 0;
    }
    
    [glibc2(FedoraCore6)]
    $ LC_CTYPE=ja_JP.eucJP echo "あいうえお" | ./test
    3:あいう
    え
    
    [VisualC/C++]
    あいうえお
    3:あいう
    え
    Press any key to continue
    
    width = 読み込むワイド文字数です。
    bufにはmb_cur_max * 4だけのサイズを確保する必要があります。

どちらの仕様が正しいかといわれると、これはglibc2とVisualC/C++が正しいと思います。
不幸なことにbufに必要なサイズが実装によって異なるので、これもオーバーランの可能性があり危険です。

@ 結論

昨日とおなじで OpenBSD
じゃなくて scanf + %ls、wscanf + %s をfield width付きで使うのは、こんな状況では避けるのが無難でしょう。

@ つか

Google codesearchで調べる限り

なんで心配するだけ無駄な気がしてきたwwwwww
古典的な w?scanf + %(l?s|S)みたいな危険なコードの方の心配の方が先かも。

2007/4/11(Wed)

今週

左背中〜脇腹にかけての疝痛と血尿で会社早退。
ほぼ間違いなく腎臓結石です、本当にありが(以下略

お陰で前日DVDで観た "Band of Brothers"の影響で、
腹部を銃撃されて出血が止まらない夢にうなされたり。
まあ実際に衛生兵が7.92mmライフル弾のような座薬(以下略

というわけでしばらくAFKです。

2007/4/17(Tue)

Linuxでscannerを使う(ぉ

CTスキャンで輪切りにされてきますた。
BrightSpeedという機種だったんだけど、これの画像処理は Xeon + Linuxでやってんのね、ほぇー。

Linux platform is twice as fast as previous architectures

とあるけど、前はSUN Ultra 60だった模様、さもありなん。

2007/4/26(Thu)

[BTS] scarab-0.21

@ 2007/5/6 追記

このTorque3.2+PostgreSQLのトラブルにはsrc/sql/postgresql/hack-db.sql
という対策がscarab-0.21に含まれてる模様、詳しくはINSTALLを読んで。

1.0-b20から0.21に移行を試みた。
Torqueが3.1.1→3.2になってどっか腐ったらしくPostgreSQLだと

Caused by: org.postgresql.util.PSQLException: ERROR: operator does not exist: smallint = boolean

	at org.postgresql.util.PSQLException.parseServerError(PSQLException.java:139)
	at org.postgresql.core.QueryExecutor.executeV3(QueryExecutor.java:154)
	at org.postgresql.core.QueryExecutor.execute(QueryExecutor.java:101)
	at org.postgresql.core.QueryExecutor.execute(QueryExecutor.java:43)
	at org.postgresql.jdbc1.AbstractJdbc1Statement.execute(AbstractJdbc1Statement.java:515)
	at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:50)
	at org.postgresql.jdbc1.AbstractJdbc1Statement.executeQuery(AbstractJdbc1Statement.java:231)
	at org.postgresql.jdbc1.AbstractJdbc1Statement.executeQuery(AbstractJdbc1Statement.java:219)
	at com.workingdogs.village.QueryDataSet.(QueryDataSet.java:113)
	at org.apache.torque.util.BasePeer.executeQuery(BasePeer.java:888)
	... 126 more

ちゅーエラーを吐いて死んでしまう、うげげ。
とりあえずTorqueのバージョンを戻すのに scarab/WEB-INF/libから

torque-3.2.jar
torque-gen-3.2.jar
torque-gen-templates-3.2.jar
maven-torque-plugin-3.2-rc2.jar

を削除して、scarab-1.0-b20に含まれる

torque-3.1.1.jar
torque-gen-3.1.1-dev.jar
maven-torque-plugin-3.1.1.jar

に差換えて回避、そのうちTorque調べる。

そもそも
http://www.solitone.org/scarab/issues/id/SCB2308
なバグが上がってるくらいだし、誰もPostgreSQLで使ってないんだろうな。