ffi 1.15.5-x64-mingw-ucrt

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +338 -0
  3. data/COPYING +49 -0
  4. data/Gemfile +14 -0
  5. data/LICENSE +24 -0
  6. data/LICENSE.SPECS +22 -0
  7. data/README.md +136 -0
  8. data/Rakefile +191 -0
  9. data/ffi.gemspec +42 -0
  10. data/lib/3.1/ffi_c.so +0 -0
  11. data/lib/ffi/abstract_memory.rb +44 -0
  12. data/lib/ffi/autopointer.rb +203 -0
  13. data/lib/ffi/buffer.rb +4 -0
  14. data/lib/ffi/callback.rb +4 -0
  15. data/lib/ffi/data_converter.rb +67 -0
  16. data/lib/ffi/enum.rb +296 -0
  17. data/lib/ffi/errno.rb +43 -0
  18. data/lib/ffi/ffi.rb +47 -0
  19. data/lib/ffi/io.rb +62 -0
  20. data/lib/ffi/library.rb +592 -0
  21. data/lib/ffi/managedstruct.rb +84 -0
  22. data/lib/ffi/memorypointer.rb +1 -0
  23. data/lib/ffi/platform/aarch64-darwin/types.conf +130 -0
  24. data/lib/ffi/platform/aarch64-freebsd/types.conf +128 -0
  25. data/lib/ffi/platform/aarch64-freebsd12/types.conf +181 -0
  26. data/lib/ffi/platform/aarch64-linux/types.conf +104 -0
  27. data/lib/ffi/platform/aarch64-openbsd/types.conf +134 -0
  28. data/lib/ffi/platform/arm-freebsd/types.conf +152 -0
  29. data/lib/ffi/platform/arm-freebsd12/types.conf +152 -0
  30. data/lib/ffi/platform/arm-linux/types.conf +132 -0
  31. data/lib/ffi/platform/i386-cygwin/types.conf +3 -0
  32. data/lib/ffi/platform/i386-darwin/types.conf +100 -0
  33. data/lib/ffi/platform/i386-freebsd/types.conf +152 -0
  34. data/lib/ffi/platform/i386-freebsd12/types.conf +152 -0
  35. data/lib/ffi/platform/i386-gnu/types.conf +107 -0
  36. data/lib/ffi/platform/i386-linux/types.conf +103 -0
  37. data/lib/ffi/platform/i386-netbsd/types.conf +126 -0
  38. data/lib/ffi/platform/i386-openbsd/types.conf +128 -0
  39. data/lib/ffi/platform/i386-solaris/types.conf +122 -0
  40. data/lib/ffi/platform/i386-windows/types.conf +52 -0
  41. data/lib/ffi/platform/ia64-linux/types.conf +104 -0
  42. data/lib/ffi/platform/mips-linux/types.conf +102 -0
  43. data/lib/ffi/platform/mips64-linux/types.conf +104 -0
  44. data/lib/ffi/platform/mips64el-linux/types.conf +104 -0
  45. data/lib/ffi/platform/mipsel-linux/types.conf +102 -0
  46. data/lib/ffi/platform/mipsisa32r6-linux/types.conf +102 -0
  47. data/lib/ffi/platform/mipsisa32r6el-linux/types.conf +102 -0
  48. data/lib/ffi/platform/mipsisa64r6-linux/types.conf +104 -0
  49. data/lib/ffi/platform/mipsisa64r6el-linux/types.conf +104 -0
  50. data/lib/ffi/platform/powerpc-aix/types.conf +180 -0
  51. data/lib/ffi/platform/powerpc-darwin/types.conf +100 -0
  52. data/lib/ffi/platform/powerpc-linux/types.conf +130 -0
  53. data/lib/ffi/platform/powerpc-openbsd/types.conf +156 -0
  54. data/lib/ffi/platform/powerpc64-linux/types.conf +104 -0
  55. data/lib/ffi/platform/powerpc64le-linux/types.conf +100 -0
  56. data/lib/ffi/platform/riscv64-linux/types.conf +104 -0
  57. data/lib/ffi/platform/s390-linux/types.conf +102 -0
  58. data/lib/ffi/platform/s390x-linux/types.conf +102 -0
  59. data/lib/ffi/platform/sparc-linux/types.conf +102 -0
  60. data/lib/ffi/platform/sparc-solaris/types.conf +128 -0
  61. data/lib/ffi/platform/sparc64-linux/types.conf +102 -0
  62. data/lib/ffi/platform/sparcv9-openbsd/types.conf +156 -0
  63. data/lib/ffi/platform/sparcv9-solaris/types.conf +128 -0
  64. data/lib/ffi/platform/x86_64-cygwin/types.conf +3 -0
  65. data/lib/ffi/platform/x86_64-darwin/types.conf +130 -0
  66. data/lib/ffi/platform/x86_64-dragonflybsd/types.conf +130 -0
  67. data/lib/ffi/platform/x86_64-freebsd/types.conf +128 -0
  68. data/lib/ffi/platform/x86_64-freebsd12/types.conf +158 -0
  69. data/lib/ffi/platform/x86_64-haiku/types.conf +117 -0
  70. data/lib/ffi/platform/x86_64-linux/types.conf +132 -0
  71. data/lib/ffi/platform/x86_64-msys/types.conf +119 -0
  72. data/lib/ffi/platform/x86_64-netbsd/types.conf +128 -0
  73. data/lib/ffi/platform/x86_64-openbsd/types.conf +134 -0
  74. data/lib/ffi/platform/x86_64-solaris/types.conf +122 -0
  75. data/lib/ffi/platform/x86_64-windows/types.conf +52 -0
  76. data/lib/ffi/platform.rb +185 -0
  77. data/lib/ffi/pointer.rb +167 -0
  78. data/lib/ffi/struct.rb +316 -0
  79. data/lib/ffi/struct_by_reference.rb +72 -0
  80. data/lib/ffi/struct_layout.rb +96 -0
  81. data/lib/ffi/struct_layout_builder.rb +227 -0
  82. data/lib/ffi/tools/const_generator.rb +232 -0
  83. data/lib/ffi/tools/generator.rb +105 -0
  84. data/lib/ffi/tools/generator_task.rb +32 -0
  85. data/lib/ffi/tools/struct_generator.rb +195 -0
  86. data/lib/ffi/tools/types_generator.rb +137 -0
  87. data/lib/ffi/types.rb +194 -0
  88. data/lib/ffi/union.rb +43 -0
  89. data/lib/ffi/variadic.rb +69 -0
  90. data/lib/ffi/version.rb +3 -0
  91. data/lib/ffi.rb +27 -0
  92. data/rakelib/ffi_gem_helper.rb +65 -0
  93. data/samples/getlogin.rb +8 -0
  94. data/samples/getpid.rb +8 -0
  95. data/samples/gettimeofday.rb +18 -0
  96. data/samples/hello.rb +8 -0
  97. data/samples/inotify.rb +60 -0
  98. data/samples/pty.rb +75 -0
  99. data/samples/qsort.rb +20 -0
  100. metadata +207 -0
