summaryrefslogtreecommitdiff
path: root/ruby_2_2/ext/fiddle/lib/fiddle
diff options
context:
space:
mode:
authorusa <usa@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2017-12-14 15:09:35 +0000
committerusa <usa@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2017-12-14 15:09:35 +0000
commit1a74fa4b04da04bd2bb33103dd3cf431438df38e (patch)
treef4a1d6c2961339e0c1d653c0f8427a53315080f0 /ruby_2_2/ext/fiddle/lib/fiddle
parenta5b755e50e2d9aabf28ba24bf58644ca22b01a4f (diff)
add tag v2_2_9
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/tags/v2_2_9@61257 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'ruby_2_2/ext/fiddle/lib/fiddle')
-rw-r--r--ruby_2_2/ext/fiddle/lib/fiddle/closure.rb48
-rw-r--r--ruby_2_2/ext/fiddle/lib/fiddle/cparser.rb176
-rw-r--r--ruby_2_2/ext/fiddle/lib/fiddle/function.rb17
-rw-r--r--ruby_2_2/ext/fiddle/lib/fiddle/import.rb314
-rw-r--r--ruby_2_2/ext/fiddle/lib/fiddle/pack.rb128
-rw-r--r--ruby_2_2/ext/fiddle/lib/fiddle/struct.rb243
-rw-r--r--ruby_2_2/ext/fiddle/lib/fiddle/types.rb71
-rw-r--r--ruby_2_2/ext/fiddle/lib/fiddle/value.rb112
8 files changed, 1109 insertions, 0 deletions
diff --git a/ruby_2_2/ext/fiddle/lib/fiddle/closure.rb b/ruby_2_2/ext/fiddle/lib/fiddle/closure.rb
new file mode 100644
index 0000000000..beb90ecbe5
--- /dev/null
+++ b/ruby_2_2/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/ruby_2_2/ext/fiddle/lib/fiddle/cparser.rb b/ruby_2_2/ext/fiddle/lib/fiddle/cparser.rb
new file mode 100644
index 0000000000..43fb184a12
--- /dev/null
+++ b/ruby_2_2/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/ruby_2_2/ext/fiddle/lib/fiddle/function.rb b/ruby_2_2/ext/fiddle/lib/fiddle/function.rb
new file mode 100644
index 0000000000..ab7496e944
--- /dev/null
+++ b/ruby_2_2/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/ruby_2_2/ext/fiddle/lib/fiddle/import.rb b/ruby_2_2/ext/fiddle/lib/fiddle/import.rb
new file mode 100644
index 0000000000..34f5d7f81c
--- /dev/null
+++ b/ruby_2_2/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/ruby_2_2/ext/fiddle/lib/fiddle/pack.rb b/ruby_2_2/ext/fiddle/lib/fiddle/pack.rb
new file mode 100644
index 0000000000..e4e9542cc0
--- /dev/null
+++ b/ruby_2_2/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/ruby_2_2/ext/fiddle/lib/fiddle/struct.rb b/ruby_2_2/ext/fiddle/lib/fiddle/struct.rb
new file mode 100644
index 0000000000..695a4d2247
--- /dev/null
+++ b/ruby_2_2/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/ruby_2_2/ext/fiddle/lib/fiddle/types.rb b/ruby_2_2/ext/fiddle/lib/fiddle/types.rb
new file mode 100644
index 0000000000..02c1d25a37
--- /dev/null
+++ b/ruby_2_2/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/ruby_2_2/ext/fiddle/lib/fiddle/value.rb b/ruby_2_2/ext/fiddle/lib/fiddle/value.rb
new file mode 100644
index 0000000000..8d71e47ce6
--- /dev/null
+++ b/ruby_2_2/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