diff options
Diffstat (limited to 'jni/ruby/ext/fiddle/lib')
-rw-r--r-- | jni/ruby/ext/fiddle/lib/fiddle.rb | 55 | ||||
-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 |
9 files changed, 1164 insertions, 0 deletions
diff --git a/jni/ruby/ext/fiddle/lib/fiddle.rb b/jni/ruby/ext/fiddle/lib/fiddle.rb new file mode 100644 index 0000000..ae6e299 --- /dev/null +++ b/jni/ruby/ext/fiddle/lib/fiddle.rb @@ -0,0 +1,55 @@ +require 'fiddle.so' +require 'fiddle/function' +require 'fiddle/closure' + +module Fiddle + if WINDOWS + # Returns the last win32 +Error+ of the current executing +Thread+ or nil + # if none + def self.win32_last_error + Thread.current[:__FIDDLE_WIN32_LAST_ERROR__] + end + + # Sets the last win32 +Error+ of the current executing +Thread+ to +error+ + def self.win32_last_error= error + Thread.current[:__FIDDLE_WIN32_LAST_ERROR__] = error + end + end + + # Returns the last +Error+ of the current executing +Thread+ or nil if none + def self.last_error + Thread.current[:__FIDDLE_LAST_ERROR__] + end + + # Sets the last +Error+ of the current executing +Thread+ to +error+ + def self.last_error= error + Thread.current[:__DL2_LAST_ERROR__] = error + Thread.current[:__FIDDLE_LAST_ERROR__] = error + end + + # call-seq: dlopen(library) => Fiddle::Handle + # + # Creates a new handler that opens +library+, and returns an instance of + # Fiddle::Handle. + # + # If +nil+ is given for the +library+, Fiddle::Handle::DEFAULT is used, which + # is the equivalent to RTLD_DEFAULT. See <code>man 3 dlopen</code> for more. + # + # lib = Fiddle.dlopen(nil) + # + # The default is dependent on OS, and provide a handle for all libraries + # already loaded. For example, in most cases you can use this to access + # +libc+ functions, or ruby functions like +rb_str_new+. + # + # See Fiddle::Handle.new for more. + def dlopen library + Fiddle::Handle.new library + end + module_function :dlopen + + # Add constants for backwards compat + + RTLD_GLOBAL = Handle::RTLD_GLOBAL # :nodoc: + RTLD_LAZY = Handle::RTLD_LAZY # :nodoc: + RTLD_NOW = Handle::RTLD_NOW # :nodoc: +end 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 |