何んとなく、いろんなことを書いていこうかな。 いろんな Tips を中心に思い付いた時に書いています。
一応、上にいく程新しいので、下から上に向かって読んでってね。
段々多くなって来たから、ページ分けようかな、、、
2002/9/8 Emacs の HTML モード完成
http://www.onicos.com/staff/iz/release/#html-edit-mode
主な機能 など。
2002/9/4 PHP の怪その4
またハマッてしまった。
以下の正規表現は JIS 半角カタカナの濁音・半濁音文字にマッチさせるもの ですが、意図した通りに動作しません。何故でしょう?
if (preg_match("/[\041-\137][\136\137]/", $string)) {
	print "match\n";
}
答え: [\136\137] というところが、パターンマッチする前に、 [^_] と展開されてしまうから、意味が変わってしまうため。
if (preg_match('/[\041-\137][\136\137]/', $string)) {
	print "match\n";
}
と書きましょう。(" => ' とする)
2002/9/4 Emacs の HTML モード
というわけで、公開。
http://www.onicos.com/staff/iz/release/#html-edit-mode
2002/8/29 Emacs の HTML モードを作ろうプロジェクトスタート
と思ったが、暇がない。ある程度できてきたら公開するっす。
事の発端は、既存の HTML モードが気に入らないから。 仕事で HTML を修正することがあるんだけど、3重、4重に入れ子になったテーブルは あたりまえで、さらに、タグの対応が取れてなかったりするのがザラにあります。 100 行くらい下のどっかで狂っているのを見つけ出さなければいけなかったり。 <font>が複雑に入り込んでたり、連続して10個くらい入れ子になってたりして。
昔作った 自作の HTML モードでは、タグの対応を探してくれる機能があるので、重宝 していました。でも、ベースとなった HTML がバージョン 2.0 なので、 もうあちこち古いです。
ということで、作り直す事を決行。
2002/8/29 sendmail.cf のルールを、メールヘッダに適用
HSubject: $>MyRule
...
SMyRule
R...
のようにして、ヘッダに対してルールが適用できたんだ。知らんかった。
うふっ、スパム保護法案ともいうべき例のやつに使えるぜ。
2002/8/29 Windows2000 で CPU 負荷を調べる。
意外と知られていないようだ。ってドキュメント化されてないそうだ。
とりあえず、サンプルコードをここ w2k-loadavg.c.bz2 に置いておきましたので、 興味のある方はどうぞ。
2002/8/26 Linux の起動を高速化
サーバ類を一つのスクリプトにまとめたら起動が速くなった。快適☆
スクリプトはこんな感じ -> services
RedHat 7.3 用です。他のディストリビューションだとヤバイかもしれないんで、要注意。
2002/8/25 gcc -fssa
昨日、ちょっと飲み会の話にでてきたのでついでですが、 gcc に -fssa (Enable SSA optimizations) という怪しいオプションがあります。 これを付けてコンパイルしたら、あるソフトで 10% くらい速度が速くなりました。 ただし、ちゃんと動く保証はない、危険なオプションなので注意してください。 安定版のリリースには使わない方がいいでしょう。
2002/8/25 Emacs: 半角カタカナ <=> 全角カタカナ変換
しょーもないけど、作ってしまったから公開しませう。 hankana.el
2002/8/25 Perl で Base64 エンコーディング
Kazu さんの IM の中にいいもん見つけました。
sub b_encode_string ($) {
    my $mod3 = length($_[0]) % 3;
    local($_);

    $_ = pack('u', $_[0]);
    chop;
    s/(^|\n).//mg;
    tr[`!-_][A-Za-z0-9+/];

    if    ($mod3 == 1) { s/..$/==/; }
    elsif ($mod3 == 2) { s/.$/=/; }

    $_;
}
うーむ、なるほど。プロバイダによっては MIME::Base64 が入っていなくて、 困る時があるんだけど、こりゃ使えそう。某 base64.pl より速度的にも速そうだし。
2002/8/25 3D Sound ライブラリ
DirectSound のバッファをコントロールして、MIDI みたいに voice コントロールを 作ってみた。せっかくいいもんができたんで、

私>フリーで公開していい?
部長>ダメ。

シクシク、、、
2002/8/19 gcc で DirectSound をビルド
mingw gcc で DirectSound をコンパイルするには、ヘッダファイルに少し修正が 必要です。以下のパッチをご利用下さい。
mingw.diff
dsound.h.diff
なお、本パッチ適用に関する責任は、あなた自分自身で取って下さい。
2002/8/14 DirectSound でのバッファーアンダーフロー検知
強引にやればできそう。
定期的に GetCurrentPosition() を呼び出して再生カーソルをプルービングさせつつ、 ストリームデータの挿入サイズと比較すればできそう。
最初、SetTimer() 使ってみたけど、GetMessage(),DispatchMessage() 呼び出すまで、 永遠にタイマーのコールバック関数が呼び出されないじゃん... (これって非同期っていうんかい?) ということで、タイマーは却下。
で、次に思い付いたのが、スレッドを裏で動かしといて、スリープしながらプルービング。 ただ単に Sleep() しながらループするのは芸がないので、WaitSingleObject() を 使って、プリマリスレッド側からもコントロール!。 うーむ、これなら出来そうだ。しかも、IDirectSoundNotify::SetNotificationPositions はもはや使わなくてもバッファの挿入タイミングのコントロールができるので、 この前から苦しんでた音飛びの問題も関係なくなるな。これで幸せになれるかな?
2002/8/12 Windows2000 の DirectSound で複数のストリームを再生させると、音が飛ぶ (続編)
以前、Windows2000 で音が飛ばないようにハックに成功したのですが、 数を多くさせていくと、 IDirectSoundNotify::SetNotificationPositions で設定したはずのイベントが発生しなくなる問題が出て来ました。 セカンダリバッファとそれに対応するイベントを 200 個くらい作って、 WaitForMultipleObjects() で待ちます。WaitForMultipleObjects() の限界数は 64 なので、スレッドを何個か立ち上げ、1 つのスレッドが待つイベント数が 64 イベント以内に収まるように割り当て、みんなで WaitForMultipleObjects() します。悲しいことに、100 セカンダリバッファくらいを越えたところで、 31 番目以降のセカンダリバッファに対応するイベントが発生しなくなりました。 1 スレッド当たりが待ち受けるイベント数に関係なく、30 個までが限界でした。 最初のうちは期待通りのイベントが発生しているのですが、CPU 負荷 100% 状態が しばらく続くと、おかしくなります。
うーむ、すみません、Tips のはずなのに、解決策は未だに見出せてません。 また今度ということで。
2002/8/9 DirectSound
はて、DSBPLAY_LOOPING でストリーミング再生時、 オーディオのバッファアンダーフローはどうやったら検出できるのだろう?
もしかして、そんなこと出来ないとか、、、Current Position は Lock() しても 通り過ぎちゃうし。
そもそも、Lock() って何をロックするんだ! サウンドバッファへ書き込む領域をリザーブするための関数なのかな? Windows プログラミングは得意でないんで良く分からない。なんとなく、 GlobalLock() 関数と関係ありそうではあるんだけど、何をロックしているのか、 記述が見当たらない。 また、アンロックしなかったらどうなるかの記述が曖昧で、いったい 何が起こるんだろう? 試しにやってみたけど、再生の Current Position はそのまま素通りで ループ再生しちゃいます。 これじゃ、オーディオのバッファアンダーフローは検出できないね。
2002/8/9 netkit-ftp
手元に netkit の ftp (Linux に入っている ftp コマンド) を 64bit 対応させて、 2G を越えるファイルを get/put できるようにしたんだけど、作った後に、 ncftp を使えば言いじゃん、ってことに気づいた(こいつは 64bit 対応してた)。
欲している人がいるなら、パッチ公開するけど、、、
2002/8/9 Windows2000 の DirectSound で複数のストリームを再生させると、音が飛ぶ
なんか、これは有名なバグのようですね。DirectX 8.1 にバージョンアップしても やっぱり直りませんでした。何かもっと根本的なバグがあるのかもしれません。
さて、「バグるなら、直してみましょう、プログラム」ということで、 このバグを回避する方法を考えましょう。
int dsb_block_num;	/* セカンダリバッファの分割数 */
int dsb_block_siz;	/* 分割セカンダリバッファの個々のバイト数 */
int dsb_buffer_size;	/* セカンダリバッファサイズ (dsb_block_num * dsb_block_siz) */
int dsb_write_point;	/* セカンダリバッファの書き込み位置 */
LPDIRECTSOUNDBUFFER dsb; /* セカンダリバッファ */

/*
 * セカンダリバッファを dsb_block_num 等分し、個々のブロックの最後の
 * 地点に、Notification Position が設定されていると仮定します。
 */

BOOL
check_lock_safe(int r1, int w1, int r2, int w2)
{
  return (w1 < r2) ^ (w2 < r1);
}

int
HandleWaveStreamNotification(void)
{
  DWORD pc, wc;
  DWORD len1;
  char *data1;

  IDirectSoundBuffer_GetCurrentPosition(dsb, &pc, &wc);

  /* ここで、異常なイベント発生を検出 */
  if (!check_lock_safe(pc, (pc <= wc ? wc : wc + dsb_buffer_size),
		       dsb_write_point, dsb_write_point + dsb_block_siz))
    return;

  len1 = 0;
  data1 = NULL;
  IDirectSoundBuffer_Lock(dsb, dsb_write_point, dsb_block_siz,
			  (void **)&data1, &len1,
			  NULL, 0, 0);

  /* データを data1 に読み込んで */

  IDirectSoundBuffer_Unlock(dsb, data1, len1, NULL, 0);
  dsb_write_point = (dsb_write_point + dsb_block_siz) % dsb_buffer_size;
}

DWORD WINAPI
NotificationProc(LPVOID param)
{
  while (1) {
    res = MsgWaitForMultipleObjects(1, &notify_event,
				    FALSE, INFINITE, QS_ALLEVENTS);
    switch (res) {
    case WAIT_OBJECT_0 + 0:
      HandleWaveStreamNotification();
      break;
    }
  }
}
こんな感じかな。check_lock_safe() がミソで、現在の再生位置を上書きしないように チェックしています。
捕捉ですが、私はハンガリー記法が大っ嫌いなので、"w" とか "dw" とかつけません。
2002/8/6 XXXX_QueryInterface (Windows)
COM オブジェクトの QueryInterface の REFIID 引数に関してだけど、 C と C++ では、REFIID の型が異なるため、ソースコードの共有ができませ〜ん。
#define XXXX_QueryInterface(p,a,b)  (p)->lpVtbl->QueryInterface(p,a,b)
というところなんですけど、
#if !defined(__cplusplus)
#define XXXX_QueryInterface(p,a,b)  (p)->lpVtbl->QueryInterface(p,&a,b)
#else
#define XXXX_QueryInterface(p,a,b)  (p)->lpVtbl->QueryInterface(p,a,b)
#endif
というのが本来のスジのような気がする。 DirectSound 使う時、<dsound.h> をインクルードする前に、 IUnknown_QueryInterface を 以下のように定義しておけば、C と C++ とでコードが共有できます。
#if !defined(__cplusplus)
#define IUnknown_QueryInterface(p,a,b)  (p)->lpVtbl->QueryInterface(p,&a,b)
#endif
#include <dsound.h>

2002/8/5 PHP Tips -- エラーに関して
PHP では、エラーハンドリングがいまいち。 $errno とか strerror() とか無いのはなぜ?
例えば、fopen() が失敗した場合、 などのエラーの原因によって挙動を変えたい場合はどうしたらいいんだろう?
どうも「そんなことは出来ない」っぽいです。理力(※) を使って調べたら、やっぱり出来なさそうで、幻滅です。
せめて、エラーメッセージだけでも得られないかと調べたら、以下のように すると出来ます。
function my_error_exit($err) {
  print "Error message: $err\n";
  exit;
}

function my_fopen($fname, $mode) {
  global $php_errormsg;
  $prev_track_errors = ini_set('track_errors', 1);
  $fd = @fopen($fname, $mode);
  ini_set('track_errors', $prev_track_errors);

  if (!$fd) {
    my_error_exit($php_errormsg);
  }

  return $fd;
}

※理力: フォース -> ソース、、、(^^
2002/8/5 PHP Tips
コンマ演算子が使えないのも辛い。 $foo = (式1, 式2) として、式1を評価し、つづいて式2を評価した結果を返したいんだけど、PHP ではこの機能が無いので 式一発で済ますのが難しくなる場合があります。コンマ演算子は
$foo = ((式1) ? (式2) : (式2));
のように書けば、似た事ができます。
式がいっぱいある場合は以下のようにするのもあり!?
$foo = pos(array_reverse(array(式1,
			       式2,
			       ...
			       式n)));
他に何か良い手がないかなぁ、、、

なぜ、一つの式に納めたいかと思い始めたかというと、preg_replace の /e オプションを使った時に、一つの式に納めなければならないから、、、 とういうのが、この議論の発端です。
Perl では、
$n = $s =~ s/正規表現/置換文字列/g;
とすることで、$s を正規表現で置換すると同時に、置換回数を得る事ができます。 これと同じことを PHP で行う事を考えます。あなたならどうします?
以下のようなコードはどんなもんでしょうか?
$n = 0;
$s = preg_replace("/正規表現/e", '$n++ ? \'置換文字列\' : \'置換文字列\'', $s);
かな?
お、文を評価できる手段があった。eval は使えないかな?
$n = 0;
$s = preg_replace("/正規表現/e", 'eval(\'\$n++; return \\\'置換文字列\\\';\')', $s);
も大丈夫そうだ。え?バックスラッシュがうざいっって、そういうときは、 以下のような方法もあります。
$n = 0;
$s = preg_replace("/正規表現/e", <<<__ENDREP__
eval('\$n++; return \'置換文字列\';')
__ENDREP__
, $s);
普通なら以下のようなコードが最初に思い付くけど、 ちょっと負けてるなぁ。複雑になった時の速度や後々の可読性の面では下記コード がよさげですが、ハッカー度数低め。
function my_replace_call_back(&$n) {
  $n++;
  return '置換文字列';
}
$n = 0;
$s = preg_replace("/正規表現/e", 'my_replace_call_back($n)', $s);
/e オプションは、置換部のコードが複雑になると遅いので、 このような関数呼び出しにしたほうが高速な場合があります。
2002/8/3 PHP の怪その3
これも落し穴。以下の実行結果はどうなるでしょうか?
<?
// $map の中の指定されたキーの数をカウントする。
// 当然、重複するキーは存在しないはずなので、0 か 1 が
// 返って来ることを期待するのだが、、、
function my_key_count($map, $search_key)
{
  $n = 0;
  foreach ($map as $key => $val) {
    if ($key == $search_key)
      $n++;
  }
  return $n;
}

$map = array();
$map['foo'] = 'a';
$map['']    = 'b';
$map['bar'] = 'c';
$map['0']   = 'd';

# 空キーの数を探そうとしてみる。
print my_key_count($map, '') . "\n";
# 結果は 2 となる。はて?どこが間違っているか分かるかな〜
?>
「ゼロ」の概念は PHP では奥が深いのだ〜。
2002/8/3 PHP の怪その2
以下の実行結果はどうなるでしょうか?
<?
$map = new array();
$map['01'] = 'a';
$map['10'] = 'b';
foreach ($map as $key => $val) {
    print typeof($key), "\n";
}
?>

2002/8/3 PHP の怪その1
仕事で PHP が必要となり、初めて PHP を使用しました。 まだ、マスターできていないので、思わぬ所でハマっています。 これは、その第一段!

$a == $b かつ $b == $c であるのに、$a == $c にならないことがあるんだな。
<?
$a = '0';
$b = 0;
$c = '';

if ($a == $b) print "eq\n"; else print "ne\n";
if ($b == $c) print "eq\n"; else print "ne\n";
if ($c == $a) print "eq\n"; else print "ne\n";
?>

2002/8/3 RedHat 7.3 less で日本語が化ける
以前、RedHat 7.3 を入れてみました。日本語版がまだ無かったので(まだでないかな?) 英語版ですけど。 言語の選択のところで日本語を指定すると、ちゃんと日本語環境で動きました。 less で日本語表示出来ない問題を除けば。less の日本語表示は Laser5 Linux 7.2 の RPM から less を とって来て上書きで入れ直せば表示できるようになります。 ただ単に、Laser5 から less バイナリを取って来て入れ直すだけで、直ります。



ご意見は私、出雲宛まで: <iz@onicos.co.jp>