class FFI::StructGenerator
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
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.
Attributes
fields[R]
size[RW]
Public Class Methods
new(name, options = {}) { |self| ... }
click to toggle source
# 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
options()
click to toggle source
# File lib/ffi/tools/struct_generator.rb, line 55 def self.options @options end
options=(options)
click to toggle source
# File lib/ffi/tools/struct_generator.rb, line 52 def self.options=(options) @options = options end
Public Instance Methods
calculate(options = {})
click to toggle source
# 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 %Q[ 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 output = %x`gcc #{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 = %x`#{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)
click to toggle source
# File lib/ffi/tools/struct_generator.rb, line 124 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)
click to toggle source
# File lib/ffi/tools/struct_generator.rb, line 114 def field(name, type=nil) field = Field.new(name, type) @fields << field return field end
found?()
click to toggle source
# File lib/ffi/tools/struct_generator.rb, line 120 def found? @found end
generate_layout()
click to toggle source
# File lib/ffi/tools/struct_generator.rb, line 130 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/ffi/tools/struct_generator.rb, line 148 def get_field(name) @fields.find { |f| name == f.name } end
include(i)
click to toggle source
# File lib/ffi/tools/struct_generator.rb, line 152 def include(i) @includes << i end
name(n)
click to toggle source
# File lib/ffi/tools/struct_generator.rb, line 156 def name(n) @struct_name = n end