diff options
Diffstat (limited to 'jni/ruby/ext/fiddle/lib/fiddle')
| -rw-r--r-- | jni/ruby/ext/fiddle/lib/fiddle/closure.rb | 48 | ||||
| -rw-r--r-- | jni/ruby/ext/fiddle/lib/fiddle/cparser.rb | 176 | ||||
| -rw-r--r-- | jni/ruby/ext/fiddle/lib/fiddle/function.rb | 17 | ||||
| -rw-r--r-- | jni/ruby/ext/fiddle/lib/fiddle/import.rb | 314 | ||||
| -rw-r--r-- | jni/ruby/ext/fiddle/lib/fiddle/pack.rb | 128 | ||||
| -rw-r--r-- | jni/ruby/ext/fiddle/lib/fiddle/struct.rb | 243 | ||||
| -rw-r--r-- | jni/ruby/ext/fiddle/lib/fiddle/types.rb | 71 | ||||
| -rw-r--r-- | jni/ruby/ext/fiddle/lib/fiddle/value.rb | 112 | 
8 files changed, 1109 insertions, 0 deletions
diff --git a/jni/ruby/ext/fiddle/lib/fiddle/closure.rb b/jni/ruby/ext/fiddle/lib/fiddle/closure.rb new file mode 100644 index 0000000..beb90ec --- /dev/null +++ b/jni/ruby/ext/fiddle/lib/fiddle/closure.rb @@ -0,0 +1,48 @@ +module Fiddle +  class Closure + +    # the C type of the return of the FFI closure +    attr_reader :ctype + +    # arguments of the FFI closure +    attr_reader :args + +    # Extends Fiddle::Closure to allow for building the closure in a block +    class BlockCaller < Fiddle::Closure + +      # == Description +      # +      # Construct a new BlockCaller object. +      # +      # * +ctype+ is the C type to be returned +      # * +args+ are passed the callback +      # * +abi+ is the abi of the closure +      # +      # If there is an error in preparing the +ffi_cif+ or +ffi_prep_closure+, +      # then a RuntimeError will be raised. +      # +      # == Example +      # +      #   include Fiddle +      # +      #   cb = Closure::BlockCaller.new(TYPE_INT, [TYPE_INT]) do |one| +      #     one +      #   end +      # +      #   func = Function.new(cb, [TYPE_INT], TYPE_INT) +      # +      def initialize ctype, args, abi = Fiddle::Function::DEFAULT, &block +        super(ctype, args, abi) +        @block = block +      end + +      # Calls the constructed BlockCaller, with +args+ +      # +      # For an example see Fiddle::Closure::BlockCaller.new +      # +      def call *args +        @block.call(*args) +      end +    end +  end +end diff --git a/jni/ruby/ext/fiddle/lib/fiddle/cparser.rb b/jni/ruby/ext/fiddle/lib/fiddle/cparser.rb new file mode 100644 index 0000000..43fb184 --- /dev/null +++ b/jni/ruby/ext/fiddle/lib/fiddle/cparser.rb @@ -0,0 +1,176 @@ +module Fiddle +  # A mixin that provides methods for parsing C struct and prototype signatures. +  # +  # == Example +  #   require 'fiddle/import' +  # +  #   include Fiddle::CParser +  #     #=> Object +  # +  #   parse_ctype('int increment(int)') +  #     #=> ["increment", Fiddle::TYPE_INT, [Fiddle::TYPE_INT]] +  # +  module CParser +    # Parses a C struct's members +    # +    # Example: +    # +    #   include Fiddle::CParser +    #     #=> Object +    # +    #   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 +    # +    # If Hash +tymap+ is provided, the return value and the arguments from the +    # +signature+ are expected to be keys, and the value will be the C type to +    # be looked up. +    # +    # 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+, returns the corresponding Fiddle constant. +    # +    # +ty+ can also accept an Array of C type Strings, and will be 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: +    # +    #   include Fiddle::CParser +    #     #=> Object +    # +    #   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 diff --git a/jni/ruby/ext/fiddle/lib/fiddle/function.rb b/jni/ruby/ext/fiddle/lib/fiddle/function.rb new file mode 100644 index 0000000..ab7496e --- /dev/null +++ b/jni/ruby/ext/fiddle/lib/fiddle/function.rb @@ -0,0 +1,17 @@ +module Fiddle +  class Function +    # The ABI of the Function. +    attr_reader :abi + +    # The address of this function +    attr_reader :ptr + +    # The name of this function +    attr_reader :name + +    # The integer memory location of this function +    def to_i +      ptr.to_i +    end +  end +end diff --git a/jni/ruby/ext/fiddle/lib/fiddle/import.rb b/jni/ruby/ext/fiddle/lib/fiddle/import.rb new file mode 100644 index 0000000..34f5d7f --- /dev/null +++ b/jni/ruby/ext/fiddle/lib/fiddle/import.rb @@ -0,0 +1,314 @@ +require 'fiddle' +require 'fiddle/struct' +require 'fiddle/cparser' + +module Fiddle + +  # Used internally by Fiddle::Importer +  class CompositeHandler +    # Create a new handler with the open +handlers+ +    # +    # Used internally by Fiddle::Importer.dlload +    def initialize(handlers) +      @handlers = handlers +    end + +    # Array of the currently loaded libraries. +    def handlers() +      @handlers +    end + +    # Returns the address as an Integer from any handlers with the function +    # named +symbol+. +    # +    # Raises a DLError if the handle is closed. +    def sym(symbol) +      @handlers.each{|handle| +        if( handle ) +          begin +            addr = handle.sym(symbol) +            return addr +          rescue DLError +          end +        end +      } +      return nil +    end + +    # See Fiddle::CompositeHandler.sym +    def [](symbol) +      sym(symbol) +    end +  end + +  # A DSL that provides 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 'fiddle' +  #   require 'fiddle/import' +  # +  #   module LibSum +  #   	extend Fiddle::Importer +  #   	dlload './libsum.so' +  #   	extern 'double sum(double*, int)' +  #   	extern 'double split(double)' +  #   end +  # +  module Importer +    include Fiddle +    include CParser +    extend Importer + +    # Creates an array of handlers for the given +libs+, can be an instance of +    # Fiddle::Handle, Fiddle::Importer, or will create a new instance of +    # Fiddle::Handle using Fiddle.dlopen +    # +    # Raises a DLError if the library cannot be loaded. +    # +    # See Fiddle.dlopen +    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 + +    # Sets the type alias for +alias_type+ as +orig_type+ +    def typealias(alias_type, orig_type) +      @type_alias[alias_type] = orig_type +    end + +    # Returns the sizeof +ty+, using Fiddle::Importer.parse_ctype to determine +    # the C type and the appropriate Fiddle constant. +    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_LONG +        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 Pointer[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 + +    # :stopdoc: +    CALL_TYPE_TO_ABI = Hash.new { |h, k| +      raise RuntimeError, "unsupported call type: #{k}" +    }.merge({ :stdcall => (Function::STDCALL rescue Function::DEFAULT), +              :cdecl   => Function::DEFAULT, +              nil      => Function::DEFAULT +            }).freeze +    private_constant :CALL_TYPE_TO_ABI +    # :startdoc: + +    # Creates a global method from the given C +signature+. +    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 + +    # Creates a global method from the given C +signature+ using the given +    # +opts+ as bind parameters with the given block. +    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] = 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 + +    # Creates a class to wrap the C struct described by +signature+. +    # +    #   MyStruct = struct ['int i', 'char c'] +    def struct(signature) +      tys, mems = parse_struct_signature(signature, @type_alias) +      Fiddle::CStructBuilder.create(CStruct, tys, mems) +    end + +    # Creates a class to wrap the C union described by +signature+. +    # +    #   MyUnion = union ['int i', 'char c'] +    def union(signature) +      tys, mems = parse_struct_signature(signature, @type_alias) +      Fiddle::CStructBuilder.create(CUnion, tys, mems) +    end + +    # Returns the function mapped to +name+, that was created by either +    # Fiddle::Importer.extern or Fiddle::Importer.bind +    def [](name) +      @func_map[name] +    end + +    # Creates a class to wrap the C struct with the value +ty+ +    # +    # See also Fiddle::Importer.struct +    def create_value(ty, val=nil) +      s = struct([ty + " value"]) +      ptr = s.malloc() +      if( val ) +        ptr.value = val +      end +      return ptr +    end +    alias value create_value + +    # Returns a new instance of the C struct with the value +ty+ at the +addr+ +    # address. +    def import_value(ty, addr) +      s = struct([ty + " value"]) +      ptr = s.new(addr) +      return ptr +    end + + +    # The Fiddle::CompositeHandler instance +    # +    # Will raise an error if no handlers are open. +    def handler +      @handler or raise "call dlload before importing symbols and functions" +    end + +    # Returns a new Fiddle::Pointer instance at the memory address of the given +    # +name+ symbol. +    # +    # Raises a DLError if the +name+ doesn't exist. +    # +    # See Fiddle::CompositeHandler.sym and Fiddle::Handle.sym +    def import_symbol(name) +      addr = handler.sym(name) +      if( !addr ) +        raise(DLError, "cannot find the symbol: #{name}") +      end +      Pointer.new(addr) +    end + +    # Returns a new Fiddle::Function instance at the memory address of the given +    # +name+ function. +    # +    # Raises a DLError if the +name+ doesn't exist. +    # +    # * +argtype+ is an Array of arguments, passed to the +name+ function. +    # * +ctype+ is the return type of the function +    # * +call_type+ is the ABI of the function +    # +    # See also Fiddle:Function.new +    # +    # See Fiddle::CompositeHandler.sym and Fiddle::Handler.sym +    def import_function(name, ctype, argtype, call_type = nil) +      addr = handler.sym(name) +      if( !addr ) +        raise(DLError, "cannot find the function: #{name}()") +      end +      Function.new(addr, argtype, ctype, CALL_TYPE_TO_ABI[call_type], +                   name: name) +    end + +    # Returns a new closure wrapper for the +name+ function. +    # +    # * +ctype+ is the return type of the function +    # * +argtype+ is an Array of arguments, passed to the callback function +    # * +call_type+ is the abi of the closure +    # * +block+ is passed to the callback +    # +    # See Fiddle::Closure +    def bind_function(name, ctype, argtype, call_type = nil, &block) +      abi = CALL_TYPE_TO_ABI[call_type] +      closure = Class.new(Fiddle::Closure) { +        define_method(:call, block) +      }.new(ctype, argtype, abi) + +      Function.new(closure, argtype, ctype, abi, name: name) +    end +  end +end diff --git a/jni/ruby/ext/fiddle/lib/fiddle/pack.rb b/jni/ruby/ext/fiddle/lib/fiddle/pack.rb new file mode 100644 index 0000000..e4e9542 --- /dev/null +++ b/jni/ruby/ext/fiddle/lib/fiddle/pack.rb @@ -0,0 +1,128 @@ +require 'fiddle' + +module Fiddle +  module PackInfo # :nodoc: all +    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 # :nodoc: all +    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 diff --git a/jni/ruby/ext/fiddle/lib/fiddle/struct.rb b/jni/ruby/ext/fiddle/lib/fiddle/struct.rb new file mode 100644 index 0000000..695a4d2 --- /dev/null +++ b/jni/ruby/ext/fiddle/lib/fiddle/struct.rb @@ -0,0 +1,243 @@ +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 'fiddle/struct' +    #   require 'fiddle/cparser' +    # +    #   include Fiddle::CParser +    # +    #   types, members = parse_struct_signature(['int i','char c']) +    # +    #   MyStruct = Fiddle::CStructBuilder.create(Fiddle::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 with the +types+ provided. +    # +    # When the instance is garbage collected, the C function +func+ is called. +    def CStructEntity.malloc(types, func = nil) +      addr = Fiddle.malloc(CStructEntity.size(types)) +      CStructEntity.new(addr, types, func) +    end + +    # Returns the offset for the packed sizes for the given +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+. +    # +    # When the instance is garbage collected, the C function +func+ is called. +    # +    # See also Fiddle::Pointer.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 + +    # Calculates the offsets and sizes for the given +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| Pointer.new(v)} +        end +      when TYPE_VOIDP +        val = Pointer.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. +    # +    # When the instance is garbage collected, the C function +func+ is called. +    def CUnionEntity.malloc(types, func=nil) +      addr = Fiddle.malloc(CUnionEntity.size(types)) +      CUnionEntity.new(addr, types, func) +    end + +    # Returns the size needed for the union with the given +types+. +    # +    #   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 + +    # Calculate the necessary offset and for each union member with the given +    # +types+ +    def set_ctypes(types) +      @ctypes = types +      @offset = Array.new(types.length, 0) +      @size   = self.class.size types +    end +  end +end + diff --git a/jni/ruby/ext/fiddle/lib/fiddle/types.rb b/jni/ruby/ext/fiddle/lib/fiddle/types.rb new file mode 100644 index 0000000..02c1d25 --- /dev/null +++ b/jni/ruby/ext/fiddle/lib/fiddle/types.rb @@ -0,0 +1,71 @@ +module Fiddle +  # Adds Windows type aliases to the including class for use with +  # Fiddle::Importer. +  # +  # The aliases added are: +  # * ATOM +  # * BOOL +  # * BYTE +  # * DWORD +  # * DWORD32 +  # * DWORD64 +  # * HANDLE +  # * HDC +  # * HINSTANCE +  # * HWND +  # * LPCSTR +  # * LPSTR +  # * PBYTE +  # * PDWORD +  # * PHANDLE +  # * PVOID +  # * PWORD +  # * UCHAR +  # * UINT +  # * ULONG +  # * WORD +  module Win32Types +    def included(m) # :nodoc: +      m.module_eval{ +        typealias "DWORD", "unsigned long" +        typealias "PDWORD", "unsigned long *" +        typealias "DWORD32", "unsigned long" +        typealias "DWORD64", "unsigned long long" +        typealias "WORD", "unsigned short" +        typealias "PWORD", "unsigned short *" +        typealias "BOOL", "int" +        typealias "ATOM", "int" +        typealias "BYTE", "unsigned char" +        typealias "PBYTE", "unsigned char *" +        typealias "UINT", "unsigned int" +        typealias "ULONG", "unsigned long" +        typealias "UCHAR", "unsigned char" +        typealias "HANDLE", "uintptr_t" +        typealias "PHANDLE", "void*" +        typealias "PVOID", "void*" +        typealias "LPCSTR", "char*" +        typealias "LPSTR", "char*" +        typealias "HINSTANCE", "unsigned int" +        typealias "HDC", "unsigned int" +        typealias "HWND", "unsigned int" +      } +    end +    module_function :included +  end + +  # Adds basic type aliases to the including class for use with Fiddle::Importer. +  # +  # The aliases added are +uint+ and +u_int+ (<tt>unsigned int</tt>) and +  # +ulong+ and +u_long+ (<tt>unsigned long</tt>) +  module BasicTypes +    def included(m) # :nodoc: +      m.module_eval{ +        typealias "uint", "unsigned int" +        typealias "u_int", "unsigned int" +        typealias "ulong", "unsigned long" +        typealias "u_long", "unsigned long" +      } +    end +    module_function :included +  end +end diff --git a/jni/ruby/ext/fiddle/lib/fiddle/value.rb b/jni/ruby/ext/fiddle/lib/fiddle/value.rb new file mode 100644 index 0000000..8d71e47 --- /dev/null +++ b/jni/ruby/ext/fiddle/lib/fiddle/value.rb @@ -0,0 +1,112 @@ +require 'fiddle' + +module Fiddle +  module ValueUtil #:nodoc: all +    def unsigned_value(val, ty) +      case ty.abs +      when TYPE_CHAR +        [val].pack("c").unpack("C")[0] +      when TYPE_SHORT +        [val].pack("s!").unpack("S!")[0] +      when TYPE_INT +        [val].pack("i!").unpack("I!")[0] +      when TYPE_LONG +        [val].pack("l!").unpack("L!")[0] +      when TYPE_LONG_LONG +        [val].pack("q").unpack("Q")[0] +      else +        val +      end +    end + +    def signed_value(val, ty) +      case ty.abs +      when TYPE_CHAR +        [val].pack("C").unpack("c")[0] +      when TYPE_SHORT +        [val].pack("S!").unpack("s!")[0] +      when TYPE_INT +        [val].pack("I!").unpack("i!")[0] +      when TYPE_LONG +        [val].pack("L!").unpack("l!")[0] +      when TYPE_LONG_LONG +        [val].pack("Q").unpack("q")[0] +      else +        val +      end +    end + +    def wrap_args(args, tys, funcs, &block) +      result = [] +      tys ||= [] +      args.each_with_index{|arg, idx| +        result.push(wrap_arg(arg, tys[idx], funcs, &block)) +      } +      result +    end + +    def wrap_arg(arg, ty, funcs = [], &block) +      funcs ||= [] +      case arg +      when nil +        return 0 +      when Pointer +        return arg.to_i +      when IO +        case ty +        when TYPE_VOIDP +          return Pointer[arg].to_i +        else +          return arg.to_i +        end +      when Function +        if( block ) +          arg.bind_at_call(&block) +          funcs.push(arg) +        elsif !arg.bound? +          raise(RuntimeError, "block must be given.") +        end +        return arg.to_i +      when String +        if( ty.is_a?(Array) ) +          return arg.unpack('C*') +        else +          case SIZEOF_VOIDP +          when SIZEOF_LONG +            return [arg].pack("p").unpack("l!")[0] +          when SIZEOF_LONG_LONG +            return [arg].pack("p").unpack("q")[0] +          else +            raise(RuntimeError, "sizeof(void*)?") +          end +        end +      when Float, Integer +        return arg +      when Array +        if( ty.is_a?(Array) ) # used only by struct +          case ty[0] +          when TYPE_VOIDP +            return arg.collect{|v| Integer(v)} +          when TYPE_CHAR +            if( arg.is_a?(String) ) +              return val.unpack('C*') +            end +          end +          return arg +        else +          return arg +        end +      else +        if( arg.respond_to?(:to_ptr) ) +          return arg.to_ptr.to_i +        else +          begin +            return Integer(arg) +          rescue +            raise(ArgumentError, "unknown argument type: #{arg.class}") +          end +        end +      end +    end +  end +end  | 
