2009年5月29日金曜日

ActionScriptでrubyのString#scanもどき

RubyのString#scan
scan.as
package com.blogspot.takumakei.utils
{
public function scan(str:String, re:RegExp):Array
{
if(!re.global){
var flags:String = 'g';

if(re.dotall)
flags += 's';
if(re.multiline)
flags += 'm';
if(re.ignoreCase)
flags += 'i';
if(re.extended)
flags += 'x';

re = new RegExp(re.source, flags);
}

var r:Array = [];
var m:Array = re.exec(str);
while(null != m){
if(1 == m.length)
r.push(m[0]);
else
r.push(m.slice(1, m.length));
m = re.exec(str);
}
return r;
}
}

サンプルスクリプト
p(scan('foobar', /./));
p(scan('foobar', /(.)/));
p(scan('foobarbazfoobarbaz', /ba./));
p(scan('foobarbazfoobarbaz', /(ba)(.)/));
実行結果
["f","o","o","b","a","r"]
[["f"],["o"],["o"],["b"],["a"],["r"]]
["bar","baz","bar","baz"]
[["ba","r"],["ba","z"],["ba","r"],["ba","z"]]

ActionScriptでrubyのObject#inspectとp

RubyのObject#inspectp
inspect.as
package com.blogspot.takumakei.utils
{
import flash.utils.getQualifiedClassName;

public function inspect(x:*):String
{
if(x == null)
return "null";

if(x is Boolean)
return (x ? 'true' : 'false');

if(x is int || x is uint || x is Number)
return x.toString();

if(x is String)
return '"' + x + '"';

if(x is Array){
var r:String = '[';
if(0 < x.length){
r += inspect(x[0]);
for(var i:int = 1; i < x.length; ++i){
r += ',';
r += inspect(x[i]);
}
}
r += ']';
return r;
}

return '#<' + flash.utils.getQualifiedClassName(x) + ':"' + x.toString() + '">';
}
}

p.as
package com.blogspot.takumakei.utils
{
public function p(x:*):void
{
trace(inspect(x));
}
}

2009年5月26日火曜日

静的にリンクしたDLLのフック

ちょっとはまった。
  1. フック対象のTARGET.DLLのファイル名をORIGINAL.DLLに変更する。
  2. 新しくTARGET.DLLを作り、ORIGINAL.DLLをLoadLibraryとGetProcAddressで元のコードを呼ぶ。
たまたま(!?)、DLLからは関数だけをEXPORTする村に住んでいるので、この手順でいけるだろうと思っていたのだが甘かった。

「ORIGINAL.DLLがEXPORTする関数を呼び出すデストラクタ」をもつオブジェクトをグローバル変数としてインスタンス化してあって、そのデストラクタが呼ばれる前にORIGINAL.DLLが解放されていた。

参考: Why are DLLs unloaded in the "wrong" order? (なぜDLLは意図しない順序でアンロードされるのか?)

解決策を探している間にみつけたMemoryModuleも面白そうだったけれど、
依存関係情報を与えるためには解決策は静的リンクにするしかなさそうだ・・・

TARGET.DLLから名前を変えられてしまったORIGINAL.DLLのインポートライブラリは、モジュール定義ファイルから簡単に生成できる。

LIB /DEF:ORIGINAL.def /MACHINE:X86 /out:ORIGINAL.lib

あとは、ORIGINAL.libを静的リンクした新しいTARGET.DLLを作ればよい。
しかし、ORIGINAL.libに含まれる関数を、新しいTARGET.DLLにも定義しなければ意味がないのに、定義してしまうと今度は名前が衝突してリンカが文句を言うだろう。その前にコンパイラが混乱するか・・。

なので、結論は

TARGET.DLL → ALIAS.DLL → ORIGINAL.DLL

このように静的リンクすること。

・・・かも-.-;

target.c
#include "alias.h"

#pragma comment(lib, "alias")

int target_function_name()
{
return alias_function_name();
}
alias.c
#include "target.h"

#pragma comment(lib, "original")

int alias_function_name()
{
return target_function_name();
}

2009年5月25日月曜日

いまやInfernoはopen sourceなのね

懐かしい。
ベル研究所で開発された分散OS、Inferno。
今ではオープンソースになってるのね。

文字列の大文字と小文字を変換する遊び

case.cc
#include <locale>
#include <string>

template<typename Elem>
std::basic_string<Elem> tolower(const std::basic_string<Elem>& x, const std::locale& loc = std::locale())
{
std::basic_string<Elem> r;
r.reserve(x.size());

typedef typename std::basic_string<Elem>::const_iterator iterator;
iterator itr = x.begin();
iterator end = x.end();
for(; itr != end; ++itr)
r.push_back(std::tolower(*itr, loc));

return r;
}

