2016年 01月 20日

Ruby の pack テンプレート文字列からそのデータサイズを求める

pack のテンプレート文字列から、それを使ってパックした結果のサイズを求めたいということはありませんか。つまりやりたいことは sizeof(struct x) です。

pack テンプレート文字列は割と複雑で、任意長や文字列やポインタなどがあり、全てにおいてうまく動作するものを作るのは無理ですが、だいたいうまくいくのは実装できそうです。

sizeof(template)

def sizeof(template)
	x = Class.new(Numeric) do
		def to_str; ""; end
		def to_int; 0; end
		# implicit to_f is called only with Numeric subclass
		def to_f; 0.0; end
	end.new
	size = template.scan(/([a-zA-Z][_!]?[<>]?)([0-9]*)/).reduce(0) {|r,(_,n)|
		r + (n.empty?? 1 : n.to_i)
	}
	# p [template, size]
	Array.new(size) { x }.pack(template).size
end

やってることは「とりあえず pack してみる」ですが、pack するためには、まずテンプレート文字列に応じて、適当な型と適当な長さの配列が必要になります。

適当な長さという点では多い分には問題ないので、適当に数えています。

適当な型というのは、厳密にやると結局テンプレート文字列をパースするのと同じぐらい面倒なので、pack が要求するデータ型すべてに暗黙的に変換可能なオブジェクトを作っています。

pack が要求する型は整数・文字列・浮動小数点数があります。整数は to_int、文字列は to_str を実装すると、Ruby はそのオブジェクトについて整数・文字列と同等に扱う(必要なら暗黙的に変換される) ことになっています。

浮動小数点数にはそういったどんなオブジェクトも浮動小数点数として扱えるようにする、というメソッド名がないのですが、Numeric のサブクラスであって to_f が実装されている場合には暗黙的に変換されるというルールがあるので、ベースクラスを Numeric にしています。

[
	"C",
	"S",
	"L",
	"Q",

	"c",
	"s",
	"l",
	"q",

	"S_", "S!",
	"I", "I_", "I!",
	"L_", "L!",
	"Q_", "Q!",

	"s_", "s!",
	"i", "i_", "i!",
	"l_", "l!",
	"q_", "q!",

	"S>", "L>", "Q>",
	"s>", "l>", "q>",
	"S!>", "I!>",
	"L!>", "Q!>",
	"s!>", "i!>",
	"l!>", "q!>",

	"S<", "L<", "Q<",
	"s<", "l<", "q<",
	"S!<", "I!<",
	"L!<", "Q!<",
	"s!<", "i!<",
	"l!<", "q!<",

	"n",
	"N",
	"v",
	"V",

	"U",
	"w",

	"D", "d",
	"F", "f",
	"E",
	"e",
	"g",
	"G",

	"A",
	"a",
	"Z",
	"B",
	"b",
	"H",
	"h",
	"u",
	"M",
	"m",

#	"P",
#	"p",
#	"@",
#	"X",
#	"x",

	"C255",
	"i!2s!2",
	"i!i!s!s!",
].each do |tmpl|
	next if tmpl =~ /q/i
	puts "sizeof(%p) = %d" % [tmpl, sizeof(tmpl)]
