unichr.so

今日は先日の unichr() 関数を PHP エクステンションとして作ってみます。
pecl-gen に喰わせる XML はこんな感じで。
(Safari から投稿しているため、バックスラッシュが文字化けしています。'?0' は '\0' に、"?n" は "\n" に読み替えてください)

<?xml version="1.0" ?>
<extension name="unichr" version="0.0.1">
<summary>Unicode character PHP extension</summary>
<description><![CDATA[
	Get the UTF-8 encoded string which corresponds to the given code point.
]]></description>
<function name="unichr">
	<proto>string unichr(int code)</proto>
	<code><![CDATA[
if (code < 0 || code > 0xFFFF) {
	RETURN_FALSE;
}
char ustr[4] = { '?0' };
int ulen = 0;
if (code < 0x80) {
	ustr[0] = (char)code;
	ulen = 1;
} else if (code < 0x800) {
	ustr[0] = 0xC0 | (char)(code >> 6);
	ustr[1] = 0x80 | (char)(code & 0x3F);
	ulen = 2;
} else {
	ustr[0] = 0xE0 | (char)(code >> 12);
	ustr[1] = 0x80 | (char)(code >> 6 & 0x3F);
	ustr[2] = 0x80 | (char)(code & 0x3F);
	ulen = 3;
}
RETURN_STRINGL(ustr, ulen, 1);
	]]></code>
</function>
</extension>

これを例によって

pecl-gen unichr.xml
cd unichr
phpize
./configure
make
sudo make install

でインストールできます。


Pure PHP の関数との比較はこうなりました。
スクリプト

<?php

dl('unichr.so');

function unichr_purephp($cp)
{
    if ($cp < 0 || $cp > 0xFFFF) {
        return false;
    }
    if ($cp < 0x80) {
        return chr($cp);
    }
    if ($cp < 0x800) {
        return pack('CC', 0xC0 | $cp >> 6, 0x80 | $cp & 0x3F);
    }
    return pack('CCC', 0xE0 | $cp >> 12, 0x80 | $cp >> 6 & 0x3F, 0x80 | $cp & 0x3F);
}

$t0 = microtime(true);
for ($i = 0; $i < 10000; $i++) unichr_purephp(0x3000);
$t1 = microtime(true);
for ($i = 0; $i < 10000; $i++) unichr(0x3000);
$t2 = microtime(true);

printf("%0.6f / %0.6f?n", $t1 - $t0, $t2 - $t1);

?>

結果:

0.166844 / 0.030950

だいたい 5〜6 倍は速いですね。また、C で書いた方が実行速度のばらつきが非常に小さかったです。