2009年12月17日木曜日

左再帰を取り除くメモ

左再帰
A : A B0
| A B1
...
| A Bn
| C0
| C1
...
| Cn
※1 B0 ... Bn はε(空)ではない
※2 C0 ... Cn は A を先頭に持たない

左再帰を消す
A : C0 A_tail?
| C1 A_tail?
...
| Cn A_tail?

A_tail : B0 A_tail?
| B1 A_tail?
...
| Bn A_tail?

2009年12月4日金曜日

Rubyでメールアドレスかどうか調べたくなってみた

結論→今日はメールアドレスかどうかをちゃんと調べる必要がないので適当にごまかしておく。

「やっとくか〜?」と思ったけれど、やっぱり面倒だった。あはは...

厳密には正しくないけど今はまぁこれで十分な感じの正規表現
valid_chars = '[0-9A-Za-z_!#\$%&*+\-\/=\?^_{|}\~\.]+'
valid_pattern = Regexp.new("^(#{valid_chars})@(#{valid_chars})$", nil, 'n')

ところで元ネタは2002年のRubyなので、Regexp#newのパラメータが現在(Ruby 1.8とか1.9)とは異なるようだ。
元ネタではRegexpの引数が2つになっていて、第2引数は 'n' だ。
この 'n' は文字コード "none" の指定じゃないかと思われる。
Regexpのリファレンスマニュアル見ると第2引数がFixnum以外で真ならIGNORECASEになるって。
当時はオプションの指定ができなかったのだろうか?それとも単なる転記ミス?

2009年11月20日金曜日

The Go Programming Language

Googleで開発中の新しい言語だ〜
The Go Programming Language
ちょっと面白そうかも。

Mac版 Google Chromeアップデートされた

バージョンが4.0.249.0になった。

2009年11月19日木曜日

RubyでGIFを読んでみた

gif.rb
class GIF
attr_accessor :version, :width, :height, :bgcolor_index, :aspect_ratio, :sort_flag
attr_accessor :color_table, :blocks

def self.load(istream)
self.new.load(istream)
end

def self.load_file(filename)
self.new.load_file(filename)
end

def initialize
@blocks = []
end

def [](index)
@blocks[index]
end

def []=(index, block)
@blocks[index] = block
end

def <<(block)
@blocks << block
end

def clear
@blocks = []
@version = @width = @height = @bgcolor_index = @aspect_ratio = @sort_flag =
@color_table = nil
end

def load(istream)
clear
reader = Reader.new(self, istream)
reader.run
end

def load_file(filename)
File.open(filename, 'rb'){|f| load(f) }
end

class GraphicControlExtension
attr_accessor :disposal_method, :user_input_flag, :delay_time, :transparent_index
# case disposal_method
# when 0; # no action is required
# when 1; # do not dispose
# when 2; # restore to background color
# when 3; # restore to previous
# end
end

class CommentExtension
attr_accessor :data
end

class PlainTextExtension
attr_accessor :x, :y, :width, :height, :cell_width, :cell_height
attr_accessor :fgcolor_index, :bgcolor_index
attr_accessor :text
end

class ApplicationExtension
attr_accessor :app_id, :auth_code, :data
end

class ImageBlock
attr_accessor :x, :y, :width, :height, :interlace_flag, :sort_flag, :color_table
attr_accessor :code_size, :data

def decode
LZW.decode(@code_size, @data)
end

def encode(indexes)
largest = indexes.sort[-1]
@code_size = ("%b" % largest).size
@code_size = 2 if 2 > @code_size
@data = LZW.encode(@code_size, indexes)
end

def encode_with_code_size(indexes, code_size)
@data = LZW.encode(@code_size = code_size, indexes)
end
end
end

######################################################################
class GIF::Reader
def initialize(gif, istream)
@gif = gif
@istream = istream
end

def run
read_header
read_blocks
@gif
end

def read_header
sig,
@gif.version, @gif.width, @gif.height,
bits,
@gif.bgcolor_index, @gif.aspect_ratio = read_fmt('a3a3vvCCC')

gctf = 0 != (0b1000_0000 & bits) # global color table flag
cr = (0b0111 & (bits >> 4)) + 1 # color resolution
@gif.sort_flag = 0 != (0b1000 & bits)
gct_size = 2 ** ((0b0111 & bits) + 1) # size of global color table
@gif.color_table = (0...gct_size).map{ read_fmt('CCC') } if gctf
end

def read_blocks
loop do
case block_type = read_fmt('C')[0]
when 0x3B # trailer
break
when 0x21 # Extension
@gif << read_extension_block
when 0x2C # Image
@gif << read_image_block
else
raise "Not Yet Impl : BlockType is 0x#{"%02x" % block_type}"
end
end
end

def read_extension_block
label = read_fmt('C')[0]
case label
when 0xF9 # Graphic Control
read_graphic_control
when 0xFE # Comment Label
read_comment_label
when 0x01 # Plain Text
read_plain_text
when 0xFF # Application Extension
read_application_extension
else
raise 'Not Yet Impl'
end
end

def read_graphic_control
b = GIF::GraphicControlExtension.new
block_size, bits, b.delay_time, b.transparent_index, zero = read_fmt('CCvCC')
b.disposal_method = 0b111 & (bits >> 2)
b.user_input_flag = 0 != 0b10 & bits
transparent_color_flag = 0 != 0b1 & bits
b.transparent_index = nil unless transparent_color_flag
b
end

def read_comment_label
b = GIF::CommentExtension.new
b.data = read_block_data
b
end

def read_plain_text
b = GIF::PlainTextExtension.new
block_size, b.x, b.y, b.width, b.height,
b.cell_width, b.cell_height,
b.fgcolor_index, b.bgcolor_index = read_fmt('CvvvvCCCC')
b.text = read_block_data
b
end

def read_application_extension
b = GIF::ApplicationExtension.new
block_size, b.app_id, b.auth_code = read_fmt('Ca8a3')
b.data = read_block_data
b
end

def read_block_data
bytes = ''
loop do
size = read_fmt('C')[0]
return bytes if size == 0
bytes << read(size)
end
end

def read_image_block
b = GIF::ImageBlock.new
b.x, b.y, b.width, b.height, bits = read_fmt('vvvvC')
lctf = (0 != 0x80 & bits) # local color table flag
b.interlace_flag = (0 != 0x40 & bits)
b.sort_flag = (0 != 0x20 & bits)
lct_size = 2 ** ((0x07 & bits) + 1)
b.color_table = (0...lct_size).map{ read_fmt('CCC') } if lctf
b.code_size = read_fmt('C')[0]
b.data = read_block_data
b
end

def read(size)
bytes = @istream.read(size)
raise 'End of file' if bytes.nil?
raise 'Not enough' if bytes.size != size
bytes
end

def read_fmt(fmt)
read(pack_size(fmt)).unpack(fmt)
end

def pack_size(fmt)
total_size = 0
item_size = 0
fmt.scan(/[a-zA-Z]|[0-9]+/).each do |v|
case v
when 'C'; item_size = 1
when 'a'; item_size = 1
when 'v'; item_size = 2
when /[0-9]+/
item_size *= v.to_i - 1
else
raise 'Not Yet'
end
total_size += item_size
end
total_size
end
end

######################################################################
module GIF::LZW
MAX_NBITS = 12

module_function
def encode(nbits, istring)
GIF::LZW::Encoder.encode(nbits, ArrayReader.new(istring), StringIO.new).string
end

def decode(nbits, istring)
GIF::LZW::Decoder.decode(nbits, StringIO.new(istring), StringIO.new).string
end

def bit_mask(nbits)
(1 << nbits) - 1
end
end

######################################################################
class GIF::LZW::Dictionary
attr_reader :clear_code, :end_code
def initialize(nbits)
dict_size = 1 << nbits
@dict = (0...dict_size).map{|k| [nil, k] }
@clear_code = dict_size
@dict << nil # Clear Code
@end_code = dict_size + 1
@dict << nil # End Code
@code = {}
end

def reset
@dict.slice!(@end_code + 1, @dict.size)
@code = {}
end

def size
@dict.size
end

def add(w, k)
new_code = @code[(w << 8) + k] = @dict.size
@dict << [w, k]
return new_code
end

def code(w, k)
return k unless w
return @code[(w << 8) + k]
end