end
sizeof("C") = 1
sizeof("S") = 2
sizeof("L") = 4
sizeof("c") = 1
sizeof("s") = 2
sizeof("l") = 4
sizeof("S_") = 2
sizeof("S!") = 2
sizeof("I") = 4
sizeof("I_") = 4
sizeof("I!") = 4
sizeof("L_") = 8
sizeof("L!") = 8
sizeof("s_") = 2
sizeof("s!") = 2
sizeof("i") = 4
sizeof("i_") = 4
sizeof("i!") = 4
sizeof("l_") = 8
sizeof("l!") = 8
sizeof("S>") = 2
sizeof("L>") = 4
sizeof("s>") = 2
sizeof("l>") = 4
sizeof("S!>") = 2
sizeof("I!>") = 4
sizeof("L!>") = 8
sizeof("s!>") = 2
sizeof("i!>") = 4
sizeof("l!>") = 8
sizeof("S<") = 2
sizeof("L<") = 4
sizeof("s<") = 2
sizeof("l<") = 4
sizeof("S!<") = 2
sizeof("I!<") = 4
sizeof("L!<") = 8
sizeof("s!<") = 2
sizeof("i!<") = 4
sizeof("l!<") = 8
sizeof("n") = 2
sizeof("N") = 4
sizeof("v") = 2
sizeof("V") = 4
sizeof("U") = 1
sizeof("w") = 1
sizeof("D") = 8
sizeof("d") = 8
sizeof("F") = 4
sizeof("f") = 4
sizeof("E") = 8
sizeof("e") = 4
sizeof("g") = 4
sizeof("G") = 8
sizeof("A") = 1
sizeof("a") = 1
sizeof("Z") = 1
sizeof("B") = 1
sizeof("b") = 1
sizeof("H") = 1
sizeof("h") = 1
sizeof("u") = 0
sizeof("M") = 47
sizeof("m") = 0
sizeof("C255") = 255
sizeof("i!2s!2") = 12
sizeof("i!i!s!s!") = 12

Ruby でネストした構造体文字列をネストした配列として unpack する

Cの構造体とかだと、構造体の中に他の構造体ということは普通にあります。

こういった構造体の文字列を unpack すると、全部フラットな配列になってしまうので、Ruby レベルのオブジェクト構造として再構成しようと思うと面倒なことになります。

ということで、入れ子に対応した unpack というのを実装してみました。

概要

以下のような感じで {} (ブレース) で入れ子を表現するようにテンプレート文字列を拡張します。

struct_foo_t = %{
	I!
	I!
	Z16
}

struct_bar_t = %{
	I!
	I!
	{
		#{struct_foo_t}
	}
	I!
} #=> "I!I!{I!I!Z16}I!"

original = [
	0xffff,
	0xfeff,
	[
		0x11,
		0x22,
		"foobar"
	],
	0x10,
]

packed = original.pack_deeply(struct_bar_t)
p packed
#=> "\xFF\xFF\x00\x00\xFF\xFE\x00\x00\x11\x00\x00\x00\"\x00\x00\x00foobar\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00"

unpacked = packed.unpack_deeply(struct_bar_t)
p unpacked
#=> [65535, 65279, [17, 34, "foobar"], 16]

p original == unpacked
#=> true

実装

先頭から unpack して入れ子を処理していくというイメージです。処理済みのバイト長が欲しいので、クソみたいですが一旦 unpack したものをもう一度 pack しています。

pack_deeply は単にフラットにして pack するだけなので簡単です。

class String
	def unpack_deeply(template)
		ret = []
		tree = [ ret ]
		n = 0
		tmpl = ""

		# unpack partial and add to result
		unpack = lambda {
			unpacked = self.slice(n..-1).unpack(tmpl)
			# re-pack to get byte length
			size = unpacked.pack(tmpl).size
			n += size
			tmpl = ""
			tree.last.concat(unpacked)
		}

		template.each_char do |chr|
			case chr
			when "{"
				unpack.call
				ary = []
				tree.last << ary
				tree << ary
			when "}"
				unpack.call
				tree.pop
			else
				tmpl << chr
			end
		end
		unpack.call
		ret
	end
end

class Array
	def pack_deeply(template)
		flatten.pack(template.gsub(/[{}]/, ''))
	end
end
2013年 12月 28日

Linux 上の LL で I2C する (Ruby)

なんとなく i2c-dev.h とかで定義されているAPIを呼ばないと使えないのかなあと思っていたけど、デバイスファイルの読み書きと ioctl だけで普通に使うことができた。

書きこみ

  • /dev/i2c-1 とかを rw+ で open
  • ioctl で I2C_SLAVE コマンドを使ってスレーブアドレスを指定する
  • write する

I2C 的には START -> SLAVE + W -> DATA送信 -> STOP が行われる。

読みこみ

  • /dev/i2c-1 とかを rw+ で open
  • ioctl で I2C_SLAVE コマンドを使ってスレーブアドレスを指定する
  • 読みこみたいアドレスを write する
  • read す

I2C 的には START -> SLAVE + W -> DATA送信 -> ReSTART -> SLAVE + R -> DATA受信 -> STOP が行われる。

