gogyou 0.1.240911.prototype → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/{LICENSE.txt → LICENSE.markdown} +35 -39
- data/README.markdown +429 -0
- data/Rakefile +106 -0
- data/gemstub.rb +40 -0
- data/lib/gogyou.rb +264 -593
- data/lib/gogyou/accessor.rb +397 -0
- data/lib/gogyou/mixin.rb +641 -0
- data/lib/gogyou/model.rb +504 -0
- data/lib/gogyou/primitives.rb +317 -0
- data/lib/gogyou/typespec.rb +15 -0
- data/mkprims.rb +172 -0
- data/spec/gogyou_spec.rb +160 -0
- metadata +65 -34
- data/README.txt +0 -3
data/lib/gogyou/model.rb
ADDED
@@ -0,0 +1,504 @@
|
|
1
|
+
module Gogyou
|
2
|
+
#
|
3
|
+
# 構造体の構成情報などを保持するクラスです。
|
4
|
+
#
|
5
|
+
# 構造体の大きさや各フィールドの名前、バイト位置、型などを管理します。
|
6
|
+
#
|
7
|
+
class Model < ::Struct.new(:bytesize, # total bytesize in bytes
|
8
|
+
:bytealign, # byte alignment
|
9
|
+
:fields) # array of field
|
10
|
+
BasicStruct = superclass
|
11
|
+
|
12
|
+
FIELDNAME_PATTERN = /\A[A-Za-z_][0-9A-Za-z_]*\Z/
|
13
|
+
|
14
|
+
undef :bytesize=, :bytealign=, :fields=
|
15
|
+
|
16
|
+
def initialize(*args)
|
17
|
+
case
|
18
|
+
when args.size < 3
|
19
|
+
raise ArgumentError, "wrong argument size (#{args.size} for 3+)"
|
20
|
+
when args.size < 4 && args[2].kind_of?(::Array)
|
21
|
+
super
|
22
|
+
else
|
23
|
+
super(args[0], args[1], args.slice(2 .. -1))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def aset(buffer, offset, value)
|
28
|
+
raise NotImplementedError
|
29
|
+
end
|
30
|
+
|
31
|
+
def aref(buffer, offset)
|
32
|
+
raise NotImplementedError
|
33
|
+
end
|
34
|
+
|
35
|
+
def extensible?
|
36
|
+
fields.any? { |f| f.extensible? }
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
"#{self.class}[bytesize=#{bytesize.inspect}, bytealign=#{bytealign.inspect}, fields=#{fields.inspect}]"
|
41
|
+
end
|
42
|
+
|
43
|
+
alias inspect to_s
|
44
|
+
|
45
|
+
def pretty_print(q)
|
46
|
+
#bytesize, bytealign, fields
|
47
|
+
q.group(1, "#{self.class}[") do
|
48
|
+
#q.breakable
|
49
|
+
q.text "bytesize="
|
50
|
+
q.pp bytesize
|
51
|
+
q.text ", "
|
52
|
+
#q.breakable
|
53
|
+
q.text "bytealign="
|
54
|
+
q.pp bytealign
|
55
|
+
q.text ", "
|
56
|
+
#q.breakable(" ")
|
57
|
+
q.text "fields="
|
58
|
+
q.breakable
|
59
|
+
q.pp fields
|
60
|
+
end
|
61
|
+
q.text "]"
|
62
|
+
end
|
63
|
+
|
64
|
+
# 構造体の各メンバの情報を保持する
|
65
|
+
class Field < ::Struct.new(:offset, # field offset in model
|
66
|
+
:name, # field name
|
67
|
+
:vector, # 要素数。任意数配列の場合は 0。配列でないならば nil。
|
68
|
+
:type, # type specification (or model object) of this field
|
69
|
+
:flags) # 0x01: const / 0x02: packed (not aligned)
|
70
|
+
BasicStruct = superclass
|
71
|
+
|
72
|
+
FLAG_CONST = 0x01
|
73
|
+
FLAG_PACKED = 0x02
|
74
|
+
|
75
|
+
def initialize(offset, name, vector, type, flags = 0)
|
76
|
+
super(offset, name, vector, type, flags)
|
77
|
+
end
|
78
|
+
|
79
|
+
def extensible?
|
80
|
+
if vector
|
81
|
+
vector[-1] == 0 ? true : false
|
82
|
+
else
|
83
|
+
type.extensible?
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def const?
|
88
|
+
((flags & FLAG_CONST) == FLAG_CONST) ? true : false
|
89
|
+
end
|
90
|
+
|
91
|
+
def packed?
|
92
|
+
((flags & FLAG_PACKED) == FLAG_PACKED) ? true : false
|
93
|
+
end
|
94
|
+
|
95
|
+
def mark_const
|
96
|
+
self.flags |= FLAG_CONST
|
97
|
+
self
|
98
|
+
end
|
99
|
+
|
100
|
+
def mark_packed
|
101
|
+
self.flags |= FLAGS_PACKED
|
102
|
+
self
|
103
|
+
end
|
104
|
+
|
105
|
+
def strflags
|
106
|
+
set = [const? ? "const" : nil, packed? ? "packed" : nil]
|
107
|
+
set.compact!
|
108
|
+
return nil if set.empty?
|
109
|
+
set.join(",")
|
110
|
+
end
|
111
|
+
|
112
|
+
def strflags_with_paren
|
113
|
+
set = strflags
|
114
|
+
set ? "(#{set})" : ""
|
115
|
+
end
|
116
|
+
|
117
|
+
def to_s
|
118
|
+
"#{self.class}[offset=#{offset.inspect}, name=#{name.inspect}, vector=#{vector.inspect}, type=#{type.inspect}, flags=0x#{flags.to_s(16)}#{strflags_with_paren}]"
|
119
|
+
end
|
120
|
+
|
121
|
+
alias inspect to_s
|
122
|
+
|
123
|
+
def pretty_print(q)
|
124
|
+
q.group(1, "#{self.class}[") do
|
125
|
+
#q.breakable
|
126
|
+
q.text "offset="
|
127
|
+
q.pp offset
|
128
|
+
q.text ", "
|
129
|
+
#q.breakable
|
130
|
+
q.text "name="
|
131
|
+
q.pp name
|
132
|
+
q.text ", "
|
133
|
+
#q.breakable
|
134
|
+
q.text "vector="
|
135
|
+
q.pp vector
|
136
|
+
q.text ", "
|
137
|
+
#q.breakable
|
138
|
+
q.text "flags=0x%02x%s" % [flags, strflags_with_paren]
|
139
|
+
q.text ","
|
140
|
+
q.breakable(" ")
|
141
|
+
q.text "type="
|
142
|
+
q.pp type
|
143
|
+
end
|
144
|
+
q.text "]"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.struct(typemap, &block)
|
149
|
+
define_container(typemap, Model::Struct, &block)
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.union(typemap, &block)
|
153
|
+
define_container(typemap, Model::Union, &block)
|
154
|
+
end
|
155
|
+
|
156
|
+
def self.typedef(typemap, type, aliasname, *elements)
|
157
|
+
raise ArgumentError, "informal aliasname (#{aliasname.inspect})" unless aliasname =~ FIELDNAME_PATTERN
|
158
|
+
aliasname = aliasname.intern
|
159
|
+
|
160
|
+
case type
|
161
|
+
when Symbol, String
|
162
|
+
type0 = type
|
163
|
+
type = typemap[type.intern]
|
164
|
+
raise ArgumentError, "type not defined (#{type0})" unless type
|
165
|
+
else
|
166
|
+
# 型情報子を用いる方法
|
167
|
+
raise ArgumentError, "type is not typeinfo (#{type.inspect})" unless Model.check_typeinfo(type)
|
168
|
+
end
|
169
|
+
|
170
|
+
unless elements.empty?
|
171
|
+
# 配列型
|
172
|
+
# TODO: Accessor::Array を構築するのではなく、Model::Array インスタンスを生成するようにする
|
173
|
+
type = Accessor.define_subarray(Model::Field[0, nil, elements, type, 0])
|
174
|
+
end
|
175
|
+
|
176
|
+
typemap[aliasname] = type
|
177
|
+
|
178
|
+
nil
|
179
|
+
end
|
180
|
+
|
181
|
+
def self.define_container(typemap, model_type, &block)
|
182
|
+
creator = model_type::Creator.new(typemap, 0, [])
|
183
|
+
proxy = model_type::Creator::Proxy.new(creator)
|
184
|
+
proxy.instance_exec(&block)
|
185
|
+
model = creator.to_model
|
186
|
+
model
|
187
|
+
end
|
188
|
+
|
189
|
+
def self.check_typeinfo(obj)
|
190
|
+
if obj.kind_of?(Model) ||
|
191
|
+
(obj.kind_of?(Module) && obj < Accessor) ||
|
192
|
+
(obj.respond_to?(:bytesize) &&
|
193
|
+
obj.respond_to?(:bytealign) &&
|
194
|
+
obj.respond_to?(:extensible?) &&
|
195
|
+
obj.respond_to?(:aset) &&
|
196
|
+
obj.respond_to?(:aref))
|
197
|
+
true
|
198
|
+
else
|
199
|
+
false
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
|
204
|
+
BasicCreator = ::Struct.new(:typemap, :offset, :fields)
|
205
|
+
|
206
|
+
class BasicCreator
|
207
|
+
def maxalign(fields = self.fields)
|
208
|
+
fields.map { |f| f.type.bytealign }.max
|
209
|
+
end
|
210
|
+
|
211
|
+
def maxsize(fields = self.fields)
|
212
|
+
fields.map { |f| s = f.type.bytesize; f.vector ? f.vector.inject(&:*) * s : s }.max
|
213
|
+
end
|
214
|
+
|
215
|
+
def flatten_field(fields = self.fields)
|
216
|
+
#pp fields
|
217
|
+
fields2 = []
|
218
|
+
fields.each do |f|
|
219
|
+
#p f
|
220
|
+
#p f.class
|
221
|
+
if f.name
|
222
|
+
fields2 << f
|
223
|
+
else
|
224
|
+
raise "BUG? : field.type is not a Model (%p)" % f.type unless f.type.kind_of?(Model)
|
225
|
+
fs = flatten_field(f.type.fields)
|
226
|
+
fs.each { |ff| ff.offset += f.offset; ff.mark_const if f.const? }
|
227
|
+
fields2.concat fs
|
228
|
+
end
|
229
|
+
end
|
230
|
+
fields2
|
231
|
+
end
|
232
|
+
|
233
|
+
# :nodoc: all
|
234
|
+
class Proxy < Object
|
235
|
+
#class Proxy < BasicObject
|
236
|
+
def initialize(creator)
|
237
|
+
#singleton_class = (class << proxy; self; end)
|
238
|
+
singleton_class.class_eval do
|
239
|
+
latest_fields = nil
|
240
|
+
#define_method(:method_missing, ->(type, *args) { latest_fields = creator.addfield(type, args); nil })
|
241
|
+
creator.typemap.each_key do |t|
|
242
|
+
define_method(t, ->(*args) { latest_fields = creator.addfield(t, args); nil })
|
243
|
+
end
|
244
|
+
define_method(:struct, ->(*args, &block) { latest_fields = creator.struct(args, &block); nil })
|
245
|
+
define_method(:union, ->(*args, &block) { latest_fields = creator.union(args, &block); nil })
|
246
|
+
define_method(:const, ->(dummy_fields) { creator.const(latest_fields); latest_fields = nil; nil })
|
247
|
+
define_method(:typedef, ->(*args, &block) { creator.typedef(args, &block) })
|
248
|
+
if creator.respond_to?(:bytealign)
|
249
|
+
define_method(:bytealign, ->(bytesize, &block) { creator.bytealign(bytesize, &block) })
|
250
|
+
end
|
251
|
+
if creator.respond_to?(:padding)
|
252
|
+
define_method(:padding, ->(bytesize, &block) { creator.padding(bytesize, &block) })
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
#
|
259
|
+
# call-seq:
|
260
|
+
# struct type, name, *vector
|
261
|
+
# struct proc, name, *vector
|
262
|
+
# struct { ... }
|
263
|
+
#
|
264
|
+
# 最初の呼び出し方法は、既存の (typedef していない) 型情報を用いる、または構造体をその場で定義するために利用できます。
|
265
|
+
#
|
266
|
+
# 二番目の呼び出し方法は、無名構造体を定義するために利用できます。
|
267
|
+
#
|
268
|
+
# === example (型情報を用いる)
|
269
|
+
#
|
270
|
+
# Type1 = struct {
|
271
|
+
# struct UserType, :a, :b, 2, 3, 4
|
272
|
+
# }
|
273
|
+
#
|
274
|
+
# === example (構造体をその場で定義して、構造体へのアクセッサを定義する)
|
275
|
+
#
|
276
|
+
# Type2 = struct {
|
277
|
+
# struct -> {
|
278
|
+
# int :x, y, z
|
279
|
+
# }, :a, :b, 2, 3, 4
|
280
|
+
# }
|
281
|
+
#
|
282
|
+
# === example (無名構造体)
|
283
|
+
#
|
284
|
+
# Type3 = struct {
|
285
|
+
# struct {
|
286
|
+
# int :a, :b, 2, 3, 4
|
287
|
+
# }
|
288
|
+
# }
|
289
|
+
#
|
290
|
+
def struct(args, &block)
|
291
|
+
define_container(args, block, Model.method(:struct))
|
292
|
+
end
|
293
|
+
|
294
|
+
#
|
295
|
+
# call-seq:
|
296
|
+
# union type, name, *vector
|
297
|
+
# union proc, name, *vector
|
298
|
+
# union { ... }
|
299
|
+
#
|
300
|
+
# 共用体を定義します。
|
301
|
+
#
|
302
|
+
# 呼び出し方は struct と変わりません。
|
303
|
+
#
|
304
|
+
# ただ、<tt>union type, ...</tt> の場合は、<tt>struct type, ...</tt> と同じ結果となります。
|
305
|
+
# これは type がどのような構造になっているのかを gogyou が管理も把握もしないためです。
|
306
|
+
# この記述ができる唯一の理由は、人間が見てわかりやすくすることを意図しています
|
307
|
+
# (ただし、ミスリードを誘う手口にも利用されてしまうのが最大の欠点です)。
|
308
|
+
#
|
309
|
+
def union(args, &block)
|
310
|
+
define_container(args, block, Model.method(:union))
|
311
|
+
end
|
312
|
+
|
313
|
+
def define_container(args, anonymblock, container)
|
314
|
+
if anonymblock
|
315
|
+
raise ArgumentError, "given block and arguments" unless args.empty?
|
316
|
+
model = container.(typemap.dup, &anonymblock)
|
317
|
+
raise "BUG - object is not a Model (#{model.class})" unless model.kind_of?(Model)
|
318
|
+
#p model: model, superclass: model.superclass
|
319
|
+
self.offset = offset.align_ceil(model.bytealign) unless kind_of?(Model::Union::Creator)
|
320
|
+
fields << f = Field[offset, nil, nil, model, 0]
|
321
|
+
self.offset += model.bytesize unless kind_of?(Model::Union::Creator)
|
322
|
+
[f]
|
323
|
+
else
|
324
|
+
type = args.shift
|
325
|
+
type = container.(typemap.dup, &type) if type.kind_of?(::Proc)
|
326
|
+
addfield!(type, args)
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
def typedef(args)
|
331
|
+
raise NotImplementedError
|
332
|
+
end
|
333
|
+
|
334
|
+
def const(fields)
|
335
|
+
fields.each { |f| f.mark_const }
|
336
|
+
end
|
337
|
+
|
338
|
+
#
|
339
|
+
# フィールド名の解析
|
340
|
+
#
|
341
|
+
def parse!(args)
|
342
|
+
raise ArgumentError, "nothing argument" if args.empty?
|
343
|
+
name = nil
|
344
|
+
vector = nil
|
345
|
+
while arg = args.shift
|
346
|
+
case arg
|
347
|
+
when Symbol, String
|
348
|
+
yield(name, vector) if name
|
349
|
+
raise ArgumentError, "informal field name (#{arg.to_s})" unless arg =~ FIELDNAME_PATTERN
|
350
|
+
name = arg.intern
|
351
|
+
vector = nil
|
352
|
+
when Integer
|
353
|
+
raise ArgumentError, "first argument is field name only (#{arg})" unless name
|
354
|
+
raise ArgumentError, "given negative number (#{arg})" unless arg >= 0
|
355
|
+
vector ||= []
|
356
|
+
vector << arg.to_i
|
357
|
+
if vector[-1] == 0
|
358
|
+
yield(name, vector)
|
359
|
+
unless args.empty?
|
360
|
+
raise ArgumentError, "given fields after extensible vector"
|
361
|
+
end
|
362
|
+
return nil
|
363
|
+
end
|
364
|
+
else
|
365
|
+
raise ArgumentError, "given any object (#{arg.inspect})"
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
yield(name, vector)
|
370
|
+
|
371
|
+
nil
|
372
|
+
end
|
373
|
+
|
374
|
+
def addfield(type, args)
|
375
|
+
typeobj = typemap[type.intern]
|
376
|
+
unless typeobj
|
377
|
+
raise NoMethodError, "typename or method is missing (#{type})"
|
378
|
+
end
|
379
|
+
|
380
|
+
addfield!(typeobj, args)
|
381
|
+
end
|
382
|
+
|
383
|
+
def addfield!(typeobj, args)
|
384
|
+
#p typeobj
|
385
|
+
# check extensible field >>> creator.fields[-1].vector[-1]
|
386
|
+
if (x = fields[-1]) && (x = x.vector) && x[-1] == 0
|
387
|
+
raise ArgumentError, "not given fields after extensible vector"
|
388
|
+
end
|
389
|
+
|
390
|
+
typesize = typeobj.bytesize
|
391
|
+
typealign = typeobj.bytealign
|
392
|
+
|
393
|
+
tmpfields = []
|
394
|
+
|
395
|
+
parse!(args) do |name, vect|
|
396
|
+
self.offset = offset.align_ceil(typealign) unless kind_of?(Model::Union::Creator)
|
397
|
+
fields << f = Field[offset, name, vect, typeobj, 0]
|
398
|
+
tmpfields << f
|
399
|
+
unless kind_of?(Model::Union::Creator)
|
400
|
+
elements = vect ? vect.inject(1, &:*) : 1
|
401
|
+
self.offset += typesize * elements
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
tmpfields
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
class Struct < Model
|
410
|
+
class Creator < Model::BasicCreator
|
411
|
+
def bytealign(bytesize)
|
412
|
+
raise NotImplementedError
|
413
|
+
end
|
414
|
+
|
415
|
+
def padding(bytesize)
|
416
|
+
raise NotImplementedError
|
417
|
+
end
|
418
|
+
|
419
|
+
def to_model
|
420
|
+
Model::Struct.new(offset.align_ceil(maxalign), maxalign, flatten_field)
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
def aset(buffer, offset, value)
|
425
|
+
raise NotImplementedError
|
426
|
+
end
|
427
|
+
|
428
|
+
def aref(buffer, offset)
|
429
|
+
v = Accessor::TemporaryStruct.new(buffer, offset, self)
|
430
|
+
v.infect_from(self, buffer) unless v.frozen?
|
431
|
+
v.freeze if frozen? || buffer.frozen?
|
432
|
+
v
|
433
|
+
end
|
434
|
+
|
435
|
+
def create_accessor
|
436
|
+
Accessor::Struct.define(self)
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
class Union < Model
|
441
|
+
class Creator < Model::BasicCreator
|
442
|
+
def to_model
|
443
|
+
Model::Union.new(maxsize.align_ceil(maxalign), maxalign, flatten_field)
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
447
|
+
def aset(buffer, offset, value)
|
448
|
+
raise NotImplementedError
|
449
|
+
end
|
450
|
+
|
451
|
+
def aref(buffer, offset)
|
452
|
+
v = Accessor::TemporaryUnion.new(buffer, offset, self)
|
453
|
+
v.infect_from(self, buffer) unless v.frozen?
|
454
|
+
v.freeze if frozen? || buffer.frozen?
|
455
|
+
v
|
456
|
+
end
|
457
|
+
|
458
|
+
def create_accessor
|
459
|
+
Accessor::Union.define(self)
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
#
|
464
|
+
# C の配列を模造するクラス。
|
465
|
+
#
|
466
|
+
class Array < Model
|
467
|
+
def extensible?
|
468
|
+
fields[-1] == 0 ? true : false
|
469
|
+
end
|
470
|
+
|
471
|
+
def aset(buffer, offset, value)
|
472
|
+
raise NotImplementedError
|
473
|
+
end
|
474
|
+
|
475
|
+
def aref(buffer, offset)
|
476
|
+
raise NotImplementedError
|
477
|
+
accessor = Accessor::Array[buffer, offset, self]
|
478
|
+
accessor.instance_eval do
|
479
|
+
field = fields[0]
|
480
|
+
type = field.type
|
481
|
+
elements = field.vector[-1]
|
482
|
+
|
483
|
+
define_singleton_method(:check_index, ->(i) {
|
484
|
+
i = i.to_i
|
485
|
+
raise IndexError unless i >= 0 && (elements.nil? || i < elements)
|
486
|
+
i
|
487
|
+
})
|
488
|
+
|
489
|
+
define_singleton_method(:[], ->(i) {
|
490
|
+
v = type.aref(buffer__GOGYOU__, offset__GOGYOU__ + type.bytesize * check_index(i))
|
491
|
+
v.infect_from(self, buffer) unless v.frozen?
|
492
|
+
v.freeze if frozen? || buffer.frozen? || field.const?
|
493
|
+
v
|
494
|
+
})
|
495
|
+
|
496
|
+
define_singleton_method(:[]=, ->(i, v) {
|
497
|
+
type.aset(buffer__GOGYOU__, offset__GOGYOU__ + type.bytesize * check_index(i), v)
|
498
|
+
})
|
499
|
+
end
|
500
|
+
accessor
|
501
|
+
end
|
502
|
+
end
|
503
|
+
end
|
504
|
+
end
|