lib/gogyou/model.rb in gogyou-0.2.1 vs lib/gogyou/model.rb in gogyou-0.2.2

- old
+ new

@@ -64,17 +64,18 @@ # 構造体の各メンバの情報を保持する class Field < ::Struct.new(:offset, # field offset in model :name, # field name :vector, # 要素数。任意数配列の場合は 0。配列でないならば nil。 :type, # type specification (or model object) of this field - :flags) # 0x01: const / 0x02: packed (not aligned) + :flags) # LSB-0+8: packed size exponent (not aligned) / LSB-16: const BasicStruct = superclass - FLAG_CONST = 0x01 - FLAG_PACKED = 0x02 + CONST_BITMASK = 1 << 16 + PACKSIZE_BITMASK = 0xff + PACKSIZE_NOTDEFINE = 20 # = (1 << 20) = 1 MiB # 事実上の無限 - def initialize(offset, name, vector, type, flags = 0) + def initialize(offset, name, vector, type, flags = 0 | PACKSIZE_NOTDEFINE) super(offset, name, vector, type, flags) end def extensible? if vector @@ -83,29 +84,34 @@ type.extensible? end end def const? - ((flags & FLAG_CONST) == FLAG_CONST) ? true : false + ((flags & CONST_BITMASK) == CONST_BITMASK) ? true : false end def packed? - ((flags & FLAG_PACKED) == FLAG_PACKED) ? true : false + ((flags & PACKSIZE_BITMASK) == PACKSIZE_NOTDEFINE) ? false : true end - def mark_const - self.flags |= FLAG_CONST + def set_const + self.flags |= CONST_BITMASK self end - def mark_packed - self.flags |= FLAGS_PACKED + def set_packsize(pack_exponent) + pack_exponent = PACKSIZE_NOTDEFINE if pack_exponent > PACKSIZE_NOTDEFINE + self.flags = flags.setbit(0, 8, pack_exponent) self end + def packsize + 1 << flags.getbit(0, 8) + end + def strflags - set = [const? ? "const" : nil, packed? ? "packed" : nil] + set = [const? ? "const" : nil, packed? ? "packed(#{packsize})" : nil] set.compact! return nil if set.empty? set.join(",") end @@ -143,16 +149,16 @@ end q.text "]" end end - def self.struct(typemap, &block) - define_container(typemap, Model::Struct, &block) + def self.struct(typemap, packexp = Field::PACKSIZE_NOTDEFINE, &block) + define_container(typemap, packexp, Model::Struct, &block) end - def self.union(typemap, &block) - define_container(typemap, Model::Union, &block) + def self.union(typemap, packexp = Field::PACKSIZE_NOTDEFINE, &block) + define_container(typemap, packexp, Model::Union, &block) end def self.typedef(typemap, type, aliasname, *elements) raise ArgumentError, "informal aliasname (#{aliasname.inspect})" unless aliasname =~ FIELDNAME_PATTERN aliasname = aliasname.intern @@ -176,13 +182,13 @@ typemap[aliasname] = type nil end - def self.define_container(typemap, model_type, &block) + def self.define_container(typemap, packexp, model_type, &block) creator = model_type::Creator.new(typemap, 0, []) - proxy = model_type::Creator::Proxy.new(creator) + proxy = model_type::Creator::Proxy.new(creator, packexp) proxy.instance_exec(&block) model = creator.to_model model end @@ -203,11 +209,11 @@ BasicCreator = ::Struct.new(:typemap, :offset, :fields) class BasicCreator def maxalign(fields = self.fields) - fields.map { |f| f.type.bytealign }.max + fields.map { |f| f.packed? ? f.packsize : f.type.bytealign }.max end def maxsize(fields = self.fields) fields.map { |f| s = f.type.bytesize; f.vector ? f.vector.inject(&:*) * s : s }.max end @@ -221,37 +227,56 @@ if f.name fields2 << f else raise "BUG? : field.type is not a Model (%p)" % f.type unless f.type.kind_of?(Model) fs = flatten_field(f.type.fields) - fs.each { |ff| ff.offset += f.offset; ff.mark_const if f.const? } + fs.each { |ff| ff.offset += f.offset; ff.set_const if f.const? } fields2.concat fs end end fields2 end # :nodoc: all class Proxy < Object #class Proxy < BasicObject - def initialize(creator) + def initialize(creator, packexp = Field::PACKSIZE_NOTDEFINE) #singleton_class = (class << proxy; self; end) singleton_class.class_eval do latest_fields = nil #define_method(:method_missing, ->(type, *args) { latest_fields = creator.addfield(type, args); nil }) creator.typemap.each_key do |t| - define_method(t, ->(*args) { latest_fields = creator.addfield(t, args); nil }) + define_method(t, ->(*args) { latest_fields = creator.addfield(t, packexp, args); nil }) end - define_method(:struct, ->(*args, &block) { latest_fields = creator.struct(args, &block); nil }) - define_method(:union, ->(*args, &block) { latest_fields = creator.union(args, &block); nil }) + define_method(:struct, ->(*args, &block) { latest_fields = creator.struct(args, packexp, &block); nil }) + define_method(:union, ->(*args, &block) { latest_fields = creator.union(args, packexp, &block); nil }) define_method(:const, ->(dummy_fields) { creator.const(latest_fields); latest_fields = nil; nil }) define_method(:typedef, ->(*args, &block) { creator.typedef(args, &block) }) + packexp0 = nil + define_method(:packed, ->(bytealign = 1, &block) { + raise "wrong nested ``packed''" if packexp0 + exp = Math.log(bytealign, 2) + # exp が Nan Infinity -Infinity の場合は例外が発生するので、それに対する処置も行う + unless ((exp = exp.to_i) rescue nil) && (1 << exp) == bytealign + raise ArgumentError, "shall be given power of two (but #{bytealign})" + end + + begin + packexp0 = packexp + packexp = exp + self.instance_exec(&block) + ensure + (packexp, packexp0) = packexp0, nil + end + + nil + }) if creator.respond_to?(:bytealign) - define_method(:bytealign, ->(bytesize, &block) { creator.bytealign(bytesize, &block) }) + define_method(:bytealign, ->(bytesize, &block) { creator.bytealign(bytesize, &block); nil }) end if creator.respond_to?(:padding) - define_method(:padding, ->(bytesize, &block) { creator.padding(bytesize, &block) }) + define_method(:padding, ->(bytesize, &block) { creator.padding(bytesize, &block); nil }) end end end end @@ -285,12 +310,12 @@ # struct { # int :a, :b, 2, 3, 4 # } # } # - def struct(args, &block) - define_container(args, block, Model.method(:struct)) + def struct(args, packexp, &block) + define_container(args, packexp, block, Model.method(:struct)) end # # call-seq: # union type, name, *vector @@ -304,37 +329,37 @@ # ただ、<tt>union type, ...</tt> の場合は、<tt>struct type, ...</tt> と同じ結果となります。 # これは type がどのような構造になっているのかを gogyou が管理も把握もしないためです。 # この記述ができる唯一の理由は、人間が見てわかりやすくすることを意図しています # (ただし、ミスリードを誘う手口にも利用されてしまうのが最大の欠点です)。 # - def union(args, &block) - define_container(args, block, Model.method(:union)) + def union(args, packexp, &block) + define_container(args, packexp, block, Model.method(:union)) end - def define_container(args, anonymblock, container) + def define_container(args, packexp, anonymblock, container) if anonymblock raise ArgumentError, "given block and arguments" unless args.empty? - model = container.(typemap.dup, &anonymblock) + model = container.(typemap.dup, packexp, &anonymblock) raise "BUG - object is not a Model (#{model.class})" unless model.kind_of?(Model) #p model: model, superclass: model.superclass self.offset = offset.align_ceil(model.bytealign) unless kind_of?(Model::Union::Creator) - fields << f = Field[offset, nil, nil, model, 0] + fields << f = Field[offset, nil, nil, model, packexp] self.offset += model.bytesize unless kind_of?(Model::Union::Creator) [f] else type = args.shift - type = container.(typemap.dup, &type) if type.kind_of?(::Proc) - addfield!(type, args) + type = container.(typemap.dup, packexp, &type) if type.kind_of?(::Proc) + addfield!(type, packexp, args) end end def typedef(args) raise NotImplementedError end def const(fields) - fields.each { |f| f.mark_const } + fields.each { |f| f.set_const } end # # フィールド名の解析 # @@ -369,33 +394,33 @@ yield(name, vector) nil end - def addfield(type, args) + def addfield(type, packexp, args) typeobj = typemap[type.intern] unless typeobj raise NoMethodError, "typename or method is missing (#{type})" end - addfield!(typeobj, args) + addfield!(typeobj, packexp, args) end - def addfield!(typeobj, args) + def addfield!(typeobj, packexp, args) #p typeobj # check extensible field >>> creator.fields[-1].vector[-1] if (x = fields[-1]) && (x = x.vector) && x[-1] == 0 raise ArgumentError, "not given fields after extensible vector" end typesize = typeobj.bytesize - typealign = typeobj.bytealign + typealign = [typeobj.bytealign, 1 << packexp].min tmpfields = [] parse!(args) do |name, vect| self.offset = offset.align_ceil(typealign) unless kind_of?(Model::Union::Creator) - fields << f = Field[offset, name, vect, typeobj, 0] + fields << f = Field[offset, name, vect, typeobj, 0 | packexp] tmpfields << f unless kind_of?(Model::Union::Creator) elements = vect ? vect.inject(1, &:*) : 1 self.offset += typesize * elements end