ffi 1.15.5-x64-mingw-ucrt

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,592 @@
1
+ #
2
+ # Copyright (C) 2008-2010 Wayne Meissner
3
+ #
4
+ # This file is part of ruby-ffi.
5
+ #
6
+ # All rights reserved.
7
+ #
8
+ # Redistribution and use in source and binary forms, with or without
9
+ # modification, are permitted provided that the following conditions are met:
10
+ #
11
+ # * Redistributions of source code must retain the above copyright notice, this
12
+ # list of conditions and the following disclaimer.
13
+ # * Redistributions in binary form must reproduce the above copyright notice
14
+ # this list of conditions and the following disclaimer in the documentation
15
+ # and/or other materials provided with the distribution.
16
+ # * Neither the name of the Ruby FFI project nor the names of its contributors
17
+ # may be used to endorse or promote products derived from this software
18
+ # without specific prior written permission.
19
+ #
20
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
24
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.#
30
+
31
+ module FFI
32
+ CURRENT_PROCESS = USE_THIS_PROCESS_AS_LIBRARY = Object.new
33
+
34
+ # @param [#to_s] lib library name
35
+ # @return [String] library name formatted for current platform
36
+ # Transform a generic library name to a platform library name
37
+ # @example
38
+ # # Linux
39
+ # FFI.map_library_name 'c' # -> "libc.so.6"
40
+ # FFI.map_library_name 'jpeg' # -> "libjpeg.so"
41
+ # # Windows
42
+ # FFI.map_library_name 'c' # -> "msvcrt.dll"
43
+ # FFI.map_library_name 'jpeg' # -> "jpeg.dll"
44
+ def self.map_library_name(lib)
45
+ # Mangle the library name to reflect the native library naming conventions
46
+ lib = Library::LIBC if lib == 'c'
47
+
48
+ if lib && File.basename(lib) == lib
49
+ lib = Platform::LIBPREFIX + lib unless lib =~ /^#{Platform::LIBPREFIX}/
50
+ r = Platform::IS_WINDOWS || Platform::IS_MAC ? "\\.#{Platform::LIBSUFFIX}$" : "\\.so($|\\.[1234567890]+)"
51
+ lib += ".#{Platform::LIBSUFFIX}" unless lib =~ /#{r}/
52
+ end
53
+
54
+ lib
55
+ end
56
+
57
+ # Exception raised when a function is not found in libraries
58
+ class NotFoundError < LoadError
59
+ def initialize(function, *libraries)
60
+ super("Function '#{function}' not found in [#{libraries[0].nil? ? 'current process' : libraries.join(", ")}]")
61
+ end
62
+ end
63
+
64
+ # This module is the base to use native functions.
65
+ #
66
+ # A basic usage may be:
67
+ # require 'ffi'
68
+ #
69
+ # module Hello
70
+ # extend FFI::Library
71
+ # ffi_lib FFI::Library::LIBC
72
+ # attach_function 'puts', [ :string ], :int
73
+ # end
74
+ #
75
+ # Hello.puts("Hello, World")
76
+ #
77
+ #
78
+ module Library
79
+ CURRENT_PROCESS = FFI::CURRENT_PROCESS
80
+ LIBC = FFI::Platform::LIBC
81
+
82
+ # @param mod extended object
83
+ # @return [nil]
84
+ # @raise {RuntimeError} if +mod+ is not a Module
85
+ # Test if extended object is a Module. If not, raise RuntimeError.
86
+ def self.extended(mod)
87
+ raise RuntimeError.new("must only be extended by module") unless mod.kind_of?(::Module)
88
+ end
89
+
90
+
91
+ # @param [Array] names names of libraries to load
92
+ # @return [Array<DynamicLibrary>]
93
+ # @raise {LoadError} if a library cannot be opened
94
+ # Load native libraries.
95
+ def ffi_lib(*names)
96
+ raise LoadError.new("library names list must not be empty") if names.empty?
97
+
98
+ lib_flags = defined?(@ffi_lib_flags) ? @ffi_lib_flags : FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_LOCAL
99
+ ffi_libs = names.map do |name|
100
+
101
+ if name == FFI::CURRENT_PROCESS
102
+ FFI::DynamicLibrary.open(nil, FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_LOCAL)
103
+
104
+ else
105
+ libnames = (name.is_a?(::Array) ? name : [ name ]).map(&:to_s).map { |n| [ n, FFI.map_library_name(n) ].uniq }.flatten.compact
106
+ lib = nil
107
+ errors = {}
108
+
109
+ libnames.each do |libname|
110
+ begin
111
+ orig = libname
112
+ lib = FFI::DynamicLibrary.open(libname, lib_flags)
113
+ break if lib
114
+
115
+ rescue Exception => ex
116
+ ldscript = false
117
+ if ex.message =~ /(([^ \t()])+\.so([^ \t:()])*):([ \t])*(invalid ELF header|file too short|invalid file format)/
118
+ if File.binread($1) =~ /(?:GROUP|INPUT) *\( *([^ \)]+)/
119
+ libname = $1
120
+ ldscript = true
121
+ end
122
+ end
123
+
124
+ if ldscript
125
+ retry
126
+ else
127
+ # TODO better library lookup logic
128
+ unless libname.start_with?("/") || FFI::Platform.windows?
129
+ path = ['/usr/lib/','/usr/local/lib/','/opt/local/lib/', '/opt/homebrew/lib/'].find do |pth|
130
+ File.exist?(pth + libname)
131
+ end
132
+ if path
133
+ libname = path + libname
134
+ retry
135
+ end
136
+ end
137
+
138
+ libr = (orig == libname ? orig : "#{orig} #{libname}")
139
+ errors[libr] = ex
140
+ end
141
+ end
142
+ end
143
+
144
+ if lib.nil?
145
+ raise LoadError.new(errors.values.join(".\n"))
146
+ end
147
+
148
+ # return the found lib
149
+ lib
150
+ end
151
+ end
152
+
153
+ @ffi_libs = ffi_libs
154
+ end
155
+
156
+ # Set the calling convention for {#attach_function} and {#callback}
157
+ #
158
+ # @see http://en.wikipedia.org/wiki/Stdcall#stdcall
159
+ # @note +:stdcall+ is typically used for attaching Windows API functions
160
+ #
161
+ # @param [Symbol] convention one of +:default+, +:stdcall+
162
+ # @return [Symbol] the new calling convention
163
+ def ffi_convention(convention = nil)
164
+ @ffi_convention ||= :default
165
+ @ffi_convention = convention if convention
166
+ @ffi_convention
167
+ end
168
+
169
+ # @see #ffi_lib
170
+ # @return [Array<FFI::DynamicLibrary>] array of currently loaded FFI libraries
171
+ # @raise [LoadError] if no libraries have been loaded (using {#ffi_lib})
172
+ # Get FFI libraries loaded using {#ffi_lib}.
173
+ def ffi_libraries
174
+ raise LoadError.new("no library specified") if !defined?(@ffi_libs) || @ffi_libs.empty?
175
+ @ffi_libs
176
+ end
177
+
178
+ # Flags used in {#ffi_lib}.
179
+ #
180
+ # This map allows you to supply symbols to {#ffi_lib_flags} instead of
181
+ # the actual constants.
182
+ FlagsMap = {
183
+ :global => DynamicLibrary::RTLD_GLOBAL,
184
+ :local => DynamicLibrary::RTLD_LOCAL,
185
+ :lazy => DynamicLibrary::RTLD_LAZY,
186
+ :now => DynamicLibrary::RTLD_NOW
187
+ }
188
+
189
+ # Sets library flags for {#ffi_lib}.
190
+ #
191
+ # @example
192
+ # ffi_lib_flags(:lazy, :local) # => 5
193
+ #
194
+ # @param [Symbol, …] flags (see {FlagsMap})
195
+ # @return [Fixnum] the new value
196
+ def ffi_lib_flags(*flags)
197
+ @ffi_lib_flags = flags.inject(0) { |result, f| result | FlagsMap[f] }
198
+ end
199
+
200
+
201
+ ##
202
+ # @overload attach_function(func, args, returns, options = {})
203
+ # @example attach function without an explicit name
204
+ # module Foo
205
+ # extend FFI::Library
206
+ # ffi_lib FFI::Library::LIBC
207
+ # attach_function :malloc, [:size_t], :pointer
208
+ # end
209
+ # # now callable via Foo.malloc
210
+ # @overload attach_function(name, func, args, returns, options = {})
211
+ # @example attach function with an explicit name
212
+ # module Bar
213
+ # extend FFI::Library
214
+ # ffi_lib FFI::Library::LIBC
215
+ # attach_function :c_malloc, :malloc, [:size_t], :pointer
216
+ # end
217
+ # # now callable via Bar.c_malloc
218
+ #
219
+ # Attach C function +func+ to this module.
220
+ #
221
+ #
222
+ # @param [#to_s] name name of ruby method to attach as
223
+ # @param [#to_s] func name of C function to attach
224
+ # @param [Array<Symbol>] args an array of types
225
+ # @param [Symbol] returns type of return value
226
+ # @option options [Boolean] :blocking (@blocking) set to true if the C function is a blocking call
227
+ # @option options [Symbol] :convention (:default) calling convention (see {#ffi_convention})
228
+ # @option options [FFI::Enums] :enums
229
+ # @option options [Hash] :type_map
230
+ #
231
+ # @return [FFI::VariadicInvoker]
232
+ #
233
+ # @raise [FFI::NotFoundError] if +func+ cannot be found in the attached libraries (see {#ffi_lib})
234
+ def attach_function(name, func, args, returns = nil, options = nil)
235
+ mname, a2, a3, a4, a5 = name, func, args, returns, options
236
+ cname, arg_types, ret_type, opts = (a4 && (a2.is_a?(String) || a2.is_a?(Symbol))) ? [ a2, a3, a4, a5 ] : [ mname.to_s, a2, a3, a4 ]
237
+
238
+ # Convert :foo to the native type
239
+ arg_types = arg_types.map { |e| find_type(e) }
240
+ options = {
241
+ :convention => ffi_convention,
242
+ :type_map => defined?(@ffi_typedefs) ? @ffi_typedefs : nil,
243
+ :blocking => defined?(@blocking) && @blocking,
244
+ :enums => defined?(@ffi_enums) ? @ffi_enums : nil,
245
+ }
246
+
247
+ @blocking = false
248
+ options.merge!(opts) if opts && opts.is_a?(Hash)
249
+
250
+ # Try to locate the function in any of the libraries
251
+ invokers = []
252
+ ffi_libraries.each do |lib|
253
+ if invokers.empty?
254
+ begin
255
+ function = nil
256
+ function_names(cname, arg_types).find do |fname|
257
+ function = lib.find_function(fname)
258
+ end
259
+ raise LoadError unless function
260
+
261
+ invokers << if arg_types.length > 0 && arg_types[arg_types.length - 1] == FFI::NativeType::VARARGS
262
+ VariadicInvoker.new(function, arg_types, find_type(ret_type), options)
263
+
264
+ else
265
+ Function.new(find_type(ret_type), arg_types, function, options)
266
+ end
267
+
268
+ rescue LoadError
269
+ end
270
+ end
271
+ end
272
+ invoker = invokers.compact.shift
273
+ raise FFI::NotFoundError.new(cname.to_s, ffi_libraries.map { |lib| lib.name }) unless invoker
274
+
275
+ invoker.attach(self, mname.to_s)
276
+ invoker
277
+ end
278
+
279
+ # @param [#to_s] name function name
280
+ # @param [Array] arg_types function's argument types
281
+ # @return [Array<String>]
282
+ # This function returns a list of possible names to lookup.
283
+ # @note Function names on windows may be decorated if they are using stdcall. See
284
+ # * http://en.wikipedia.org/wiki/Name_mangling#C_name_decoration_in_Microsoft_Windows
285
+ # * http://msdn.microsoft.com/en-us/library/zxk0tw93%28v=VS.100%29.aspx
286
+ # * http://en.wikibooks.org/wiki/X86_Disassembly/Calling_Conventions#STDCALL
287
+ # Note that decorated names can be overridden via def files. Also note that the
288
+ # windows api, although using, doesn't have decorated names.
289
+ def function_names(name, arg_types)
290
+ result = [name.to_s]
291
+ if ffi_convention == :stdcall
292
+ # Get the size of each parameter
293
+ size = arg_types.inject(0) do |mem, arg|
294
+ size = arg.size
295
+ # The size must be a multiple of 4
296
+ size += (4 - size) % 4
297
+ mem + size
298
+ end
299
+
300
+ result << "_#{name.to_s}@#{size}" # win32
301
+ result << "#{name.to_s}@#{size}" # win64
302
+ end
303
+ result
304
+ end
305
+
306
+ # @overload attach_variable(mname, cname, type)
307
+ # @param [#to_s] mname name of ruby method to attach as
308
+ # @param [#to_s] cname name of C variable to attach
309
+ # @param [DataConverter, Struct, Symbol, Type] type C variable's type
310
+ # @example
311
+ # module Bar
312
+ # extend FFI::Library
313
+ # ffi_lib 'my_lib'
314
+ # attach_variable :c_myvar, :myvar, :long
315
+ # end
316
+ # # now callable via Bar.c_myvar
317
+ # @overload attach_variable(cname, type)
318
+ # @param [#to_s] mname name of ruby method to attach as
319
+ # @param [DataConverter, Struct, Symbol, Type] type C variable's type
320
+ # @example
321
+ # module Bar
322
+ # extend FFI::Library
323
+ # ffi_lib 'my_lib'
324
+ # attach_variable :myvar, :long
325
+ # end
326
+ # # now callable via Bar.myvar
327
+ # @return [DynamicLibrary::Symbol]
328
+ # @raise {FFI::NotFoundError} if +cname+ cannot be found in libraries
329
+ #
330
+ # Attach C variable +cname+ to this module.
331
+ def attach_variable(mname, a1, a2 = nil)
332
+ cname, type = a2 ? [ a1, a2 ] : [ mname.to_s, a1 ]
333
+ address = nil
334
+ ffi_libraries.each do |lib|
335
+ begin
336
+ address = lib.find_variable(cname.to_s)
337
+ break unless address.nil?
338
+ rescue LoadError
339
+ end
340
+ end
341
+
342
+ raise FFI::NotFoundError.new(cname, ffi_libraries) if address.nil? || address.null?
343
+ if type.is_a?(Class) && type < FFI::Struct
344
+ # If it is a global struct, just attach directly to the pointer
345
+ s = s = type.new(address) # Assigning twice to suppress unused variable warning
346
+ self.module_eval <<-code, __FILE__, __LINE__
347
+ @@ffi_gvar_#{mname} = s
348
+ def self.#{mname}
349
+ @@ffi_gvar_#{mname}
350
+ end
351
+ code
352
+
353
+ else
354
+ sc = Class.new(FFI::Struct)
355
+ sc.layout :gvar, find_type(type)
356
+ s = sc.new(address)
357
+ #
358
+ # Attach to this module as mname/mname=
359
+ #
360
+ self.module_eval <<-code, __FILE__, __LINE__
361
+ @@ffi_gvar_#{mname} = s
362
+ def self.#{mname}
363
+ @@ffi_gvar_#{mname}[:gvar]
364
+ end
365
+ def self.#{mname}=(value)
366
+ @@ffi_gvar_#{mname}[:gvar] = value
367
+ end
368
+ code
369
+
370
+ end
371
+
372
+ address
373
+ end
374
+
375
+
376
+ # @overload callback(name, params, ret)
377
+ # @param name callback name to add to type map
378
+ # @param [Array] params array of parameters' types
379
+ # @param [DataConverter, Struct, Symbol, Type] ret callback return type
380
+ # @overload callback(params, ret)
381
+ # @param [Array] params array of parameters' types
382
+ # @param [DataConverter, Struct, Symbol, Type] ret callback return type
383
+ # @return [FFI::CallbackInfo]
384
+ def callback(*args)
385
+ raise ArgumentError, "wrong number of arguments" if args.length < 2 || args.length > 3
386
+ name, params, ret = if args.length == 3
387
+ args
388
+ else
389
+ [ nil, args[0], args[1] ]
390
+ end
391
+
392
+ native_params = params.map { |e| find_type(e) }
393
+ raise ArgumentError, "callbacks cannot have variadic parameters" if native_params.include?(FFI::Type::VARARGS)
394
+ options = Hash.new
395
+ options[:convention] = ffi_convention
396
+ options[:enums] = @ffi_enums if defined?(@ffi_enums)
397
+ ret_type = find_type(ret)
398
+ if ret_type == Type::STRING
399
+ raise TypeError, ":string is not allowed as return type of callbacks"
400
+ end
401
+ cb = FFI::CallbackInfo.new(ret_type, native_params, options)
402
+
403
+ # Add to the symbol -> type map (unless there was no name)
404
+ unless name.nil?
405
+ typedef cb, name
406
+ end
407
+
408
+ cb
409
+ end
410
+
411
+ # Register or get an already registered type definition.
412
+ #
413
+ # To register a new type definition, +old+ should be a {FFI::Type}. +add+
414
+ # is in this case the type definition.
415
+ #
416
+ # If +old+ is a {DataConverter}, a {Type::Mapped} is returned.
417
+ #
418
+ # If +old+ is +:enum+
419
+ # * and +add+ is an +Array+, a call to {#enum} is made with +add+ as single parameter;
420
+ # * in others cases, +info+ is used to create a named enum.
421
+ #
422
+ # If +old+ is a key for type map, #typedef get +old+ type definition.
423
+ #
424
+ # @param [DataConverter, Symbol, Type] old
425
+ # @param [Symbol] add
426
+ # @param [Symbol] info
427
+ # @return [FFI::Enum, FFI::Type]
428
+ def typedef(old, add, info=nil)
429
+ @ffi_typedefs = Hash.new unless defined?(@ffi_typedefs)
430
+
431
+ @ffi_typedefs[add] = if old.kind_of?(FFI::Type)
432
+ old
433
+
434
+ elsif @ffi_typedefs.has_key?(old)
435
+ @ffi_typedefs[old]
436
+
437
+ elsif old.is_a?(DataConverter)
438
+ FFI::Type::Mapped.new(old)
439
+
440
+ elsif old == :enum
441
+ if add.kind_of?(Array)
442
+ self.enum(add)
443
+ else
444
+ self.enum(info, add)
445
+ end
446
+
447
+ else
448
+ FFI.find_type(old)
449
+ end
450
+ end
451
+
452
+ private
453
+ # Generic enum builder
454
+ # @param [Class] klass can be one of FFI::Enum or FFI::Bitmask
455
+ # @param args (see #enum or #bitmask)
456
+ def generic_enum(klass, *args)
457
+ native_type = args.first.kind_of?(FFI::Type) ? args.shift : nil
458
+ name, values = if args[0].kind_of?(Symbol) && args[1].kind_of?(Array)
459
+ [ args[0], args[1] ]
460
+ elsif args[0].kind_of?(Array)
461
+ [ nil, args[0] ]
462
+ else
463
+ [ nil, args ]
464
+ end
465
+ @ffi_enums = FFI::Enums.new unless defined?(@ffi_enums)
466
+ @ffi_enums << (e = native_type ? klass.new(native_type, values, name) : klass.new(values, name))
467
+
468
+ # If called with a name, add a typedef alias
469
+ typedef(e, name) if name
470
+ e
471
+ end
472
+
473
+ public
474
+ # @overload enum(name, values)
475
+ # Create a named enum.
476
+ # @example
477
+ # enum :foo, [:zero, :one, :two] # named enum
478
+ # @param [Symbol] name name for new enum
479
+ # @param [Array] values values for enum
480
+ # @overload enum(*args)
481
+ # Create an unnamed enum.
482
+ # @example
483
+ # enum :zero, :one, :two # unnamed enum
484
+ # @param args values for enum
485
+ # @overload enum(values)
486
+ # Create an unnamed enum.
487
+ # @example
488
+ # enum [:zero, :one, :two] # unnamed enum, equivalent to above example
489
+ # @param [Array] values values for enum
490
+ # @overload enum(native_type, name, values)
491
+ # Create a named enum and specify the native type.
492
+ # @example
493
+ # enum FFI::Type::UINT64, :foo, [:zero, :one, :two] # named enum
494
+ # @param [FFI::Type] native_type native type for new enum
495
+ # @param [Symbol] name name for new enum
496
+ # @param [Array] values values for enum
497
+ # @overload enum(native_type, *args)
498
+ # Create an unnamed enum and specify the native type.
499
+ # @example
500
+ # enum FFI::Type::UINT64, :zero, :one, :two # unnamed enum
501
+ # @param [FFI::Type] native_type native type for new enum
502
+ # @param args values for enum
503
+ # @overload enum(native_type, values)
504
+ # Create an unnamed enum and specify the native type.
505
+ # @example
506
+ # enum Type::UINT64, [:zero, :one, :two] # unnamed enum, equivalent to above example
507
+ # @param [FFI::Type] native_type native type for new enum
508
+ # @param [Array] values values for enum
509
+ # @return [FFI::Enum]
510
+ # Create a new {FFI::Enum}.
511
+ def enum(*args)
512
+ generic_enum(FFI::Enum, *args)
513
+ end
514
+
515
+ # @overload bitmask(name, values)
516
+ # Create a named bitmask
517
+ # @example
518
+ # bitmask :foo, [:red, :green, :blue] # bits 0,1,2 are used
519
+ # bitmask :foo, [:red, :green, 5, :blue] # bits 0,5,6 are used
520
+ # @param [Symbol] name for new bitmask
521
+ # @param [Array<Symbol, Integer>] values for new bitmask
522
+ # @overload bitmask(*args)
523
+ # Create an unamed bitmask
524
+ # @example
525
+ # bm = bitmask :red, :green, :blue # bits 0,1,2 are used
526
+ # bm = bitmask :red, :green, 5, blue # bits 0,5,6 are used
527
+ # @param [Symbol, Integer] args values for new bitmask
528
+ # @overload bitmask(values)
529
+ # Create an unamed bitmask
530
+ # @example
531
+ # bm = bitmask [:red, :green, :blue] # bits 0,1,2 are used
532
+ # bm = bitmask [:red, :green, 5, blue] # bits 0,5,6 are used
533
+ # @param [Array<Symbol, Integer>] values for new bitmask
534
+ # @overload bitmask(native_type, name, values)
535
+ # Create a named enum and specify the native type.
536
+ # @example
537
+ # bitmask FFI::Type::UINT64, :foo, [:red, :green, :blue]
538
+ # @param [FFI::Type] native_type native type for new bitmask
539
+ # @param [Symbol] name for new bitmask
540
+ # @param [Array<Symbol, Integer>] values for new bitmask
541
+ # @overload bitmask(native_type, *args)
542
+ # @example
543
+ # bitmask FFI::Type::UINT64, :red, :green, :blue
544
+ # @param [FFI::Type] native_type native type for new bitmask
545
+ # @param [Symbol, Integer] args values for new bitmask
546
+ # @overload bitmask(native_type, values)
547
+ # Create a named enum and specify the native type.
548
+ # @example
549
+ # bitmask FFI::Type::UINT64, [:red, :green, :blue]
550
+ # @param [FFI::Type] native_type native type for new bitmask
551
+ # @param [Array<Symbol, Integer>] values for new bitmask
552
+ # @return [FFI::Bitmask]
553
+ # Create a new FFI::Bitmask
554
+ def bitmask(*args)
555
+ generic_enum(FFI::Bitmask, *args)
556
+ end
557
+
558
+ # @param name
559
+ # @return [FFI::Enum]
560
+ # Find an enum by name.
561
+ def enum_type(name)
562
+ @ffi_enums.find(name) if defined?(@ffi_enums)
563
+ end
564
+
565
+ # @param symbol
566
+ # @return [FFI::Enum]
567
+ # Find an enum by a symbol it contains.
568
+ def enum_value(symbol)
569
+ @ffi_enums.__map_symbol(symbol)
570
+ end
571
+
572
+ # @param [DataConverter, Type, Struct, Symbol] t type to find
573
+ # @return [Type]
574
+ # Find a type definition.
575
+ def find_type(t)
576
+ if t.kind_of?(Type)
577
+ t
578
+
579
+ elsif defined?(@ffi_typedefs) && @ffi_typedefs.has_key?(t)
580
+ @ffi_typedefs[t]
581
+
582
+ elsif t.is_a?(Class) && t < Struct
583
+ Type::POINTER
584
+
585
+ elsif t.is_a?(DataConverter)
586
+ # Add a typedef so next time the converter is used, it hits the cache
587
+ typedef Type::Mapped.new(t), t
588
+
589
+ end || FFI.find_type(t)
590
+ end
591
+ end
592
+ end
@@ -0,0 +1,84 @@
1
+ # Copyright (C) 2008 Mike Dalessio
2
+ #
3
+ # This file is part of ruby-ffi.
4
+ #
5
+ # All rights reserved.
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions are met:
9
+ #
10
+ # * Redistributions of source code must retain the above copyright notice, this
11
+ # list of conditions and the following disclaimer.
12
+ # * Redistributions in binary form must reproduce the above copyright notice
13
+ # this list of conditions and the following disclaimer in the documentation
14
+ # and/or other materials provided with the distribution.
15
+ # * Neither the name of the Ruby FFI project nor the names of its contributors
16
+ # may be used to endorse or promote products derived from this software
17
+ # without specific prior written permission.
18
+ #
19
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
23
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+
30
+ module FFI
31
+ #
32
+ # FFI::ManagedStruct allows custom garbage-collection of your FFI::Structs.
33
+ #
34
+ # The typical use case would be when interacting with a library
35
+ # that has a nontrivial memory management design, such as a linked
36
+ # list or a binary tree.
37
+ #
38
+ # When the {Struct} instance is garbage collected, FFI::ManagedStruct will
39
+ # invoke the class's release() method during object finalization.
40
+ #
41
+ # @example Example usage:
42
+ # module MyLibrary
43
+ # ffi_lib "libmylibrary"
44
+ # attach_function :new_dlist, [], :pointer
45
+ # attach_function :destroy_dlist, [:pointer], :void
46
+ # end
47
+ #
48
+ # class DoublyLinkedList < FFI::ManagedStruct
49
+ # @@@
50
+ # struct do |s|
51
+ # s.name 'struct dlist'
52
+ # s.include 'dlist.h'
53
+ # s.field :head, :pointer
54
+ # s.field :tail, :pointer
55
+ # end
56
+ # @@@
57
+ #
58
+ # def self.release ptr
59
+ # MyLibrary.destroy_dlist(ptr)
60
+ # end
61
+ # end
62
+ #
63
+ # begin
64
+ # ptr = DoublyLinkedList.new(MyLibrary.new_dlist)
65
+ # # do something with the list
66
+ # end
67
+ # # struct is out of scope, and will be GC'd using DoublyLinkedList#release
68
+ #
69
+ #
70
+ class ManagedStruct < FFI::Struct
71
+
72
+ # @overload initialize(pointer)
73
+ # @param [Pointer] pointer
74
+ # Create a new ManagedStruct which will invoke the class method #release on
75
+ # @overload initialize
76
+ # A new instance of FFI::ManagedStruct.
77
+ def initialize(pointer=nil)
78
+ raise NoMethodError, "release() not implemented for class #{self}" unless self.class.respond_to? :release
79
+ raise ArgumentError, "Must supply a pointer to memory for the Struct" unless pointer
80
+ super AutoPointer.new(pointer, self.class.method(:release))
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1 @@
1
+ # This class is now implemented in C