def string(code)
w, k = @dict[code]
return nil unless k
return string(w) << k if w
return [k]
end
end

######################################################################
# src[0] : <abcde> : 11100
# src[1] : <fghij> : 11000
# src[2] : <klmno> : 10000
#
# dst[0] : <hijabcde> : 00011100
# dst[1] : <.klmnofg> : 01000011
class GIF::LZW::BitWriter
attr_reader :stream

def initialize(stream)
@stream = stream
@value = @nbits = 0
end

def write(value, nbits)
@value |= (GIF::LZW.bit_mask(nbits) & value) << @nbits
@nbits += nbits
while 8 <= @nbits
@stream.write([0xFF & @value].pack('C'))
@value >>= 8
@nbits -= 8
end
end

def flush
if 0 < @nbits
@stream.write([0xFF & @value].pack('C'))
@value = @nbits = 0
end
@stream.flush
end
end

######################################################################
class GIF::LZW::BitReader
attr_reader :stream

def initialize(stream)
@stream = stream
@value = @nbits = 0
end

def read(nbits)
while @nbits < nbits
byte = @stream.read(1)
return nil unless byte
byte = byte.unpack('C')[0]
@value |= byte << @nbits
@nbits += 8
end
v = GIF::LZW.bit_mask(nbits) & @value
@value >>= nbits
@nbits -= nbits
return v
end
end

######################################################################
class GIF::LZW::ArrayReader
def initialize(array)
@array = array
@pos = 0
end

def getc
if @pos < @array.size
pos = @pos
@pos += 1
return @array[pos]
end
end
end

######################################################################
class GIF::LZW::Encoder
# @return ostream
def self.encode(nbits, istream, ostream)
encoder = self.new(nbits, istream, ostream)
encoder.run
end

def initialize(nbits, istream, ostream)
@nbits = nbits
@istream = istream
@bit_writer = GIF::LZW::BitWriter.new(ostream)
@code_size = nbits + 1
@dict = GIF::LZW::Dictionary.new(@nbits)
@w = nil
write_code(@dict.clear_code)
end

def run
while k = @istream.getc
write(k)
end
finish
end

def write(k)
if c = @dict.code(@w, k)
@w = c
return
end

c = @dict.add(@w, k)
write_code(@w)
@w = k

return if (1 << @code_size) != c

if @code_size < GIF::LZW::MAX_NBITS
@code_size += 1
return
end

write_code(@dict.clear_code)
@code_size = @nbits + 1
@dict.reset
end

def finish
write_code(@w) if @w
write_code(@dict.end_code)
@bit_writer.flush
end

def write_code(code)
@bit_writer.write(code, @code_size)
end
end

######################################################################
class GIF::LZW::Decoder
# @return ostream
def self.decode(nbits, istream, ostream)
decoder = self.new(nbits, istream, ostream)
decoder.run
end

def initialize(nbits, istream, ostream)
@nbits = nbits
@bit_reader = GIF::LZW::BitReader.new(istream)
@ostream = ostream
@code_size = nbits + 1
@dict = GIF::LZW::Dictionary.new(@nbits)
end

def run
decode_1st && while decode_2nd; end
@ostream.flush
end

def decode_1st
@w = read_code
@w = read_code while @dict.clear_code == @w
return nil if @w.nil? || @dict.end_code == @w
@ws = @dict.string(@w)
@ostream.write(@ws.pack('C*'))
true
end

def decode_2nd
@code_size += 1 if @dict.size == (1 << @code_size) && @code_size < GIF::LZW::MAX_NBITS
k = read_code

return if k.nil? || @dict.end_code == k

if @dict.clear_code == k
@code_size = @nbits + 1
@dict.reset
return decode_1st
end

ks = @dict.string(k)
ks = @ws + @ws.slice(0, 1) unless ks
@ostream.write(ks.pack('C*'))
@dict.add(@w, ks[0])
@w, @ws = k, ks
end

def read_code
@bit_reader.read(@code_size)
end
end

LZWをRubyで実装してみた

lzw.rb
#
# LZW Algorithm
#
class Dic
def initialize
@dict = (0...256).map{|k| [nil, k]}
@code = {}
end

def add(w, k)
new_code = @code[(w << 8) + k] = @dict.size
@dict << [w, k]
return new_code
end

def code(w, k)
return k unless w
return @code[(w << 8) + k]
end

def string(code)
w, k = @dict[code]
return nil unless k
return string(w) << k if w
return [k]
end
end

def encode(input)
output = []
dic = Dic.new
w = nil
while k = input.shift
c = dic.code(w, k)
if c
w = c
else
output << w
dic.add(w, k)
w = k
end
end
output << w if w
return output
end

def decode(input)
output = []
dic = Dic.new
w = input.shift
return output unless w
ws = dic.string(w)
ws.each{|v| output << v}
while k = input.shift
ks = dic.string(k)
ks = ws + ws.slice(0, 1) unless ks
ks.each{|v| output << v}
dic.add(w, ks[0])
w, ws = k, ks
end
return output
end

encode([0,1,0,1,0,1,0,1]) # => [0, 1, 256, 258, 1]
decode([0,1,256,258,1]) # => [0, 1, 0, 1, 0, 1, 0, 1]

2009年10月14日水曜日

不等号演算を論理積でつなげてみた

if A < B < C
# 本体
end
こんな条件式を書けたら素敵じゃない?

処理の流れは、
  1. Aを評価して(a)に保存する
  2. Bを評価して(b)に保存する
  3. (a) < (b) を評価してfalseなら終了
  4. Cを評価して(c)に保存する
  5. (b) < (c) を評価してtrueなら本体を実行
とする。

Rubyで書くとこんな感じ↓
a = A
b = B
if a < b
c = C
if b < c
# 本体
end
end

Bの評価を1回だけ行うところがミソ。
こんな風に書けたら便利だと思う。

ところで、

A < B < C
A < B <= C
A <= B < C
A <= B <= C
A > B > C
A > B >= C
A >= B > C
A >= B >= C

これらの組み合わせは、並び順が大小関係になっているので認識しやすいと思う。

一方、

A > B && B < C → A > B < C
A > B && B < C → A > B < C
A < B && B < C && C > D && D < E → A < B < C > D < E

こんなのもあったら便利かな?

2項演算子は左辺のオブジェクトのメソッド呼び出しだから、記述できても何ら不都合はないはず。
むしろHaskellのモナドみたいで面白いかもしれないね。

a = A
b = B
if a < b
c = C
if b < c
d = D
if c > d
e = E
d < e # 最後の条件判定の結果は使われていないのでif文を省略
end
end
end

2009年10月11日日曜日

Mac版 Google Chrome使ってみた

バージョンは4.0.221.8
いつもの散歩コースに出てみたけれど
バグっぽい挙動には出会わなかった。
YouTubeも表示できてるし、JSNESのドンキーコングも動かせた。

不満はMicrosoft Wireless Mouse 5000についている「戻る」と「進む」のボタンが使えないことぐらいかな。ショートカットキーは同じなのに、なぜ?

まぁ、もうしばらく使ってみよう。

2009年9月21日月曜日

Treetop - RubyのPEG

これは面白いかも

Treetop is a language for describing languages. Combining the elegance of Ruby with cutting-edge parsing expression grammars, it helps you analyze syntax with revolutionarily ease.

Treetopは言語を記述するための言語だ。Rubyの簡潔さと最先端のParsing Expression Grammarを融合させたもので、構文解析を革新的に容易にする。

http://treetop.rubyforge.org/syntactic_recognition.html

2009年9月16日水曜日

Rubyでwithしてみた

VBで評価の高いWithをRubyで。
とっても簡単。
あらためてRubyすごいなぁ。

sample.rb
def with(instance, &block)
instance.instance_eval(&block)
end

class Hello
def say
puts 'hello world'
end
end

with(Hello.new) do
say
say
say
end

2009年9月3日木曜日

Flash Lite 1.1 で関数

言語で関数を定義することはできないけれど
通常は再生されないフレームにアクションを書いて
call(フレーム名) とする方法が使われる。

しかし、これは関数と言うよりはサブルーチンだ。
普通は再帰呼び出しすることはできない。

「それじゃ、引数や返り値の受け渡しのためにスタックを実装すれば、関数になるのでは?」

と思って作ってみた。

