gogyou 0.1.240911.prototype

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/LICENSE.txt +39 -0
  2. data/README.txt +3 -0
  3. data/lib/gogyou.rb +630 -0
  4. 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
@@ -0,0 +1,3 @@
1
+ = About gogyou
2
+
3
+ ゴギョウ (御形) は、バイナリ文字列にパックまたはアンパックする構造体クラスを定義するライブラリです。
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