A pointer to a C structure
# File tmp/rubies/ruby-3.4.1/ext/fiddle/lib/fiddle/struct.rb, line 277
def CStructEntity.alignment(types)
max = 1
types.each do |type, count = 1|
if type.respond_to?(:entity_class)
n = type.alignment
else
n = ALIGN_MAP[type]
end
max = n if n > max
end
max
end
# File tmp/rubies/ruby-3.4.1/ext/fiddle/lib/fiddle/struct.rb, line 293
def CStructEntity.malloc(types, func = nil, size = size(types))
if block_given? and func.nil?
message = "a free function must be supplied to #{self}.malloc " +
"when it is called with a block"
raise ArgumentError, message
end
pointer = Pointer.malloc(size)
begin
struct = new(pointer, types, func)
rescue
pointer.free = func
pointer.call_free
raise
end
if block_given?
begin
yield(struct)
ensure
struct.call_free
end
else
struct
end
end
Allocates a C struct with the types provided.
See Fiddle::Pointer.malloc for memory management issues.
# File tmp/rubies/ruby-3.4.1/ext/fiddle/lib/fiddle/struct.rb, line 353
def initialize(addr, types, func = nil)
if func && addr.is_a?(Pointer) && addr.free
raise ArgumentError, 'free function specified on both underlying struct Pointer and when creating a CStructEntity - who do you want to free this?'
end
set_ctypes(types)
super(addr, @size, func)
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
# File tmp/rubies/ruby-3.4.1/ext/fiddle/lib/fiddle/struct.rb, line 326
def CStructEntity.size(types)
offset = 0
max_align = types.map { |type, count = 1|
last_offset = offset
if type.respond_to?(:entity_class)
align = type.alignment
type_size = type.size
else
align = PackInfo::ALIGN_MAP[type]
type_size = PackInfo::SIZE_MAP[type]
end
offset = PackInfo.align(last_offset, align) +
(type_size * count)
align
}.max
PackInfo.align(offset, max_align)
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
# File tmp/rubies/ruby-3.4.1/ext/fiddle/lib/fiddle/struct.rb, line 522
def +(delta)
Pointer.new(to_i + delta, @size - delta)
end
# File tmp/rubies/ruby-3.4.1/ext/fiddle/lib/fiddle/struct.rb, line 526
def -(delta)
Pointer.new(to_i - delta, @size + delta)
end
# File tmp/rubies/ruby-3.4.1/ext/fiddle/lib/fiddle/struct.rb, line 426
def [](*args)
return super(*args) if args.size > 1
name = args[0]
idx = @members.index(name)
if( idx.nil? )
raise(ArgumentError, "no such member: #{name}")
end
ty = @ctypes[idx]
if( ty.is_a?(Array) )
if ty.first.respond_to?(:entity_class)
return @nested_structs[name]
else
r = super(@offset[idx], SIZE_MAP[ty[0]] * ty[1])
end
elsif ty.respond_to?(:entity_class)
return @nested_structs[name]
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 StructArray.new(self + @offset[idx], ty[0], val)
else
return val
end
end
Fetch struct member name if only one argument is specified. If two arguments are specified, the first is an offset and the second is a length and this method returns the string of length bytes beginning at offset.
Examples:
my_struct = struct(['int id']).malloc my_struct.id = 1 my_struct['id'] # => 1 my_struct[0, 4] # => "\x01\x00\x00\x00".b
# File tmp/rubies/ruby-3.4.1/ext/fiddle/lib/fiddle/struct.rb, line 478
def []=(*args)
return super(*args) if args.size > 2
name, val = *args
name = name.to_s if name.is_a?(Symbol)
nested_struct = @nested_structs[name]
if nested_struct
if nested_struct.is_a?(StructArray)
if val.nil?
nested_struct.each do |s|
s.replace(nil)
end
else
val.each_with_index do |v, i|
nested_struct[i] = v
end
end
else
nested_struct.replace(val)
end
return val
end
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
Set struct member name, to value val. If more arguments are specified, writes the string of bytes to the memory at the given offset and length.
Examples:
my_struct = struct(['int id']).malloc my_struct['id'] = 1 my_struct[0, 4] = "\x01\x00\x00\x00".b my_struct.id # => 1
# File tmp/rubies/ruby-3.4.1/ext/fiddle/lib/fiddle/struct.rb, line 362
def assign_names(members)
@members = []
@nested_structs = {}
members.each_with_index do |member, index|
if member.is_a?(Array) # nested struct
member_name = member[0]
struct_type, struct_count = @ctypes[index]
if struct_count.nil?
struct = struct_type.new(to_i + @offset[index])
else
structs = struct_count.times.map do |i|
struct_type.new(to_i + @offset[index] + i * struct_type.size)
end
struct = StructArray.new(to_i + @offset[index],
struct_type,
structs)
end
@nested_structs[member_name] = struct
else
member_name = member
end
@members << member_name
end
end
Set the names of the members in this C struct
# File tmp/rubies/ruby-3.4.1/ext/fiddle/lib/fiddle/struct.rb, line 388
def set_ctypes(types)
@ctypes = types
@offset = []
offset = 0
max_align = types.map { |type, count = 1|
orig_offset = offset
if type.respond_to?(:entity_class)
align = type.alignment
type_size = type.size
else
align = ALIGN_MAP[type]
type_size = SIZE_MAP[type]
end
offset = PackInfo.align(orig_offset, align)
@offset << offset
offset += (type_size * count)
align
}.max
@size = PackInfo.align(offset, max_align)
end
Calculates the offsets and sizes for the given types in the struct.