いろいろな実装方法が考えられるけれど、とりあえず次のようなルールとする。
  • 関数への引数は、関数を呼び出す側でスタックに積む。
  • 関数はスタックを参照して引数を得る。
  • 関数は返り値をスタックに積む。
  • 関数呼び出しの後に引数と返り値をスタックから取り除くのは、関数を呼び出す側の責任。

ルートムービークリップのフレーム1のアクション
sp = -1; // スタックポインタの初期化

// 引数をスタックに積む
++sp;
eval( "s" add sp ) = 12;

call ( "fact" ); // 関数呼び出し

// 答えをスタックから取り除く
r = eval ( "s" add sp );
--sp;

trace ( r ); // 確認

stop();

ルートムービークリップのフレーム2のアクション
// 階乗を計算する関数
n = eval ( "s" add sp );
if ( n == 0 ) {
eval ( "s" add sp ) = 1; // 引数をスタックから取り除き、答えをスタックに積む。つまりspは変化しない。
} else {
// 引数をスタックに積む
++sp;
eval ( "s" add sp ) = n - 1;
call ( "fact" );
r = eval ( "s" add sp ); // 関数の答えを取得する。
--sp; // スタックポインタを戻す
n = eval ( "s" add sp ); // n は再帰呼び出しで変化してるので復帰させる。
eval ( "s" add sp ) = r * n; // 引数をスタックから取り除き、答えをスタックに積む。つまりspは変化しない。
}

最後にルートムービークリップのフレーム2に名前"fact"をつけてプレビュー。

479001600

12!(12の階乗)を計算できた。
関数できた。
・・・かなり面倒だな。

Flash Lite 1.1でカレンダー計算:前の日を求める

prevday.as
// 前の日の日付を計算する
//
// @param a0 ... 西暦年 (例: 2009 )
// @param a1 ... 月 (1?12)
// @param a2 ... 日 (1?31)
//
// @return a0 ... 西暦年 (例: 2009 )
// @return a1 ... 月 (1?12)
// @return a2 ... 日 (1?31)
//
--a2;
if (0 >= a2) {
switch (a1) {
case 1:
a0 -= 1; a1 = 12; a2 = 31;
break;
case 2:
case 4:
case 6:
case 8:
case 9:
case 11:
--a1; a2 = 31;
break;
case 3:
a1 = 2;
if ((a0 % 4 == 0 && a0 % 100 != 0) || (a0 % 400 == 0)) { // 閏年
a2 = 29;
} else {
a2 = 28;
}
break;
case 5:
case 7:
case 10:
case 12:
--a1; a2 = 30;
break;
}
}

Flash Lite 1.1でカレンダー計算:次の日を求める

nextday.as
// 次の日の日付を計算する
//
// @param a0 ... 西暦年 (例: 2009 )
// @param a1 ... 月 (1?12)
// @param a2 ... 日 (1?31)
//
// @return a0 ... 西暦年 (例: 2009 )
// @return a1 ... 月 (1?12)
// @return a2 ... 日 (1?31)
//
++a2;
switch (a1) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
if (31 < a2) { ++a1; a2 = 1; }
break;
case 2:
if ((a0 % 4 == 0 && a0 % 100 != 0) || (a0 % 400 == 0)) { // 閏年
if (29 < a2) { ++a1; a2 = 1; }
} else {
if (28 < a2) { ++a1; a2 = 1; }
}
break;
case 4:
case 6:
case 9:
case 11:
if (30 < a2) { ++a1; a2 = 1; }
break;
case 12:
if (31 < a2) { ++a0; a1 = 1; a2 = 1; }
break;
}

Flash Lite 1.1でカレンダー計算:曜日を求める

weekday.as
// 日付から曜日を求める
//
// @param a0 ... 西暦年 (例: 2009)
// @param a1 ... 月 (1?12)
// @param a2 ... 日 (1?31)
//
// @return r0 ... 0?6 {0:日曜, 1:月曜, ... , 6:土曜}
//
if (2 >= a1) { // 1月と2月は前年の13月と14月と見なす
--a0;
a1 += 12;
}
// Zellerの公式の変形式
r0 = (a0+int(a0/4)-int(a0/100)+int(a0/400)+int((13*a1+8)/5)+a2)%7;
if (12 < a1) { // 13月と14月を元に戻す
++a0;
a1 -= 12;
}

SWF4でevalする時のTips

Flash CS4で作成したSWF4のバイトコードを読んでみた。
SWF4はFlash Lite 1.1で作成したプロジェクトの出力フォーマット。

ActionScriptで
eval(varInstName).varname = "value";
このコードは、次のようなバイトコードになる。
1: ActionPush "varInstName"
2: ActionGetVariable
3: ActionPush ":"
4: ActionStringAdd
5: ActionPush "varname"
6: ActionStringAdd
7: ActionPush "value"
8: ActionSetVariable
一方、
eval(varInstName add ":varname") = "value";
同じ効果を持つこのコードは、
1: ActionPush "varInstName"
2: ActionGetVariable
3: ActionPush ":varname"
4: ActionStringAdd
5: ActionPush "value"
6: ActionSetVariable
にコンパイルされるので、":"と"varname"をActionStringAddするコードを1つにできて、swfのバイトコードを6バイト減量できる。
命令数も減るので運がよければ速くなるかも。

ちなみに、
eval("/InstName:varname") = "value";
_root.InstName.varname = "value";
は、同じバイトコードを出力し、
1: ActionPush "/InstName:varname"
2: ActionPush "value"
3: ActionSetVariable
だった。

2009年9月2日水曜日

Zellerの公式

まぁ普通はZellerの公式使ってすっきり書くのでしょうねぇ・・・

weekday.rb
#!/usr/bin/env ruby

# 曜日を求める
# y ... 西暦年
# m ... 月
# d ... 日
def weekday(y, m, d)
if 2 >= m
y -= 1
m += 12
end
(y + (y / 4) - (y / 100) + (y / 400) + (13 * m + 8) / 5 + d) % 7
end

require 'date'

def check
d = Date.new(2000, 1, 1)
50000.times do
unless d.wday == weekday(d.year, d.month, d.day)
puts "#{d} ng"
break
end
d = d.next
end
end

check

2009年9月1日火曜日

曜日を計算する関数を作ってみた

カレンダーライブラリが使えない環境で任意の日付の曜日を求める必要があったので、いろいろごちゃごちゃやっている間に何となくできた関数。

weekday.rb
#!/usr/bin/env ruby

# 曜日を求める
# y ... 西暦年
# m ... 月
# d ... 日
def weekday(y, m, d)
ms = [ 0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5 ]
x = y
if 2 >= m
y -= 1
end
(x + (y / 4) - (y / 100) + (y / 400) + ms[m-1] + (d-1)) % 7
end

require 'date'

def check
d = Date.new(2000, 1, 1)
50000.times do
unless d.wday == weekday(d.year, d.month, d.day)
puts "#{d} ng"
break
end
d = d.next
end
end

check

2009年8月21日金曜日

QRコードをAS3とかJavaScriptとかで実装したライブラリ

これは便利かも。
AS3JavaScript

virtualboxの仮想HDDをwritethroughにしてみた

VirtualBoxの仮想HDDは3つのモードがある。
GUIでは操作できないみたいだけど、コマンドラインで簡単に変更できる。
  • 普通のモード
  • スナップショットを作成できないモード
  • 再起動するともとに戻るモード
VBoxManage modifyhd --type [normal|writethrough|immutable]

仮想化したときのメリットとして、なんでも試してみて気に入らなければ気軽にロールバックしちゃうなんて使い方がある。
でも、ホームディレクトリなんかはロールバックしちゃったらもったいない状況も多々ある訳で、そんなときにスナップショットを作成できないモードにしておく。writethroughモード。

2009年8月19日水曜日

SELinuxのせいでreadline.soをロード失敗してみた

CentOS 5.2 で ruby-1.8.7-p174 をビルド&インストールした時のこと。
irb -rirb/completionしたら下記のエラー。

/usr/local/lib/ruby/1.8/i686-linux/readline.so: /usr/local/lib/ruby/1.8/i686-linux/readline.so: cannot restore segment prot after reloc: Permission denied - /usr/local/lib/ruby/1.8/i686-linux/readline.so (LoadError)

これはSELinuxの設定が原因。