たぶん一度 write/read するたびに Repeated Start が送られるのかな。

Ruby での例

Ruby でやる場合以下のような感じでいけた。デバイスによってはうまくいかないかもしれない。

class I2CDevice
	# ioctl command
	# Ref. https://www.kernel.org/pub/linux/kernel/people/marcelo/linux-2.4/include/linux/i2c.h
	I2C_SLAVE       = 0x0703

	attr_accessor :address

	def initialize(address)
		@address = address
	end

	def i2cget(address)
		i2c = File.open("/dev/i2c-1", "r+")
		i2c.ioctl(I2C_SLAVE, @address)
		i2c.write(address.chr)
		ret = i2c.read(1).ord
		i2c.close
		ret
	end

	def i2cset(*data)
		i2c = File.open("/dev/i2c-1", "r+")
		i2c.ioctl(I2C_SLAVE, @address)
		i2c.write(data.pack("C*"))
		i2c.close
	end
end

2006年 07月 28日

開発用プロキシ鯖を設定より規約に

proxy.rb, proxy-config.yaml

とかいろいろあるやつを作ってみた。なんでそのまま使わないかっていうと、いちいち設定書くのが面倒くさかった気分。

設定は殆ど読まず、スクリプト単体で殆ど動くように。デフォルトでは files ディレクトリ以下に書き換え用のファイルを置く。

リクエストが example.com/css/base.css であれば、以下のようなファイルを探して置き換える。

  1. files/example.com/css/base.css
  2. files/example.com/base.css
  3. files/base.css

ma.la さんのソースでなんで :ProxyContentHandler を使わないんだろうって少し思ったけど、たぶん向こうの鯖へのアクセス数を減らしてレスポンスをよくするためっぽい? 全部置き換えちゃうわけだし、こっちのほうがいいので、このスクリプトもそうしてる。その辺はまるパクり!

あと流行りの Pathname を使ってみた。らくちん。


さらに規約を設定にしたら、設定より規約ってハードコーディングじゃね?みたい感じをなくせそう。

実装を見直し、規約を設定にした。好きな規約を設定して設定より規約なことができます (意味不明)

実装の見直しでは、完全にサブクラスかみたいな感じにして説明するのめんどい。

2006年 06月 24日

Rails 入門, DB/ORM

DHH 本がきたので、ちゃんと入門するよ!

ところで全然どうでもいいけど、本を読むと本のマジックというかなんというか、読んでいる間は「うわすげぇー簡単じゃーん」とかわかった気になるんだけど、実際のケース (今作ろうとしているのも) に当てはめようとするとことごとくうまく行かないという泣きたくなる情況が頻発して凹む。

とりあえず多対多の場合 ActiveRecords は id カラムを持たないテーブルを一個仲介役に作ってあげるみたい。んでテーブル名は二つのテーブルをアンダースコアでつなげたものにするらしい。(244 ページ, has_and_belongs_to_many)

それで、一個疑問は解けたけど、実際にやってみようとすると問題がまたでてくる。例えば vote っていうのはそれぞれの Topic に対して User がそれぞれ投票できるような感じ (Topic と User が多対多で、vote という意味で繋がってる) だけど、っていうかコード書いてみよう。

class Topic < ActiveRecord::Base
has_and_belongs_to_many :users
end
class User < ActiveRecord::Base
has_and_belongs_to_many :topics
end

とかやると vote っていう名前が一切でてこなくなる。

でもってどうするんのよ!って聞いてみたけど、要は has_and_belongs_to_many のリファレンス嫁ってことらしく読んでみる。実際実行して試したいけど、どうやって試せばいいか分からない。うーん気持ち悪い

下のようにするっぽい?

class Topic < ActiveRecord::Base
has_and_belongs_to_many :votes,
:join_table => "topic_votes"
:class_name => "User",
:foreign_key => "user_id"
end
class User < ActiveRecord::Base
has_and_belongs_to_many :voted_topics,
:join_table => "topic_votes"
:class_name => "Topic",
:foreign_key => "topic_id"
end

この has_* 系メソッドの第一引数は、そのまま@をつけてインスタンス変数名になるのかな。そうっぽいけど明確に書いてあるリソースが見つけられない。ソース嫁ってか。