template<typename Elem>
std::basic_string<Elem> toupper(const std::basic_string<Elem>& x, const std::locale& loc = std::locale())
{
std::basic_string<Elem> r;
r.reserve(x.size());

typedef typename std::basic_string<Elem>::const_iterator iterator;
iterator itr = x.begin();
iterator end = x.end();
for(; itr != end; ++itr)
r.push_back(std::toupper(*itr, loc));

return r;
}

2009年5月22日金曜日

STLコンテナにファイルの内容を丸ごとコピーする遊び

read_file.cc
#include <fstream>
#include <iterator>

template<typename Container>
bool read(const char* filename, Container& cont)
{
std::ifstream in(filename, std::ios_base::in | std::ios_base::binary);
if(!in)
return false;
in.unsetf(std::ios::skipws);
std::copy(
std::istream_iterator<typename Container::value_type>(in),
std::istream_iterator<typename Container::value_type>(),
std::back_inserter(cont));
return true;
}

#include <list>
#include <string>

int main(int, char*[])
{
std::list<char> x;
read(__FILE__, x);
std::string s;
read(__FILE__, s);
return 0;
}

2009年5月15日金曜日

文字列をRubyっぽく数値(浮動小数)変換する

整数変換を作ったついでに浮動小数も作ってみた。
整数変換の方は整理できたけど、こっちは落書きみたいだ・・・

float.cpp
namespace Float {

double base(double direction, int volume)
{
double position = 1.0;
for(int i = 0; i < volume; ++i)
{
position *= direction;
}
return position;
}

double to_f(const char* itr, const char* end)
{
if(itr == end){ return 0.0; } // NG

double flag = 1.0;
switch(*itr)
{
case '-':
flag = -1.0;
// no break needed
case '+':
while(true)
{
++itr;
if(itr == end){ return 0.0; } // NG
if(*itr != ' '){ break; }
}
break;
}

double mantissa = 0.0;

if(Integer::DecimalNumber::acceptable(*itr))
{
mantissa = Integer::DecimalNumber::to_i(*itr);
while(true)
{
++itr;
if(itr == end){ return flag * mantissa; } // OK
if(*itr == '_')
{
++itr;
if(itr == end){ return flag * mantissa; } // OK (with garbage)
}
if(!Integer::DecimalNumber::acceptable(*itr))
{
break;
}
mantissa = mantissa * 10.0 + Integer::DecimalNumber::to_i(*itr);
}
}

if('.' == *itr)
{
++itr;
if(itr == end){ return flag * mantissa; } // OK (with garbage)

if(!Integer::DecimalNumber::acceptable(*itr))
{
return flag * mantissa; // OK (with garbage)
}

double position = 0.1;
while(true)
{
mantissa += position * Integer::DecimalNumber::to_i(*itr);
++itr;
if(itr == end){ break; }
if('_' == *itr)
{
++itr;
if(itr == end){ break; }
}
if(!Integer::DecimalNumber::acceptable(*itr)){ break; }
position *= 0.1;
}
}

if('e' != *itr && 'E' != *itr){ return flag * mantissa; }

++itr;
if(itr == end){ return flag * mantissa; }

if('-' == *itr)
return flag * mantissa * base(0.1, Integer::parse_first<Integer::DecimalNumber>(itr, end));

if('+' == *itr)
return flag * mantissa * base(10.0, Integer::parse_first<Integer::DecimalNumber>(itr, end));

if(Integer::DecimalNumber::acceptable(*itr))
return flag * mantissa * base(10.0, Integer::parse_last<Integer::DecimalNumber>(itr, end));

return flag * mantissa;
}
}

文字列をRubyっぽく数値(整数)変換する

可能な限り数値に変換しようとするのでちょっと挙動は違う。