ls -lZ /usr/local/lib/ruby/1.8/i686-linux/*.so

してみたところ、

-rwxr-xr-x 1 user_u:object_r:lib_t root root 116642 8月 19 17:14 readline.so

とのこと。

chcon system_u:object_r:textrel_shlib_t zlib.so

このコマンドを実行して設定を正しくする。

-rwxr-xr-x 1 system_u:object_r:textrel_shlib_t root root 116642 8月 19 17:14 readline.so

2009年8月7日金曜日

壁のばし方で迷路を作ってみた

maze.rb
#!/usr/bin/env ruby
# -*- encoding: utf-8 -*-

require 'rubygems'
require 'win32console'
require 'term/ansicolor'

include Win32::Console::ANSI
include Term::ANSIColor

class Maze
PATHWAY = 1
WALL = 2

attr_accessor :width, :height

def initialize(width, height)
@width = width * 2 + 1
@height = height * 2 + 1
@map = [ PATHWAY ] * @width * @height
@console = Win32::Console.new(STD_OUTPUT_HANDLE)

prologue
dig
epilogue
end

def prologue
@console.Cls
print on_white
dig_edge
end

def epilogue
print reset
@console.Cursor(0, 1)
print ' '
@console.Cursor(@width - 1, @height - 2)
print ' '
@console.Cursor(0, @height + 1)
end

def dig
loop do
w0, w1 = select_next
break unless w0
set_wall(*w0)
set_wall(*w1)
loop do
w0, w1 = candidates(*w1)
break unless w0
set_wall(*w0)
set_wall(*w1)
sleep 0
end
end
end

def select_next
r = walls
loop do
i = rand(r.size)
x, y = r[i]
r.delete_at(i)
z = candidates(x, y)
return z if z
break if r.empty?
end
end

def candidates(x, y)
c = []
f0(c, x + 1, y, x + 2, y)
f0(c, x - 1, y, x - 2, y)
f0(c, x, y + 1, x, y + 2)
f0(c, x, y - 1, x, y - 2)
c[rand(c.size)] unless c.empty?
end

def f0(z, x, y, x1, x2)
if PATHWAY == self[x1, x2]
z << [ [x, y], [x1, x2] ]
end
end

def walls
r = []
0.step(@height - 1, 2) do |y|
0.step(@width - 1, 2) do |x|
r << [x, y] if WALL == self[x, y]
end
end
r
end

def dig_edge
x = @width - 1
@height.times do |y|
set_wall(0, y)
set_wall(x, y)
end

y = @height - 1
@width.times do |x|
set_wall(x, 0)
set_wall(x, y)
end
end

def set_wall(x, y)
@map[x + y * @width] = WALL
@console.Cursor(x, y)
print ' '
end

def [](x, y)
return nil if x < 0 || @width <= x
return nil if y < 0 || @height <= y
@map[x + y * @width]
end
end

m = Maze.new(39, 30)

2009年8月5日水曜日

穴掘り法で迷路を作ってみた

maze.rb
#!/usr/bin/env ruby
# -*- encoding: utf-8 -*-

require 'rubygems'
require 'win32console'
require 'term/ansicolor'

include Win32::Console::ANSI
include Term::ANSIColor

class Maze
OUTSIDE = -1
PATHWAY = 0
WALL = 1

def initialize(width, height)
@console = Win32::Console.new(STD_OUTPUT_HANDLE)
@width = width * 2 + 1
@height = height * 2 + 1
@data = [WALL] * @width * @height
draw_bg
dig
finish
end

def finish
@console.Cursor(0, 1)
print ' '
@console.Cursor(@width + 1, @height)
print ' '
@console.Cursor(0, @height + 1)
end

def dig
x = rand(@width / 2) * 2
y = rand(@height / 2) * 2
loop do
dig_path(x, y)
r = select_next(x, y)
break unless r
set_pathway(*r[0])
x, y = r[1]
end
end

def select_next(x, y)
r = walk(x, y)
until r.empty?
i = rand(r.size)
x, y = r[i]
r.delete_at(i)
s = candidates(x, y)
return s[rand(s.size)] unless s.empty?
end
end

def walk(tx, ty, x = nil, y = nil, fx = nil, fy = nil, r = [])
return if fx == tx && fy == ty
return if PATHWAY != self[tx, ty]
r << [tx, ty] if tx % 2 == 0 && ty % 2 == 0
walk(tx + 1, ty, tx, ty, x, y, r)
walk(tx - 1, ty, tx, ty, x, y, r)
walk(tx, ty + 1, tx, ty, x, y, r)
walk(tx, ty - 1, tx, ty, x, y, r)
r
end

def dig_path(x, y)
set_pathway(x, y)
loop do
s = candidates(x, y)
break if s.empty?
s = s[rand(s.size)]
set_pathway(*s[0])
set_pathway(*s[1])
x, y = s[1]
end
end

def candidates(x, y)
r = []
try_dig(x, y, 1, 0, r)
try_dig(x, y, -1, 0, r)
try_dig(x, y, 0, 1, r)
try_dig(x, y, 0, -1, r)
r
end

def try_dig(x, y, dx, dy, r)
x += dx
y += dy
s = x + dx
t = y + dy
if WALL == self[s, t]
r << [[x, y], [s, t]]
end
end

def [](x, y)
return OUTSIDE if x < 0 || @width <= x
return OUTSIDE if y < 0 || @height <= y
@data[x + y * @width]
end

def set_pathway(x, y)
@data[x + y * @width] = PATHWAY
@console.Cursor(x + 1, y + 1)
print ' '
end

def draw_bg
@console.Cls
(@height + 2).times do
print on_white
print ' ' * (@width + 2)
print reset
puts
end
end
end

#Maze.new(38, 30)
Maze.new(10, 10)

PassengerTempDirを設定したほうが幸せ?

Apache + PassengerをCentOS 5.2で稼働させているのだけれど、たまに、落ちる。
Apacheのerror_logには

The spawn server has exited unexpectedly.

とか

Exception Errno::ENOENT in PhusionPassenger::Rack::ApplicationSpawner (No such file or directory ...

と記録されている。

/tmp/passenger.????

を見てみたら・・・あれ?なんだかキレイ・・・

/tmpなのでファイルを消されちゃっても文句は言えないのかも?

ってなわけで、httpd.conf で PassengerTempDir を勝手にいじられるはずがないところに変更してみた。
この設定でしばらく様子を見てみよう。

せっかくなので、PassengerTempDirは、アプリと同じパーティションのディレクトリを指定。
ファイルアップロードでコピーじゃなくてリネームを使えるかも~?

2009年8月4日火曜日

エラーハンドリングフレームワーク

[cppll:13411] エラーハンドリングフレームワーク
便利かな~
最近C++書いてないなぁ

2009年8月2日日曜日

iPhoneアプリ開発に惹かれてる

Macを手にしてからずいぶん時間が経ってるなぁ。
いまさらだけど、iPhoneアプリ開発に興味が出てきた。
エキスパートの前にまずはビギナーから…

2009年7月29日水曜日

Flash Lite 1.1 のルートムービークリップの変数を動的生成してみた

swfの動的生成はswfmillmingという方法があるけれど、ひな形となるswfの変数だけを動的に変えるだけでよい場合にはswfバイナリを直接変更するという方法もあるという例。

生成した変数はルートムービークリップの変数になるようだ。変数名と同じ変数名でダイナミックTextインスタンスをステージに配置すれば、動的設定した文字列を表示することもできる。

ちなみに、検証にはFlash Lite 1.1で作成したswfを使った。

swf.rb
#!/usr/bin/env ruby
# -*- encoding: utf-8 -*-

require 'stringio'

module SWF
def self.read(src)
header = src.read(9)
x = (header[8] >> 3) * 4 + 5
header_size = ((((8 - (x & 7)) & 7) + x) / 8) + 12 + 5
header << src.read(header_size - 9)
body = src.read
[header, body]
end

class Vars
HD = "\x3f\x03\x00\x00\x00\x00"
BE = "\x96"
ZE = "\x00"
ZB = ZE + BE
ED = "\x00\x1d"

def initialize
@vars = []
end

def add(key, value)
@vars << [key, value]
end

def to_bytes
bytes = StringIO.new
bytes << HD
@vars.each do |key, val|
bytes << BE << [key.size + 2].pack('v') << ZE << key
bytes << ZB << [val.size + 2].pack('v') << ZE << val << ED
end
bytes << ZE
bytes.seek 0
bytes = bytes.read
bytes[2, 4] = [bytes.size - 6].pack('V')
bytes
end

def apply(src_io, dst_io)
vars = to_bytes
header, body = SWF.read(src_io)
header[4, 4] = [header.size + body.size + vars.size].pack('V')
dst_io << header
dst_io << vars
dst_io << body
end
end
end

if __FILE__ == $0
require 'yaml'
require 'optparse'
dat, src, dst = nil
OptionParser.new do |opt|
opt.on('-v VARS.yaml'){|v| dat = v }
opt.on('-i INPUT.swf'){|v| src = v }
opt.on('-o OUTPUT.swf'){|v| dst = v }
opt.parse!(ARGV)
unless dat && src && dst
opt.parse!(["-help"])
end
end
vars = SWF::Vars.new
YAML.load_file(dat).each{|key, value| vars.add(key, value) }
File.open(src, 'rb') do |s|
File.open(dst, 'wb') do |d|
vars.apply(s, d)
end
end
end

2009年7月24日金曜日

Androidのタイトルバーにアイコンを表示してみた

import android.view.Window;
Window w = getWindow();
w.requestFeature(Window.FEATURE_LEFT_ICON);
setContentView(R.layout.search);
w.setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, R.drawable.icon);
とっても簡単。
Window.FEATURE_???を調べたらいろいろできることが増えるのかも。

Eclipse 3.5 Galileo に ADT を入れてみた

Eclipse IDE for Java Developers
Build id: 20090619-0625
Android SDK: 1.5r3

デバッグ実行してブレークポイントで止まるし、特に問題無く動作している。

Android Emulatorの為にSDカードイメージを作ってみた

SDカードイメージを作成するには、2つの方法がある。
  1. Android Virtual Devices Managerを使う方法
  2. コマンドラインプログラムで作成する方法
どちらもできることは同じ。たぶん。

EclipseにADTを導入するとAndroid Virtual Devices Managerが使える。
Android Virtual Devices Managerを使う方が断然楽。

コマンドラインの場合Android SDKのtoolsにあるmksdcardコマンドを使う。

mksdcard.exe -l "ラベル名" サイズ SDカードイメージファイル名

サイズに単位を指定しないと、バイト単位になる。
Kをつけるとキビバイト、Mをつけるとメビバイトになる。

生成されたファイルは生のイメージなのでファイルのサイズは指定したサイズと等しくなる。
大きなサイズを指定すると生成にも時間がかかった。


Android EmulatorのSkinをHT-03Aにしてみた

白も黒もどっちもできる。

参考: Android Wiki

2009年7月22日水曜日

Android NDKってなに?

Androidでネイティブコードを動かすための開発ツールを見つけた。

Android NDKは、Androidアプリ開発者のみんなが、君たちのアプリケーションに、ネイティブコードで書かれたコンポーネントを埋め込むことができるようになるツールだよ。

AndroidアプリはDalvik仮想マシンの上で動くんだけど、このNDKを使えば、CやC++みたいなネイティブコード言語を使ってアプリケーションのパーツを開発できるようになるんだ。

そうすれば、ある種のアプリケーションにはメリットがあるんだ。それらの言語で書かれたコードを再利用することができるとか、時にはスピードアップとかね。

このNDKに入れてあるもの

  • CやC++のソースコードからネイティブコードライブラリを生成する事ができるツールやビルドファイル。
  • ネイティブコードライブラリをAndroidデバイスにデプロイ可能なアプリケーションパッケージファイル(.apks)に埋め込む方法。
  • ネイティブシステムヘッダーやライブラリのセット。これらは将来にわたってAndroid 1.5以降の全てのAndroidプラットフォームでサポートされるよ。
  • ドキュメント、サンプル、それにチュートリアル

このリリースのNDKはARMv5TEのマシン語をサポートしている。それに、安定したlibcのヘッダーファイル(Cライブラリ)やlibm(数学ライブラリ)、JNIインターフェースやその他のライブラリを入れてあるよ。

このNDKは、ほとんどのアプリケーションには役に立たないだろうな。開発者として、メリットとデメリットのバランスを見極める必要があるよ。特に、ネイティブコードを使っても、自動的にパフォーマンスが向上するわけじゃないし、そのくせ必ずアプリケーションを複雑にしてしまうからね。

NDKを使うよい例は、自己完結的で、メモリアロケーションを伴わない、CPUをこき使うような処理だね。例えば、シグナル処理とか物理シミュレーションとかね。なにかのメソッドを単純にCで書き換えたとしても、たいていはたいしてパフォーマンス向上につながらないよ。でもね、NDKは既に世の中に存在するCやC++のコードを再利用する時には効率的な手段になり得るんだ。

注意して欲しいんだけど、NDKを使ってもネイティブコードだけのアプリケーションを作ることはできないよ。AndroidのメインのランタイムはDalvik仮想マシンのままだからね。


2009年7月21日火曜日

HTC-03AでHello, Androidをデバッグ実行してみた

PC: Intel 32bit
OS: WindowsXP SP3
IDE: Eclipse Java EE IDE for Java Developers, Version: 3.4.2 Build id: M20090211-1700
Android SDK: 1.5r3

最初に実機をPCに接続した時、デバッグモードにせずにつないでしまったためか、USB記憶装置として認識されてしまった。デバイスを削除して、再インストールしようとしても、.infファイルのハードウェアの記述が異なるみたいで該当なしと言われる。

同じ状態になってしまった人もたくさんいるみたいで、探してみたらいくつか情報が。


とりあえず、HTCのサイトから
デバイスドライバのアップデータ(?!)をダウンロードして、実行。
実機を接続していなかったので「実機つないでくれんと・・・」と怒られる。

しかし、ここで思い立って、先に、Android SDKに付属の
usb_driver\x86\android_usb\infをインストールしてから、デバッグモードに
した実機をUSBに接続したら、デバイスドライバをインストールするウィザードが開いてくれた。
手動でドライバを選択したら待通りにインストール完了。

Eclipse経由でHello, Androidを動かしてちょっと満足。

デバイスドライバのアップデータを実行したのが吉だったのかな・・。

2009年7月17日金曜日

Rubyの拡張モジュールでリソースを解放する方法

RubyはGCを持っているので、Rubyの世界の中ではリソースの解放をそれほど気にしなくてもよいけれど、Cで記述したRubyの拡張モジュールではそうはいかない。

インスタンス生成で資源を確保し、GCがインスタンスを回収した時に資源を解放するサンプル。

foo.c
#include <ruby.h>
#include <rubysig.h>

static VALUE g_cFoo = Qnil;

struct FooData
{
int x;
int y;
};

static void
Foo_free_data(struct FooData* data)
{
fprintf(stderr, "free 0x%08x\n", data);
#if defined(USE_RUBY_XMALLOC)
ruby_xfree(data);
#else
free(data);
#endif
}

static VALUE
Foo_alloc_data(VALUE self)
{
return Data_Wrap_Struct(self, 0, &Foo_free_data, 0);
}

static struct FooData*
Foo_data(VALUE self)
{
struct FooData* sval = NULL;
Data_Get_Struct(self, struct FooData, sval);
return sval;
}

static VALUE
Foo_initialize(VALUE self)
{
#if defined(USE_RUBY_XMALLOC)
DATA_PTR(self) = ruby_xmalloc(sizeof(struct FooData));
#else
DATA_PTR(self) = malloc(sizeof(struct FooData));
#endif
fprintf(stderr, "alloc 0x%08x\n", DATA_PTR(self));
return self;
}

Init_foo()
{
g_cFoo = rb_define_class("Foo", rb_cObject);

rb_define_alloc_func(g_cFoo, &Foo_alloc_data);
rb_define_method(g_cFoo, "initialize", &Foo_initialize, 0);
}

※注意!このモジュールを使って ruby 1.8.7 p174 {mingw32|linux}で ruby -rfoo -e 'loop{Foo.new}' したら Segmentation faultになった。原因不明>_<

2009年7月16日木曜日

ruby embedded into c++

2003年に書かれたものだから、古いかもしれないけれど、Rubyの拡張モジュールを作成する時の参考になるかもしれない。
http://metaeditor.sourceforge.net/embed/

2009年7月8日水曜日

emacsでruby-modeを使う設定

.emacs
;;;;
;;;; Ruby
;;;;
(add-to-list 'load-path "c:/somewhere/site-lisp")
(autoload 'ruby-mode "ruby-mode"
"Mode for editing ruby source files" t)
(setq auto-mode-alist
(append '(("\\.rb$" . ruby-mode)) auto-mode-alist))
(setq interpreter-mode-alist (append '(("ruby" . ruby-mode))
interpreter-mode-alist))
(autoload 'run-ruby "inf-ruby"
"Run an inferior Ruby process")
(autoload 'inf-ruby-keys "inf-ruby"
"Set local key defs for inf-ruby in ruby-mode")
(add-hook 'ruby-mode-hook
'(lambda ()
(inf-ruby-keys)))

2009年7月7日火曜日

Dokan Ruby 0.1.4 のパッチ

Windows用の仮想ファイルシステムドライバDokanライブラリを使ってみた。
Dokan Rubyを使うとRubyで手軽にファイルシステムを書けてしまうという
素晴らしいライブラリだ。

そのサンプルにmirror.rbというものがある。
任意のフォルダを指定して、ドライブにマウントするサンプルだ。
SUBSTコマンドのようなもの。

しかし、このmirror.rbでマウントしたドライブは、
Explorerでファイルを見ることはできるものの、
コマンドプロンプトで cd FolderName を実行しても
「指定されたパスが見つかりません。」と言われる。

ばぐ?

Dokanライブラリのreadme.ja.txtに答えが書いてあった。

ディレクトリに対するアクセスで有るのにも関わらず,CreateFile が呼ばれることもあります.その場合は,DokanFileInfo->IsDirectory は FALSE がセットされています.ディレクトリの属性を取得する場合などに OpenDirectory ではなく,CreateFileが呼ばれるようです.ディレクトリに対するアクセスなのにも関わらず,CreateFile が呼ばれた場合は,必ず DokanFileInfo->IsDirectory にTRUEをセットしてからreturnしてください.正しくセットされていないと,Dokanライブラリは,そのアクセスがディレクトリに対するアクセスかどうか判断できず,Dokanファイルシステムで Windows に対して正確な情報を返すことが出来なくなります.

mirror.rbはopenでFile.file?(path)の結果を返していて、ディレクトリの場合に対応できていない。
そもそも、dokan_lib.soでFileInfo#directory=が定義されていないし、instans_eval等で無理矢理値を変えたとしても、FileInfoをDokanFileInfoに書き戻すコードは無かった。

dokan_lib.cとmirror.rbを少しだけ書き換えたらうまくいった。めでたしめでたし♪

dokan_lib.c.patch
110a111,118
> static VALUE
> DR_FileInfo_set_directory_p(
> VALUE self,
> VALUE value)
> {
> rb_iv_set(self, "@is_directory", value);
> return self;
> }
151a160
> VALUE is_dir;
154a164,166
>
> is_dir = rb_iv_get(FileInfo, "@is_directory");
> DokanFileInfo->IsDirectory = (Qfalse != is_dir && Qnil != is_dir);
> /* DokanFileInfo->IsDirectory = RTEST(is_dir); */ /* 普通はこう書くのかな? */
173c185,186
< rb_define_method(g_cFileInfo, "directory?", DR_FileInfo_directory_p, 0);
---
> rb_define_method(g_cFileInfo, "directory?", DR_FileInfo_directory_p, 0);
> rb_define_method(g_cFileInfo, "directory=", DR_FileInfo_set_directory_p, 1);
mirror.rb.patch
19c19,24
< File.file?(get_path(path))
---
> s = File.stat(get_path(path)) rescue nil
> if s
> fileinfo.directory = s.directory?
> true
> else
> false
> end

