123456789_123456789_123456789_123456789_123456789_

Class: Fiddle::CStructEntity

Relationships & Source Files
Extension / Inclusion / Inheritance Descendants
Subclasses:
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, Pointer, FFI::DataConverter
Instance Chain:
Inherits: Fiddle::Pointer
Defined in: ext/fiddle/lib/fiddle/struct.rb

Overview

A pointer to a C structure

Constant Summary

PackInfo - Included

ALIGN_MAP, PACK_MAP, SIZE_MAP

Class Method Summary

Pointer - Inherited

.[]

call-seq:

.from_native, .malloc,
.new

Create a new pointer to address with an optional .size and freefunc.

.read

Or read the memory at address address with length len and return a string with that memory.

.to_native,
.to_ptr

Alias for Pointer.[].

.write

Write bytes in str to the location pointed to by address.

Instance Attribute Summary

Pointer - Inherited

#ffi_ptr,
#free

Get the free function for this pointer.

#free=

Set the free function for this pointer to function in the given Function.

#freed?

Returns if the free function for this pointer has been called.

#null?

Returns true if this is a null pointer.

#size

Get the size of this pointer.

#size=

Set the size of this pointer to .size

Instance Method Summary

ValueUtil - Included

PackInfo - Included

Pointer - Inherited

#+

Returns a new pointer instance that has been advanced n bytes.

#+@

Returns a new Pointer instance that is a dereferenced pointer for this pointer.

#-

Returns a new pointer instance that has been moved back n bytes.

#-@

Returns a new Pointer instance that is a reference pointer for this pointer.

#<=>

Returns -1 if less than, 0 if equal to, 1 if greater than other.

#==

Returns true if other wraps the same pointer, otherwise returns false.

#[]

Returns integer stored at index.

#[]=

Set the value at index to int.

#call_free

Call the free function for this pointer.

#eql?

Alias for Pointer#==.

#initialize,
#inspect

Returns a string formatted with an easily readable representation of the internal state of the pointer.

#ptr

Alias for Pointer#+@.

#ref

Alias for Pointer#-@.

#to_i

Returns the integer memory location of this pointer.

#to_int

Alias for Pointer#to_i.

#to_ptr,
#to_s

Returns the pointer contents as a string.

#to_str

Returns the pointer contents as a string.

#to_value

Cast this pointer to a ruby object.

Constructor Details

.new(addr, types, func = nil) ⇒ CStructEntity

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 Pointer.new

[ GitHub ]

  
# File '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

Class Method Details

.alignment(types)

[ GitHub ]

  
# File '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

.malloc(types, func = nil, size = size(types))

Allocates a C struct with the types provided.

See Pointer.malloc for memory management issues.

[ GitHub ]

  
# File '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

.size(types)

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
[ GitHub ]

  
# File '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

Instance Method Details

#+(delta)

[ GitHub ]

  
# File 'ext/fiddle/lib/fiddle/struct.rb', line 522

def +(delta)
  Pointer.new(to_i + delta, @size - delta)
end

#-(delta)

[ GitHub ]

  
# File 'ext/fiddle/lib/fiddle/struct.rb', line 526

def -(delta)
  Pointer.new(to_i - delta, @size + delta)
end

#[](*args)

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
[ GitHub ]

  
# File '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

#[]=(*args)

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
[ GitHub ]

  
# File '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

#assign_names(members)

Set the names of the members in this C struct

[ GitHub ]

  
# File '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_ctypes(types)

Calculates the offsets and sizes for the given types in the struct.

[ GitHub ]

  
# File '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

#to_s

This method is for internal use only.
[ GitHub ]

  
# File 'ext/fiddle/lib/fiddle/struct.rb', line 518

def to_s() # :nodoc:
  super(@size)
end