gogyou 0.1.240911.prototype
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE.txt +39 -0
- data/README.txt +3 -0
- data/lib/gogyou.rb +630 -0
- metadata +69 -0
data/LICENSE.txt
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
= License (2-clause BSD License)
|
2
|
+
|
3
|
+
|
4
|
+
----
|
5
|
+
|
6
|
+
|
7
|
+
Copyright (c) 2012, dearblue
|
8
|
+
All rights reserved.
|
9
|
+
|
10
|
+
Redistribution and use in source and binary forms, with or without
|
11
|
+
modification, are permitted provided that the following conditions are met:
|
12
|
+
|
13
|
+
1. Redistributions of source code must retain the above copyright notice,
|
14
|
+
this list of conditions and the following disclaimer.
|
15
|
+
|
16
|
+
2. Redistributions in binary form must reproduce the above copyright
|
17
|
+
notice, this list of conditions and the following disclaimer in the
|
18
|
+
documentation and/or other materials provided with the distribution.
|
19
|
+
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
23
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
24
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
25
|
+
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
26
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
27
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
28
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
29
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
30
|
+
POSSIBILITY OF SUCH DAMAGE.
|
31
|
+
|
32
|
+
|
33
|
+
----
|
34
|
+
|
35
|
+
|
36
|
+
In Japanese.
|
37
|
+
|
38
|
+
日本語訳については次のページが参考になるでしょう:
|
39
|
+
http://www.freebsd.org/ja/copyright/freebsd-license.html
|
data/README.txt
ADDED
data/lib/gogyou.rb
ADDED
@@ -0,0 +1,630 @@
|
|
1
|
+
#vim: set fileencoding:utf-8
|
2
|
+
|
3
|
+
# gogyou.rb
|
4
|
+
# - AUTHOR: dearblue <dearblue@sourceforge.jp>
|
5
|
+
# - WEBSIZE: http://sourceforge.jp/projects/rutsubo/
|
6
|
+
# - LICENSE: same as 2-clause BSD License
|
7
|
+
|
8
|
+
#--
|
9
|
+
# this space block is for rdoc. #
|
10
|
+
# #
|
11
|
+
# #
|
12
|
+
# #
|
13
|
+
# #
|
14
|
+
# #
|
15
|
+
# #
|
16
|
+
# #
|
17
|
+
# #
|
18
|
+
# #
|
19
|
+
# #
|
20
|
+
#++
|
21
|
+
|
22
|
+
# gogyou は ruby 向けに作成され、バイナリデータと構造体との変換を容易にするためのライブラリです。名前は春の七種の一つ『ごぎょう』からとりました。
|
23
|
+
#
|
24
|
+
# ruby に組み込まれている String#unpack / Array#pack は Struct クラスとの関係性はまったくありません。対応付けをするのも自力で書く必要があり、パックフォーマットと変数の関係性の直接的な結びつきがないため視認性に乏しいと言わざるをえません。
|
25
|
+
#
|
26
|
+
# gogyou ライブラリはこの負担を軽減することとともに、視認性の向上を目的に作成されました。
|
27
|
+
#
|
28
|
+
# ただし、rdoc の適用対象とは (やっぱり) なりません。必要であれば定数定義の前にコメントを入力するか、サブクラスを定義してそこで rdoc を記述するのがいいでしょう。
|
29
|
+
#
|
30
|
+
# == 基本
|
31
|
+
#
|
32
|
+
# gogyou ライブラリを用いることによって以下のように記述できます。
|
33
|
+
#
|
34
|
+
# require "gogyou"
|
35
|
+
#
|
36
|
+
# Sample = Gogyou.struct do
|
37
|
+
# uint32_t :data1 # C の uint32_t (32ビット無符号整数値) を定義
|
38
|
+
# int16_t :data2, 2 # C の int16_t (16ビット符号付き整数値) を要素数 2 で定義
|
39
|
+
# uint64_t :data3 # C の uint64_t (64ビット無符号整数値) を定義
|
40
|
+
# uint8_t :data4, 2 # C の uint8_t (8ビット無符号整数値) を要素数 2 で定義
|
41
|
+
# char :data5, 2 # C の char (8ビット符号付き整数値) を要素数 2 で定義
|
42
|
+
# ustring :data6, 4 # NULL 終端の UTF-8 文字列を定義 / 文字列長は 4 / 要素数 1
|
43
|
+
# binary :data7, 8 # ASCII-8BIT 文字列を定義 / 文字列長は 8 / 要素数 1
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# p Sample::PACK_FORMAT # => "I s2 Q C2 c2 Z4 a8" # 実際には空白は含まれません
|
47
|
+
# p Sample::SIZE # => 32
|
48
|
+
#
|
49
|
+
# Sample クラスは Struct クラスのインスタンス (ruby から見ればクラスオブジェクト) で、PACK_FORMAT、SIZE の定数が定義され、.unpack / .pack メソッドが定義されます。
|
50
|
+
#
|
51
|
+
# つまり上記の例であれば、
|
52
|
+
#
|
53
|
+
# sample = Sample.unpack(data_sequence)
|
54
|
+
# data_sequence = sample_data.pack
|
55
|
+
#
|
56
|
+
# というようなメソッド呼び出しができる Sample クラスが定義されることになります。
|
57
|
+
#
|
58
|
+
# == Gogyou.struct ブロック内は self コンテキストが切り替わる
|
59
|
+
#
|
60
|
+
# Gogyou.struct を呼び出すときに渡すブロックは、そのブロック内での self コンテキストが切り替わります。
|
61
|
+
#
|
62
|
+
# ブロックの外で呼び出せるメソッドがブロック内で呼び出せないもしくは意図しない結果になる場合は、このことが原因と見ると対策しやすいかもしれません。
|
63
|
+
#
|
64
|
+
# こんな (一見横暴な) 仕様になっているのは、ブロックパラメータの受け取りと型名を記述する際の変数の記述という煩わしさから開放するという目的を達成させるためです。
|
65
|
+
#
|
66
|
+
# 実装としては Gogyou.struct メソッド内で instance_eval にブロックをそのまま渡しています。
|
67
|
+
#
|
68
|
+
# == 各要素は境界上に配置される
|
69
|
+
#
|
70
|
+
# それぞれの要素のバイトオフセットは C 言語の構造体に倣って、各要素の境界上に配置されます。
|
71
|
+
#
|
72
|
+
# Sample = Gogyou.struct do
|
73
|
+
# uint8_t :data1 # 0000h に配置。幅は1。
|
74
|
+
# uint16_t :data2 # 0002h に配置。幅は2。
|
75
|
+
# uint8_t :data3 # 0004h に配置。幅は1。
|
76
|
+
# uint32_t :data3 # 0008h に配置。幅は4。
|
77
|
+
# uint64_t :data3 # 0010h に配置。幅は8。
|
78
|
+
# end
|
79
|
+
#
|
80
|
+
# p Sample::PACK_FORMAT # => "C x S C x3 L x4 Q"
|
81
|
+
#
|
82
|
+
# == 入れ子になった構造体
|
83
|
+
#
|
84
|
+
# 入れ子の構造体表記も可能です。入れ子の入れ子のさらに・・・みたいなやつも記述可能ですが、展開がその分遅くなります。
|
85
|
+
#
|
86
|
+
# Sample = Gogyou.struct do
|
87
|
+
# struct :ref1 do # 入れ子の構造体。名無しクラス (Struct を祖に持つクラス) を返します。
|
88
|
+
# uint32_t :data1 # ref1.data1 で参照できます。
|
89
|
+
# uint32_t :data2
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# int :data3
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
# p Sample::PACK_FORMAT # => "a8 i"
|
96
|
+
#
|
97
|
+
# == エンディアン指定
|
98
|
+
#
|
99
|
+
# エンディアン指定も可能です。サフィックスが『_t』で終わるやつは基本的に『_le』でリトルエンディアン、『_be』かサフィックスなしでビッグエンディアンになります。
|
100
|
+
#
|
101
|
+
# Sample = Gogyou.struct do
|
102
|
+
# uint32_t :data1 # 動作環境に依存。
|
103
|
+
# uint32 :data2 # 常にビッグエンディアン (ネットワークエンディアン) / パックフォーマットでは『N』と同等。
|
104
|
+
# uint32_be :data3 # uint32 と同じ。
|
105
|
+
# uint32_le :data4 # 常にリトルエンディアン (バックスエンディアン) / パックフォーマットでは『V』と同等。
|
106
|
+
# end
|
107
|
+
#
|
108
|
+
# p Sample::PACK_FORMAT # => "L N N V" # 実際は環境によって変わってきます
|
109
|
+
#
|
110
|
+
# == 境界上に配置されない要素
|
111
|
+
#
|
112
|
+
# 型名の後ろに感嘆符『!』をつけるとその型の境界上に配置されるのを抑制します。GCC でいうところの "__attribute__((__packed__))" が付いている構造体です。
|
113
|
+
#
|
114
|
+
# Sample = Gogyou.struct do
|
115
|
+
# uint8_t! :data1, 3 # 0000h に配置。幅は1。
|
116
|
+
# uint16_t! :data2, 3 # 0003h に配置。幅は2。
|
117
|
+
# uint32_t! :data3, 3 # 0009h に配置。幅は4。
|
118
|
+
# end
|
119
|
+
#
|
120
|
+
# p Sample::PACK_FORMAT # => "C3 S3 L3"
|
121
|
+
# p Sample::SIZE # => 21
|
122
|
+
#
|
123
|
+
# == 境界指定
|
124
|
+
#
|
125
|
+
# "alignment" (短縮して "align" も利用可) で、次の要素の配置が指定境界に来るようになります。ただし要素自体の境界配置が無効化さるわけではないため、そのことも含めて強制させるためには『!』を用いる必要があります。
|
126
|
+
#
|
127
|
+
# Sample = Gogyou.struct do
|
128
|
+
# uint8_t :data1 # 0000h に配置。
|
129
|
+
# alignment 16 # 穴埋め。次の要素は0010hに配置される。
|
130
|
+
# uint32_t :data2 # 0010h に配置。
|
131
|
+
# end
|
132
|
+
#
|
133
|
+
# p Sample::PACK_FORMAT # => "C x15 L"
|
134
|
+
#
|
135
|
+
# == 任意幅の穴埋め
|
136
|
+
#
|
137
|
+
# "padding" で、任意幅の穴埋めができます。
|
138
|
+
#
|
139
|
+
# Sample = Gogyou.struct do
|
140
|
+
# uint8_t :data1 # 0000h に配置。
|
141
|
+
# padding 8 # 穴埋め。次の要素は 0009h に配置される。
|
142
|
+
# uint32_t :data2 # 4バイト境界上の 000Ch に配置。
|
143
|
+
# end
|
144
|
+
#
|
145
|
+
# p Sample::PACK_FORMAT # => "C x8 x3 L"
|
146
|
+
#
|
147
|
+
# == 型の別名定義
|
148
|
+
#
|
149
|
+
# "typedef" で、C でいう型の別名が定義できます。
|
150
|
+
#
|
151
|
+
# Sample = Gogyou.struct do
|
152
|
+
# typedef :uint, :HANDLE # 以降 HANDLE を使うと uint を指定したことになる
|
153
|
+
#
|
154
|
+
# uint8_t :data1, 3
|
155
|
+
# HANDLE :data2
|
156
|
+
# end
|
157
|
+
#
|
158
|
+
# p Sample::PACK_FORMAT # => "C3 x I"
|
159
|
+
#
|
160
|
+
# 入れ子定義内でも親で定義した typedef は有効です。
|
161
|
+
#
|
162
|
+
# Sample = Gogyou.struct do
|
163
|
+
# typedef :uint, :HANDLE
|
164
|
+
#
|
165
|
+
# HANDLE :data1
|
166
|
+
# struct :dataset do
|
167
|
+
# HANDLE :data1 # この HANDLE は親スコープで typedef したものが継承されたものです。
|
168
|
+
# HANDLE :data2
|
169
|
+
# end
|
170
|
+
# end
|
171
|
+
#
|
172
|
+
# また任意のクラスを指定することで外部クラスをブロック内で定義することができるようになります。
|
173
|
+
#
|
174
|
+
# Sample = Gogyou.struct do
|
175
|
+
# typedef MD5 :MD5 # 以降 MD5 で MD5 クラスが展開と格納を行う
|
176
|
+
#
|
177
|
+
# MD5 :md5
|
178
|
+
# end
|
179
|
+
#
|
180
|
+
# この MD5 クラス (モジュールでも可) は、pack / unpack メソッドを持ち、SIZE 定数 (バイト数。MD5 なので 16) を持つ必要があります。
|
181
|
+
#
|
182
|
+
# class MD5
|
183
|
+
# SIZE = 16
|
184
|
+
#
|
185
|
+
# def self.pack(obj)
|
186
|
+
# ...
|
187
|
+
# # string オブジェクトを返さなければならない
|
188
|
+
# # bytesize は SIZE と等しくなければならない
|
189
|
+
# end
|
190
|
+
#
|
191
|
+
# def self.unpack(str)
|
192
|
+
# # str は SIZE 定数で指定した分量のバイナリ文字列
|
193
|
+
# ...
|
194
|
+
# # 展開したオブジェクトを返す
|
195
|
+
# end
|
196
|
+
# end
|
197
|
+
#
|
198
|
+
# == 型名一覧
|
199
|
+
#
|
200
|
+
# それぞれ正規表現に沿った見方をしてください。。。ちょーみづれー
|
201
|
+
#
|
202
|
+
# - バイナリ列: binary
|
203
|
+
# - UTF-8 文字列: ustring
|
204
|
+
# - 整数 (環境依存 : ビット数、エンディアン): u?char, u?short!?, u?int!?, u?long!?, u?longlong!?
|
205
|
+
# - 整数 (環境依存 : ビット数、エンディアン): s?size_t!?, u?intptr_t!?
|
206
|
+
# - 整数 (環境依存 : エンディアン): u?int16_t!?, u?int32_t!?, u?int64_t!?
|
207
|
+
# - 浮動少数 (環境依存 : ビット数、エンディアン): float!?, double!?
|
208
|
+
# - 浮動少数 (環境非依存): float(_be|_le)!?, double(_be|_le)!?
|
209
|
+
# - 整数 (環境非依存): u?int8(_t)?, u?int16(_be|_le)?!?, u?int32(_be|_le)?!?, u?int64(_be|_le)?!?
|
210
|
+
#
|
211
|
+
# == 実行速度面では不利
|
212
|
+
#
|
213
|
+
# 構造体定義は結構重い処理 (数ミリ秒) になっています。しかし、アプリケーション初期化時において各構造体ごとに一度だけ処理されるため、気になることはあまりないと思います。
|
214
|
+
#
|
215
|
+
# そしてやはり pack / unpack は自力で記述した場合と比べれば重くなりますが、記述の容易さ・可読性に免じて目を瞑ってください。
|
216
|
+
|
217
|
+
module Gogyou
|
218
|
+
Gogyou = self
|
219
|
+
|
220
|
+
# このメソッドにブロックつきで呼び出すことによって、Struct の構築と、Array#pack および String#unpack を用いてデータの詰め込みと展開を行える機能を持ったクラスを構築することができます。
|
221
|
+
#
|
222
|
+
# 詳しい説明は Gogyou モジュールに記述してあります。
|
223
|
+
def self.struct(&block)
|
224
|
+
farm = StructFarm.new
|
225
|
+
farm.instance_eval(&block)
|
226
|
+
define_struct(farm)
|
227
|
+
end
|
228
|
+
|
229
|
+
# Gogyou.struct によって生成したクラスが extend によって組み込むモジュールです。
|
230
|
+
#
|
231
|
+
# 利用者定義クラスのクラスメソッドとして利用されます。
|
232
|
+
module ModuleUnpacker
|
233
|
+
def unpack(str)
|
234
|
+
ary = str.unpack(self::PACK_FORMAT)
|
235
|
+
self::PROPERTIES.each_with_index do |(name, size, offset, pack, unpack), i|
|
236
|
+
unless offset
|
237
|
+
# メンバ変数の宣言のみ
|
238
|
+
ary.insert(i, nil)
|
239
|
+
next
|
240
|
+
end
|
241
|
+
|
242
|
+
if size && size > 1
|
243
|
+
ary[i] = ary.slice(i, size)
|
244
|
+
ary.slice!(i + 1, size - 1)
|
245
|
+
end
|
246
|
+
|
247
|
+
if unpack
|
248
|
+
if size > 1
|
249
|
+
a = ary[i]
|
250
|
+
size.times do |j|
|
251
|
+
a[j] = unpack.(a[j])
|
252
|
+
end
|
253
|
+
else
|
254
|
+
ary[i] = unpack.(ary[i])
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
new(*ary)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
# Gogyou.struct によって生成したクラスが extend によって組み込むモジュールです。
|
263
|
+
#
|
264
|
+
# 利用者定義クラスのクラスメソッドとして利用されます。
|
265
|
+
module ModulePacker
|
266
|
+
def pack(obj)
|
267
|
+
obj = obj.each
|
268
|
+
ary = []
|
269
|
+
self::PROPERTIES.each do |name, size, offset, pack, unpack|
|
270
|
+
#p [name, size, offset, pack, unpack]
|
271
|
+
unless offset
|
272
|
+
# メンバ変数の宣言のみ
|
273
|
+
obj.next
|
274
|
+
next
|
275
|
+
end
|
276
|
+
|
277
|
+
if pack
|
278
|
+
if size > 1
|
279
|
+
obj.next.each do |a|
|
280
|
+
ary << pack.call(a)
|
281
|
+
end
|
282
|
+
else
|
283
|
+
ary << pack.call(obj.next)
|
284
|
+
end
|
285
|
+
else
|
286
|
+
if size > 1
|
287
|
+
ary.concat obj.next
|
288
|
+
else
|
289
|
+
ary << obj.next
|
290
|
+
end
|
291
|
+
end
|
292
|
+
#p ary
|
293
|
+
end
|
294
|
+
|
295
|
+
ary.pack(self::PACK_FORMAT)
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
# Gogyou.struct によって生成したクラスが include によって組み込むモジュールです。
|
300
|
+
#
|
301
|
+
# 利用者定義クラスのインスタンスメソッドとして利用されます。
|
302
|
+
module Packer
|
303
|
+
def pack
|
304
|
+
self.class.pack(self)
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
module Types
|
309
|
+
SIZEOF_CHAR = [0].pack("C").bytesize
|
310
|
+
SIZEOF_SHORT = [0].pack("S").bytesize
|
311
|
+
SIZEOF_INT = [0].pack("I").bytesize
|
312
|
+
SIZEOF_LONG = [0].pack("L").bytesize
|
313
|
+
SIZEOF_LONGLONG = [0].pack("Q").bytesize
|
314
|
+
SIZEOF_SIZE_T = [nil].pack("P").bytesize
|
315
|
+
SIZEOF_FLOAT = [0].pack("F").bytesize
|
316
|
+
SIZEOF_DOUBLE = [0].pack("D").bytesize
|
317
|
+
|
318
|
+
FORMATOF_INT8_T = "c"
|
319
|
+
FORMATOF_UINT8_T = "C"
|
320
|
+
|
321
|
+
FORMATOF_INT16_T = "s"
|
322
|
+
FORMATOF_INT16_BE = FORMATOF_INT16_T + ">"
|
323
|
+
FORMATOF_INT16_LE = FORMATOF_INT16_T + "<"
|
324
|
+
FORMATOF_UINT16_T = "S"
|
325
|
+
FORMATOF_UINT16_BE = FORMATOF_UINT16_T + ">"
|
326
|
+
FORMATOF_UINT16_LE = FORMATOF_UINT16_T + "<"
|
327
|
+
|
328
|
+
case
|
329
|
+
when SIZEOF_LONG == 4
|
330
|
+
FORMATOF_INT32_T = "l"
|
331
|
+
FORMATOF_UINT32_T = "L"
|
332
|
+
when SIZEOF_INT == 4
|
333
|
+
FORMATOF_INT32_T = "i"
|
334
|
+
FORMATOF_UINT32_T = "I"
|
335
|
+
else
|
336
|
+
raise "can not be define int32_t type"
|
337
|
+
end
|
338
|
+
|
339
|
+
FORMATOF_INT32_BE = FORMATOF_INT32_T + ">"
|
340
|
+
FORMATOF_INT32_LE = FORMATOF_INT32_T + "<"
|
341
|
+
FORMATOF_UINT32_BE = FORMATOF_UINT32_T + ">"
|
342
|
+
FORMATOF_UINT32_LE = FORMATOF_UINT32_T + "<"
|
343
|
+
|
344
|
+
case
|
345
|
+
when SIZEOF_LONG == 8
|
346
|
+
FORMATOF_INT64_T = "l"
|
347
|
+
FORMATOF_UINT64_T = "L"
|
348
|
+
when SIZEOF_LONGLONG == 8
|
349
|
+
FORMATOF_INT64_T = "q"
|
350
|
+
FORMATOF_UINT64_T = "Q"
|
351
|
+
else
|
352
|
+
raise "can not be define int64_t type"
|
353
|
+
end
|
354
|
+
|
355
|
+
FORMATOF_INT64_BE = FORMATOF_INT64_T + ">"
|
356
|
+
FORMATOF_INT64_LE = FORMATOF_INT64_T + "<"
|
357
|
+
FORMATOF_UINT64_BE = FORMATOF_UINT64_T + ">"
|
358
|
+
FORMATOF_UINT64_LE = FORMATOF_UINT64_T + "<"
|
359
|
+
|
360
|
+
case
|
361
|
+
when SIZEOF_SIZE_T == 4
|
362
|
+
FORMATOF_SSIZE_T = FORMATOF_INT32_T
|
363
|
+
FORMATOF_SIZE_T = FORMATOF_UINT32_T
|
364
|
+
when SIZEOF_SIZE_T == 8
|
365
|
+
FORMATOF_SSIZE_T = FORMATOF_INT64_T
|
366
|
+
FORMATOF_SIZE_T = FORMATOF_UINT64_T
|
367
|
+
else
|
368
|
+
raise "can not be define size_t type"
|
369
|
+
end
|
370
|
+
|
371
|
+
FORMATOF_INTPTR_T = FORMATOF_SSIZE_T
|
372
|
+
FORMATOF_UINTPTR_T = FORMATOF_SIZE_T
|
373
|
+
SIZEOF_INTPTR_T = SIZEOF_UINTPTR_T = SIZEOF_SIZE_T
|
374
|
+
end
|
375
|
+
|
376
|
+
if false
|
377
|
+
Types.constants.sort.each do |e|
|
378
|
+
puts "#{e}: #{Types.const_get(e).inspect}."
|
379
|
+
end
|
380
|
+
raise "TEST BREAK!"
|
381
|
+
end
|
382
|
+
|
383
|
+
class Property < Struct.new(:name, :elementof, :offset, :pack, :unpack)
|
384
|
+
BasicStruct = superclass
|
385
|
+
end
|
386
|
+
|
387
|
+
# 構造体クラスの定義作業に使われるクラスです。
|
388
|
+
#
|
389
|
+
# Gogyou.struct に渡すブロック内において self はこのクラスのインスタンスに切り替わるため、レシーバなしのメソッド呼び出しはこのクラスのインスタンスメソッドが呼ばれることになります。
|
390
|
+
#
|
391
|
+
# もしブロック外では問題ないのにブロック内で問題が出る場合、このことが原因になることがあります。
|
392
|
+
class StructFarm < Struct.new(:packlist,
|
393
|
+
:properties,
|
394
|
+
:offset)
|
395
|
+
|
396
|
+
# Gogyou::StructFarm の派生元クラスとなる Struct インスタントクラス。
|
397
|
+
BasicStruct = superclass
|
398
|
+
|
399
|
+
include Types
|
400
|
+
|
401
|
+
def initialize
|
402
|
+
super([], [], 0)
|
403
|
+
end
|
404
|
+
|
405
|
+
def initialize_clone(obj)
|
406
|
+
self.packlist = []
|
407
|
+
self.properties = []
|
408
|
+
self.offset = 0
|
409
|
+
end
|
410
|
+
|
411
|
+
def variable?
|
412
|
+
false
|
413
|
+
end
|
414
|
+
|
415
|
+
def union(name, size = 1, &block)
|
416
|
+
raise NotImplementedError
|
417
|
+
f = UnionFarm.new
|
418
|
+
end
|
419
|
+
|
420
|
+
def struct(name, elementof = 1, &block)
|
421
|
+
f = clone
|
422
|
+
f.instance_eval(&block)
|
423
|
+
type = Gogyou.instance_eval { define_struct(f) }
|
424
|
+
|
425
|
+
name = name.to_sym
|
426
|
+
elementof = _convert_size(elementof)
|
427
|
+
elementof.times { packlist << "a#{type::SIZE}" }
|
428
|
+
properties << [name, elementof, offset, type.method(:pack), type.method(:unpack)]
|
429
|
+
self.offset += type::SIZE * elementof
|
430
|
+
type
|
431
|
+
end
|
432
|
+
|
433
|
+
def voidp(name, size = 1)
|
434
|
+
define(name, "P", 1, size, SIZEOF_SIZE_T, nil)
|
435
|
+
end
|
436
|
+
|
437
|
+
def typedef(target, aliasname)
|
438
|
+
case target
|
439
|
+
when String, Symbol
|
440
|
+
singleton_class.class_eval do
|
441
|
+
alias_method aliasname, target
|
442
|
+
alias_method :"#{aliasname}!", :"#{target}!" unless aliasname =~ /!$/ || target =~ /!$/
|
443
|
+
end
|
444
|
+
when Class
|
445
|
+
sizeof = target.const_get(:SIZE)
|
446
|
+
pack = target.method(:pack)
|
447
|
+
unpack = target.method(:unpack)
|
448
|
+
usertype(aliasname, sizeof, pack, unpack)
|
449
|
+
else
|
450
|
+
sizeof = target.size
|
451
|
+
pack = target.method(:pack)
|
452
|
+
unpack = target.method(:unpack)
|
453
|
+
usertype(aliasname, sizeof, pack, unpack)
|
454
|
+
end
|
455
|
+
self
|
456
|
+
end
|
457
|
+
|
458
|
+
# [tipename (require)]
|
459
|
+
# 型名
|
460
|
+
# [sizeof (require)]
|
461
|
+
# 1要素あたりのオクテット数
|
462
|
+
# [pack (optional)]
|
463
|
+
# 格納する場合に呼び出されるメソッド。不要であれば nil を指定可。
|
464
|
+
# [unpack (optional)]
|
465
|
+
# 展開する場合に呼び出されるメソッド。不要であれば nil を指定可。
|
466
|
+
def usertype(typename, sizeof, pack = nil, unpack = nil)
|
467
|
+
#raise(ArgumentError, "need block") unless reduce
|
468
|
+
#typename = typename.to_sym
|
469
|
+
#raise(ArgumentError, "wrong typename (#{typename})") if typename =~ /\s/m || typename !~ /^[_\w][_\w\d]*$/
|
470
|
+
define_singleton_method(typename, &->(name, size = 1) {
|
471
|
+
name = name.to_sym
|
472
|
+
size = _convert_size(size)
|
473
|
+
packlist.concat(["a#{sizeof}"] * size)
|
474
|
+
properties << [name, size, offset, pack, unpack]
|
475
|
+
self.offset += sizeof * size
|
476
|
+
self
|
477
|
+
})
|
478
|
+
self
|
479
|
+
end
|
480
|
+
|
481
|
+
def padding(size)
|
482
|
+
size = _convert_size(size)
|
483
|
+
packlist << (size > 1 ? "x#{size}" : "x")
|
484
|
+
self.offset += size
|
485
|
+
self
|
486
|
+
end
|
487
|
+
|
488
|
+
def alignment(elementof)
|
489
|
+
elementof = _convert_size(elementof)
|
490
|
+
size = (elementof - offset % elementof) % elementof
|
491
|
+
padding(size) if size > 0
|
492
|
+
self
|
493
|
+
end
|
494
|
+
|
495
|
+
alias align alignment
|
496
|
+
|
497
|
+
# call-seq:
|
498
|
+
# exclude(name, ...)
|
499
|
+
#
|
500
|
+
# 追加するメンバ変数を定義します。これで追加されたメンバ変数は pack / unpack の対象外として扱われます。
|
501
|
+
#
|
502
|
+
# インスタンス変数の代わりに定義することを想定しています。
|
503
|
+
def exclude(*names)
|
504
|
+
names.each do |n|
|
505
|
+
property = [n.to_sym, nil, nil]
|
506
|
+
properties << property
|
507
|
+
end
|
508
|
+
self
|
509
|
+
end
|
510
|
+
|
511
|
+
# メンバ変数宣言の実体。int や long などの型指定時に呼び出されます。
|
512
|
+
#
|
513
|
+
# [name] アクセッサ名 (メンバ名)
|
514
|
+
# [format] パックフォーマット (Array#pack や String#unpack を参照)
|
515
|
+
# [elementof] 要素数
|
516
|
+
# [sizeof] 1要素あたりのオクテット数
|
517
|
+
# [elements] パックフォーマットの要素数が複数要素として展開される場合は nil を指定する
|
518
|
+
# [pack] Array#pack の時に置き換える場合の前処理
|
519
|
+
# [unpack] String#unpack の時に置き換える後処理
|
520
|
+
def define(name, format, elementof, sizeof, elements = nil, packer = nil, unpacker = nil)
|
521
|
+
name = name.to_sym
|
522
|
+
elementof = _convert_size(elementof)
|
523
|
+
packlist << "#{format}#{elementof > 1 ? elementof : nil}"
|
524
|
+
property = [name, elements || elementof, offset]
|
525
|
+
property << packer << unpacker if packer || unpacker
|
526
|
+
properties << property
|
527
|
+
self.offset += sizeof * elementof
|
528
|
+
self
|
529
|
+
end
|
530
|
+
|
531
|
+
def unpack_ustring(str)
|
532
|
+
str.force_encoding(Encoding::UTF_8)
|
533
|
+
end
|
534
|
+
|
535
|
+
def pack_ustring(str)
|
536
|
+
str
|
537
|
+
end
|
538
|
+
|
539
|
+
[
|
540
|
+
# 0: type name
|
541
|
+
# 1: size of type
|
542
|
+
# 2: number of multiple elements
|
543
|
+
# 3: default number of element for pack format
|
544
|
+
# 4.0: pack format (non-suffix)
|
545
|
+
# 4.1: pack format ("_be" suffixed)
|
546
|
+
# 4.2: pack format ("_le" suffixed)
|
547
|
+
# 4.3: pack format ("_t" suffixed)
|
548
|
+
# 5: pack method (optional)
|
549
|
+
# 6: unpack method (optional)
|
550
|
+
[:binary, 1, 1, false, ["a", nil, nil, nil]],
|
551
|
+
[:ustring, 1, 1, false, ["Z", nil, nil, nil], :pack_ustring, :unpack_ustring],
|
552
|
+
[:char, SIZEOF_CHAR, nil, true, ["c", nil, nil, nil]],
|
553
|
+
[:uchar, SIZEOF_CHAR, nil, true, ["C", nil, nil, nil]],
|
554
|
+
[:short, SIZEOF_SHORT, nil, true, ["s", nil, nil, nil]],
|
555
|
+
[:ushort, SIZEOF_SHORT, nil, true, ["S", nil, nil, nil]],
|
556
|
+
[:int, SIZEOF_INT, nil, true, ["i", nil, nil, nil]],
|
557
|
+
[:uint, SIZEOF_INT, nil, true, ["I", nil, nil, nil]],
|
558
|
+
[:long, SIZEOF_LONG, nil, true, ["l", nil, nil, nil]],
|
559
|
+
[:ulong, SIZEOF_LONG, nil, true, ["L", nil, nil, nil]],
|
560
|
+
[:longlong, SIZEOF_LONGLONG, nil, true, ["q", nil, nil, nil]],
|
561
|
+
[:ulonglong, SIZEOF_LONGLONG, nil, true, ["Q", nil, nil, nil]],
|
562
|
+
[:float, SIZEOF_FLOAT, nil, true, ["F", "g", "e", nil]],
|
563
|
+
[:double, SIZEOF_DOUBLE, nil, true, ["D", "G", "E", nil]],
|
564
|
+
[:size_t, SIZEOF_SIZE_T, nil, true, [FORMATOF_SIZE_T, nil, nil, nil]],
|
565
|
+
[:ssize_t, SIZEOF_SIZE_T, nil, true, [FORMATOF_SSIZE_T, nil, nil, nil]],
|
566
|
+
[:intptr_t, SIZEOF_INTPTR_T, nil, true, [FORMATOF_INTPTR_T, nil, nil, nil]],
|
567
|
+
[:uintptr_t, SIZEOF_INTPTR_T, nil, true, [FORMATOF_UINTPTR_T, nil, nil, nil]],
|
568
|
+
[:int8, 1, nil, true, [FORMATOF_INT8_T, nil, nil, FORMATOF_INT8_T]],
|
569
|
+
[:uint8, 1, nil, true, [FORMATOF_UINT8_T, nil, nil, FORMATOF_UINT8_T]],
|
570
|
+
[:int16, 2, nil, true, [FORMATOF_INT16_BE, FORMATOF_INT16_BE, FORMATOF_INT16_LE, FORMATOF_INT16_T]],
|
571
|
+
[:uint16, 2, nil, true, [FORMATOF_UINT16_BE, FORMATOF_UINT16_BE, FORMATOF_UINT16_LE, FORMATOF_UINT16_T]],
|
572
|
+
[:int32, 4, nil, true, [FORMATOF_INT32_BE, FORMATOF_INT32_BE, FORMATOF_INT32_LE, FORMATOF_INT32_T]],
|
573
|
+
[:uint32, 4, nil, true, [FORMATOF_UINT32_BE, FORMATOF_UINT32_BE, FORMATOF_UINT32_LE, FORMATOF_UINT32_T]],
|
574
|
+
[:int64, 8, nil, true, [FORMATOF_INT64_BE, FORMATOF_INT64_BE, FORMATOF_INT64_LE, FORMATOF_INT64_T]],
|
575
|
+
[:uint64, 8, nil, true, [FORMATOF_UINT64_BE, FORMATOF_UINT64_BE, FORMATOF_UINT64_LE, FORMATOF_UINT64_T]],
|
576
|
+
].each do |name, sizeof, ismultielement, defaultelements, format, pack, unpack|
|
577
|
+
default_elementnum = defaultelements ? " = 1" : ""
|
578
|
+
["", "_be", "_le", "_t"].zip(format).each do |suffix, f|
|
579
|
+
next unless f
|
580
|
+
|
581
|
+
if pack || unpack
|
582
|
+
reduce = ""
|
583
|
+
reduce << ", " << (pack ? "method(#{pack.to_sym.inspect})" : "nil")
|
584
|
+
reduce << ", " << (unpack ? "method(#{unpack.to_sym.inspect})" : "nil")
|
585
|
+
end
|
586
|
+
|
587
|
+
class_eval(x = <<-EOS, "#{__FILE__}<#{name}#{suffix}>", __LINE__ + 1)
|
588
|
+
def #{name}#{suffix}(name, elementnum#{default_elementnum})
|
589
|
+
alignment #{sizeof}
|
590
|
+
define(name, #{f.inspect}, elementnum, #{sizeof}, #{ismultielement.inspect}#{reduce})
|
591
|
+
end
|
592
|
+
|
593
|
+
def #{name}#{suffix}!(name, elementnum#{default_elementnum})
|
594
|
+
define(name, #{f.inspect}, elementnum, #{sizeof}, #{ismultielement.inspect}#{reduce})
|
595
|
+
end
|
596
|
+
EOS
|
597
|
+
#puts x.gsub!(/\s+/m, " ").strip
|
598
|
+
end
|
599
|
+
end
|
600
|
+
|
601
|
+
#p instance_methods.sort - Object.methods
|
602
|
+
|
603
|
+
def _convert_size(size)
|
604
|
+
size = size.to_i
|
605
|
+
raise ArgumentError, "size is must not zero or negative" unless size > 0
|
606
|
+
size
|
607
|
+
end
|
608
|
+
end
|
609
|
+
|
610
|
+
class << self
|
611
|
+
private
|
612
|
+
def define_struct(farm)
|
613
|
+
syms = farm.properties.map { |e| e[0].to_sym }
|
614
|
+
raise(ArgumentError, "not defined struct members") if syms.empty?
|
615
|
+
type0 = ::Struct.new(*syms)
|
616
|
+
type0.class_eval do
|
617
|
+
const_set(:PACK_FORMAT, farm.packlist.join("").freeze)
|
618
|
+
const_set(:SIZE, farm.offset)
|
619
|
+
const_set(:VARIABLE, farm.variable?)
|
620
|
+
const_set(:PROPERTIES, farm.properties.freeze)
|
621
|
+
extend ModuleUnpacker
|
622
|
+
extend ModulePacker
|
623
|
+
include Packer
|
624
|
+
end
|
625
|
+
type = Class.new(type0)
|
626
|
+
type.const_set(:BasicStruct, type0)
|
627
|
+
type
|
628
|
+
end
|
629
|
+
end
|
630
|
+
end
|
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gogyou
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.240911.prototype
|
5
|
+
prerelease: 11
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- dearblue
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-09-11 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: ! 'gogyou is a library for define the data struct for packing and unpacking
|
15
|
+
to binary strings.
|
16
|
+
|
17
|
+
The C style types are usable.
|
18
|
+
|
19
|
+
Also:
|
20
|
+
|
21
|
+
- Define struct in struct is possible.
|
22
|
+
|
23
|
+
- By using the typedef, you can define any class.
|
24
|
+
|
25
|
+
- Definition of a one-dimensional array is possible.
|
26
|
+
|
27
|
+
'
|
28
|
+
email: dearblue@users.sourceforge.jp
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files:
|
32
|
+
- README.txt
|
33
|
+
- LICENSE.txt
|
34
|
+
- lib/gogyou.rb
|
35
|
+
files:
|
36
|
+
- README.txt
|
37
|
+
- LICENSE.txt
|
38
|
+
- lib/gogyou.rb
|
39
|
+
homepage: http://sourceforge.jp/projects/rutsubo/
|
40
|
+
licenses:
|
41
|
+
- 2-clause BSD License
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options:
|
44
|
+
- -e
|
45
|
+
- UTF-8
|
46
|
+
- -m
|
47
|
+
- README.txt
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 1.9.3
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>'
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.3.1
|
62
|
+
requirements: []
|
63
|
+
rubyforge_project:
|
64
|
+
rubygems_version: 1.8.24
|
65
|
+
signing_key:
|
66
|
+
specification_version: 3
|
67
|
+
summary: define the data struct for packing and unpacking to binary strings
|
68
|
+
test_files: []
|
69
|
+
has_rdoc: false
|