gogyou 0.1.240911.prototype → 0.2

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.
@@ -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