class FFI::StructGenerator

def self.options

def self.options
  @options
end

def self.options=(options)

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

def calculate(options = {})

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}));
F
    end
    f.puts "\n  return 0;\n}"
    f.flush
    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`
    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

def dump_config(io)

def dump_config(io)
  io.puts "rbx.platform.#{@name}.sizeof = #{@size}"
  @fields.each { |field| io.puts field.to_config(@name) }
end

def field(name, type=nil)

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

def found?

def found?
  @found
end

def generate_layout

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

def get_field(name)

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

def include(i)

def include(i)
  @includes << i
end

def initialize(name, options = {})

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

def name(n)

def name(n)
  @struct_name = n
end