2009年6月29日月曜日

gemの作り方

自前の gem の作り方
その記事の中で
と書いてある。
そのうち調べよう。

set_trace_funcは使ったことがない

Ruby で debug する7つの方法という記事。

set_trace_func は使ったことがない。
間接的にはお世話になってるけれど、直接使ったことはない。

2009年6月23日火曜日

"make install"したソフトウェアを管理できる超便利ツール「Paco」

"make install"したソフトウェアを管理できる超便利ツールPaco
へぇ~
もうちょっと早くきがついていたら、使ってみる気になったかもな~。

CentOS 5.2 で swfmill をソースからビルド

pkg-configが使えなくてちょっと苦しんだ。

libxml2-2.7.3.tar.gz を ./configure ; make ; make install
libxslt-1.1.25.tar.gz を ./configure ; make ; make install
freetype-2.3.9.tar.bz2 を ./configure ; make ; make install
libpng-1.2.37.tar.bz2 を ./configure ; make ; make install

最後に、swfmill-0.2.12.tar.gz を

patch -p 1 <>
export XML_CFLAGS=-I/usr/local/include/libxml2
export XML_LIBS="-L/usr/local/lib -lxml2"
export XSLT_CFLAGS=-I/usr/local/include/libxslt
export XSLT_LIBS="-L/usr/local/lib -lxslt -lexslt"
export FREETYPE_CFLAGS=-I/usr/local/include/freetype2
export FREETYPE_LIBS="-L/usr/local/lib -lfreetype"
export PNG_CFLAGS=-I/usr/local/include/libpng12
export PNG_LIBS="-L/usr/local/lib -lpng"
./configure
make