@@ -0,0 +1,232 @@
1
+ require 'tempfile'
2
+ require 'open3'
3
+
4
+ module FFI
5
+
6
+ # ConstGenerator turns C constants into ruby values.
7
+ #
8
+ # @example a simple example for stdio
9
+ # require 'ffi/tools/const_generator'
10
+ # cg = FFI::ConstGenerator.new('stdio') do |gen|
11
+ # gen.const(:SEEK_SET)
12
+ # gen.const('SEEK_CUR')
13
+ # gen.const('seek_end') # this constant does not exist
14
+ # end # #calculate called automatically at the end of the block
15
+ #
16
+ # cg['SEEK_SET'] # => 0
17
+ # cg['SEEK_CUR'] # => 1
18
+ # cg['seek_end'] # => nil
19
+ # cg.to_ruby # => "SEEK_SET = 0\nSEEK_CUR = 1\n# seek_end not available"
20
+ class ConstGenerator
21
+ @options = {}
22
+ attr_reader :constants
23
+
24
+ # Creates a new constant generator that uses +prefix+ as a name, and an
25
+ # options hash.
26
+ #
27
+ # The only option is +:required+, which if set to +true+ raises an error if a
28
+ # constant you have requested was not found.
29
+ #
30
+ # @param [#to_s] prefix
31
+ # @param [Hash] options
32
+ # @return
33
+ # @option options [Boolean] :required
34
+ # @overload initialize(prefix, options)
35
+ # @overload initialize(prefix, options) { |gen| ... }
36
+ # @yieldparam [ConstGenerator] gen new generator is passed to the block
37
+ # When passed a block, {#calculate} is automatically called at the end of
38
+ # the block, otherwise you must call it yourself.
39
+ def initialize(prefix = nil, options = {})
40
+ @includes = ['stdio.h', 'stddef.h']
41
+ @constants = {}
42
+ @prefix = prefix
43
+
44
+ @required = options[:required]
45
+ @options = options
46
+
47
+ if block_given? then
48
+ yield self
49
+ calculate self.class.options.merge(options)
50
+ end
51
+ end
52
+ # Set class options
53
+ # These options are merged with {#initialize} options when it is called with a block.
54
+ # @param [Hash] options
55
+ # @return [Hash] class options
56
+ def self.options=(options)
57
+ @options = options
58
+ end
59
+ # Get class options.
60
+ # @return [Hash] class options
61
+ def self.options
62
+ @options
63
+ end
64
+ # @param [String] name
65
+ # @return constant value (converted if a +converter+ was defined).
66
+ # Access a constant by name.
67
+ def [](name)
68
+ @constants[name].converted_value
69
+ end
70
+
71
+ # Request the value for C constant +name+.
72
+ #
73
+ # @param [#to_s] name C constant name
74
+ # @param [String] format a printf format string to print the value out
75
+ # @param [String] cast a C cast for the value
76
+ # @param ruby_name alternate ruby name for {#to_ruby}
77
+ #
78
+ # @overload const(name, format=nil, cast='', ruby_name=nil, converter=nil)
79
+ # +converter+ is a Method or a Proc.
80
+ # @param [#call] converter convert the value from a string to the appropriate
81
+ # type for {#to_ruby}.
82
+ # @overload const(name, format=nil, cast='', ruby_name=nil) { |value| ... }
83
+ # Use a converter block. This block convert the value from a string to the
84
+ # appropriate type for {#to_ruby}.
85
+ # @yieldparam value constant value
86
+ def const(name, format = nil, cast = '', ruby_name = nil, converter = nil,
87
+ &converter_proc)
88
+ format ||= '%d'
89
+ cast ||= ''
90
+
91
+ if converter_proc and converter then
92
+ raise ArgumentError, "Supply only converter or converter block"
93
+ end
94
+
95
+ converter = converter_proc if converter.nil?
96
+
97
+ const = Constant.new name, format, cast, ruby_name, converter
98
+ @constants[name.to_s] = const
99
+ return const
100
+ end
101
+
102
+ # Calculate constants values.
103
+ # @param [Hash] options
104
+ # @option options [String] :cppflags flags for C compiler
105
+ # @return [nil]
106
+ # @raise if a constant is missing and +:required+ was set to +true+ (see {#initialize})
107
+ def calculate(options = {})
108
+ binary_path = nil
109
+
110
+ Tempfile.open("#{@prefix}.const_generator") do |f|
111
+ binary_path = f.path + ".bin"
112
+ @includes.each do |inc|
113
+ f.puts "#include <#{inc}>"
114
+ end
115
+ f.puts "\nint main(int argc, char **argv)\n{"
116
+
117
+ @constants.each_value do |const|
118
+ f.puts <<-EOF
119
+ #ifdef #{const.name}
120
+ printf("#{const.name} #{const.format}\\n", #{const.cast}#{const.name});
121
+ #endif
122
+ EOF
123
+ end
124
+
125
+ f.puts "\n\treturn 0;\n}"
126
+ f.flush
127
+
128
+ cc = ENV['CC'] || 'gcc'
129
+ output = `#{cc} #{options[:cppflags]} -D_DARWIN_USE_64_BIT_INODE -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -x c -Wall -Werror #{f.path} -o #{binary_path} 2>&1`
130
+
131
+ unless $?.success? then
132
+ output = output.split("\n").map { |l| "\t#{l}" }.join "\n"
133
+ raise "Compilation error generating constants #{@prefix}:\n#{output}"
134
+ end
135
+ end
136
+
137
+ output = `#{binary_path}`
138
+ File.unlink(binary_path + (FFI::Platform.windows? ? ".exe" : ""))
139
+ output.each_line do |line|
140
+ line =~ /^(\S+)\s(.*)$/
141
+ const = @constants[$1]
142
+ const.value = $2
143
+ end
144
+
145
+ missing_constants = @constants.select do |name, constant|
146
+ constant.value.nil?
147
+ end.map { |name,| name }
148
+
149
+ if @required and not missing_constants.empty? then
150
+ raise "Missing required constants for #{@prefix}: #{missing_constants.join ', '}"
151
+ end
152
+ end
153
+
154
+ # Dump constants to +io+.
155
+ # @param [#puts] io
156
+ # @return [nil]
157
+ def dump_constants(io)
158
+ @constants.each do |name, constant|
159
+ name = [@prefix, name].join '.' if @prefix
160
+ io.puts "#{name} = #{constant.converted_value}"
161
+ end
162
+ end
163
+
164
+ # Outputs values for discovered constants. If the constant's value was
165
+ # not discovered it is not omitted.
166
+ # @return [String]
167
+ def to_ruby
168
+ @constants.sort_by { |name,| name }.map do |name, constant|
169
+ if constant.value.nil? then
170
+ "# #{name} not available"
171
+ else
172
+ constant.to_ruby
173
+ end
174
+ end.join "\n"
175
+ end
176
+
177
+ # Add additional C include file(s) to calculate constants from.
178
+ # @note +stdio.h+ and +stddef.h+ automatically included
179
+ # @param [List<String>, Array<String>] i include file(s)
180
+ # @return [Array<String>] array of include files
181
+ def include(*i)
182
+ @includes |= i.flatten
183
+ end
184
+
185
+ end
186
+
187
+ # This class hold constants for {ConstGenerator}
188
+ class ConstGenerator::Constant
189
+
190
+ attr_reader :name, :format, :cast
191
+ attr_accessor :value
192
+
193
+ # @param [#to_s] name
194
+ # @param [String] format a printf format string to print the value out
195
+ # @param [String] cast a C cast for the value
196
+ # @param ruby_name alternate ruby name for {#to_ruby}
197
+ # @param [#call] converter convert the value from a string to the appropriate
198
+ # type for {#to_ruby}.
199
+ def initialize(name, format, cast, ruby_name = nil, converter=nil)
200
+ @name = name
201
+ @format = format
202
+ @cast = cast
203
+ @ruby_name = ruby_name
204
+ @converter = converter
205
+ @value = nil
206
+ end
207
+
208
+ # Return constant value (converted if a +converter+ was defined).
209
+ # @return constant value.
210
+ def converted_value
211
+ if @converter
212
+ @converter.call(@value)
213
+ else
214
+ @value
215
+ end
216
+ end
217
+
218
+ # get constant ruby name
219
+ # @return [String]
220
+ def ruby_name
221
+ @ruby_name || @name
222
+ end
223
+
224
+ # Get an evaluable string from constant.
225
+ # @return [String]
226
+ def to_ruby
227
+ "#{ruby_name} = #{converted_value}"
228
+ end
229
+
230
+ end
231
+
232
+ end
@@ -0,0 +1,105 @@
1
+ require 'ffi/tools/struct_generator'
2
+ require 'ffi/tools/const_generator'
3
+
4
+ module FFI
5
+
6
+ ##
7
+ # Generate files with C structs for FFI::Struct and C constants.
8
+ #
9
+ # == A simple example
10
+ #
11
+ # In file +zlib.rb.ffi+:
12
+ # module Zlib
13
+ # @@@
14
+ # constants do |c|
15
+ # c.include "zlib.h"
16
+ # c.const :ZLIB_VERNUM
17
+ # end
18
+ # @@@
19
+ #
20
+ # class ZStream < FFI::Struct
21
+ #
22
+ # struct do |s|
23
+ # s.name "struct z_stream_s"
24
+ # s.include "zlib.h"
25
+ #
26
+ # s.field :next_in, :pointer
27
+ # s.field :avail_in, :uint
28
+ # s.field :total_in, :ulong
29
+ # end
30
+ # @@@
31
+ # end
32
+ # end
33
+ #
34
+ # Translate the file:
35
+ # require "ffi/tools/generator"
36
+ # FFI::Generator.new "zlib.rb.ffi", "zlib.rb"
37
+ #
38
+ # Generates the file +zlib.rb+ with constant values and offsets:
39
+ # module Zlib
40
+ # ZLIB_VERNUM = 4784
41
+ #
42
+ # class ZStream < FFI::Struct
43
+ # layout :next_in, :pointer, 0,
44
+ # :avail_in, :uint, 8,
45
+ # :total_in, :ulong, 16
46
+ # end
47
+ #
48
+ # @see FFI::Generator::Task for easy integration in a Rakefile
49
+ class Generator
50
+
51
+ def initialize(ffi_name, rb_name, options = {})
52
+ @ffi_name = ffi_name
53
+ @rb_name = rb_name
54
+ @options = options
55
+ @name = File.basename rb_name, '.rb'
56
+
57
+ file = File.read @ffi_name
58
+
59
+ new_file = file.gsub(/^( *)@@@(.*?)@@@/m) do
60
+ @constants = []
61
+ @structs = []
62
+
63
+ indent = $1
64
+ original_lines = $2.count "\n"
65
+
66
+ instance_eval $2, @ffi_name, $`.count("\n")
67
+
68
+ new_lines = []
69
+ @constants.each { |c| new_lines << c.to_ruby }
70
+ @structs.each { |s| new_lines << s.generate_layout }
71
+
72
+ new_lines = new_lines.join("\n").split "\n" # expand multiline blocks
73
+ new_lines = new_lines.map { |line| indent + line }
74
+
75
+ padding = original_lines - new_lines.length
76
+ new_lines += [nil] * padding if padding >= 0
77
+
78
+ new_lines.join "\n"
79
+ end
80
+
81
+ open @rb_name, 'w' do |f|
82
+ f.puts "# This file is generated from `#{@ffi_name}'. Do not edit."
83
+ f.puts
84
+ f.puts new_file
85
+ end
86
+ end
87
+
88
+ def constants(options = {}, &block)
89
+ @constants << FFI::ConstGenerator.new(@name, @options.merge(options), &block)
90
+ end
91
+
92
+ def struct(options = {}, &block)
93
+ @structs << FFI::StructGenerator.new(@name, @options.merge(options), &block)
94
+ end
95
+
96
+ ##
97
+ # Utility converter for constants
98
+
99
+ def to_s
100
+ proc { |obj| obj.to_s.inspect }
101
+ end
102
+
103
+ end
104
+ end
105
+
@@ -0,0 +1,32 @@
1
+ require 'ffi/tools/generator'
2
+ require 'rake'
3
+ require 'rake/tasklib'
4
+
5
+ ##
6
+ # Add Rake tasks that generate files with C structs for FFI::Struct and C constants.
7
+ #
8
+ # @example a simple example for your Rakefile
9
+ # require "ffi/tools/generator_task"
10
+ # # Add a task to generate my_object.rb out of my_object.rb.ffi
11
+ # FFI::Generator::Task.new ["my_object.rb"], cflags: "-I/usr/local/mylibrary"
12
+ #
13
+ # The generated files are also added to the 'clear' task.
14
+ #
15
+ # @see FFI::Generator for a description of the file content
16
+ class FFI::Generator::Task < Rake::TaskLib
17
+
18
+ def initialize(rb_names, options={})
19
+ task :clean do rm_f rb_names end
20
+
21
+ rb_names.each do |rb_name|
22
+ ffi_name = "#{rb_name}.ffi"
23
+
24
+ file rb_name => ffi_name do |t|
25
+ puts "Generating #{rb_name}..." if Rake.application.options.trace
26
+
27
+ FFI::Generator.new ffi_name, rb_name, options
28
+ end
29
+ end
30
+ end
31
+
32
+ end
@@ -0,0 +1,195 @@
1
+ require 'tempfile'
2
+
3
+ module FFI
4
+
5
+ ##
6
+ # Generates an FFI Struct layout.
7
+ #
8
+ # Given the @@@ portion in:
9
+ #
10
+ # class Zlib::ZStream < FFI::Struct
11
+ # @@@
12
+ # name "struct z_stream_s"
13
+ # include "zlib.h"
14
+ #
15
+ # field :next_in, :pointer
16
+ # field :avail_in, :uint
17
+ # field :total_in, :ulong
18
+ #
19
+ # # ...
20
+ # @@@
21
+ # end
22
+ #
23
+ # StructGenerator will create the layout:
24
+ #
25
+ # layout :next_in, :pointer, 0,
26
+ # :avail_in, :uint, 4,
27
+ # :total_in, :ulong, 8,
28
+ # # ...
29
+ #
30
+ # StructGenerator does its best to pad the layout it produces to preserve
31
+ # line numbers. Place the struct definition as close to the top of the file
32
+ # for best results.
33
+
34
+ class StructGenerator
35
+ @options = {}
36
+ attr_accessor :size
37
+ attr_reader :fields
38
+
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
52
+ def self.options=(options)
53
+ @options = options
54
+ end
55
+ def self.options
56
+ @options
57
+ end
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 <<-EOF
77
+ printf("#{field.name} %u %u\\n", (unsigned int) offsetof(#{@struct_name}, #{field.name}),
78
+ (unsigned int) sizeof(s.#{field.name}));
79
+ EOF
80
+ end
81
+
82
+ f.puts "\n return 0;\n}"
83
+ f.flush
84
+
85
+ cc = ENV['CC'] || 'gcc'
86
+ 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`
87
+
88
+ unless $?.success? then
89
+ @found = false
90
+ output = output.split("\n").map { |l| "\t#{l}" }.join "\n"
91
+ raise "Compilation error generating struct #{@name} (#{@struct_name}):\n#{output}"
92
+ end
93
+ end
94
+
95
+ output = `#{binary}`.split "\n"
96
+ File.unlink(binary + (FFI::Platform.windows? ? ".exe" : ""))
97
+ sizeof = output.shift
98
+ unless @size
99
+ m = /\s*sizeof\([^)]+\) (\d+)/.match sizeof
100
+ @size = m[1]
101
+ end
102
+
103
+ line_no = 0
104
+ output.each do |line|
105
+ md = line.match(/.+ (\d+) (\d+)/)
106
+ @fields[line_no].offset = md[1].to_i
107
+ @fields[line_no].size = md[2].to_i
108
+
109
+ line_no += 1
110
+ end
111
+
112
+ @found = true
113
+ end
114
+
115
+ def field(name, type=nil)
116
+ field = Field.new(name, type)
117
+ @fields << field
118
+ return field
119
+ end
120
+
121
+ def found?
122
+ @found
123
+ end
124
+
125
+ def dump_config(io)
126
+ io.puts "rbx.platform.#{@name}.sizeof = #{@size}"
127
+
128
+ @fields.each { |field| io.puts field.to_config(@name) }
129
+ end
130
+
131
+ def generate_layout
132
+ buf = ""
133
+
134
+ @fields.each_with_index do |field, i|
135
+ if buf.empty?
136
+ buf << "layout :#{field.name}, :#{field.type}, #{field.offset}"
137
+ else
138
+ buf << " :#{field.name}, :#{field.type}, #{field.offset}"
139
+ end
140
+
141
+ if i < @fields.length - 1
142
+ buf << ",\n"
143
+ end
144
+ end
145
+
146
+ buf
147
+ end
148
+
149
+ def get_field(name)
150
+ @fields.find { |f| name == f.name }
151
+ end
152
+
153
+ def include(i)
154
+ @includes << i
155
+ end
156
+
157
+ def name(n)
158
+ @struct_name = n
159
+ end
160
+
161
+ end
162
+
163
+ ##
164
+ # A field in a Struct.
165
+
166
+ class StructGenerator::Field
167
+
168
+ attr_reader :name
169
+ attr_reader :type
170
+ attr_reader :offset
171
+ attr_accessor :size
172
+
173
+ def initialize(name, type)
174
+ @name = name
175
+ @type = type
176
+ @offset = nil
177
+ @size = nil
178
+ end
179
+
180
+ def offset=(o)
181
+ @offset = o
182
+ end
183
+
184
+ def to_config(name)
185
+ buf = []
186
+ buf << "rbx.platform.#{name}.#{@name}.offset = #{@offset}"
187
+ buf << "rbx.platform.#{name}.#{@name}.size = #{@size}"
188
+ buf << "rbx.platform.#{name}.#{@name}.type = #{@type}" if @type
189
+ buf
190
+ end
191
+
192
+ end
193
+
194
+ end
195
+
@@ -0,0 +1,137 @@
1
+ require 'tempfile'
2
+
3
+ module FFI
4
+
5
+ # @private
6
+ class TypesGenerator
7
+
8
+ ##
9
+ # Maps different C types to the C type representations we use
10
+
11
+ TYPE_MAP = {
12
+ "char" => :char,
13
+ "signed char" => :char,
14
+ "__signed char" => :char,
15
+ "unsigned char" => :uchar,
16
+
17
+ "short" => :short,
18
+ "signed short" => :short,
19
+ "signed short int" => :short,
20
+ "unsigned short" => :ushort,
21
+ "unsigned short int" => :ushort,
22
+
23
+ "int" => :int,
24
+ "signed int" => :int,
25
+ "unsigned int" => :uint,
26
+
27
+ "long" => :long,
28
+ "long int" => :long,
29
+ "signed long" => :long,
30
+ "signed long int" => :long,
31
+ "unsigned long" => :ulong,
32
+ "unsigned long int" => :ulong,
33
+ "long unsigned int" => :ulong,
34
+
35
+ "long long" => :long_long,
36
+ "long long int" => :long_long,
37
+ "signed long long" => :long_long,
38
+ "signed long long int" => :long_long,
39
+ "unsigned long long" => :ulong_long,
40
+ "unsigned long long int" => :ulong_long,
41
+
42
+ "char *" => :string,
43
+ "void *" => :pointer,
44
+ }
45
+
46
+ def self.generate(options = {})
47
+ typedefs = nil
48
+ Tempfile.open 'ffi_types_generator' do |io|
49
+ io.puts <<-C
50
+ #include <stdint.h>
51
+ #include <stddef.h>
52
+ #include <sys/types.h>
53
+ #if !(defined(WIN32))
54
+ #include <sys/socket.h>
55
+ #include <netinet/in.h>
56
+ #include <sys/resource.h>
57
+ #endif
58
+ C
59
+
60
+ io.close
61
+ cc = ENV['CC'] || 'gcc'
62
+ cmd = "#{cc} -E -x c #{options[:cppflags]} -D_DARWIN_USE_64_BIT_INODE -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -c"
63
+ if options[:input]
64
+ typedefs = File.read(options[:input])
65
+ elsif options[:remote]
66
+ typedefs = `ssh #{options[:remote]} #{cmd} - < #{io.path}`
67
+ else
68
+ typedefs = `#{cmd} #{io.path}`
69
+ end
70
+ end
71
+
72
+ code = []
73
+
74
+ typedefs.each_line do |type|
75
+ # We only care about single line typedef
76
+ next unless type =~ /typedef/
77
+ # Ignore unions or structs
78
+ next if type =~ /union|struct/
79
+
80
+ # strip off the starting typedef and ending ;
81
+ type.gsub!(/^(.*typedef\s*)/, "")
82
+ type.gsub!(/\s*;\s*$/, "")
83
+
84
+ parts = type.split(/\s+/)
85
+ def_type = parts.join(" ")
86
+
87
+ # GCC does mapping with __attribute__ stuf, also see
88
+ # http://hal.cs.berkeley.edu/cil/cil016.html section 16.2.7. Problem
89
+ # with this is that the __attribute__ stuff can either occur before or
90
+ # after the new type that is defined...
91
+ if type =~ /__attribute__/
92
+ if parts.last =~ /__QI__|__HI__|__SI__|__DI__|__word__/
93
+
94
+ # In this case, the new type is BEFORE __attribute__ we need to
95
+ # find the final_type as the type before the part that starts with
96
+ # __attribute__
97
+ final_type = ""
98
+ parts.each do |p|
99
+ break if p =~ /__attribute__/
100
+ final_type = p
101
+ end
102
+ else
103
+ final_type = parts.pop
104
+ end
105
+
106
+ def_type = case type
107
+ when /__QI__/ then "char"
108
+ when /__HI__/ then "short"
109
+ when /__SI__/ then "int"
110
+ when /__DI__/ then "long long"
111
+ when /__word__/ then "long"
112
+ else "int"
113
+ end
114
+
115
+ def_type = "unsigned #{def_type}" if type =~ /unsigned/
116
+ else
117
+ final_type = parts.pop
118
+ def_type = parts.join(" ")
119
+ end
120
+
121
+ if type = TYPE_MAP[def_type]
122
+ code << "rbx.platform.typedef.#{final_type} = #{type}"
123
+ TYPE_MAP[final_type] = TYPE_MAP[def_type]
124
+ else
125
+ # Fallback to an ordinary pointer if we don't know the type
126
+ if def_type =~ /\*/
127
+ code << "rbx.platform.typedef.#{final_type} = pointer"
128
+ TYPE_MAP[final_type] = :pointer
129
+ end
130
+ end
131
+ end
132
+
133
+ code.sort.join("\n")
134
+ end
135
+ end
136
+ end
137
+