class FFI::Generators::Structures

Generates an FFI Struct layout.

Given the @@@ portion in:

module Zlib::ZStream < FFI::Struct
  @@@
  name "struct z_stream_s"
  include "zlib.h"

  field :next_in,   :pointer
  field :avail_in,  :uint
  field :total_in,  :ulong

  # ...
  @@@
end

Structures will create the layout:

layout :next_in, :pointer, 0,
       :avail_in, :uint, 4,
       :total_in, :ulong, 8,
       # ...

StructGenerator does its best to pad the layout it produces to preserve line numbers. Place the struct definition as close to the top of the file for best results.

Attributes

fields[R]
size[RW]

Public Class Methods

new(name) { |self| ... } click to toggle source
# File lib/ffi2/generators/structures.rb, line 68
def initialize(name)
  @name = name
  @struct_name = nil
  @includes = []
  @include_dirs = []
  @fields = []
  @found = false
  @size = nil
  @platform = Platform.new

  if block_given? then
    yield self
    calculate
  end
end

Public Instance Methods

calculate() click to toggle source
# File lib/ffi2/generators/structures.rb, line 122
def calculate
  raise "struct name not set" if @struct_name.nil?

  output = BodyGuard.new(self, @struct_name, @platform).perform.split "\n"

  sizeof = output.shift
  unless @size
    m = /\s*sizeof\([^)]+\) (\d+)/.match sizeof
    @size = m[1]
  end

  line_no = 0
  output.each do |line|
    md = line.match(/.+ (\d+) (\d+)/)
    @fields[line_no].offset = md[1].to_i
    @fields[line_no].size   = md[2].to_i

    line_no += 1
  end

  @found = true
end
field(name, type=nil) click to toggle source
# File lib/ffi2/generators/structures.rb, line 145
def field(name, type=nil)
  field = Field.new(name, type)
  @fields << field
  return field
end
found?() click to toggle source
# File lib/ffi2/generators/structures.rb, line 151
def found?
  @found
end
generate_layout() click to toggle source
# File lib/ffi2/generators/structures.rb, line 161
def generate_layout
  buf = ""

  @fields.each_with_index do |field, i|
    if buf.empty?
      buf << "layout :#{field.name}, :#{field.type}, #{field.offset}"
    else
      buf << "       :#{field.name}, :#{field.type}, #{field.offset}"
    end

    if i < @fields.length - 1
      buf << ",\n"
    end
  end

  buf
end
get_field(name) click to toggle source
# File lib/ffi2/generators/structures.rb, line 179
def get_field(name)
  @fields.find { |f| name == f.name }
end
include(i) click to toggle source
# File lib/ffi2/generators/structures.rb, line 183
def include(i)
  @includes << i
end
include_dir(i) click to toggle source
# File lib/ffi2/generators/structures.rb, line 187
def include_dir(i)
  @include_dirs << i
end
name(n) click to toggle source
# File lib/ffi2/generators/structures.rb, line 191
def name(n)
  @struct_name = n
end
prepare(name, target) click to toggle source
# File lib/ffi2/generators/structures.rb, line 106
def prepare(name, target)
  @platform.compile(@include_dirs, name, target)
end
prepare_failed() click to toggle source
# File lib/ffi2/generators/structures.rb, line 110
def prepare_failed
  "Compilation error generating struct #{@name} (#{@struct_name})"
end
process(target) click to toggle source
# File lib/ffi2/generators/structures.rb, line 114
def process(target)
  target
end
process_failed() click to toggle source
# File lib/ffi2/generators/structures.rb, line 118
def process_failed
  "Error generating struct #{@name} (#{@struct_name})"
end
source(io) click to toggle source
# File lib/ffi2/generators/structures.rb, line 84
      def source(io)
        io.puts "#include <stdio.h>"

        @includes.each do |inc|
          io.puts "#include <#{inc}>"
        end

        io.puts "#include <stddef.h>\n\n"
        io.puts "int main(int argc, char **argv)\n{"
        io.puts "  #{@struct_name} s;"
        io.puts %[  printf("sizeof(#{@struct_name}) %u\\n", (unsigned int) sizeof(#{@struct_name}));]

        @fields.each do |field|
          io.puts <<-EOF
    printf("#{field.name} %u %u\\n", (unsigned int) offsetof(#{@struct_name}, #{field.name}),
           (unsigned int) sizeof(s.#{field.name}));
  EOF
        end

        io.puts "\n  return 0;\n}"
      end
write_config(io) click to toggle source
# File lib/ffi2/generators/structures.rb, line 155
def write_config(io)
  io.puts "rbx.platform.#{@name}.sizeof = #{@size}"

  @fields.each { |field| io.puts field.to_config(@name) }
end