それにしてもテーブル作って、generate model した直後でテストが通らない。もっと小さいアプリケーションからやるべきだなぁ……でもそういうアプリケーションで作りたいのがない。

2006年 06月 21日

Rails ができない

データベースの設計ができないからなんだと思う。Rails ってまずはじめにデータベース設計ありき、みたいな雰囲気があって、どうしたらいいかわからない。きっとこの辺が分かれば高速に開発できるのだろうけど、いかんせんデータベースって「レスポンスがいいけど面倒くさい」というイメージがずっとある。

というか実際問題面倒くさい。ロックが効かないシステム (学校の鯖) でやろうとするとデータベース使うって段階で既に躓く。自宅鯖でやるとなると、デーモンが一個動いたりするから、セキュリティ云々で面倒くさい (SQLite を使うのはスタンダードじゃなくて躓く)。セキュリティ云々が面倒くさいとかいう人間に公開サーバーを運用する資格なんてないので、この辺で脱落する。公開できないサービスなんて面白くない。だからといって開発用に鯖を借りる金は出てこない。そこまでモチベーションがあるわけじゃない。

データベースありき。データベース側からやっていくと、そのものの表面的カタチ (インターフェイス) がなかなか見えてこなくてつまらないし、いざインターフェイスを作る段階になると、ロジックで疲れ果てててきとーなモノになってしまう (「よしじゃあ rhtml と CSS 書こうか、でももう一応できてる (scaffold のおかげ) しなぁ、めんどいからいいや」みたいな)。

ユーザーインターフェイスなんて後からどうにでもなる、わけない。最初に作ったイメージはあとあとまで引き継がれてしまう。最初のできが悪いと、ずっと悪いまま。すごく優秀な「別の」人が関わらない限り、それは直らない。(自分としてはこういう新しい視点を提供してくれる頭のいい女の子に憧れる。関係ない)

CSS 書くときと、プログラムのロジックを書くときの脳みその状態が全然違う。切り替えるのが面倒くさい。先に見た目を作ると、あとで「あーこれが本当に動いたらなぁ」なんて気分になってロジック書く気になるけど、逆だとならない。

根本的に何か勘違いしている可能性がありまくるけど、現状だといまいちやる気にならない。今作りたいものはあるけど、データベース設計で既に躓いている (だいたいテーブルの数が5個ぐらい)。だからとりあえず UI だけ作ろうかと思った。そのうち。いつか。

2006年 06月 20日

スローライフしたい。

throw :life
#=> ThreadError : "uncaught throw `life' in thread 0x854b6a8"

スローライフの意味がやっとわかった。@IRC by mayuki さん

2006年 03月 11日

Ruby, collect or map

ずっと collect を使ってきたんだけど、マイノリティなのか。

今日 Symbol#to_proc 関連で map をよく見かけたから、map ってなんだっけーとか思いつつリファレンスをひいたら、あら不思議、collect のシノニムだった。IRC でそんなこと言ってたら「collect ってむしろなんだよ」的で驚いた。

map のほうが短くていいからこれからは map にしようかと思いつつ、やっぱどうも map に慣れないいやそれは当たり前だけど。

そもそもなんで collect を使い始めたんだろう。謎だ。リファレンスマニュアルで map より上にあるからだろうか。

あーそうだ。一応忘れないように印象を書いておいてみる。ちなみに俺は数学がわからんちんな坊やなので、map が写像だとかってのは微塵も知りませんでした。いやもだめすぎなんですが、その辺を考慮してください (謎)。

map ってなんか戻り値が予想できない感じがする。配列の各要素にブロックを適用する (脳内での map のイメージは適用だった)、までは名前から分かるのだけれど、そのあとどうやって返ってくるのかが直感的にわからなかった。

対して collect は、集めて配列にするみたいなことが名前からわかるので戻り値は直感的だった。ついでに配列のメソッドにブロックを与えている時点でブロックを適用する感じを醸し出していたので、動作が予想できる感じ。

書くなら断然 map のほうが楽だなぁ……

ちなみに collect, find, select を使ってる。find_all じゃなくてなぜか select, detect じゃなくてなぜか find。この場合 finddetect にして語尾を統一すべきか。

