Zephirで62進数10進数変換PHPエクステンションを作る

# 最初に
Phalcon 2.0でも採用されているZephirを使うと、PHPエクステンションが(比較的)簡単にできる。しかもコンパイル済みなのでとっても速い。ということで、試しに62進数10進数を作ってみた。
環境はCentOS7のインストール直後です。

# Zephirインストールの前準備

```lang:bash
# yum install gcc automake autoconf libtool php-devel
# yum install http://pkgs.repoforge.org/re2c/re2c-0.13.5-1.el6.rf.x86_64.rpm
```
※ re2cは最新版だと他パッケージとの依存関係で面倒なので、`上記バージョン`を入れる。他のリポジトリを使っていて、そっちにre2cがあればyumで入れてもOK。

# インストール
```lang:bash
# cd /usr/local/src
# git clone https://github.com/phalcon/zephir
# git clone https://github.com/phalcon/json-c zephir/json-c
# cd zephir
# ./install-json
# ./install -c
# zephir version
0.5.9a
# php -v
PHP 5.4.16 (cli) (built: Oct 31 2014 12:59:36)
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2013 Zend Technologies
```

# コードを書く
```lang:bash
# cd
# zephir init orenokaisha
# cd orenokaisha
# vi orenokaisha/utils.zep
```

zephirのソースはこちら。拡張子がzepですね。

```lang:orenokaisha/utils.zep
namespace Orenokaisha;

class Utils
{
	const HASH_TABLE = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

	public static function b10to62(long value) -> string
	{
		string result = "0";
		if (value > 0) {
			long i; string hashtable, ch = "";
			let hashtable = self::HASH_TABLE;
			let result = "";
			while(value > 0) {
				let i = value % 62;
				let ch = hashtable[i];
				let result =  ch . result;
				let value = (value - i) / 62;
			}
		}
		return result;
	}

	public static function b62to10(string value) -> long
	{
		string hashtable; long result = 0, len; int i;
		let hashtable = self::HASH_TABLE;
		let len = value->length() - 1;
		for i in reverse range(0, len) {
			let result += pow(62, len - i) * hashtable->index(chr(value[i]));
		}
		return result;
	}

}
```
※ ちなみに0以下の数値が入ってきた場合は、全て0で返してますので、ご注意を。

# buildとエクステンションの登録と確認
```lang:bash
# zephir build
# echo 'extension=orenokaisha.so' > /etc/php.d/orenokaisha.ini
# php -i|grep 'orenokaisha'
```
※ php.iniのtimezoneは設定しておくべし

# 比較実験のコード
ではどのくらい速いのか比較して実験してみましょう♪ 100万回ループで、最後にどんだけ速いかの倍率がでます。

```lang:test.php
<?php
class Utils
{
	const HASH_TABLE = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

	public static function b10to62($value)
	{
		$result = "0";
		if ($value > 0) {
			$hashtable = self::HASH_TABLE;
			$result = "";
			while($value > 0) {
				$i = $value % 62;
				$ch = $hashtable[$i];
				$result =  $ch . $result;
				$value = ($value - $i) / 62;
			}
		}
		return $result;
	}

	public static function b62to10($s)
	{
		$result = 0;
		$hashtable = self::HASH_TABLE;
		$len = strlen($s);
		for ($i = 0; $i < strlen($s); $i++) {
			$result += pow(62, $len - 1 - $i) * strpos($hashtable, ($s[$i]));
		}
		return $result;
	}

}

function test1($value)
{
	$b62 = Orenokaisha\Utils::b10to62($value);
	$b10 = Orenokaisha\Utils::b62to10($b62);
	return ($b10 === $value);
}

function test2($value)
{
	$b62 = Utils::b10to62($value);
	$b10 = Utils::b62to10($b62);
	return ($b10 === $value);
}

$count = 10000 * 100;

$ok1 = 0;
$err1 = 0;
$time1 = microtime(true);
for ($i = 0; $i < $count; $i++) {
	if (test1($i)) {
		$ok1++;
	} else {
		$err1++;
	}
}
$lap1 = microtime(true) - $time1;

$ok2 = 0;
$err2 = 0;
$time2 = microtime(true);
for ($i = 0; $i < $count; $i++) {
	if (test2($i)) {
		$ok2++;
	} else {
		$err2++;
	}
}
$lap2 = microtime(true) - $time2;

echo "test1: $ok1, $err1 => $lap1\n";
echo "test2: $ok2, $err2 => $lap2\n";
echo $lap2 / $lap1 . "\n";
```

# 結果
```lang:php
# php test.php
test1: 1000000, 0 => 2.6385660171509
test2: 1000000, 0 => 3.9418640136719
1.4939417805162
```
結果は、どどーんと1.49倍！！(処理時間は環境によるので、相対値のみ気にしてください)
って、あれあれあれー、これだけ？
10倍とか100倍とか速いのを期待していたわけですが、そもそもあまり複雑な処理ではなくて元から結構速いということもあり、そんなもんみたいです。
苦労の割にちょっぴり残念な結果ですが、requireの数が減るという特典もありますしね、、、。