塩崎さんとこより。
以前のことですがFreeBSDのvfprintf(3)に比べてNetBSDの実装が異常に遅かったんで
gprof(1)で統計取ったんですけど、やっぱりmbrtowc(3)はボトルネックになってましたね。
(intmax_t使いまくりなのが最大のボトルネックで、それ直すだけでだいぶ改善したんですが)
先日mbrtowc(3)呼ばないと困るなぁとは書いたんですが、以前のvfprintf(3)の実装では
という方法だったので、ja_JP.ISO2022-JP localeの場合
これとか
これがあって、どのみち問題ありだったのを思い出しました。
というわけで
の3つくらいを用意するべきなんでしょうかね。
今の実装でも頑張ればMB_CUR_MAXとmbrtowc(NULL, NULL, 0, ps)の戻り値のチェックで
ある程度は最適化できるかもしれないですけど。
昨日の続き。
これは大丈夫。
#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です。
これも大丈夫。
#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 + %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;
}
以上のコードを実行した結果
stdinからは(EOFにならない限り)width=3byteだけ読み込み$ LC_CTYPE=ja_JP.eucJP echo "あいうえお" | ./test 3:あ
widthを読む込むべきワイド文字の数と解釈し、6byte読み込んでいます。[VisualC/C++] あいうえお 6:あいう Press any key to continue [glibc2(FedoraCore6)] $ LC_CTYPE=ja_JP.eucJP echo "あいうえお" | ./test あいうえお 6:あいう
どちらの仕様が正しいかといわれると、私はFreeBSDとNetBSDだと思います。
ウンザリしてきましたが orz
wscanf + %c + field widthと同様、実装によって差があります。
以上のコードを実行した結果
#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;
}
widthはbyte数を表します、width + 1('\0'による終端分)の確保が必要です。$ LC_CTYPE=ja_JP.eucJP echo "あいうえお" | ./test 1:あ い
#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;
}
width = 読み込むワイド文字数です。[glibc2(FedoraCore6)] $ LC_CTYPE=ja_JP.eucJP echo "あいうえお" | ./test 3:あいう え [VisualC/C++] あいうえお 3:あいう え Press any key to continue
どちらの仕様が正しいかといわれると、これはglibc2とVisualC/C++が正しいと思います。
不幸なことにbufに必要なサイズが実装によって異なるので、これもオーバーランの可能性があり危険です。
昨日とおなじで
OpenBSD
じゃなくて scanf + %ls、wscanf + %s をfield width付きで使うのは、こんな状況では避けるのが無難でしょう。
Google codesearchで調べる限り
なんで心配するだけ無駄な気がしてきたwwwwww
古典的な
w?scanf + %(l?s|S)みたいな危険なコードの方の心配の方が先かも。