Friday, December 24, 2010

HipHop for PHP をインストールして apache で proxy してみた


依存関係が相当面倒なので、rpm が用意されている CentOS 5 にインストールすることにした。
CentoOS 5系であれば、以下のようなページも用意されている。
https://github.com/facebook/hiphop-php/wiki/Installing-or-Building-HipHop-PHP-via-RPM-on-CentOS-5
それでも相当いろんなパッケージを入れないといけない。

説明のとおり、yum の設定用パッケージなどを以下のとおり実行
[sikaku@localhost hiphop_rpm]$ sudo rpm -ivh http://epel.osuosl.org/5/x86_64/epel-release-5-4.noarch.rpm
[sikaku@localhost hiphop_rpm]$ sudo rpm -ivh http://dl.iuscommunity.org/pub/ius/stable/Redhat/5/x86_64/ius-release-1.0-6.ius.el5.noarch.rpm
[sikaku@localhost hiphop_rpm]$ sudo rpm -ivh http://pkg.tag1consulting.com/hphp/x86_64/hphp-release-1.0-2.el5.noarch.rpm
[sikaku@localhost hiphop_rpm]$ sudo rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/x86_64/epel-release-5-3.noarch.rpm
[sikaku@localhost hiphop_rpm]$ sudo rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-5.rpm
[sikaku@localhost hiphop_rpm]$ sudo rpm -Uvh http://centos.alt.ru/repository/centos/5/x86_64/centalt-release-5-3.noarch.rpm
[sikaku@localhost hiphop_rpm]$ sudo rpm -Uvh http://dag.wieers.com/rpm/packages/rpmforge-release/rpmforge-release-0.3.6-1.el5.rf.x86_64.rpm
[sikaku@localhost hiphop_rpm]$ sudo yum update
以下のとおり、大量に yum でパッケージを入れる。 何度もエラーと戦い続けて、以下が必要なことがわかった。
[sikaku@localhost hiphop_rpm]$ sudo yum install libidn-devel openssl-devel cvs cvsps gcc gcc-c++ libmcrypt zlib-devel automake libmcrypt mhash libtidy mysql
[sikaku@localhost hiphop_rpm]$ sudo yum install cmake mysql-devel pcre-devel gd-devel expat-devel libmcrypt-devel libcap-devel binutils-devel flex tbb-devel
以下に用意されているパッケージを全部ダウンロードする。
http://sourceforge.net/projects/hphp/files/CentOS%205%2064bit/
このダウンロードは、Irvine などでリンクインポートして一括ダウンロードすると良い。
mysqlのライブラリが古いと、エラーが出るので以下だけ、直接 mysql からダウンロードしてくる。
[sikaku@localhost hiphop_rpm]$ wget "http://dev.mysql.com/get/Downloads/MySQL-5.1/MySQL-shared-community-5.1.54-1.rhel5.x86_64.rpm/from/http://ftp.jaist.ac.jp/pub/mysql/"
で、以下のとおりインストール。一部ダウンロードしたファイルで、依存関係が問題で入らないのがあったので、除外している。
[sikaku@localhost hiphop_rpm]$ sudo rpm -Uvh hiphop-php-1.0-2.x86_64.rpm  boost-1.37.0-1.x86_64.rpm libicu-4.2.1-6.x86_64.rpm oniguruma-5.9.2-1.x86_64.rpm php52-5.2.12-1.ius.x86_64.rpm php52-cli-5.2.12-1.ius.x86_64.rpm php52-common-5.2.12-1.ius.x86_64.rpm tbb-2.2-1.20090809.x86_64.rpm php52-mysql-5.2.12-1.ius.x86_64.rpm MySQL-shared-community-5.1.54-1.rhel5.x86_64.rpm php52-pdo-5.2.12-1.ius.x86_64.rpm libxml2-*.rpm boost-devel-1.37.0-1.x86_64.rpm libevent-*.rpm php52-gd-5.2.12-1.ius.x86_64.rpm curl-devel-7.20.0-1hiphop.x86_64.rpm curl-7.20.0-1hiphop.x86_64.rpm icu-4.2.1-6.x86_64.rpm libicu-devel-4.2.1-6.x86_64.rpm oniguruma-devel-5.9.2-1.x86_64.rpm flex-2.5.35-7.x86_64.rpm
実際に実行してみた。vm環境で 256M しか割り当ててなかったからか、スワップしまくって7分もかかった。
[sikaku@localhost dev]$ cat test.php
<?php
 echo "hello world!";
?>
[sikaku@localhost dev]$ time hphp test.php
hello world!
real    7m8.916s
user    1m2.269s
sys     0m33.538s
[sikaku@localhost dev]$
ポート8999 で起動してみる。
[sikaku@localhost dev]$ mkdir bin
[sikaku@localhost dev]$ time hphp test.php --keep-tempdir=1 -o bin
hello world!
real    9m0.035s
user    1m5.599s
sys     0m34.827s
[sikaku@localhost dev]$ bin/program -m server -p 8999
Could not mlockall
loading static content...
loading static content took 0'00" (0 ms) wall time
page server started
admin server started
all servers started
以下のようにアクセスできる。
[sikaku@localhost ~]$ curl "http://localhost:8999/test.php"
hello world!
続いて、apache の設定を以下のように追加する。mod_rewrite か、mod_proxy で proxy 設定を書く。
[sikaku@localhost dev]$ cat /etc/httpd/conf.d/custom.conf
#RewriteEngine on
#RewriteRule /(.*) http://localhost:8999/$1 [P,L]
ProxyPassMatch ^/(.*)$ http://localhost:8999/$1
[sikaku@localhost dev]$ sudo /etc/init.d/httpd restart
80 ポートからアクセスできた。
[sikaku@localhost ~]$ curl "http://localhost/test.php"
hello world!

