Current File : //proc/thread-self/root/opt/alt/ruby20/lib64/ruby/2.0.0/dl/struct.rb |
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