swfmill.cpp:395: error: ‘xslt_simple’ was not declared in this scope
こんなこと言われたので、vi +395 src/swfmill.cpp して、適当な位置に extern const char* xslt_simple; 追加。

make
su
make install
ldconfig

2009年6月20日土曜日

Hamlしてみた

Haml

なんだか便利そう・・・だけどまだよく分からない。

sample.rb
require 'rubygems'
require 'haml'

e = Haml::Engine.new('%p= foo')
puts e.render(Object.new, :foo => "Hello World")

e = Haml::Engine.new('%p= yield')
puts( e.render do
'Hello World ' * 2
end )

FLEX BUILDER 3 をアップデート

Flex Builder 3 をアップデートしたら 3.0.2.214193 になった。

2009年6月17日水曜日

RoRのActiveRecord

SinatraActiveRecord使いたい。
あ、組み合わせとして使えないのではなくて、使ったことがないだけ。

Sinatraでアップローダー

Rubyスクリプト1つで書けるところが面白い。

app.rb
#!ruby
require 'rubygems'
require 'sinatra'
require 'sinatra/authorization'
require 'erb'
require 'fileutils'

helpers do
def authorization_realm; 'Private Zone'; end

def authorize(login, password)
login == 'id' && password == 'pw'
end
end

module Uploader
attr_reader :upload_path, :upload_dir
def initialize
@upload_path = 'files'
@upload_dir = File.join(Sinatra::Application.public, @upload_path)
end

def files
Dir.entries(upload_dir).select do |x|
!File.directory?(x)
end
end

def floor3(x)
(x * 1000).floor / 1000.0
end

def file_size(x)
s = File.stat(File.join(upload_dir, x)).size
case
when 1024 * 1024 < s
"#{floor3(s/(1024.0 * 1024.0))} M"
when 1024 < s
"#{floor3(s/1024.0)} K"
else
"#{s} bytes"
end
end
end

helpers Uploader

get '/' do
login_required
erb :index
end

post '/upload' do
if params[:file]
save_file = File.join(upload_dir, File.basename(params[:file][:filename]))
File.open(save_file, 'wb'){|f| f.write(params[:file][:tempfile].read)}
end
redirect '/'
end

get '/remove' do
if params[:file]
FileUtils.rm(File.join(upload_dir, File.basename(params[:file]))) rescue nil
end
redirect '/'
end

get '/logout' do
redirect '/' unless Rack::Auth::Basic::Request.new(request.env).provided?
response["WWW-Authenticate"] = %(Basic realm="#{authorization_realm}")
halt 401, 'Authorization Required'
end

get '*' do
redirect '/'
end

__END__

@@ index
<html>
<head>
<title>K</title>
</head>
<body>
<a href="/logout">logout</a>
<form method="POST" action="/upload" enctype="multipart/form-data">
<input type="file" name="file"><input type="submit" value="upload">
</form><%
files.sort.each do |x|
%><a href="<%=escape_html(File.join(upload_path, x))%>"><%
%><%=escape_html(x)%></a> (<%=file_size(x)%>)<%
%> <a href="/remove?file=<%=escape(x)%>">remove</a><br><%
end
%></body>
</html>

