mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00

to Fiddle::Handle. * ext/fiddle/pointer.c: Make Fiddle independent of DL, copy DL::Pointer to Fiddle::Pointer. * test/fiddle/test_func.rb: relevent tests * test/fiddle/test_handle.rb: ditto * test/fiddle/test_pointer.rb: ditto * ext/dl/lib/dl/struct.rb: use Fiddle::Pointer if available * ext/fiddle/extconf.rb: check for dlfcn.h * ext/fiddle/fiddle.c: add constants for sizeof() things * ext/fiddle/fiddle.h: include dlfcn.h * ext/fiddle/function.c: expose a C function for creating new Fiddle::Function objects. * ext/fiddle/lib/fiddle.rb: include constants for dl backwards compat * ext/fiddle/lib/fiddle/function.rb: read the pointer from the function for dl backwards compat. * test/dl/test_callback.rb: check the addresses of the pointers rather than their types. * test/fiddle/helper.rb: remove dependency on dl * test/fiddle/test_closure.rb: ditto * test/fiddle/test_fiddle.rb: ditto git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37907 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
236 lines
6.2 KiB
Ruby
236 lines
6.2 KiB
Ruby
require 'dl'
|
|
require 'dl/value'
|
|
require 'dl/pack.rb'
|
|
|
|
module DL
|
|
# C struct shell
|
|
class CStruct
|
|
# accessor to DL::CStructEntity
|
|
def CStruct.entity_class()
|
|
CStructEntity
|
|
end
|
|
end
|
|
|
|
# C union shell
|
|
class CUnion
|
|
# accessor to DL::CUnionEntity
|
|
def CUnion.entity_class()
|
|
CUnionEntity
|
|
end
|
|
end
|
|
|
|
# Used to construct C classes (CUnion, CStruct, etc)
|
|
#
|
|
# DL::Importer#struct and DL::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+ (DL:TYPE_INT, DL::TYPE_SIZE_T, etc., see the C types
|
|
# constants)
|
|
# * corresponding +members+
|
|
#
|
|
# DL::Importer#struct and DL::Importer#union wrap this functionality in an
|
|
# easy-to-use manner.
|
|
#
|
|
# Example:
|
|
#
|
|
# require 'dl/struct'
|
|
# require 'dl/cparser'
|
|
#
|
|
# include DL::CParser
|
|
#
|
|
# types, members = parse_struct_signature(['int i','char c'])
|
|
#
|
|
# MyStruct = DL::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 = DL.malloc(#{size})
|
|
new(addr)
|
|
end
|
|
EOS
|
|
return new_class
|
|
end
|
|
module_function :create
|
|
end
|
|
|
|
# A C struct wrapper
|
|
class CStructEntity < (DL.fiddle? ? Fiddle::Pointer : CPtr)
|
|
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 = DL.malloc(CStructEntity.size(types))
|
|
CStructEntity.new(addr, types, func)
|
|
end
|
|
|
|
# Given +types+, returns the offset for the packed sizes of those types
|
|
#
|
|
# DL::CStructEntity.size([DL::TYPE_DOUBLE, DL::TYPE_INT, DL::TYPE_CHAR,
|
|
# DL::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 DL::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 = DL.malloc(CUnionEntity.size(types))
|
|
CUnionEntity.new(addr, types, func)
|
|
end
|
|
|
|
# Given +types+, returns the size needed for the union.
|
|
#
|
|
# DL::CUnionEntity.size([DL::TYPE_DOUBLE, DL::TYPE_INT, DL::TYPE_CHAR,
|
|
# DL::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
|
|
|