Class: FFI::StructGenerator

Inherits:
Object
  • Object
show all
Defined in:
lib/ffi/tools/struct_generator.rb

Overview

Generates an FFI Struct layout.

Given the @@@ portion in:

class 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

StructGenerator 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.

Defined Under Namespace

Classes: Field

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, options = {}) ⇒ StructGenerator

Returns a new instance of StructGenerator.



39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/ffi/tools/struct_generator.rb', line 39

def initialize(name, options = {})
  @name = name
  @struct_name = nil
  @includes = []
  @fields = []
  @found = false
  @size = nil

  if block_given? then
    yield self
    calculate self.class.options.merge(options)
  end
end

Instance Attribute Details

#fieldsObject (readonly)

Returns the value of attribute fields.



37
38
39
# File 'lib/ffi/tools/struct_generator.rb', line 37

def fields
  @fields
end

#sizeObject

Returns the value of attribute size.



36
37
38
# File 'lib/ffi/tools/struct_generator.rb', line 36

def size
  @size
end

Class Method Details

.optionsObject



55
56
57
# File 'lib/ffi/tools/struct_generator.rb', line 55

def self.options
  @options
end

.options=(options) ⇒ Object



52
53
54
# File 'lib/ffi/tools/struct_generator.rb', line 52

def self.options=(options)
  @options = options
end

Instance Method Details

#calculate(options = {}) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/ffi/tools/struct_generator.rb', line 58

def calculate(options = {})
  binary = File.join Dir.tmpdir, "rb_struct_gen_bin_#{Process.pid}"

  raise "struct name not set" if @struct_name.nil?

  Tempfile.open("#{@name}.struct_generator") do |f|
    f.puts "#include <stdio.h>"

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

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

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

    f.puts "\n  return 0;\n}"
    f.flush

    cc = ENV['CC'] || 'gcc'
    output = `#{cc} #{options[:cppflags]} #{options[:cflags]} -D_DARWIN_USE_64_BIT_INODE -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -x c -Wall -Werror #{f.path} -o #{binary} 2>&1`

    unless $?.success? then
      @found = false
      output = output.split("\n").map { |l| "\t#{l}" }.join "\n"
      raise "Compilation error generating struct #{@name} (#{@struct_name}):\n#{output}"
    end
  end

  output = `#{binary}`.split "\n"
  File.unlink(binary + (FFI::Platform.windows? ? ".exe" : ""))
  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

#dump_config(io) ⇒ Object



125
126
127
128
129
# File 'lib/ffi/tools/struct_generator.rb', line 125

def dump_config(io)
  io.puts "rbx.platform.#{@name}.sizeof = #{@size}"

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

#field(name, type = nil) ⇒ Object



115
116
117
118
119
# File 'lib/ffi/tools/struct_generator.rb', line 115

def field(name, type=nil)
  field = Field.new(name, type)
  @fields << field
  return field
end

#found?Boolean

Returns:

  • (Boolean)


121
122
123
# File 'lib/ffi/tools/struct_generator.rb', line 121

def found?
  @found
end

#generate_layoutObject



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/ffi/tools/struct_generator.rb', line 131

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) ⇒ Object



149
150
151
# File 'lib/ffi/tools/struct_generator.rb', line 149

def get_field(name)
  @fields.find { |f| name == f.name }
end

#include(i) ⇒ Object



153
154
155
# File 'lib/ffi/tools/struct_generator.rb', line 153

def include(i)
  @includes << i
end

#name(n) ⇒ Object



157
158
159
# File 'lib/ffi/tools/struct_generator.rb', line 157

def name(n)
  @struct_name = n
end