SinatraとSession

SinatraでCookieベースのSessionを使って認証。
sinatra-authorizationを調べる前に、とりあえず目的達成できそうな方法を模索してみた。

study_auth.rb
use Rack::Session::Cookie,
# :key => 'rack.session',
# :domain => 'takumakei.blogspot.com',
# :path => '/',
:expire_after => 3600,
:secret => 'changeme'

helpers do
def auth_ok?(id, pw)
id == 'id' && pw == 'pw'
end

def login
if auth_ok?(params['id'], params['pw'])
session[:login] = 'What should i have to put here ?'
redirect '/'
else
erb :login
end
end

def logout
session.delete(:login)
redirect '/'
end

def need_auth
unless session[:login]
erb :login
else
yield
end
end
end
app.rb
#!ruby
require 'rubygems'
require 'sinatra'
require 'erb'
require 'study_auth'

get '/' do
need_auth do
erb :index
end
end

get '/login' do
login
end

post '/login' do
login
end

get '/logout' do
logout
end

get '/*' do
login
end
index.erb
<html>
<head>
<title>index</title>
</head>
<body>
<%=Time.new.to_s%><br/>
<a href="logout">logout</a>
</body>
</html>
login.erb
<html>
<head>
<title>login</title>
</head>
<body>
<form action="login" method="POST">
<input type="text" name="id" value="<%=params['id']%>">
<input type="password" name="pw">
<input type="submit" value="LOGIN">
</form>
</body>
</html>

2009年6月16日火曜日

FlashコンテンツからFlash Lite 1.1コンテンツへの変換

最近の贅沢な高級言語からFlash Lite 1.1初心者になるといろいろ足りなくて困る。
後で読もう。
FlashコンテンツからFlash Lite 1.1コンテンツへの変換

Flash Lite 1.1 でカウントダウンしてみた

CountDown
//////////////////////////////////////////////////////////////////////
// 現在日時からXデーまでのカウントダウン
//
// 結果は4つの変数に算出する。「あと{dT}日と{HT}時間{MT}分{ST}秒」
//dT = 0; // 残り日数
//HT = 0; // 時間
//MT = 0; // 分
//ST = 0; // 秒
//////////////////////////////////////////////////////////////////////
// Xデー
yL = 2009;
mmL = 7;
dL = 7;
HL = 0;
ML = 0;
SL = 0;
//////////////////////////////////////////////////////////////////////
// 現在日時取得
yR =fscommand2("GetDateYear");
mmR = fscommand2("GetDateMonth");
dR = fscommand2("GetDateDay");
HR = fscommand2("GetTimeHours");
MR = fscommand2("GetTimeMinutes");
SR = fscommand2("GetTimeSeconds");
if(!yR){
debug_ = '現在時刻を取得できなかったYo';
stop();
}
//////////////////////////////////////////////////////////////////////
// Xデーを表示する。「年」を表示
yyyy_ = yL;
//////////////////////////////////////////////////////////////////////
// Xデーを表示する。「月」を2桁で表示
if(10 > mmL){
mo_ = '0' add mmL;
}
else{
mo_ = String(mmL);
}
//////////////////////////////////////////////////////////////////////
// Xデーを表示する。「日」を2桁で表示
if(10 > dL){
dd_ = '0' add dL;
}else{
dd_ = String(dL);
}
//////////////////////////////////////////////////////////////////////
// Xデーを表示する。「時」を2桁で表示
if(10 > HL){
hh_ = '0' add HL;
}else{
hh_ = String(HL);
}
//////////////////////////////////////////////////////////////////////
// Xデーを表示する。「分」を2桁で表示
if(10 > ML){
mi_ = '0' add ML;
}else{
mi_ = ML;
}
//////////////////////////////////////////////////////////////////////
// Xデーを表示する。「秒」を2桁で表示
if(10 > SL){
ss_ = '0' add SL;
}else{
ss_ = SL;
}
//////////////////////////////////////////////////////////////////////
// Xデーを2000年1月1日からの経過秒数に変換する。dtLが結果。
dtL = 0;
for(i = 2000; i < yL; ++i){
if((i % 4 == 0) ? ((i % 100 == 0) ? (i % 400 == 0) : true) : false){
dtL += 366;
} else {
dtL += 365;
}
}
switch(mmL){
case 2: dtL += 31; break;
case 3: dtL += 59; break;
case 4: dtL += 90; break;
case 5: dtL += 120; break;
case 6: dtL += 151; break;
case 7: dtL += 181; break;
case 8: dtL += 212; break;
case 9: dtL += 243; break;
case 10: dtL += 273; break;
case 11: dtL += 304; break;
case 12: dtL += 334; break;
}
if(2 < mmL && ((yL % 4 == 0) ? ((yL % 100 == 0) ? (yL % 400 == 0) : true) : false)){
dtL += 1;
}
dtL += dL;
dtL = (dtL * 24 * 60 * 60) + (HL * 60 * 60) + (ML * 60) + SL;
//////////////////////////////////////////////////////////////////////
// 現在日時を2000年1月1日からの経過秒数に変換する。dtRが結果。
dtR = 0;
for(i = 2000; i < yR; ++i){
if((i % 4 == 0) ? ((i % 100 == 0) ? (i % 400 == 0) : true) : false){
dtR += 366;
} else {
dtR += 365;
}
}
switch(mmR){
case 2: dtR += 31; break;
case 3: dtR += 59; break;
case 4: dtR += 90; break;
case 5: dtR += 120; break;
case 6: dtR += 151; break;
case 7: dtR += 181; break;
case 8: dtR += 212; break;
case 9: dtR += 243; break;
case 10: dtR += 273; break;
case 11: dtR += 304; break;
case 12: dtR += 334; break;
}
if(2 < mmR && ((yR % 4 == 0) ? ((yR % 100 == 0) ? (yR % 400 == 0) : true) : false)){
dtR += 1;
}
dtR += dR;
dtR = (dtR * 24 * 60 * 60) + (HR * 60 * 60) + (MR * 60) + SR;
//////////////////////////////////////////////////////////////////////
// Xデーと現在日時の差を計算して、{dT}日{HT}時間{MT}分{ST}秒にする。
if(dtL < dtR){
debug_ = 'もう過ぎてるYo';
stop();
}
dtT = dtL - dtR;
dT = int(dtT / (24 * 60 * 60));
HT = int((dtT / (60 * 60)) % 24);
MT = int((dtT / 60) % 60);
ST = int(dtT % 60);
//////////////////////////////////////////////////////////////////////
// Xデーまでの「日数」を4桁で表示
if(10 > dT){
dT_ = '000' add dT;
}
else if(100 > dT){
dT_ = '00' add dT;
}
else if(1000 > dT){
dT_ = '0' add dT;
}
else{
dT_ = String(dT);
}
//////////////////////////////////////////////////////////////////////
// Xデーまでの「時間」を2桁で表示
if(10 > HT){
HT_ = '0' add HT;
}
else{
HT_ = String(HT);
}
//////////////////////////////////////////////////////////////////////
// Xデーまでの「分」を2桁で表示
if(10 > MT){
MT_ = '0' add MT;
}
else{
MT_ = String(MT);
}
//////////////////////////////////////////////////////////////////////
// Xデーまでの「秒」を2桁で表示
if(10 > ST){
ST_ = '0' add ST;
}
else{
ST_ = String(ST);
}
//////////////////////////////////////////////////////////////////////

QRコードを表示してみた


QRcodeG.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
layout="vertical"
showStatusBar="false" paddingBottom="3" paddingLeft="3" paddingRight="3" paddingTop="3"
width="174" height="188"
minWidth="174" minHeight="188">
<mx:Script>
<![CDATA[
private const GOOGLE_CHART:String
= 'http://chart.apis.google.com/chart?chs=150x150&cht=qr&chl=';
private function create():void
{
var url:String = GOOGLE_CHART + encodeURIComponent(txt_.text);
qrImage_.scaleContent = true;
qrImage_.source = url;
}
]]>
</mx:Script>
<mx:HBox width="100%">
<mx:TextInput id="txt_" width="100%"/>
<mx:Button label="QR" click="create()"/>
</mx:HBox>
<mx:HBox width="100%" height="100%" horizontalAlign="center" verticalAlign="middle">
<mx:Image id="qrImage_" width="100%" height="100%"
source="icons/icon_128.png" scaleContent="false"
autoLoad="true" verticalAlign="middle" horizontalAlign="center"/>
</mx:HBox>
</mx:WindowedApplication>