Sunday, December 5, 2010

libev を使った web server Lighttz を make してみた

まずは、libev のインストール

[192.168.162.128] cvs -z3 -d :pserver:anonymous@cvs.schmorp.de/schmorpforge co libev
[192.168.162.128] cd libev
[192.168.162.128] autoheader
[192.168.162.128] aclocal
[192.168.162.128] autoconf
[192.168.162.128] touch ltmain.sh
[192.168.162.128] automake -a
[192.168.162.128] ./configure --prefix=$HOME/local
[192.168.162.128] rm libtool
[192.168.162.128] ln -s /usr/bin/libtool libtool
[192.168.162.128] make
[192.168.162.128] make install
libtool がうまく動かなかったので置き換え lighttz の方では、以下のように Makefile を書き換えて gmake
[192.168.162.128] cat Makefile
lighttz: lighttz.o
        gcc -o lighttz ./lighttz.o -L$$HOME/local/lib -lev -u is_default_loop

lighttz.o: lighttz.c
        gcc -O3 -Wall -c -fmessage-length=0 -MMD -MP -MF"lighttz.d" -MT"lighttz.d" -I../libev -o $@ ./lighttz.c

clean:
        rm -f lighttz.o lighttz

start:
        LD_LIBRARY_PATH=/home/sikaku/local/lib/ ./lighttz &
起動してみる。
[192.168.162.128] LD_LIBRARY_PATH=/home/sikaku/local/lib/ ./lighttz &

[192.168.162.128] curl http://localhost:8002/
curl: (18) transfer closed with 1 bytes remaining to read
Hello World

Hello World

[192.168.162.128]
どうやら、1byte 書き出しが少ないようだ。
以下のように修正するとうまくいく
Index: lighttz.c
===================================================================
--- lighttz.c   (revision 103)
+++ lighttz.c   (working copy)
@@ -76,7 +76,7 @@
        char superjared[]="HTTP/1.1 200 OK\r\nContent-Length: 336\r\nConnection: close\r\nContent-Type: text/html\r\nDate: Sat, 26 Apr 2008 01:13:35 GMT\r\nServer: lighttz/0.1\r\n\r\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\"><html xmlns=\"http://www.w3.org/1999/xhtml\" version=\"-//W3C//DTD XHTML 1.1//EN\" xml:lang=\"en\"><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/><title>Hello World</title></head><body><p>Hello World</p></body></html>";

        if (revents & EV_WRITE){
-               write(cli->fd,superjared,strlen(superjared));
+               write(cli->fd,superjared,strlen(superjared)+1);
                ev_io_stop(EV_A_ w);
        }
        close(cli->fd);

Sunday, November 7, 2010

apache と nginx から node.js に proxy させる方法

node.js と nginx のインストールメモと、apache と nginx から proxy させる方法メモ

node.js のインストール

git からとってきて ./configure してみる

