|
|
By utahta, on 1月 24th, 2010%
pthread_key 周辺の使い方を勉強。
スレッドごとにメモリ領域を確保する仕組み。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
static pthread_key_t g_key;
// スレッド単位で保持するデータ
struct MyData{
int count;
char data;
};
static void* thread_call( void *arg )
{
pthread_t self_id = pthread_self();
printf( "self_id:%u\n", self_id );
// データがすでに存在するか調査
struct MyData *p;
if( pthread_getspecific( g_key ) ){
printf( "self_id:%u Already exists.", self_id );
return NULL;
}
// データ作成
if( !(p = (struct MyData *)calloc( 1, sizeof(*p) )) ){
perror( "calloc()" );
return NULL;
}
pthread_setspecific( g_key, p );
p->count = 0;
p->data = 'A';
// 本当にスレッド単位で保持されているかテスト
int i;
for( i = 0; i < 26; ++i ){
p = (struct MyData *)(pthread_getspecific( g_key ));
printf( "self_id:%u id:%u data:%c\n", self_id, p->count++, p->data++ );
usleep(1000);
}
p = (struct MyData *)(pthread_getspecific( g_key ));
if( p->count == 26 ){
printf( "self_id:%u success.\n", self_id );
}
else{
printf( "self_id:%u error!!\n", self_id );
}
// 終了
free(p);
pthread_setspecific( g_key, 0 );
}
int main()
{
// pthread key を初期化
if( pthread_key_create( &g_key, NULL ) ){
perror( "pthread_key_create()" );
return 0;
}
// スレッドを複数作成。
const int TNUM = 3;
pthread_t tid[TNUM];
int i;
for( i = 0; i < TNUM; ++i ){
if( pthread_create( &tid[i], NULL, thread_call, NULL ) ){
perror( "pthread_create()" );
continue;
}
printf( "Created a thread. id:%u\n", tid[i] );
}
// 全スレッドが終了するのを待機。
for( i = 0; i < TNUM; ++i ){
pthread_join( tid[i], NULL );
}
// pthread key を削除
pthread_key_delete( g_key );
return 0;
}
実行
$ gcc main.c -lpthread
$ ./a.out
Created a thread. id:663552
Created a thread. id:2625536
Created a thread. id:3162112
self_id:663552
self_id:2625536
self_id:3162112
self_id:663552 id:0 data:A
self_id:2625536 id:0 data:A
self_id:3162112 id:0 data:A
self_id:663552 id:1 data:B
self_id:2625536 id:1 data:B
...
self_id:2625536 id:25 data:Z
self_id:663552 success.
self_id:3162112 id:25 data:Z
self_id:2625536 success.
self_id:3162112 success.
By utahta, on 1月 14th, 2010%
send と recv の正しい使い方と送信バッファ溢れについて。
manpage
Manpage of SEND
Manpage of RECV
SEND
non-blocking の場合。
send が失敗して外部変数 errno に EINTR が設定されている場合は、再度送信を試みる必要がある。
データをソケットの送信バッファに入れることが出来ない場合、外部変数 errno に EAGAIN か EWOULDBLOCK が設定される。
いつデータが送信できるようになるかを知るために select などのシステムコールを使うことが出来る。(後述:送信バッファ溢れ)
以下、ソースコードから抜粋した send のサンプル。
int res = 0;
char* p = buffer; // 送るデータ
size_t len = buffer_size; // 送るデータのサイズ
while( len > 0 ){
while( true ){
res = send( socket, p, len, MSG_DONTWAIT ); // 一度に全部送れるとは限らない
if( errno != EINTR ) break; // システム割り込みチェック
}
if( res < 0 ){
if( errno == EAGAIN ||
errno == EWOULDBLOCK ){
// 書き込めない状態。それなりの対応が必要(後述:送信バッファ溢れ)
}
else{
// なんらかのエラー(コネクション切断とか)
}
}
len -= res;
p += res;
}
RECV
non-blocking の場合。
操作が停止するような場合、外部変数 errno に EAGAIN か EWOULDBLOCK が設定される。
以下、ソースコードから抜粋した recv のサンプル。
int res = 0;
while( true ){
res = recv( socket, buffer, buffer_size, MSG_DONTWAIT );
if( errno != EINTR ) break;
}
if( res == 0 ){
// EOF
}
if( res < 0 ){
if( errno == EAGAIN ||
errno == EWOULDBLOCK ){
// not ready yet.
}
else{
// error.
}
}
送信バッファ溢れ
データ送信時に送信バッファが溢れてしまった場合、書き込み可能なタイミングを見計らって、溢れたデータを再送信してあげる必要がある。
送信バッファが溢れたとき、send 関数は失敗し、errno に EAGAIN, EWOULDBLOCK を格納する。
対策として例えば、送信データをキューなり可変長バッファなりで一時的に保持し、select などで書き込み可能になるタイミングを監視して、再送信する方法がある。
サンプル:sendbuffer-test
事前に libevent をインストールしておく必要あり。
動作環境 Linux のみ。(送信バッファと受信バッファの数値を linux/sockios.h 使って確認してる為)
1.サーバは受信時に3秒間待機する。
2.クライアントはサーバへデータを送信しまくり、EAGAIN or EWOULDBLOCK を発生させる。
3.エラー発生後、クライアントは書き込み可能イベントを監視し、検知したらデータを再送信する。
2ー3の動作を繰り返し、サーバに規定のデータ(0 – 499 までの連番)が全部届いたら成功。
インストール
$ git clone http://github.com/utahta/sendbuffer-test.git
$ cd sendbuffer-test
$ ./configure --prefix=/path/to/sendbuffer-test --with-libevent=/path/to/libevent
$ make
$ make install
実行
コマンドプロンプトをふたつ立ち上げ、1つめにサーバを起動します。
$ /path/to/sendbuffer-test/bin/sbt_server
2つめにクライアントを起動します。
$ /path/to/sendbuffer-test/bin/sbt_client
手元では、以下のような結果になりました。
$ /path/to/sendbuffer-test/bin/sbt_server
connected. sd:7
recv buffer size:274 sd:7
waiting 3 sec.
... 省略
message received. sd:7 str:abcdefghijklnmopqrstuvwxyz:495
message received. sd:7 str:abcdefghijklnmopqrstuvwxyz:496
message received. sd:7 str:abcdefghijklnmopqrstuvwxyz:497
message received. sd:7 str:abcdefghijklnmopqrstuvwxyz:498
message received. sd:7 str:abcdefghijklnmopqrstuvwxyz:499 // 499 まで届いた
$ /path/to/sendbuffer-test/bin/sbt_client
send buffer size. :50436
... 省略
send buffer size:48498 res:0 sd:6
send buffer size:48772 res:0 sd:6
send buffer size:49046 res:0 sd:6
not ready yet: Resource temporarily unavailable // 書き込み失敗
send buffer size:49152 res:-10 sd:6
not ready yet: Resource temporarily unavailable
send buffer size:49152 res:-10 sd:6
... 省略
resend buffer size:88558 res:0 sd:6 // 書き込み可能になったので再送信
このサンプルでは、可変長処理をさぼってあらかじめ大きなバッファを確保してごまかしてたり。
終了処理に手抜きがあったり。(delete してなかったり)
とりあえずサンプルということで。
By utahta, on 1月 8th, 2010%
TCPサーバのサンプルコードのメモ。
簡単なコードだからか、何度も書いてはどこかへやってたのでいい加減保存することに。
動作は一応 Linux CentOS5, Mac OS X 10.6 で確認済み。
server/main.cpp に socket, bind, listen, accept, select, recv など。
client/main.cpp に socket, connect, send など。
ソースコード
easy-tcpserver
インストール
$ git clone http://github.com/utahta/easy-tcpserver.git
$ cd easy-tcpserver
$ ./configure --prefix=/path/to/easy-tcpserver
$ make
$ make install
実行
・サーバ
$ cd /path/to/easy-tcpserver/bin
$ ./easy_tcpserver
・クライアント
$ cd /path/to/easy-tcpserver/bin
$ ./easy_tcpclient
By utahta, on 11月 26th, 2009%
MySQL 5.1.41 を GDB 使って解析するメモ。取っかかり編。
環境 : CentOS 5.2
ダウンロード
MySQL 5.1.41 のソースをダウンロードする。
インストール
/usr/local/mysql-5.1.41 へインストールする。
$ tar zxvf mysql-5.1.41.tar.gz
$ cd mysql-5.1.41
$ ./configure --prefix=/usr/local/mysql-5.1.41 --with-unix-socket-path=/usr/local/mysql-5.1.41/tmp/mysql.sock --with-charset=utf8 --with-extra-charsets=all --enable-thread-safe-client --with-plugins=innodb_plugin --with-readline --with-debug
$ make
$ make install
$ cd /usr/local/mysql-5.1.41
$ sudo cp share/mysql/my-medium.cnf /etc/my.cnf
GDB
最初に mysqld を gdb 経由で実行する。
$ gdb ./libexec/mysqld
GNU gdb Red Hat Linux (6.5-25.el5rh)
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".
立ち上がったら続けて、main に break ポイントを打ってみる。
(gdb) b main
Breakpoint 1 at 0x81b697a: file mysqld.cc, line 4261.
main は sql/mysqld.cc の 4261 行目にある。
とりあえず実行。
(gdb) r
Starting program: /usr/local/mysql-5.1.41/libexec/mysqld
[Thread debugging using libthread_db enabled]
[New Thread -1209051440 (LWP 10008)]
[Switching to Thread -1209051440 (LWP 10008)]
Breakpoint 1, main (argc=1, argv=0xbf96e304) at mysqld.cc:4261
4261 MY_INIT(argv[0]); // init my_sys library & pthreads
MY_INIT(argv[0]) のとこで止まった。(ちなみに MY_INIT() はマクロで、include/my_sys.h の 40 行目にある)
いま止まってるとこ付近のソースを表示する。
(gdb) l
4256 int win_main(int argc, char **argv)
4257 #else
4258 int main(int argc, char **argv)
4259 #endif
4260 {
4261 MY_INIT(argv[0]); // init my_sys library & pthreads
4262 /* nothing should come before this line ^^^ */
4263
4264 /* Set signal used to kill MySQL */
4265 #if defined(SIGUSR2)
main の直後 に MY_INIT() が呼ばれており、#else 〜 #endif で windows とそれ以外で main を切り分けてるのがなんとなく分かる。
ステップ実行して MY_INIT() に入る。
(gdb) s
my_init () at my_init.c:73
73 if (my_init_done)
Current language: auto; currently c
my_init() へ辿り着いた。my_init() は MY_INIT() の中で呼ばれており、mysys/my_init.c の 73 行目にある。
my_init_done の値を確認する。
(gdb) p my_init_done
$1 = 0 '\0'
0 が入ってる。my_init() 実行前だから当然っちゃ当然。(my_init_done は my_bool型(typedef char))
やっぱりステップ実行は便利ですね。
参考
GDB マニュアル
By utahta, on 11月 17th, 2009%
libevent のソースコードを参考にしつつ epoll を使ってみたメモ。
ただの興味本位。
動作は、Linux CentOS 5 で確認。
エラー処理とかはやってたりやってなかったり、わりと適当。
server.cpp
/**
* @file server.cpp
* @brief epoll test server.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/epoll.h>
#define LISTEN_PORT (12345) ///< 待ち受けポート
#define MAX_EVENTS (1000) ///< 最大イベント数
#define MAX_BACKLOG (10) ///< リクエスト待機キューの格納数
struct epollev
{
void (*callback)(int, void *); ///< callback function.
void *arg; ///< user data.
};
struct epollop
{
int epfd; ///< epoll fd.
struct epoll_event *events; ///< epoll events.
int num_events; ///< maximum number of events.
struct epollev *fds; ///< fds
};
static epollop g_epollop;
static void callback_accept( int fd, void *arg );
static void callback_client( int fd, void *arg );
//! @brief 強制終了
static void server_exit( const char *log )
{
perror( log );
exit( EXIT_FAILURE );
}
//! @brief イベント初期化
static void server_init()
{
int epfd, nfiles = MAX_EVENTS;
if( ( epfd = epoll_create( nfiles ) ) == -1 ){
server_exit( "epoll_create()" );
}
g_epollop.epfd = epfd;
g_epollop.events = new struct epoll_event[nfiles];
memset( g_epollop.events, 0, sizeof(struct epoll_event)*nfiles );
g_epollop.num_events = MAX_EVENTS;
g_epollop.fds = new epollev[nfiles];
memset( g_epollop.fds, 0, sizeof(struct epollev)*nfiles );
}
//! @brief イベント追加
static void server_add( int fd, int events, void (*callback)(int, void *), void *arg )
{
struct epoll_event epev = {0, {0}};
struct epollev *ev = &g_epollop.fds[fd];
int op = EPOLL_CTL_ADD;
if( ev->callback != NULL ){
op = EPOLL_CTL_MOD;
}
epev.data.fd = fd;
epev.events = events;
if( epoll_ctl( g_epollop.epfd, op, fd, &epev ) == -1 ){
perror( "epoll_ctl()" );
return;
}
ev->callback = callback;
ev->arg = arg;
}
//! @brief イベント削除
static void server_del( int fd )
{
struct epoll_event epev = {0, {0}};
struct epollev *ev = &g_epollop.fds[fd];
int op;
op = EPOLL_CTL_DEL;
epev.data.fd = fd;
memset( ev, 0, sizeof( struct epollev ) );
if( epoll_ctl( g_epollop.epfd, op, fd, &epev ) == -1 ){
perror( "epoll_ctl()" );
return;
}
close( fd );
}
//! @brief 受付
static void server_listen()
{
int sock;
struct sockaddr_in saddr;
if( ( sock = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 ){
server_exit( "socket()" );
}
memset( &saddr, 0, sizeof( saddr ) );
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl( INADDR_ANY );
saddr.sin_port = htons( LISTEN_PORT );
if( bind( sock, (struct sockaddr *) &saddr, sizeof(saddr) ) != 0 ){
close( sock );
server_exit( "bind()" );
}
if( listen( sock, MAX_BACKLOG ) < 0 ){
close( sock );
server_exit( "listen()" );
}
server_add( sock, EPOLLIN, callback_accept, NULL );
}
//! @brief メインループ
static int server_loop( )
{
fprintf( stdout, "in server_loop.\n" );
while( true ){
int i, res;
int timeout = -1;
struct epoll_event *events = g_epollop.events;
res = epoll_wait( g_epollop.epfd, events, g_epollop.num_events, timeout );
if( res == -1 ){
server_exit( "epoll_wait()" );
}
for( i = 0; i < res; ++i ){
int fd = events[i].data.fd;
struct epollev *ev = &g_epollop.fds[fd];
ev->callback( fd, ev->arg );
}
}
return 0;
}
//! @brief 受付コールバック
static void callback_accept( int fd, void *arg )
{
fprintf( stdout, "in callback_accept. fd:%d\n", fd );
int sock;
struct sockaddr saddr;
socklen_t len = sizeof( struct sockaddr_in );
if( ( sock = accept( fd, (struct sockaddr *)&saddr, &len ) ) < 0 ){
perror( "accept()" );
return;
}
int flag = fcntl( sock, F_GETFL, 0 );
fcntl( sock, F_SETFL, flag | O_NONBLOCK );
fprintf( stdout, "accept. sock:%d\n", sock );
server_add( sock, EPOLLIN, callback_client, NULL );
}
//! @brief クライアントコールバック
static void callback_client( int fd, void *arg )
{
fprintf( stdout, "in callback_client. fd:%d\n", fd );
char buff[1024];
int ret = ::recv( fd, buff, sizeof(buff), 0 );
if( ret <= 0 ){
fprintf( stdout, "exit. fd:%d ret:%d\n", fd, ret );
server_del( fd );
}
else{
fprintf( stdout, "fd:%d -> %s\n", fd, buff );
}
}
int main()
{
server_init();
server_listen();
return server_loop();
}
client.cpp
/**
* @file client.cpp
* @brief test client.
*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#define CONNECT_ADDR "127.0.0.1"
#define CONNECT_PORT (12345)
bool setnonblock(int fd)
{
int flags = fcntl( fd, F_GETFL );
if( flags < 0 ){
return false;
}
if( fcntl( fd, F_SETFL, flags |= O_NONBLOCK ) < 0 ){
return false;
}
return true;
}
int main(void)
{
int ret = 0, sock = 0, clilen = 0;
struct sockaddr_in cliaddr;
// socket
if( ( sock = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 ){
puts( "socket error!!" );
return 1;
}
memset( &cliaddr, 0, sizeof( cliaddr ) );
cliaddr.sin_addr.s_addr = inet_addr( CONNECT_ADDR );
cliaddr.sin_family = AF_INET;
cliaddr.sin_port = htons( CONNECT_PORT );
clilen = sizeof( cliaddr );
if( ( ret = connect( sock, (struct sockaddr *)&cliaddr, clilen ) ) < 0 ){
perror( "connect()" );
return 1;
}
if( !setnonblock( sock ) ){
perror( "setnonblock()" );
}
puts( "in client loop." );
char str[1024];
char *strpos;
while( 1 )
{
memset( str, 0, sizeof( str ) );
fgets( str, sizeof(str), stdin );
// \n を除去
if( ( strpos = strchr( str, '\n' ) ) != NULL ){
(*strpos) = '\0';
}
// exit
if( strncmp( str, "exit", 4 ) == 0 )
break;
// send
if( send( sock, str, strlen( str ) + 1, 0 ) < 0 ){
perror( "send()" );
break;
}
}
// 後始末
while( 1 ){
ret = recv( sock, str, sizeof(str), 0 );
if( ret <= 0 )
break;
}
close( sock );
return 0;
}
By utahta, on 10月 27th, 2009%
core ファイルを解析するメモ。
下準備
まず意図的に SEGV させるコードを書く。
#include <stdio.h>
class CPrint
{
private:
int m_number;
char *m_str;
public:
CPrint() : m_number(10), m_str(NULL) {}
~CPrint() {}
void print(){
// ここで SEGV る予定
printf( "%d, %c\n", m_number, m_str[0] );
}
};
int main()
{
CPrint p;
p.print();
return 0;
}
続けて core を出力させる設定。環境は Linux CentOS 5。
core dumped
下準備で作成したソースコードをコンパイル。-g を忘れずに。
実行する。当然 SEGV る。
$ ./a.out
セグメンテーション違反です (core dumped)
core ファイルが吐かれていることを確認。
$ ls
a.cpp a.out core.1613
core 解析
gdb を立ち上げる。gdb 実行バイナリ coreファイル
$ gdb a.out core.1613
...
Program terminated with signal 11, Segmentation fault.
#0 0x08048568 in CPrint::print (this=0xbfe45794) at a.cpp:15
15 printf( "%d, %c\n", m_number, m_str[0] );
(gdb)
呼び出された順番で関数を表示する。
(gdb) where
#0 0x08048568 in CPrint::print (this=0xbfe45794) at a.cpp:15
#1 0x080484fd in main () at a.cpp:22
(gdb)
ソースコードを表示する。
(gdb) list
10 CPrint() : m_number(10), m_str(NULL) {}
11 ~CPrint() {}
12
13 void print(){
14 // ここで SEGV る予定
15 printf( "%d, %c\n", m_number, m_str[0] );
16 }
17 };
18
19 int main()
(gdb)
変数の値を確認する。
クラスのメンバ変数もアドレスさえ分かれば確認できる。
(gdb) p ((class CPrint *) 0xbfe45794)->m_number
$2 = 10
(gdb)
いちいち ((class CPrint *) 0xbfe45794) とうつのが面倒な場合は、変数に代入する。
(gdb) set $c = ((class CPrint *) 0xbfe45794)
(gdb) p $c->m_number
$3 = 10
(gdb) p $c->m_str
$4 = 0x0
By utahta, on 6月 15th, 2009%
setrlimit(2), getrlimit(2) メモ。
コマンドラインから ulimit -n 1024 などとせず、プログラム上から設定するやつ。
limits.conf 以上の値を割り当てようとすると失敗するぽい。
サンプルソース
#include <stdio.h>
#include <sys/time.h>
#include <sys/resource.h>
#ifndef RLIM_INFINITY
#define RLIM_INFINITY ((unsigned int)0xffffffff)
#endif
int main()
{
struct rlimit rl;
rl.rlim_cur = 0; /* ソフトリミット */
rl.rlim_max = 0; /* ハードリミット */
/* 現在設定されている値を取得する */
if( getrlimit( RLIMIT_NOFILE, &rl ) ){
puts( "getrlimit error." );
return 0;
}
printf( "before open_files:%d\n", rl.rlim_cur );
/* 制限なし状態かチェック */
if( rl.rlim_cur == RLIM_INFINITY ){
printf( "infinity:%d\n", rl.rlim_cur );
}
/* 試しに新しい値をセットしてみる */
rl.rlim_cur = 512;
/*rl.rlim_cur = 2048;*/
if( setrlimit( RLIMIT_NOFILE, &rl ) ){
puts( "setrlimit error." );
return 0;
}
/* さっき設定した値を取得する */
rl.rlim_cur = 0;
getrlimit( RLIMIT_NOFILE, &rl );
printf( "after open_files:%d\n", rl.rlim_cur );
return 0;
}
他にもスタックだったりcoreファイルサイズだったり色々変更できる。
詳しくは Manpage of GETRLIMIT で。
あんまし使わないけど知ってると便利っすなー。
By utahta, on 4月 26th, 2009%
valgrind とは?
linux 環境で動く超強力なメモリデバッガー。
メモリリークや、セグメンテーション違反を起こしている正確な位置を教えてくれる。
以下、使い方メモ。
使用バージョンは、valgrind-3.2.1
メモリリーク検出など
$ valgrind --leak-check=full ./program arg1 arg2
ヒーププロファイラ
$ valgrind --tool=massif ./program arg1 arg2
メモリをデバッグしてみる
1. バッファオーバーランとメモリリークを行うソースコードを記述
$ vi main.cpp
#include <stdio.h>
int main()
{
int *a = new int[2];
a[2] = 0; // バッファオーバーラン
return 0; // メモリリーク
}
2. コンパイルする
3. 検証する
$ valgrind --leak-check=full ./a.out
... 省略 ...
・バッファオーバーラン検出
==10945== Invalid write of size 4
==10945== at 0x80484AA: main (main.cpp:7)
==10945== Address 0x410E030 is 0 bytes after a block of size 8 alloc'd
==10945== at 0x40057F5: operator new[](unsigned) (vg_replace_malloc.c:195)
==10945== by 0x80484A0: main (main.cpp:5)
==10945==
==10945== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 17 from 1)
==10945== malloc/free: in use at exit: 8 bytes in 1 blocks.
==10945== malloc/free: 1 allocs, 0 frees, 8 bytes allocated.
==10945== For counts of detected errors, rerun with: -v
==10945== searching for pointers to 1 not-freed blocks.
==10945== checked 87,724 bytes.
・メモリリーク検出
==10945== 8 bytes in 1 blocks are definitely lost in loss record 1 of 1
==10945== at 0x40057F5: operator new[](unsigned) (vg_replace_malloc.c:195)
==10945== by 0x80484A0: main (main.cpp:5)
==10945==
==10945== LEAK SUMMARY:
==10945== definitely lost: 8 bytes in 1 blocks.
==10945== possibly lost: 0 bytes in 0 blocks.
==10945== still reachable: 0 bytes in 0 blocks.
==10945== suppressed: 0 bytes in 0 blocks.
==10945== Reachable blocks (those to which a pointer was found) are not shown.
==10945== To see them, rerun with: --show-reachable=yes
ヒーププロファイラを試してみる
1. massifモードで起動する
$ valgrind --tool=massif ./a.out
2. massif.pid.ps ファイルが作成されている。Postscriptでは見づらいので、PDFへ変換すると良さげ。
雑感
頭良すぎだろ。惚れた。
By utahta, on 1月 12th, 2009%
C++ で UTF-8 の文字数を数えるコードを試しに書いてみたメモ。
バイト数ではなく、文字数。
#include <stdio.h>
#include <string.h>
int strlen_utf8( const char *buff )
{
if( buff == NULL ) return 0;
int count = 0;
int pos = 0;
int max_bytes = strlen( buff );
// BOM 読み飛ばし
if( max_bytes >= 3 )
{
if( static_cast<unsigned char>( buff[0] ) == 0xEF &&
static_cast<unsigned char>( buff[1] ) == 0xBB &&
static_cast<unsigned char>( buff[2] ) == 0xBF )
{
pos += 3;
}
}
while( pos < max_bytes )
{
++count; // 文字数カウント
if( ( buff[pos] & 0x80 ) == 0 )
{
++pos; // 1バイト文字
}
else
{
for( char tmp = buff[pos] & 0xfc; (tmp & 0x80); tmp = tmp << 1 )
{
++pos; // 複数バイト文字
}
}
}
return count;
}
int main()
{
char *str = "あ過サタな";
int cnt = strlen_utf8( str );
printf( "String: %s\n", str );
printf( "Count: %d\n", cnt );
return 0;
}
結果
おしまい
UTF-8 は1バイト目に後に続くバイト数が示されている為、簡単に実装できた。
意外とここら辺の勉強もしてみると面白いなー。
参考: UTF-8 Wikipedia
|
|