2009年6月12日金曜日

Flash Lite 1.1 遊び始めました

Flashで遊び初めて2日目。
対象デバイスは携帯なので、Flash Lite。
複数バージョン存在するけれど、ひとつひとつ対応していられないのでFlash Lite 1.1をターゲットにするのが現時点での最適解みたい。
しかし、なんと制約が多いことか。
Flashの動的生成とか動的合成が不可欠なのね。

Sinatra + Passenger

さっそく遊び始めた。

2009年6月9日火曜日

Sinatra + Passenger

最近気になってること

2009年6月4日木曜日

ActionScriptでISO8601をDateに変換してみた

ちゃんとテストしてない。ていうか、ほとんどテストしてない。そのうえ作る時に考えてない。
parseIso8601
private static const ISO8601_RE:RegExp
= /^(\d{4})(-?(((\d{2})(-?(\d{2}))?)|(\d{3})|(W(\d{2})-?(\d)))([T ](\d{2})(:?(\d{2})(:?(\d{2})([,\.](\d+))?)?)?(Z|(([+-])(\d{2})(:?(\d{2}))?))?)?)?$/;
public static function parseIso8601(s:String):Date
{
var year:int = 1970;
var month:int = 0;
var date:int = 1;
var hour:int = 0;
var minute:int = 0;
var second:int = 0;
var millisec:int = 0;
var offset:int = 0;
var m:Array = s.match(ISO8601_RE);
if(!m) return null;
if(m[1]) year = int(m[1]);
if(m[3]) date = int(m[3]);
if(m[5]) month = int(m[5]) - 1;
if(m[7]) date = int(m[7]);
if(m[10] && m[11]){
var first:Date = new Date(year, 0, 1);
var w:int = (first.day == 0 ? 6 : first.day - 1);
date = 8 - w + (int(m[11]) - 1) + 7 * (int(m[10]) - 2);
}
if(m[13]) hour = int(m[13]) % 24;
if(m[15]) minute = int(m[15]);
if(m[17]) second = int(m[17]);
if(m[19]) millisec = int(m[19]);
if(m[20] && m[20] == 'Z')
return new Date(Date.UTC(year, month, date, hour, minute, second, millisec));

if(m[23]) offset = int(m[23]) * 60;
if(m[25]) offset += int(m[25]);
if(m[22] && m[22] == '-') offset = -offset;

offset *= 60 * 1000;
return new Date(Date.UTC(year, month, date, hour, minute, second, millisec) - offset);
}

列挙型がないAS3で列挙を使う


「ActionScript 3.0 は、C++ の enum キーワードや Java の Enumeration インターフェイスとは異なり、特定の列挙機能をサポートしません」

と書いてある。さらに、代替手段も記載されている。

一つ目の方法は「staticなパブリック定数(String型かint型かuint型)を定義する」というもの。

Flex3のイベント名が類似品だろう。
この方法だと、定義していない列挙をうっかり使ってしまったり、既に定義してある列挙値と同じ列挙値をうっかり違うものと思い込んで使ったりしてしまうという心配がある。

二つ目の方法は「staticなパブリック定数にクラスのインスタンスを入れる」というもの。

これを使ったコードはまだ見たことがなかったし、考えもしなかったのでなるほどと思った。
この方法だと、値のセットを任意の新しい型として作ることができる。
それでも、任意の列挙値を後から作れてしまう。
C++だとコンストラクタをprivateにしてインスタンスを作れないようにもできるのだが・・・。
day
public final class Day {
public static const MONDAY:Day = new Day();
public static const TUESDAY:Day = new Day();
...
}
nasty day
public final class NastyDay {
public static const GALACTICDAY:Day = new Day();
}

2009年6月3日水曜日

AIRでタスクバーに表示されないアプリケーションを作る


ウィジェットをAIRで作ってみたくなった。

ウィジェットがタスクバーに表示されていたら邪魔だから、タスクバーに表示されないようにしたい。
そのためにはウィンドウのtypeをlightweightにすればよい。

  1. ベースを<mx:Window>にしたカスタムコンポーネントを作りtype="lightweight"にする。
  2. (1)が閉じられたらアプリケーションを終了させるコードを書く
  3. アプリケーションファイルを<mx:Application>にして(1)を表示する

ウィンドウの位置を動かすためにnativeWindow.startMove()をmouseDownイベントのハンドラで呼び出す。このときmouseDownをカスタムコンポーネントで拾うと、イベントバブリングがあるので、全ての子コンポーネントのmouseDownを拾ってしまう。解決方法は、イベントバブリングを中断するという方法もあるのだろうけれど(...調べてないからやり方は分からない)、mouseDownを受け取るコンポーネントを配置する方法のほうが簡単だと思う(...知識は少なくても解決できたから^^)。

ちなみに、<mx:Application>を使う理由は、Flex Builder 3でAIRアプリのプロジェクトを作ると、アプリケーションファイルは<mx:WindowedApplication>になるのだが、このときウィンドウのtypeをlightweightにする方法を見つけられなかったから。

WidgetWindow.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Window xmlns:mx="http://www.adobe.com/2006/mxml"
maximizable="false"
minimizable="false"
resizable="false"
systemChrome="none"
showFlexChrome="false"
transparent="true"
type="lightweight"
layout="absolute"
width="100"
height="100"
close="exit()">
<mx:Script>
<![CDATA[
private function exit():void
{
NativeApplication.nativeApplication.exit(0);
}
]]>
</mx:Script>
<mx:Canvas width="100%" height="100%"
cornerRadius="8" borderStyle="solid" backgroundColor="#F3F3D1"
mouseDown="nativeWindow.startMove()" backgroundAlpha="0.7"/>
<mx:VBox horizontalCenter="0" verticalCenter="0" horizontalAlign="center">
<mx:Label text="Hello world"/>
<mx:Button label="close" click="exit()"/>
</mx:VBox>
</mx:Window>

Widget.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
initialize="init()">
<mx:Script>
<![CDATA[
private function init():void
{
new WidgetWindow().open();
}
]]>
</mx:Script>
</mx:Application>

2009年6月1日月曜日

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>

2009年4月30日木曜日

DLLからDEFファイルを作る

DLLファイルからDEFファイルを自動生成する必要があって、MinGWのpexportsを持っていなかったので、さくっと書いてみた。

dll_to_def.rb
#!ruby -Ku
# Generate the .def file from .dll using 'dumpbin.exe'
# 2009-04-30 23:43:20

class ExportsList
FUNCTION_LINE_PATTERN = /^\s+(\d+)\s+[0-9A-Fa-f]+\s+[0-9A-Fa-f]+\s+(.*)$/

def initialize(dllname)
@dllname = dllname
@max_name_length = 0
@exports = []
dumpbin(dllname) do |line|
next unless line =~ FUNCTION_LINE_PATTERN
@exports << [$1, $2]
@max_name_length = $2.length if @max_name_length < $2.length
end
end

def save_as_def
width = ((@max_name_length + 7) / 8) * 8
basename = File.basename(@dllname, '.dll')
File.open(basename + '.def', 'w') do |f|
f.puts(%Q|LIBRARY "#{basename}"|)
f.puts
f.puts('EXPORTS')
@exports.each do |i, name|
f.print("\t#{name}")
f.print("\t" * ((width + 7 - name.length) / 8))
f.puts("@#{i}")
end
end
end

private
def dumpbin(dllname)
raise "Command not found: dumpbin" unless cmd_exist?('dumpbin')
Kernel.open("|dumpbin /exports #{dllname}") do |f|
while line = f.gets
yield(line)
end
end
end

def cmd_exist?(cmd)
ENV['PATH'].split(';').each do |path|
(';' + ENV['PATHEXT']).split(';').each do |ext|
pathname = File.join(File.expand_path(path), cmd + ext)
return true if File.exist?(pathname)
end
end
nil
end
end

if $0 == __FILE__
ARGV.each do |dllname|
ExportsList.new(dllname).save_as_def
end
end

2009年4月28日火曜日

そろそろProcessingはじめてみる?


Quartz Composerにも興味津々だけど、その前にProcessingしてみる?