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
  1. トップ
  2. tech
  3. Ruby の pack テンプレート文字列からそのデータサイズを求める
  1. トップ
  2. ruby
  3. Ruby の pack テンプレート文字列からそのデータサイズを求める
▲ この日のエントリ