integer.cpp
namespace Integer {

struct BinaryNumber
{
static const int BASE = 2;
static bool acceptable(char ch)
{
return '0' == ch || '1' == ch;
}
static int to_i(char ch)
{
return ch - '0';
}
};

struct OctalNumber
{
static const int BASE = 8;
static bool acceptable(char ch)
{
return '0' <= ch && ch <= '7';
}
static int to_i(char ch)
{
return ch - '0';
}
};

struct DecimalNumber
{
static const int BASE = 10;
static bool acceptable(char ch)
{
return '0' <= ch && ch <= '9';
}
static int to_i(char ch)
{
return ch - '0';
}
};

struct HexadecimalNumber
{
static const int BASE = 16;
static bool acceptable(char ch)
{
return
('0' <= ch && ch <= '9')
|| ('a' <= ch && ch <= 'f')
|| ('A' <= ch && ch <= 'F');
}
static int to_i(char ch)
{
if('0' <= ch && ch <= '9'){ return ch - '0'; }
if('a' <= ch && ch <= 'f'){ return ch - 'a' + 10; }
return ch - 'A' + 10;
}
};

template<typename BaseNumberTraits>
int parse_last(const char* itr, const char* end)
{
int value = BaseNumberTraits::to_i(*itr);
while(true)
{
++itr;
if(itr == end){ return value; } // OK
if(*itr == '_'){
++itr;
if(itr == end){ return value; } // OK (with garbage)
}
if(!BaseNumberTraits::acceptable(*itr)){ return value; } // OK (with garbage)
value =
value * BaseNumberTraits::BASE
+ BaseNumberTraits::to_i(*itr);
}
}

template<typename BaseNumberTraits>
int parse_first(const char* itr, const char* end)
{
++itr;
if(itr == end){ return 0; } // NG
if(!BaseNumberTraits::acceptable(*itr)){ return 0; } // NG
return parse_last<BaseNumberTraits>(itr, end);
}

int to_i(const char* itr, const char* end)
{
if(itr == end){ return 0; } // NG

int flag = 1;
switch(*itr)
{
case '-':
flag = -1;
// no break needed
case '+':
while(true)
{
++itr;
if(itr == end){ return 0; } // NG
if(*itr != ' '){ break; }
}
break;
}

if(*itr == '0')
{
++itr;
if(itr == end){ return 0; } // OK

if('x' == *itr || 'X' == *itr)
return flag * parse_first<HexadecimalNumber>(itr, end);

if(OctalNumber::acceptable(*itr))
return flag * parse_last<OctalNumber>(itr, end);

if('_' == *itr)
return flag * parse_first<OctalNumber>(itr, end);

if('o' == *itr || 'O' == *itr)
return flag * parse_first<OctalNumber>(itr, end);

if('b' == *itr || 'B' == *itr)
return flag * parse_first<BinaryNumber>(itr, end);

if('d' == *itr || 'D' == *itr)
return flag * parse_first<DecimalNumber>(itr, end);

return 0; // NG
}

if(DecimalNumber::acceptable(*itr))
return flag * parse_last<DecimalNumber>(itr, end);

return 0; // NG
}
}

2009年5月12日火曜日

basic_streambufの継承は面倒?

ちょっとbasic_streambufを継承したくなって見つけた記事


バッファリングしない場合は想像より簡単みたい。

2009年5月11日月曜日

IOCP使ってサーバを作る簡単な方法

the code projectとかsourceforgeとかcodeguruとか、ちょっと探せばIOCPを使うためのフレームワークやチュートリアルは山ほどヒットする。

でも、どれもいまいち。なんとなくしっくりきてなかったのだけれど、もっと身近なところにあったのね。


Windowsの実装では、asio::io_serviceがwin_iocp_io_serviceというクラスに依存していてIOCPによって実現されている。

boostならWindowsでもLinuxでも同じコード使えるし、気持ち的に安心だし、知らなくて損した気分。

Linuxの実装の基盤は何だろう?今度調べてみよう。

C10k問題とかA Design Framework for Highly Concurrent Systemsがふたたび気になり始めたなぁ

メンバ関数で自分自身のshared_ptrを得る

こんな便利な機能があるなんて知りませんでしたよ。
これを知らずにboost::shared_ptr使ってきてたことが、ある意味頑張ってたのかも。

1.38.0の実装では、メンバ変数にweak_ptrを持たせているので、x86だと4バイトのサイズ増加。
このメンバ変数はthisに対するshared_ptrを作成した時に、shared_ptrのコンストラクタで初期化されている。

enable_shared_from_this.hppのコメントに、

「コードじゃなくてドキュメントを読め」

と書いてあって、ちょっと笑えた。

2009年5月4日月曜日

VirtualBox 2.2.2 released !

2009年4月28日に VirtualBox 2.2.2 がリリースされてた。
メンテナンスリリースとはいえ1ヶ月たたないうちにアップデートとは、がんばってる感がビシビシ伝わってくる。
応援したくなるなぁ~

2009年5月3日日曜日

boost::asioを見てみた

boostならばportableだし、知っておいて損はしない。
あまり注目していなかったけれど、ようやくasioを見てみた。
boostなだけあってライブラリのインターフェースは好きになれそう。
非同期サーバの実装の裏側はどうなってるのか興味がわいてきた。

2009年5月1日金曜日

windowsでlibeventしてみた

前から使ってみたいと思っていたlibevent。ようやく試してみる機会があった。

ん~。

当然IOCP対応してるんだろーと思ったらlibevent-1.4.10-stableでは未対応なのね...

libevent-2.0.1-alphaWhat's newにはIOCPに対応始めたみたいなことが書いてある。

vc90でビルドするのも一手間必要。

なんとかregressにパスするバイナリ作れたのでテストコード。

evhttpd.cc
#include <stdio.h>

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>

#include <event.h>
#include <evhttp.h>