マイノリティじゃないかも。

Rubyist Magazine - Rubyist Hotlinks 【第 1 回】 最初は map はなかったみたいだ。だから collect のほうが上にあるのか。

2006年 03月 10日

ログインに使ってるスクリプト

weblogin.rb, 名前失敗したけどもういいや。

そのうち忘れるので使い方をめもる。

@cgi.header を使うことを前提にしてる。

api_keys = {
"Flickr" => {
"api_key" => "Flickr70c55b82e10021ffaaapi_key6",
"secret"  => "Flickrdf5secret3"
},
"TypeKey" => "fsakfsTypeKeyafasAPI"
}
require "weblogin.rb"
@cgi = CGI.new
WebLogin.open(@cgi, api_keys) do |login|
begin
case login.auth
when :login
# login したときの処理
# DB にユーザ ID とユーザ情報を保存したり
end
rescue WebLogin::NoActionError => e
puts @cgi.header("type" => "text/plain")
puts login.services # 有効なサービスの配列
end
end

上のようなのを認証スクリプトとしておいとく。このサイトだと /login.rb がコレ。ログインスクリプトの query_string が logout だったらセッションを破棄してリファラにリダイレクトする。ちょっとアレ。

/login.rb/Flickr?return_path= のようなリクエストをすると Flickr にリダイレクトして認証させる。認証されるとコールバック (Flickr の対応する API key のコールバックには http://example.com/login.rb/Flickr/callback のように指定しておく) にリダイレクトされるので、セッションに情報をセットして、return_path にリダイレクトする。

login.auth は、path_info に何も指定されていなかったりすると NoActionError を投げる。

require "weblogin.rb"
@cgi = CGI.new
WebLogin.open(@cgi, api_keys) do |login|
if login.service
# login している場合
login.service #=> "Flickr"
login.id #=> "25514198@N00"
login.user_name #=> "cho45"
login.profile_uri #=> "http://www.flickr.com/photos/25514198@N00/"
login.icon #=> "http://static.flickr.com/23/buddyicons/25514198@N00.jpg"
login.full_name #=> "Hiroh Satoh"
else
# login していない場合
# id その他上記メソッドは全て nil
end
end

いまいち気持ち悪いんだけどどうにもこうにもわからんのでこんな感じ。ブロックをとっているのはセッションファイルを確実に閉じるため。fcgi 環境で死ぬことがあったのでこういうことやってる。

WebLogin クラスは、それ以下にある、サービス名に対応するクラスをインスタンス化してメソッドを呼び出す。例えば /login.rb/Flickr?return_path=somepath の場合、WebLogin::Flickr.new(@cgi, @session, @api_keys["Flickr"]).auth が呼ばれる。

各サービス名の auth メソッドはサービスの認証画面にリダイレクトする。サービスは /login.rb/Flickr/callback にリダイレクトする。

callback が指定されている場合、インスタンス化されたサービスクラスの callback メソッドを呼ぶ。callback メソッドはユーザ情報をまとめて Hash にして返す。WebLogin はその Hash をセッションに保存して、最初に指定された return_path にリダイレクトする。

いろいろアレなことが思い浮かんだけどあとで考えよう。

new にブロックって一般的じゃないんじゃ?という指摘をうけたので open にした。確かに new にブロックは微妙だった。

2006年 03月 06日

ruby-xslt の parameters=

parameters= に渡す値はすべて自分でクオートしとこう。

libxslt はもともとパラメータの値に XPath そのまんま書けるんだけど、なぜか ruby-xslt の parameters= ではクオートして必ず文字列で渡す。しかしながら、なんかこのクオートの実装が狂ってて、自分でクオート付け足してる癖に Invalid expression とか言い出してくれちゃう。やれやれだぜ!

正確に言うとクオートを付与する段階で、与えた文字列を中途半端に破壊的に変更するらしく、クオーテーションがかたっぽだけ付く。セッションとかで保存させながらのコードでもうハマったハマった。dup してどうにかしたけど、自分でクオートして明示的に文字列にしたほうがよさげ。

ソースは読んでないので間違っているかも。あくまで挙動からの推測