Parent

Namespace

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

size[RW]
fields[R]

Public Class Methods

new(name, options = {}) click to toggle source
    # File lib/ffi/tools/struct_generator.rb, line 39
39:     def initialize(name, options = {})
40:       @name = name
41:       @struct_name = nil
42:       @includes = []
43:       @fields = []
44:       @found = false
45:       @size = nil
46: 
47:       if block_given? then
48:         yield self
49:         calculate self.class.options.merge(options)
50:       end
51:     end
options() click to toggle source
    # File lib/ffi/tools/struct_generator.rb, line 55
55:     def self.options
56:       @options
57:     end
options=(options) click to toggle source
    # File lib/ffi/tools/struct_generator.rb, line 52
52:     def self.options=(options)
53:       @options = options
54:     end

Public Instance Methods

calculate(options = {}) click to toggle source
     # File lib/ffi/tools/struct_generator.rb, line 58
 58:     def calculate(options = {})
 59:       binary = File.join Dir.tmpdir, "rb_struct_gen_bin_#{Process.pid}"
 60: 
 61:       raise "struct name not set" if @struct_name.nil?
 62: 
 63:       Tempfile.open("#{@name}.struct_generator") do |f|
 64:         f.puts "#include <stdio.h>"
 65: 
 66:         @includes.each do |inc|
 67:           f.puts "#include <#{inc}>"
 68:         end
 69: 
 70:         f.puts "#include <stddef.h>\n\n"
 71:         f.puts "int main(int argc, char **argv)\n{"
 72:         f.puts "  #{@struct_name} s;"
 73:         f.puts %[  printf("sizeof(#{@struct_name}) %u\\n", (unsigned int) sizeof(#{@struct_name}));]
 74: 
 75:         @fields.each do |field|
 76:           f.puts     printf("#{field.name} %u %u\\n", (unsigned int) offsetof(#{@struct_name}, #{field.name}),           (unsigned int) sizeof(s.#{field.name}));
 77:         end
 78: 
 79:         f.puts "\n  return 0;\n}"
 80:         f.flush
 81: 
 82:         output = `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`
 83: 
 84:         unless $?.success? then
 85:           @found = false
 86:           output = output.split("\n").map { |l| "\t#{l}" }.join "\n"
 87:           raise "Compilation error generating struct #{@name} (#{@struct_name}):\n#{output}"
 88:         end
 89:       end
 90:       
 91:       output = `#{binary}`.split "\n"
 92:       File.unlink(binary + (FFI::Platform.windows? ? ".exe" : ""))
 93:       sizeof = output.shift
 94:       unless @size
 95:         m = /\s*sizeof\([^)]+\) (\d+)/.match sizeof
 96:         @size = m[1]
 97:       end
 98: 
 99:       line_no = 0
100:       output.each do |line|
101:         md = line.match(/.+ (\d+) (\d+)/)
102:         @fields[line_no].offset = md[1].to_i
103:         @fields[line_no].size   = md[2].to_i
104: 
105:         line_no += 1
106:       end
107: 
108:       @found = true
109:     end
dump_config(io) click to toggle source
     # File lib/ffi/tools/struct_generator.rb, line 124
124:     def dump_config(io)
125:       io.puts "rbx.platform.#{@name}.sizeof = #{@size}"
126: 
127:       @fields.each { |field| io.puts field.to_config(@name) }
128:     end
field(name, type=nil) click to toggle source
     # File lib/ffi/tools/struct_generator.rb, line 114
114:     def field(name, type=nil)
115:       field = Field.new(name, type)
116:       @fields << field
117:       return field
118:     end
found?() click to toggle source
     # File lib/ffi/tools/struct_generator.rb, line 120
120:     def found?
121:       @found
122:     end
generate_layout() click to toggle source
     # File lib/ffi/tools/struct_generator.rb, line 130
130:     def generate_layout
131:       buf = ""
132: 
133:       @fields.each_with_index do |field, i|
134:         if buf.empty?
135:           buf << "layout :#{field.name}, :#{field.type}, #{field.offset}"
136:         else
137:           buf << "       :#{field.name}, :#{field.type}, #{field.offset}"
138:         end
139: 
140:         if i < @fields.length - 1
141:           buf << ",\n"
142:         end
143:       end
144: 
145:       buf
146:     end
get_field(name) click to toggle source
     # File lib/ffi/tools/struct_generator.rb, line 148
148:     def get_field(name)
149:       @fields.find { |f| name == f.name }
150:     end
include(i) click to toggle source
     # File lib/ffi/tools/struct_generator.rb, line 152
152:     def include(i)
153:       @includes << i
154:     end
name(n) click to toggle source
     # File lib/ffi/tools/struct_generator.rb, line 156
156:     def name(n)
157:       @struct_name = n
158:     end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.