#pragma comment(lib, "ws2_32")

struct EnableWinsock
{
EnableWinsock(){
WSADATA wsaData;
const int r = WSAStartup(MAKEWORD(2, 2), &wsaData);
}
~EnableWinsock(){
WSACleanup();
}
};

void root_handler(evhttp_request* req, void* arg)
{
evbuffer* buf = evbuffer_new();
if(!buf){
puts("failed to create response buffer");
return;
}

evbuffer_add_printf(buf, "Hello: %s\n", evhttp_request_uri(req));
evhttp_send_reply(req, HTTP_OK, "OK", buf);
}

void generic_handler(evhttp_request* req, void* arg)
{
evbuffer* buf = evbuffer_new();
if(!buf){
puts("failed to create response buffer");
return;
}

evbuffer_add_printf(buf, "Requested: %s\n", evhttp_request_uri(req));
evhttp_send_reply(req, HTTP_OK, "OK", buf);
}

int wmain(int argc, wchar_t* argv[])
{
EnableWinsock enableWinsock;

event_init();

evhttp* httpd = evhttp_start("0.0.0.0", 8880);
if(!httpd){ return 1; }

evhttp_set_cb(httpd, "/", root_handler, NULL);
evhttp_set_gencb(httpd, generic_handler, NULL);

event_dispatch();

evhttp_free(httpd);

return 0;
}

as3cryptoを使ってみた


ようやくas3cryptoを試してみた。
ドキュメント少ないなぁ・・・と思っていたけれど、始めてみたらDemoソースコードを読めるので難しいことはなかった。
StudyCrypto.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
layout="vertical"
showStatusBar="false"
width="320" height="300"
styleName="study"
borderThickness="1">
<mx:Style>
.study {
paddingBottom: 3;
paddingLeft: 3;
paddingRight: 3;
paddingTop: 3;
}
</mx:Style>
<mx:Script>
<![CDATA[
import com.hurlant.crypto.Crypto;
import com.hurlant.crypto.symmetric.ICipher;
import com.hurlant.crypto.symmetric.IPad;
import com.hurlant.crypto.symmetric.IVMode;
import com.hurlant.crypto.symmetric.PKCS5;
import com.hurlant.util.Base64;
import com.hurlant.util.Hex;

public static function encrypt(name:String, k:String, s:String):ByteArray {
var e:ByteArray = new ByteArray();
var key:ByteArray = Hex.toArray(Hex.fromString(k));
var data:ByteArray = Hex.toArray(Hex.fromString(s));
var pad:IPad = new PKCS5();
var cipher:ICipher = Crypto.getCipher(name, key, pad);
pad.setBlockSize(cipher.getBlockSize());
cipher.encrypt(data);
e.writeObject(data);
if(cipher is IVMode){
var ivmode:IVMode = cipher as IVMode;
e.writeObject(ivmode.IV);
}
e.position = 0;
return e;
}

public static function decrypt(name:String, k:String, e:ByteArray):String {
var data:ByteArray = e.readObject();
var kdata:ByteArray = Hex.toArray(Hex.fromString(k));
var pad:IPad = new PKCS5();
var cipher:ICipher = Crypto.getCipher(name, kdata, pad);
if(cipher is IVMode){
var ivmode:IVMode = cipher as IVMode;
ivmode.IV = e.readObject();
}
cipher.decrypt(data);
return Hex.toString(Hex.fromArray(data));
}

private function run():void {
const name:String = 'aes-cbc';
var e:ByteArray = encrypt(name, key.text, src.text);
o0.text = Base64.encode(Hex.fromArray(e));
o1.text = decrypt(name, key.text, Hex.toArray(Base64.decode(o0.text)));
}
]]>
</mx:Script>
<mx:Form styleName="study" width="100%" cornerRadius="6" borderColor="#218BD5" borderStyle="solid">
<mx:FormItem label="Password" width="100%">
<mx:TextInput width="100%" id="key" change="btn.enabled = (4 &lt;= key.text.length)"/>
</mx:FormItem>
<mx:FormItem label="Input" width="100%">
<mx:TextInput width="100%" id="src"/>
</mx:FormItem>
<mx:FormItem width="100%">
<mx:Button id="btn" label="run" click="run()" enabled="false"/>
</mx:FormItem>
</mx:Form>
<mx:Form styleName="study" width="100%" height="100%" cornerRadius="6" borderColor="#218BD5" borderStyle="solid">
<mx:FormItem label="Encrypt" width="100%" height="100%">
<mx:TextArea width="100%" height="100%" id="o0"/>
</mx:FormItem>
<mx:FormItem label="Decrypt" width="100%" height="100%">
<mx:TextArea width="100%" height="100%" id="o1"/>
</mx:FormItem>
</mx:Form>
</mx:WindowedApplication>