cd /home/sikaku/src
git clone git://github.com/ry/node.git
cd node
./configure
しかし、環境が古くて ./configure が通らない
../src/platform_linux.cc: In static member function `static void node::OS::SetProcessTitle(char*)':
../src/platform_linux.cc:29: error: `PR_SET_NAME' was not declared in this scope
Waf: Leaving directory `/home/sikaku/src/node/build'
Build failed:  -> task failed (err #1):
        {task: cxx platform_linux.cc -> platform_linux_4.o}
gmake: *** [all] Error 1
以下を見ると、ほぼ debug 用のようなので、
http://stackoverflow.com/questions/778085/how-to-name-a-thread-in-linux
以下のようなパッチをあててしのぐことにする
[192.168.162.128] diff -ru src/platform_linux.cc src/platform_linux.cc.NEW
--- src/platform_linux.cc       2010-10-22 11:09:49.000000000 +0900
+++ src/platform_linux.cc.NEW   2010-10-22 11:09:46.000000000 +0900
@@ -26,7 +26,7 @@
 void OS::SetProcessTitle(char *title) {
   if (process_title) free(process_title);
   process_title = strdup(title);
-  prctl(PR_SET_NAME, process_title);
+//  prctl(PR_SET_NAME, process_title);
 }
openssl が入っていなかったので、yum install しておく
sudo yum install openssl-devel
ようやく make install まで成功
./configure
make
sudo make install
以下のようなコードを書いて、実行してみる
[192.168.162.128] cat http.js
    var sys = require('sys'),
        http = require('http'),
        port = 8000; 
    http.createServer(function(request, response) {
        response.writeHead(200, {
            'Content-Type': 'text/html'
        });

        response.end( request.url + ' - Hello World\n');
    }).listen(port);
    sys.puts('Server listening on port ' + port);
起動してみる
node http.js &
アクセスしてみる
[192.168.162.128] curl "http://192.168.162.128:8000/test"
/test - Hello World

apache から node.js に proxy させる

rewrite で P をフラグをつければいいだけ
[192.168.162.128] cat /etc/httpd/conf.d/custom.conf
    RewriteEngine on
    RewriteRule /(.*) http://192.168.162.128:8000/$1 [P,L]
再起動
sudo //etc/init.d/httpd restart
apache にアクセスすると動いた。
[192.168.162.128] curl "http://192.168.162.128/test"
/test - Hello World

nginx のインストール

まったく問題なく make install まで成功した
cd /home/sikaku/src
wget "http://www.nginx.org/download/nginx-0.8.53.tar.gz"
tar zxvf nginx-0.8.53.tar.gz
cd nginx-0.8.53
./configure 
make
sudo make install
ポートを変えて起動してみる
[192.168.162.128] diff -ru /usr/local/nginx/conf/nginx.conf /usr/local/nginx/conf/nginx.conf.NEW
--- /usr/local/nginx/conf/nginx.conf    2010-10-22 13:38:25.000000000 +0900
+++ /usr/local/nginx/conf/nginx.conf.NEW        2010-10-22 13:38:33.000000000 +0900
@@ -33,7 +33,7 @@
     #gzip  on;

     server {
-        listen       80;
+        listen       8001;
         server_name  localhost;

         #charset koi8-r;
アクセスしてみる。
[192.168.162.128] curl "http://192.168.162.128:8001/"
<html>
<head>
<title>Welcome to nginx!</title>
</head>
<body bgcolor="white" text="black">
<center><h1>Welcome to nginx!</h1></center>
</body>
</html>

nginx から node.js に proxy させる

以下のように、nginx.conf を書き換えて、nginx を再起動する
[192.168.162.128] cat /usr/local/nginx/conf/nginx.conf | perl -nle 'print unless(/\s*#/ || /^\s*$/)'
worker_processes  1;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    server {
        listen       8001;
        server_name  localhost;
        location / {
            root   html;
            index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
        location ~ .*$ {
            proxy_pass   http://192.168.162.128:8000;
        }
    }
}
問題なく動いた
[192.168.162.128] curl "http://192.168.162.128:8001/test"
/test - Hello World

Sunday, October 10, 2010

NICの設定を表示する: linux

NICの設定を確認する方法。

[sikaku@localhost ~]$ sudo /sbin/ethtool eth0
Settings for eth0:
        Supported ports: [ MII ]
        Supported link modes:   10baseT/Half 10baseT/Full
                                100baseT/Half 100baseT/Full
                                1000baseT/Full
        Supports auto-negotiation: Yes
        Advertised link modes:  10baseT/Half 10baseT/Full
                                100baseT/Half 100baseT/Full
                                1000baseT/Full
        Advertised auto-negotiation: Yes
        Speed: 100Mb/s
        Duplex: Full
        Port: MII
        PHYAD: 19
        Transceiver: external
        Auto-negotiation: on
        Supports Wake-on: g
        Wake-on: d
        Link detected: yes
ethtool は、表示と設定変更を行える
[sikaku@localhost ~]$ whatis ethtool
ethtool              (8)  - Display or change ethernet card settings
ethtool             (rpm) - Ethernet settings tool for PCI ethernet cards

Sunday, August 8, 2010

convert integer to std::string

int型変数を std::string に変換する方法を考えてみた。

C言語で、char * にするなら、反射的に、sprintf などを使うと思うが、 std::string に入れたいときに一番いい方法はあまり思いつかない。 今回は、いくつか案を考えて実際に家のPCで計測してみた。 以下のような関数をそれぞれ定義して、計測する。

void func(int num, std::string &str);

計測に使った関数は以下のとおり。 getMicroTime はマイクロタイムを取得する関数。

void testCount(const char *msg, int count, void (*func)(int, std::string &)){
    std::string str;
    int num;

    long start, ustart, end, uend;
    getMicroTime(start, ustart);
    for(int i=-count; i< count; i++){
        num = i;
        (*func)(num, str);
    }
    getMicroTime(end, uend);
    double i = (end - start) * 1000000;
    double f = uend - ustart;

    printf("%s time:%lf\n", msg, (i+f)/2/count );
}

[1]std::streamstring を使う

一番 C++ らしい方法はこれかもしれない。
void intToString1(int num, std::string &str){
    std::stringstream ss;
    ss << num;
    str = ss.str();
}
しかし、今回調べた中で最も遅かった。

[2]sprintf を使う

C言語のスタンダードな方法だと思う。
void intToString2(int num, std::string &str){
    char buf[32];
    sprintf(buf, "%d", num);
    str = buf;
}
[1]に比べたら、5~6倍ぐらい早かった

[3]10 のmodulo演算を使用

modulo 演算で、1の位から順に決定していく。
void intToString3(int num, std::string &str){
    int snum = num;
    if(num<0)
        snum = ~num + 1;
    char buf[12];
    char *ptr = buf+11;
    *ptr = '\0';
    while(snum > 0){
        *(--ptr) = '0' + snum % 10;
        snum /= 10;
    }
    if(num<0)
        *(--ptr) = '-';

    str = ptr;
}
[2]よりも、2~3倍速い

[4]長さを求めて、引数のstd::string に直接書き込む

作業用バッファを用意するのではなく、引数の std::string に直接書き込む方法とした。
void intToString4(int num, std::string &str){
    int snum = num & 0x80000000 ? ~num + 1 : num;

    int len = snum < 10000  ? (snum < 100 ? ( snum < 10 ? 1 : 2 ) : snum < 1000 ? 3 : 4)
         : snum < 100000000 ? (snum < 1000000 ? ( snum < 100000 ? 5 : 6 ) : snum < 10000000 ? 7 : 8)
         : snum < 1000000000 ? 9 : 10;

    if(num & 0x80000000)
        ++len;
    str.resize(len);

    while(snum > 0){
        str[--len] = '0' + snum % 10;
        snum /= 10;
    }
    if(num & 0x80000000)
        str[0] = '-';
}
[3]よりもだいたい 1.5 倍くらい早い

[5]std::string::data() で内部メモリに直接書く

[4]を修正し、str.data() で取得したアドレスに直接書いてみた。
    int snum = num & 0x80000000 ? ~num + 1 : num;

    int len = snum < 10000  ? (snum < 100 ? ( snum < 10 ? 1 : 2 ) : snum < 1000 ? 3 : 4)
         : snum < 100000000 ? (snum < 1000000 ? ( snum < 100000 ? 5 : 6 ) : snum < 10000000 ? 7 : 8)
         : snum < 1000000000 ? 9 : 10;
    if(num<0)
        ++len;
    str.resize(len);

    char *ptr = (char *)str.data()+len;
    while(snum > 0){
        *(--ptr) = '0' + snum % 10;
        snum /= 10;
    }
    if(num & 0x80000000)
        *(--ptr) = '-';
}
[4]より1.2倍くらい早くなった。

[6]再度sprintf

内部メモリに直接書く方法で、sprintf を使う方法にしてみた。
void intToString6(int num, std::string &str){
    int snum = num & 0x80000000 ? ~num + 1 : num;

    int len = snum < 10000  ? (snum < 100 ? ( snum < 10 ? 1 : 2 ) : snum < 1000 ? 3 : 4)
         : snum < 100000000 ? (snum < 1000000 ? ( snum < 100000 ? 5 : 6 ) : snum < 10000000 ? 7 : 8)
         : snum < 1000000000 ? 9 : 10;
    if(num<0)
        ++len;
    str.resize(len);

    char *ptr = (char *)str.data()+len;
    sprintf(ptr, "%d", num);
}
[5]に比べると、5倍~6倍くらい遅くなってしまった。

[7]大きい位の数から順に決めていく

長さを先に決めているので、大きい位から代入していくことにした。
void intToString7(int num, std::string &str){
    int snum = num & 0x80000000 ? ~num + 1 : num;

    int len = snum < 10000  ? (snum < 100 ? ( snum < 10 ? 1 : 2 ) : snum < 1000 ? 3 : 4)
         : snum < 100000000 ? (snum < 1000000 ? ( snum < 100000 ? 5 : 6 ) : snum < 10000000 ? 7 : 8)
         : snum < 1000000000 ? 9 : 10;
    if(num & 0x80000000)
        ++len;
    str.resize(len);

    char *ptr = (char *)str.data()-1;
    if( num & 0x80000000 ){
        *(++ptr) = '-';
         --len;
    }

    int one;
    if( len > 9 )
        one = snum / 1000000000, *(++ptr) = '0' + one, snum -= 1000000000 * one;
    if( len > 8 )
        one = snum / 100000000, *(++ptr) = '0' + one, snum -= 100000000  * one;
    if( len > 7 )
        one = snum / 10000000, *(++ptr) = '0' + one, snum -= 10000000 * one;
    if( len > 6 )
        one = snum / 1000000, *(++ptr) = '0' + one, snum -= 1000000 * one;
    if( len > 5 )
        one = snum / 100000, *(++ptr) = '0' + one, snum -= 100000 * one;
    if( len > 4 )
        one = snum / 10000, *(++ptr) = '0' + one, snum -= 10000 * one;
    if( len > 3 )
        one = snum / 1000, *(++ptr) = '0' + one, snum -= 1000 * one;
    if( len > 2 )
        one = snum / 100, *(++ptr) = '0' + one, snum -= 100  * one;
    if( len > 1 )
        one = snum / 10, *(++ptr) = '0' + one, snum -= 10 * one;

    *(++ptr) = '0' + snum;
}
これが、今回最も早い方法で、[5]よりも少し速くなった。

[8]少し改良

[7]を while を使って、キレイに書いてみた。
void intToString8(int num, std::string &str){
    static int tens[]={ 0, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
    int snum = num & 0x80000000 ? ~num + 1 : num;

    int len = snum < 10000  ? (snum < 100 ? ( snum < 10 ? 1 : 2 ) : snum < 1000 ? 3 : 4)
         : snum < 100000000 ? (snum < 1000000 ? ( snum < 100000 ? 5 : 6 ) : snum < 10000000 ? 7 : 8)
         : snum < 1000000000 ? 9 : 10;
    if(num & 0x80000000)
        ++len;
    str.resize(len);

    char *ptr = (char *)str.data();
    if(num & 0x80000000){
        *(ptr++) = '-';
         --len;
    }
    --ptr;

    int one = 0;
    int *tptr = &tens[len];
    while(*(--tptr)){
        one = snum / *tptr;
        *(++ptr) = '0' + one;
        snum -= *tptr * one;
    }
    *(++ptr) = '0' + snum;
}
なぜか、このコードは比較的遅いという結果になった。 配列tensから値を引くところが遅いようである。 [7]を配列tensを使うように変えると遅くなり、O2か、O3を指定すると[7]と同じ速度になった。

以上の計測結果は以下のとおり。

./out speed (最適化コンパイルなし )
intToString1 time:1.052384
intToString2 time:0.232779
intToString3 time:0.114349
intToString4 time:0.127272
intToString5 time:0.095429
intToString6 time:0.214056
intToString7 time:0.073023
intToString8 time:0.186148
./out1 speed ( -O コンパイルオプション )
intToString1 time:0.745957
intToString2 time:0.215731
intToString3 time:0.066834
intToString4 time:0.042813
intToString5 time:0.031119
intToString6 time:0.177850
intToString7 time:0.036448
intToString8 time:0.116570
./out2 speed ( -O2 コンパイルオプション )
intToString1 time:0.714182
intToString2 time:0.223773
intToString3 time:0.062432
intToString4 time:0.048148
intToString5 time:0.033870
intToString6 time:0.192560
intToString7 time:0.032088
intToString8 time:0.115626
./out3 speed ( -O3 コンパイルオプション )
intToString1 time:0.780855
intToString2 time:0.201429
intToString3 time:0.061252
intToString4 time:0.048085
intToString5 time:0.034723
intToString6 time:0.186857
intToString7 time:0.032486
intToString8 time:0.115503

こうしてみると、最速の[7] が、stringstream [1] よりも 20倍くらい異なる結果となった。 しかし単位はマイクロタイムなので、少し改良したところで大した差にはならない上、現状でもかなり速い。 改善するなら、全てビット演算だけで記述できればもっと改善できるかもしれない。

C言語(C++)で microtime を取得する

C言語で microtime を計算する方法が分からなかったので調べてみた。

#include <sys/time.h>

static long start, end;
void getMicroTime(long &sec, long &usec ){
    struct timeval now;
    int rv = gettimeofday(&now, 0);
    if (rv != 0){
        sec = 0;
        usec = 0;
        return;
    }
    sec = now.tv_sec;
    usec = now.tv_usec;
}
double getMicroTimeDouble(){
    struct timeval now;
    int rv = gettimeofday(&now, 0);
    if (rv != 0){
        return 0;
    }
    return (double)now.tv_sec + (double)now.tv_usec / 1000000;
}

Monday, August 2, 2010

zlib の deflate inflate sample

zlib を少し調べたので、std::string に対して、 deflate と inflate を行うクラスを作ってみた。

//ZlibUtil.h //ZlibUtilTest.cc
#include "ZlibUtil.h"

int main(int argc, char **argv)
{
    int ret;

    ZlibUtil zlib;

    std::string from, to;
    from = "あいうえおあいうえおあいうえおあいうえおあいうえお";
    ret = zlib.def(from, to, 9);
    printf("compress=%s %d\n", to.c_str(), to.length());

    ret = zlib.inf(to, from);
    printf("decompress=%s %d\n", from.c_str(), from.length());
}
[sikaku@localhost compress]$ ./zlibutil
compress=xレ{ワリア蚋cロ翦?])・27
decompress=あいうえおあいうえおあいうえおあいうえおあいうえお 75

difference between strncpy and memcpy

思いっきりはまってしまったので、メモ。 strncpy と memcpy の違い。

strncpy は、src 側の文字列に(途中に) \0 が含まれると、copy をやめてしまう。

また、当たり前だが、const char * を string のコンストラクタに入れると、 \0 まで読み込んで止まってしまうので、サイズも一緒に入れてやるとよい。

[sikaku@localhost compress]$ ./a.out
0 1 2  4 5 6
0 1 2
8
3

Sunday, July 25, 2010

linux で /usr/local/lib を search library path に追加

共有オブジェクトの search library path に /usr/local/lib を追加し、 また、その際、reboot 時にも反映されるようにする。 さらに、/etc/ld.so.cache を更新する。

echo -e "/usr/local/lib/" | sudo tee /etc/ld.so.conf.d/local_lib.conf
sudo /sbin/ldconfig

一時的に追加するだけなら、以下のようにする

sudo /sbin/ldconfig /usr/local/lib

参考
http://www.eyrie.org/~eagle/notes/rpath.html

Tuesday, July 20, 2010

tdb を 64bit 対応にする

tdb(Trivial Database) は、dbm の一種で、key/value の形でデータを保存できる。 install の仕方は、以下のとおり

以下から download する
http://sourceforge.net/projects/tdb/

$ wget "http://downloads.sourceforge.net/project/tdb/tdb/1.0.6/tdb-1.0.6.tar.gz?use_mirror=jaist&ts=1279553081"
$ tar zxvf tdb-1.0.6.tar.gz
$ cd tdb-1.0.6
$ ./configure --prefix=$HOME/local --host="i860-linux-gnu" --enable-shared
$ perl -pi -e 's/$/\\n\\/ if( $.>=172 && $.<=187)' tdbtool.c
$ perl -pi -e 's/#define u32 unsigned/#define u32 unsigned long long/' tdb.h
$ make
$ make install

tdb は、ファイル1つに対しデータベースを作っており、offset と size でデータを取り出したり、 保存したりしている。 offset の指定が unsigned int になっているので、4GB を超えるあたりで、動かなくなる問題があったので、 改善方法を考えてみた。

offset は u32 型で定義しているので、以下のようにmacroを書き換えればよい

$ perl -pi -e 's/#define u32 unsigned/#define u32 unsigned long long/' tdb.h

正常に動くかどうかは、以下のプログラムを動かして確認した。

$ cat tdb_overflow.cc
#include 
#include 
#include "tdb.h"
#include 
#include 

using namespace std;

#define TDB_DB "tdb.db"
#define TDB_HASH_SIZE 2000*10000

void log(TDB_CONTEXT *tdb, int Num, const char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    vprintf(fmt, ap);
    va_end(ap);
}

void get_tdb_val(TDB_CONTEXT *tdb, int num, TDB_DATA *tkey, TDB_DATA *tvalue){
    static string pre_key = "";
    static string pre_value = "";
    if( "" == pre_key ){
        pre_key = "key";
    }
    if( "" == pre_value ){
        for(int i=0;i<100*5000;++i){
            pre_value += "0123456789";
        }
    }
    char numStr[1024];
    snprintf(numStr, 1024, "%d", num);

    static string key;
    key = pre_key + numStr;
    tkey->dptr = (char *)key.c_str();
    tkey->dsize = key.length() + 1;

    static string value;
    value = pre_value + numStr;
    tvalue->dptr = (char *)value.c_str();
    tvalue->dsize = value.length() + 1;
}
int main(){
    printf("tdb file:%s\n", TDB_DB);
    printf("hash size:%u\n", TDB_HASH_SIZE);

    TDB_CONTEXT *tdb;
    tdb = tdb_open_ex(TDB_DB, TDB_HASH_SIZE, TDB_CLEAR_IF_FIRST|TDB_NOMMAP, O_RDWR|O_CREAT, S_IRWXU, &log);
    if(NULL == tdb){
        fprintf(stderr, "can't open tdb\n");
        return -1;
    }

    // set
    TDB_DATA tkey;
    TDB_DATA tvalue;
    for(int i=0;i<1000;++i){
        get_tdb_val(tdb, i, &tkey, &tvalue);
        if(0 != tdb_store(tdb, tkey, tvalue, TDB_INSERT)){
            printf("fault. key:%s val:%s\n", tkey.dptr, tvalue.dptr);
            continue;
        }
        printf("success set %d\n", i);
    }

    // get
    for(int i=0;i<1000;++i){
        TDB_DATA tret;
        get_tdb_val(tdb, i, &tkey, &tvalue);

        tret = tdb_fetch(tdb, tkey);
        if( strcmp(tvalue.dptr, tret.dptr) ){
            printf("fault. key:%s val:%s ret:%s\n", tkey.dptr, tvalue.dptr, tret.dptr);
            continue;
        }
        delete tret.dptr;
        printf("success. get %d\n", i);
    }
    tdb_close(tdb);
    printf("success end.\n");
}
$ cat Makefile
INCDIR=/home/sikaku/local/include
LIBDIR=/home/sikaku/local/lib

default: all

tdb_overflow:tdb_overflow.cc
    g++ -g -I$(INCDIR) -L$(LIBDIR) -ltdb tdb_overflow.cc -o $@

all:tdb_overflow

clean:
    rm ./tdb_overflow

test:
    LD_LIBRARY_PATH=$(LIBDIR) ./tdb_overflow

5160394752byte の tdb.db が作成されて、正常に終了した。

Tuesday, June 8, 2010

2ch の live28 httpd.conf 設定

2ch の最新 live28 サーバの httpd.conf の設定
<IfModule mpm_prefork_module>
StartServers 704
MinSpareServers 703
MaxSpareServers 704
ServerLimit 704
MaxClients 704
MaxRequestsPerChild 10000
MaxMemFree 2000
</IfModule>
64bit で mpm-worker にすれば 700 も子プロセス(スレッド)増やせれるのか。 仕事でセットアップしてる apache は MaxClients 32 しかないのだが・・。

Thursday, June 3, 2010

指定したコマンドを daemon で起動するプログラム(c 言語)

指定したコマンドを daemon で起動するプログラムを作ってみた。
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

void usage(){
    printf("usage: daemon [command]+\n");
}

int main(int argc, char *argv[]) {
    if( argc < 2 ){
        usage();
        exit(1);
    }

    pid_t pid;
    pid = fork();
    if (pid < 0) {
        fprintf(stderr, "cant fork()\n");
        exit(1);
    } else if (pid > 0) {
        // end parent process
        exit(0);
    }
    umask(0);

    pid_t sid = setsid();
    if (sid < 0) {
        fprintf(stderr, "cant setsid()\n");
        exit(1);
    }

    if ((chdir("/")) < 0) {
        fprintf(stderr, "cant chdir()\n");
        exit(1);
    }

    int i;
    for (i=getdtablesize();i>=0;--i){
        close(i);
    }

    // handle standard I/O
    i=open("/dev/null",O_RDWR);
    dup(i);
    dup(i);

    // execute command, and don't return
    execvp(argv[1],argv+1);

    return 0;
}
参考ページ

http://systhread.net/texts/200506cdaemon1.php
http://www.enderunix.org/documents/eng/daemon.php

Monday, May 17, 2010

使いやすそうなカラーピッカーを作ってみた

YUIの colorpicker + button をちょっと修正しました。
サンプル

 
<script type="syntaxhighlighter" class="brush:html">
<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/combo?2.8.1/build/assets/skins/sam/skin.css">
<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.8.1/build/fonts/fonts-min.css" />
<script type="text/javascript" src="http://yui.yahooapis.com/combo?2.8.1/build/yahoo-dom-event/yahoo-dom-event.js&2.8.1/build/element/element-min.js&2.8.1/build/button/button-min.js&2.8.1/build/dragdrop/dragdrop-min.js"></script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.8.1/build/slider/slider-min.js"></script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.8.1/build/colorpicker/colorpicker-min.js"></script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.8.1/build/container/container_core-min.js"></script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.8.1/build/menu/menu-min.js"></script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.8.1/build/button/button-min.js"></script>

<style type="text/css">
    body {
        margin:0;
        padding:0;
    }
    div.yuimenu .bd {
        zoom: normal;
    }
    .current-color {
        display: block;
        width: 1em;
        height: 1em;
        overflow: hidden;
        text-indent: 1em;
        background-color: #fff;
        white-space: nowrap;
        border: solid 1px #000;
    }
    .yui-menu-button-menu .yui-picker-controls,
    .yui-menu-button-menu .yui-picker-swatch,
    .yui-menu-button-menu .yui-picker-websafe-swatch {
        display: none;
    }
    .yui-skin-sam .yui-button{
        background:none;
        border:none;
        margin:0;
    }
    .yui-skin-sam .yui-button .first-child{
        background:none;
        border:none;
    }
    .yui-skin-sam .yui-menu-button button{
        background:none;
        margin:0;
        padding:0;
    }
</style>
<div class="yui-skin-sam">
<script type="text/javascript">
    YAHOO.namespace('ypicker');

    YAHOO.ypicker = function(container, input){
        this.container = container;
        this.input = input;
        this.menu = YAHOO.util.Dom.generateId();
        this.button = YAHOO.util.Dom.generateId();
        this.pickerContainer = YAHOO.util.Dom.generateId();
        this.pickerMenu = YAHOO.util.Dom.generateId();
        this.currentColor = YAHOO.util.Dom.generateId();

        YAHOO.util.Event.onContentReady(this.container, this.bind(this.init));
    }
    YAHOO.ypicker.prototype = {
        bind:function(func, args){
            if('undefined' == typeof args){
                args = [];
            }
            var self = this;
            return function(){
                return func.apply(self, args);
            };
        },
        onButton:function(){
            if( this.isColorPickerInit ){
                return;
            }
            this.isColorPickerInit = true;
            this.oColorPicker = new YAHOO.widget.ColorPicker(this.oColorPickerMenu.body.id, {
                                    showcontrols: false,
                                    images: {
                                        PICKER_THUMB: "http://developer.yahoo.com/yui/build/colorpicker/assets/picker_thumb.png",
                                        HUE_THUMB: "http://developer.yahoo.com/yui/build/colorpicker/assets/hue_thumb.png"
                                    }
                                });

            this.oColorPicker.on("rgbChange", this.bind(function(e){
                var sColor = "#" + this.oColorPicker.get("hex");
                this.oColorPicker.set("value", sColor);
                YAHOO.util.Dom.setStyle(this.currentColor, "backgroundColor", sColor);
                YAHOO.util.Dom.get(this.input).value = sColor;
            }));
        },
        init:function(){
            // Create a Menu instance to house the ColorPicker instance
            this.oColorPickerMenu = new YAHOO.widget.Menu(this.pickerMenu);

            // Create a Button instance of type "split"
            this.oButton = new YAHOO.widget.Button({
                                                type: "menu",
                                                id: this.button,
                                                label: '<span id="'+this.currentColor+'" class="current-color"></span>',
                                                menu: this.oColorPickerMenu,
                                                container: this.container });

            this.oButton.on("appendTo", this.bind(function () {
                this.oColorPickerMenu.setBody(" ");
                this.oColorPickerMenu.body.id = this.pickerContainer;
                YAHOO.util.Dom.setStyle( this.oColorPickerMenu.body, 'width', '220px');
                YAHOO.util.Dom.setStyle( this.oColorPickerMenu.body, 'height', '190px');

                // Render the Menu into the Button instance's parent element
                this.oColorPickerMenu.render(this.oButton.get("container"));
            }));

            this.isColorPickerInit = false;
            this.oButton.on("click", this.bind(this.onButton));
        }
    }

</script>
<script>
    (new YAHOO.ypicker("ybutton", "ybutton-value")),
    (new YAHOO.ypicker("ybutton2", "ybutton-value2"));
</script>
<input id="ybutton-value" type="text" style="font-size:11px;"/><span id="ybutton"></span>
<input id="ybutton-value2" type="text" style="font-size:11px;"/><span id="ybutton2"></span>
</div>
</script>

Sunday, May 9, 2010

ループバック用のIPアドレスの割り当て方(linux編)

ループバック用のIPアドレスの割り当て方。
とりあえず、192.56.76.4 に割り当ててみる。

 
$ sudo /sbin/route add -host 192.56.76.4 dev eth0
$ sudo /sbin/ifconfig eth0:0 192.56.76.4
ping を送ってみる。
 
$ ping 192.56.76.4 -c 1
PING 192.56.76.4 (192.56.76.4) 56(84) bytes of data.
64 bytes from 192.56.76.4: icmp_seq=1 ttl=64 time=0.073 ms

--- 192.56.76.4 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.073/0.073/0.073/0.000 ms
成功した。

削除するときは、

 
$ sudo /sbin/route del -host 192.56.76.4 dev eth0
$ sudo /sbin/ifconfig eth0:0 down
route は、ネットマスクを使って以下のように書いてもいい
 
sudo /sbin/route add -net 192.56.76.0 netmask 255.255.255.0 dev eth0

Monday, April 26, 2010

UnitTestしやすいクラスの作り方

以下の要約。
http://misko.hevery.com/2008/07/08/how-to-think-about-the-new-operator/

Unit Test しやすいクラスは、クラス間の依存グラフにおいて、 終端に位置するクラス(以下、leafクラス)である。 なぜなら、それだけを Unit Test することができるから。 例えば、以下のようなクラス。

 
class House {
  private boolean isLocked;

  private boolean isLocked() {
    return isLocked;
  }

  private boolean lock() {
    isLocked = true;
  }
}

しかし、以下のようなクラスでは、Kitchenクラスを生成せずに、 House クラスだけを生成できない。 理由は、House のロジック内に、Kitchen の new 演算子が 含まれているから。 そのため、House クラスのみを Unit Test することができない。

 
class House {
  private final Kitchen kitchen = new Kitchen();
  private boolean isLocked;

  private boolean isLocked() {
    return isLocked;
  }

  private boolean lock() {
    kitchen.lock();
    isLocked = true;
  }
}

Houseだけを個別に Unit Test するには、偽の Kitchen で、Houseを生成する必要がある。

 
class House {
  private final Kitchen kitchen;
  private boolean isLocked;

   public House(Kitchen kitchen) {
    this.kitchen = kitchen;
  }

  private boolean isLocked() {
    return isLocked;
  }

  private boolean lock() {
    kitchen.lock();
    isLocked = true;
  }
}

上のように、new演算子をロジックから取り除けば、 テストを簡単に行える。 Kitchen のモックを作れば、Houseだけを Unit Test できる。 これにより、leaf クラスでなくても、個別にテストを行える。

どこで、new演算子を使えばいいかというと、 以下のようにFactoryクラスを作ってやればよい。

 
class ApplicationBuilder {
  House build() {
    return new House(new Kitchen(
               new Sink(),
               new Dishwasher(),
               new Refrigerator())
           );
  }
}

Main メソッドでは、このFactoryを使ってオブジェクトの依存関係を 生成すればよい。

 
class Main {
  public static void main(String...args) {
    House house = new ApplicationBuilder().build();
    house.lock();
  }
}

コードがシンプルで多機能なlight box系js

コードがシンプルで多機能なlight box系js
http://colorpowered.com/colorbox/

サンプル

iframe なども画像と同じように表示できたり、動的に生成されるURLに対しても問題なく使える。 ソース読み込み以外で js を書いてるところをタグ内に完全に埋めれるようにしたり、コード内でべた書き class 名を使ってしまってるところをキレイにしたい。 あと、会社でYUIを使ってるのでYUIバージョンを作りたい。

Sunday, April 18, 2010

googleに学ぶ角丸の作り方(html、css)2

さっきの方法だと、背景色が透過しないので、以下のようにすると透過する
角丸の中の
テキスト

googleに学ぶ角丸の作り方(html、css)

グーグルがこの前始めた、最新検索では、 吹き出しのような表示がある。

そこの吹き出しから軽量な角丸の作り方を学ぶことができる。
一つ目の画像:
二つ目の画像:

グーグルが置いてる画像はこの二つだけ。
それに対し角丸を置いてるサイトの画像を見てみると、四角形の上部の辺を全て画像にしているサイトも多い。
グーグルの方法だと
・横幅を柔軟に変更できる
・画像サイズがもっとも軽量
という利点がある。

吹き出し部分の画像は特に必要ないので、それを取り除いて実際に角丸を作ってみた。

角丸の中の
テキスト

ここでは、2つの画像を使っている。

http://sikaku-chat.com/stt/circle/bg0.png
用途:角丸の角、四角形の左辺と右辺に対応

http://sikaku-chat.com/stt/circle/bg1.png
用途:四角形の上辺、下辺に対応

ソース部分のポイントは3つだけ
・background-position を指定し、円の画像からうまく角丸を作成
・全体的にtable を使用
・角丸部分には box のfloatを使用

こう見るとたいしたことはやっていない。
知らなかったのは、float container に table の要素が使えることだろうか。
float container ってtable使えばよかったのか?

Saturday, April 17, 2010

透過PNGをマルチブラウザで指定する方法

CSS で以下のように指定すればよい
 ・block 要素に background で画像を指定
 ・アンダースコアハックでIE6だけAlphaImageLoaderを使用 サンプル:

参考:http://developer.yahoo.com/yui/examples/button/btn_example11.html

Sunday, April 11, 2010

301 リダイレクトをchromeがキャッシュする

Cookieを使ってユーザの tracking をする場合、redirect に 301 を使うのは要注意。

Google が 301 リダイレクトを推奨しているように、301 を使うと search engine の score がリダイレクト先に移動するので、 サイトの引越しには 301 を使うと良い。

でも、アフィリエイトとかで Cookie を使って tracking している場合には、301 は使えない。 なぜなら、chrome がリダイレクト先をキャッシュするから。

以下の記事で書いてるとおり、最近のブラウザでは chrome のみキャッシュする。
http://bugsquash.blogspot.com/2008/12/google-chrome-caches-301-redirects.html

検証コードを書いてみた。
a.php
b.php
サンプル

a.php でクッキーを保存し、301リダイレクト
b.php でクッキーを参照&削除

chrome でアクセスすると、最初はクッキーに保存されるが、 2回目以降のアクセスでは、a.php にアクセスしなくなる。

IE8、Firefox3、Opera、Safari では、キャッシュしなかった。

もうちょっと調べると、2007年の記事で、MozillaとFirefox がキャッシュするという内容を見つけた。
http://www.webmasterworld.com/google/3229652.htm

RFC2616 では、 10.3.2 301 Moved Permanently のところで、

This response is cacheable unless indicated otherwise. 
となっているので、chrome の動作は悪くない。

ふと思い立って、Pragma: no-cache を付けてみることにした。

a.php

chrome でもリダイレクトしてくれた。

結論としては、Pragma: no-cache をつければ、chrome でもキャッシュしないが、 古いブラウザでもちゃんと動作するかは検証が必要。