class Fiddle::CStructEntity - Documentation for Ruby 3.2

# class Fiddle::CStructEntity

A pointer to a C structure

### Public Class Methods

alignment(types) click to toggle source

    # 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)) { |struct| ... } click to toggle source

Allocates a C struct with the `types` provided.

See `Fiddle::Pointer.malloc` for memory management issues.

Calls superclass method `Fiddle::Pointer::malloc`

    # File ext/fiddle/lib/fiddle/struct.rb, line 293
    def CStructEntity.malloc(types, func = nil, size = size(types), &block)
      if block_given?
        super(size, func) do |struct|
          struct.set_ctypes types
          yield struct
        end
      else
        struct = super(size, func)
        struct.set_ctypes types
        struct
      end
    end

new(addr, types, func = nil) click to toggle source

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`

Calls superclass method `Fiddle::Pointer::new`

    # File ext/fiddle/lib/fiddle/struct.rb, line 340
    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

size(types) click to toggle source

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 ext/fiddle/lib/fiddle/struct.rb, line 313
    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

### Public Instance Methods

[](*args) click to toggle source

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

Calls superclass method `Fiddle::Pointer::[]`

    # File ext/fiddle/lib/fiddle/struct.rb, line 413
    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) click to toggle source

`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

Calls superclass method `Fiddle::Pointer#[]=`

    # File ext/fiddle/lib/fiddle/struct.rb, line 465
    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) click to toggle source

`Set` the names of the `members` in this C struct

    # File ext/fiddle/lib/fiddle/struct.rb, line 349
    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) click to toggle source

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

    # File ext/fiddle/lib/fiddle/struct.rb, line 375
    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