ruby-changes:25857
From: tenderlove <ko1@a...>
Date: Wed, 28 Nov 2012 09:09:32 +0900 (JST)
Subject: [ruby-changes:25857] tenderlove:r37914 (trunk): * ext/fiddle/fiddle.c: adding alignment constants for compatibility
tenderlove 2012-11-28 09:02:49 +0900 (Wed, 28 Nov 2012) New Revision: 37914 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=37914 Log: * ext/fiddle/fiddle.c: adding alignment constants for compatibility with DL. * ext/fiddle/fiddle.h: ditto * ext/fiddle/lib/fiddle/cparser.rb: importing the C parser for DL backwards compatibility. * ext/fiddle/lib/fiddle/import.rb: importing the import DSL for DL backwards compatibility. * ext/fiddle/lib/fiddle/pack.rb: importing structure pack for DL backwards compatibility. * ext/fiddle/lib/fiddle/value.rb: ditto * ext/fiddle/lib/fiddle/struct.rb: importing struct DSL for DL backwards compatibility. * test/dl/test_c_struct_entry.rb: importing tests * test/dl/test_c_union_entity.rb: ditto * test/dl/test_cparser.rb: ditto * test/dl/test_import.rb: ditto * test/fiddle/test_c_struct_entry.rb: ditto * test/fiddle/test_c_union_entity.rb: ditto * test/fiddle/test_cparser.rb: ditto * test/fiddle/test_import.rb: ditto Added files: trunk/ext/fiddle/lib/fiddle/cparser.rb trunk/ext/fiddle/lib/fiddle/import.rb trunk/ext/fiddle/lib/fiddle/pack.rb trunk/ext/fiddle/lib/fiddle/struct.rb trunk/ext/fiddle/lib/fiddle/value.rb trunk/test/fiddle/test_c_struct_entry.rb trunk/test/fiddle/test_c_union_entity.rb trunk/test/fiddle/test_cparser.rb trunk/test/fiddle/test_import.rb Modified files: trunk/ChangeLog trunk/ext/fiddle/fiddle.c trunk/ext/fiddle/fiddle.h trunk/test/dl/test_c_struct_entry.rb trunk/test/dl/test_c_union_entity.rb trunk/test/dl/test_cparser.rb trunk/test/dl/test_import.rb Index: ChangeLog =================================================================== --- ChangeLog (revision 37913) +++ ChangeLog (revision 37914) @@ -1,3 +1,26 @@ +Wed Nov 28 09:00:34 2012 Aaron Patterson <aaron@t...> + + * ext/fiddle/fiddle.c: adding alignment constants for compatibility + with DL. + * ext/fiddle/fiddle.h: ditto + * ext/fiddle/lib/fiddle/cparser.rb: importing the C parser for DL + backwards compatibility. + * ext/fiddle/lib/fiddle/import.rb: importing the import DSL for DL + backwards compatibility. + * ext/fiddle/lib/fiddle/pack.rb: importing structure pack for DL + backwards compatibility. + * ext/fiddle/lib/fiddle/value.rb: ditto + * ext/fiddle/lib/fiddle/struct.rb: importing struct DSL for DL backwards + compatibility. + * test/dl/test_c_struct_entry.rb: importing tests + * test/dl/test_c_union_entity.rb: ditto + * test/dl/test_cparser.rb: ditto + * test/dl/test_import.rb: ditto + * test/fiddle/test_c_struct_entry.rb: ditto + * test/fiddle/test_c_union_entity.rb: ditto + * test/fiddle/test_cparser.rb: ditto + * test/fiddle/test_import.rb: ditto + Wed Nov 28 08:56:00 2012 Zachary Scott <zachary@z...> * doc/globals.rdoc: Add documentation file for magic globals Index: ext/fiddle/lib/fiddle/pack.rb =================================================================== --- ext/fiddle/lib/fiddle/pack.rb (revision 0) +++ ext/fiddle/lib/fiddle/pack.rb (revision 37914) @@ -0,0 +1,128 @@ +require 'fiddle' + +module Fiddle + module PackInfo + ALIGN_MAP = { + TYPE_VOIDP => ALIGN_VOIDP, + TYPE_CHAR => ALIGN_CHAR, + TYPE_SHORT => ALIGN_SHORT, + TYPE_INT => ALIGN_INT, + TYPE_LONG => ALIGN_LONG, + TYPE_FLOAT => ALIGN_FLOAT, + TYPE_DOUBLE => ALIGN_DOUBLE, + -TYPE_CHAR => ALIGN_CHAR, + -TYPE_SHORT => ALIGN_SHORT, + -TYPE_INT => ALIGN_INT, + -TYPE_LONG => ALIGN_LONG, + } + + PACK_MAP = { + TYPE_VOIDP => ((SIZEOF_VOIDP == SIZEOF_LONG_LONG) ? "q" : "l!"), + TYPE_CHAR => "c", + TYPE_SHORT => "s!", + TYPE_INT => "i!", + TYPE_LONG => "l!", + TYPE_FLOAT => "f", + TYPE_DOUBLE => "d", + -TYPE_CHAR => "c", + -TYPE_SHORT => "s!", + -TYPE_INT => "i!", + -TYPE_LONG => "l!", + } + + SIZE_MAP = { + TYPE_VOIDP => SIZEOF_VOIDP, + TYPE_CHAR => SIZEOF_CHAR, + TYPE_SHORT => SIZEOF_SHORT, + TYPE_INT => SIZEOF_INT, + TYPE_LONG => SIZEOF_LONG, + TYPE_FLOAT => SIZEOF_FLOAT, + TYPE_DOUBLE => SIZEOF_DOUBLE, + -TYPE_CHAR => SIZEOF_CHAR, + -TYPE_SHORT => SIZEOF_SHORT, + -TYPE_INT => SIZEOF_INT, + -TYPE_LONG => SIZEOF_LONG, + } + if defined?(TYPE_LONG_LONG) + ALIGN_MAP[TYPE_LONG_LONG] = ALIGN_MAP[-TYPE_LONG_LONG] = ALIGN_LONG_LONG + PACK_MAP[TYPE_LONG_LONG] = PACK_MAP[-TYPE_LONG_LONG] = "q" + SIZE_MAP[TYPE_LONG_LONG] = SIZE_MAP[-TYPE_LONG_LONG] = SIZEOF_LONG_LONG + end + + def align(addr, align) + d = addr % align + if( d == 0 ) + addr + else + addr + (align - d) + end + end + module_function :align + end + + class Packer + include PackInfo + + def self.[](*types) + new(types) + end + + def initialize(types) + parse_types(types) + end + + def size() + @size + end + + def pack(ary) + case SIZEOF_VOIDP + when SIZEOF_LONG + ary.pack(@template) + when SIZEOF_LONG_LONG + ary.pack(@template) + else + raise(RuntimeError, "sizeof(void*)?") + end + end + + def unpack(ary) + case SIZEOF_VOIDP + when SIZEOF_LONG + ary.join().unpack(@template) + when SIZEOF_LONG_LONG + ary.join().unpack(@template) + else + raise(RuntimeError, "sizeof(void*)?") + end + end + + private + + def parse_types(types) + @template = "" + addr = 0 + types.each{|t| + orig_addr = addr + if( t.is_a?(Array) ) + addr = align(orig_addr, ALIGN_MAP[TYPE_VOIDP]) + else + addr = align(orig_addr, ALIGN_MAP[t]) + end + d = addr - orig_addr + if( d > 0 ) + @template << "x#{d}" + end + if( t.is_a?(Array) ) + @template << (PACK_MAP[t[0]] * t[1]) + addr += (SIZE_MAP[t[0]] * t[1]) + else + @template << PACK_MAP[t] + addr += SIZE_MAP[t] + end + } + addr = align(addr, ALIGN_MAP[TYPE_VOIDP]) + @size = addr + end + end +end Index: ext/fiddle/lib/fiddle/cparser.rb =================================================================== --- ext/fiddle/lib/fiddle/cparser.rb (revision 0) +++ ext/fiddle/lib/fiddle/cparser.rb (revision 37914) @@ -0,0 +1,156 @@ +module Fiddle + # Methods for parsing C struct and C prototype signatures. + module CParser + # Parses a C struct's members + # + # Example: + # + # parse_struct_signature(['int i', 'char c']) + # => [[Fiddle::TYPE_INT, Fiddle::TYPE_CHAR], ["i", "c"]] + # + def parse_struct_signature(signature, tymap=nil) + if( signature.is_a?(String) ) + signature = signature.split(/\s*,\s*/) + end + mems = [] + tys = [] + signature.each{|msig| + tks = msig.split(/\s+(\*)?/) + ty = tks[0..-2].join(" ") + member = tks[-1] + + case ty + when /\[(\d+)\]/ + n = $1.to_i + ty.gsub!(/\s*\[\d+\]/,"") + ty = [ty, n] + when /\[\]/ + ty.gsub!(/\s*\[\]/, "*") + end + + case member + when /\[(\d+)\]/ + ty = [ty, $1.to_i] + member.gsub!(/\s*\[\d+\]/,"") + when /\[\]/ + ty = ty + "*" + member.gsub!(/\s*\[\]/, "") + end + + mems.push(member) + tys.push(parse_ctype(ty,tymap)) + } + return tys, mems + end + + # Parses a C prototype signature + # + # Example: + # + # include Fiddle::CParser + # => Object + # + # parse_signature('double sum(double, double)') + # => ["sum", Fiddle::TYPE_DOUBLE, [Fiddle::TYPE_DOUBLE, Fiddle::TYPE_DOUBLE]] + # + def parse_signature(signature, tymap=nil) + tymap ||= {} + signature = signature.gsub(/\s+/, " ").strip + case signature + when /^([\w@\*\s]+)\(([\w\*\s\,\[\]]*)\)$/ + ret = $1 + (args = $2).strip! + ret = ret.split(/\s+/) + args = args.split(/\s*,\s*/) + func = ret.pop + if( func =~ /^\*/ ) + func.gsub!(/^\*+/,"") + ret.push("*") + end + ret = ret.join(" ") + return [func, parse_ctype(ret, tymap), args.collect{|arg| parse_ctype(arg, tymap)}] + else + raise(RuntimeError,"can't parse the function prototype: #{signature}") + end + end + + # Given a String of C type +ty+, return the corresponding DL constant. + # + # +ty+ can also accept an Array of C type Strings, and will returned in a + # corresponding Array. + # + # If Hash +tymap+ is provided, +ty+ is expected to be the key, and the + # value will be the C type to be looked up. + # + # Example: + # + # parse_ctype('int') + # => Fiddle::TYPE_INT + # + # parse_ctype('double') + # => Fiddle::TYPE_DOUBLE + # + # parse_ctype('unsigned char') + # => -Fiddle::TYPE_CHAR + # + def parse_ctype(ty, tymap=nil) + tymap ||= {} + case ty + when Array + return [parse_ctype(ty[0], tymap), ty[1]] + when "void" + return TYPE_VOID + when "char" + return TYPE_CHAR + when "unsigned char" + return -TYPE_CHAR + when "short" + return TYPE_SHORT + when "unsigned short" + return -TYPE_SHORT + when "int" + return TYPE_INT + when "unsigned int", 'uint' + return -TYPE_INT + when "long" + return TYPE_LONG + when "unsigned long" + return -TYPE_LONG + when "long long" + if( defined?(TYPE_LONG_LONG) ) + return TYPE_LONG_LONG + else + raise(RuntimeError, "unsupported type: #{ty}") + end + when "unsigned long long" + if( defined?(TYPE_LONG_LONG) ) + return -TYPE_LONG_LONG + else + raise(RuntimeError, "unsupported type: #{ty}") + end + when "float" + return TYPE_FLOAT + when "double" + return TYPE_DOUBLE + when "size_t" + return TYPE_SIZE_T + when "ssize_t" + return TYPE_SSIZE_T + when "ptrdiff_t" + return TYPE_PTRDIFF_T + when "intptr_t" + return TYPE_INTPTR_T + when "uintptr_t" + return TYPE_UINTPTR_T + when /\*/, /\[\s*\]/ + return TYPE_VOIDP + else + if( tymap[ty] ) + return parse_ctype(tymap[ty], tymap) + else + raise(DLError, "unknown type: #{ty}") + end + end + end + end +end Index: ext/fiddle/lib/fiddle/struct.rb =================================================================== --- ext/fiddle/lib/fiddle/struct.rb (revision 0) +++ ext/fiddle/lib/fiddle/struct.rb (revision 37914) @@ -0,0 +1,236 @@ +require 'fiddle' +require 'fiddle/value' +require 'fiddle/pack' + +module Fiddle + # C struct shell + class CStruct + # accessor to Fiddle::CStructEntity + def CStruct.entity_class + CStructEntity + end + end + + # C union shell + class CUnion + # accessor to Fiddle::CUnionEntity + def CUnion.entity_class + CUnionEntity + end + end + + # Used to construct C classes (CUnion, CStruct, etc) + # + # Fiddle::Importer#struct and Fiddle::Importer#union wrap this functionality in an + # easy-to-use manner. + module CStructBuilder + # Construct a new class given a C: + # * class +klass+ (CUnion, CStruct, or other that provide an + # #entity_class) + # * +types+ (Fiddle::TYPE_INT, Fiddle::TYPE_SIZE_T, etc., see the C types + # constants) + # * corresponding +members+ + # + # Fiddle::Importer#struct and Fiddle::Importer#union wrap this functionality in an + # easy-to-use manner. + # + # Example: + # + # require 'dl/struct' + # require 'dl/cparser' + # + # include Fiddle::CParser + # + # types, members = parse_struct_signature(['int i','char c']) + # + # MyStruct = Fiddle::CStructBuilder.create(CUnion, types, members) + # + # obj = MyStruct.allocate + # + def create(klass, types, members) + new_class = Class.new(klass){ + define_method(:initialize){|addr| + @entity = klass.entity_class.new(addr, types) + @entity.assign_names(members) + } + define_method(:to_ptr){ @entity } + define_method(:to_i){ @entity.to_i } + members.each{|name| + define_method(name){ @entity[name] } + define_method(name + "="){|val| @entity[name] = val } + } + } + size = klass.entity_class.size(types) + new_class.module_eval(<<-EOS, __FILE__, __LINE__+1) + def new_class.size() + #{size} + end + def new_class.malloc() + addr = Fiddle.malloc(#{size}) + new(addr) + end + EOS + return new_class + end + module_function :create + end + + # A C struct wrapper + class CStructEntity < Fiddle::Pointer + include PackInfo + include ValueUtil + + # Allocates a C struct the +types+ provided. The C function +func+ is + # called when the instance is garbage collected. + def CStructEntity.malloc(types, func = nil) + addr = Fiddle.malloc(CStructEntity.size(types)) + CStructEntity.new(addr, types, func) + end + + # Given +types+, returns the offset for the packed sizes of those types + # + # Fiddle::CStructEntity.size([Fiddle::TYPE_DOUBLE, Fiddle::TYPE_INT, Fiddle::TYPE_CHAR, + # Fiddle::TYPE_VOIDP]) + # => 24 + def CStructEntity.size(types) + offset = 0 + + max_align = types.map { |type, count = 1| + last_offset = offset + + align = PackInfo::ALIGN_MAP[type] + offset = PackInfo.align(last_offset, align) + + (PackInfo::SIZE_MAP[type] * count) + + align + }.max + + PackInfo.align(offset, max_align) + end + + # Wraps the C pointer +addr+ as a C struct with the given +types+. The C + # function +func+ is called when the instance is garbage collected. + # + # See also Fiddle::CPtr.new + def initialize(addr, types, func = nil) + set_ctypes(types) + super(addr, @size, func) + end + + # Set the names of the +members+ in this C struct + def assign_names(members) + @members = members + end + + # Given +types+, calculate the offsets and sizes for the types in the + # struct. + def set_ctypes(types) + @ctypes = types + @offset = [] + offset = 0 + + max_align = types.map { |type, count = 1| + orig_offset = offset + align = ALIGN_MAP[type] + offset = PackInfo.align(orig_offset, align) + + @offset << offset + + offset += (SIZE_MAP[type] * count) + + align + }.max + + @size = PackInfo.align(offset, max_align) + end + + # Fetch struct member +name+ + def [](name) + idx = @members.index(name) + if( idx.nil? ) + raise(ArgumentError, "no such member: #{name}") + end + ty = @ctypes[idx] + if( ty.is_a?(Array) ) + r = super(@offset[idx], SIZE_MAP[ty[0]] * ty[1]) + else + r = super(@offset[idx], SIZE_MAP[ty.abs]) + end + packer = Packer.new([ty]) + val = packer.unpack([r]) + case ty + when Array + case ty[0] + when TYPE_VOIDP + val = val.collect{|v| CPtr.new(v)} + end + when TYPE_VOIDP + val = CPtr.new(val[0]) + else + val = val[0] + end + if( ty.is_a?(Integer) && (ty < 0) ) + return unsigned_value(val, ty) + elsif( ty.is_a?(Array) && (ty[0] < 0) ) + return val.collect{|v| unsigned_value(v,ty[0])} + else + return val + end + end + + # Set struct member +name+, to value +val+ + def []=(name, val) + idx = @members.index(name) + if( idx.nil? ) + raise(ArgumentError, "no such member: #{name}") + end + ty = @ctypes[idx] + packer = Packer.new([ty]) + val = wrap_arg(val, ty, []) + buff = packer.pack([val].flatten()) + super(@offset[idx], buff.size, buff) + if( ty.is_a?(Integer) && (ty < 0) ) + return unsigned_value(val, ty) + elsif( ty.is_a?(Array) && (ty[0] < 0) ) + return val.collect{|v| unsigned_value(v,ty[0])} + else + return val + end + end + + def to_s() # :nodoc: + super(@size) + end + end + + # A C union wrapper + class CUnionEntity < CStructEntity + include PackInfo + + # Allocates a C union the +types+ provided. The C function +func+ is + # called when the instance is garbage collected. + def CUnionEntity.malloc(types, func=nil) + addr = Fiddle.malloc(CUnionEntity.size(types)) + CUnionEntity.new(addr, types, func) + end + + # Given +types+, returns the size needed for the union. + # + # Fiddle::CUnionEntity.size([Fiddle::TYPE_DOUBLE, Fiddle::TYPE_INT, Fiddle::TYPE_CHAR, + # Fiddle::TYPE_VOIDP]) + # => 8 + def CUnionEntity.size(types) + types.map { |type, count = 1| + PackInfo::SIZE_MAP[type] * count + }.max + end + + # Given +types+, calculate the necessary offset and for each union member + def set_ctypes(types) + @ctypes = types + @offset = Array.new(types.length, 0) + @size = self.class.size types + end + end +end + Index: ext/fiddle/lib/fiddle/import.rb =================================================================== --- ext/fiddle/lib/fiddle/import.rb (revision 0) +++ ext/fiddle/lib/fiddle/import.rb (revision 37914) @@ -0,0 +1,241 @@ +require 'fiddle' +require 'fiddle/struct' +require 'fiddle/cparser' + +module Fiddle + class CompositeHandler + def initialize(handlers) + @handlers = handlers + end + + def handlers() + @handlers + end + + def sym(symbol) + @handlers.each{|handle| + if( handle ) + begin + addr = handle.sym(symbol) + return addr + rescue DLError + end + end + } + return nil + end + + def [](symbol) + sym(symbol) + end + end + + # DL::Importer includes the means to dynamically load libraries and build + # modules around them including calling extern functions within the C + # library that has been loaded. + # + # == Example + # + # require 'dl' + # require 'dl/import' + # + # module LibSum + # extend DL::Importer + # dlload './libsum.so' + # extern 'double sum(double*, int)' + # extern 'double split(double)' + # end + # + module Importer + include Fiddle + include CParser + extend Importer + + def dlload(*libs) + handles = libs.collect{|lib| + case lib + when nil + nil + when Handle + lib + when Importer + lib.handlers + else + begin + Fiddle.dlopen(lib) + rescue DLError + raise(DLError, "can't load #{lib}") + end + end + }.flatten() + @handler = CompositeHandler.new(handles) + @func_map = {} + @type_alias = {} + end + + def typealias(alias_type, orig_type) + @type_alias[alias_type] = orig_type + end + + def sizeof(ty) + case ty + when String + ty = parse_ctype(ty, @type_alias).abs() + case ty + when TYPE_CHAR + return SIZEOF_CHAR + when TYPE_SHORT + return SIZEOF_SHORT + when TYPE_INT + return SIZEOF_INT + when TYPE_LONG + return SIZEOF_LONG + when TYPE_LONG_LONG + return SIZEOF_LONG_LON + when TYPE_FLOAT + return SIZEOF_FLOAT + when TYPE_DOUBLE + return SIZEOF_DOUBLE + when TYPE_VOIDP + return SIZEOF_VOIDP + else + raise(DLError, "unknown type: #{ty}") + end + when Class + if( ty.instance_methods().include?(:to_ptr) ) + return ty.size() + end + end + return CPtr[ty].size() + end + + def parse_bind_options(opts) + h = {} + while( opt = opts.shift() ) + case opt + when :stdcall, :cdecl + h[:call_type] = opt + when :carried, :temp, :temporal, :bind + h[:callback_type] = opt + h[:carrier] = opts.shift() + else + h[opt] = true + end + end + h + end + private :parse_bind_options + + def extern(signature, *opts) + symname, ctype, argtype = parse_signature(signature, @type_alias) + opt = parse_bind_options(opts) + f = import_function(symname, ctype, argtype, opt[:call_type]) + name = symname.gsub(/@.+/,'') + @func_map[name] = f + # define_method(name){|*args,&block| f.call(*args,&block)} + begin + /^(.+?):(\d+)/ =~ caller.first + file, line = $1, $2.to_i + rescue + file, line = __FILE__, __LINE__+3 + end + module_eval(<<-EOS, file, line) + def #{name}(*args, &block) + @func_map['#{name}'].call(*args,&block) + end + EOS + module_function(name) + f + end + + def bind(signature, *opts, &blk) + name, ctype, argtype = parse_signature(signature, @type_alias) + h = parse_bind_options(opts) + case h[:callback_type] + when :bind, nil + f = bind_function(name, ctype, argtype, h[:call_type], &blk) + else + raise(RuntimeError, "unknown callback type: #{h[:callback_type]}") + end + @func_map[name (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/