alinta-ffi 1.9.19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (195) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +49 -0
  3. data/LICENSE +24 -0
  4. data/README.md +112 -0
  5. data/Rakefile +243 -0
  6. data/ext/ffi_c/AbstractMemory.c +1109 -0
  7. data/ext/ffi_c/AbstractMemory.h +175 -0
  8. data/ext/ffi_c/ArrayType.c +162 -0
  9. data/ext/ffi_c/ArrayType.h +59 -0
  10. data/ext/ffi_c/Buffer.c +365 -0
  11. data/ext/ffi_c/Call.c +517 -0
  12. data/ext/ffi_c/Call.h +110 -0
  13. data/ext/ffi_c/ClosurePool.c +283 -0
  14. data/ext/ffi_c/ClosurePool.h +57 -0
  15. data/ext/ffi_c/DataConverter.c +91 -0
  16. data/ext/ffi_c/DynamicLibrary.c +339 -0
  17. data/ext/ffi_c/DynamicLibrary.h +98 -0
  18. data/ext/ffi_c/Function.c +998 -0
  19. data/ext/ffi_c/Function.h +87 -0
  20. data/ext/ffi_c/FunctionInfo.c +271 -0
  21. data/ext/ffi_c/LastError.c +184 -0
  22. data/ext/ffi_c/LastError.h +47 -0
  23. data/ext/ffi_c/LongDouble.c +63 -0
  24. data/ext/ffi_c/LongDouble.h +51 -0
  25. data/ext/ffi_c/MappedType.c +168 -0
  26. data/ext/ffi_c/MappedType.h +59 -0
  27. data/ext/ffi_c/MemoryPointer.c +197 -0
  28. data/ext/ffi_c/MemoryPointer.h +53 -0
  29. data/ext/ffi_c/MethodHandle.c +358 -0
  30. data/ext/ffi_c/MethodHandle.h +55 -0
  31. data/ext/ffi_c/Platform.c +129 -0
  32. data/ext/ffi_c/Platform.h +45 -0
  33. data/ext/ffi_c/Pointer.c +508 -0
  34. data/ext/ffi_c/Pointer.h +63 -0
  35. data/ext/ffi_c/Struct.c +829 -0
  36. data/ext/ffi_c/Struct.h +106 -0
  37. data/ext/ffi_c/StructByReference.c +190 -0
  38. data/ext/ffi_c/StructByReference.h +50 -0
  39. data/ext/ffi_c/StructByValue.c +150 -0
  40. data/ext/ffi_c/StructByValue.h +55 -0
  41. data/ext/ffi_c/StructLayout.c +698 -0
  42. data/ext/ffi_c/Thread.c +352 -0
  43. data/ext/ffi_c/Thread.h +95 -0
  44. data/ext/ffi_c/Type.c +397 -0
  45. data/ext/ffi_c/Type.h +62 -0
  46. data/ext/ffi_c/Types.c +139 -0
  47. data/ext/ffi_c/Types.h +89 -0
  48. data/ext/ffi_c/Variadic.c +304 -0
  49. data/ext/ffi_c/compat.h +78 -0
  50. data/ext/ffi_c/extconf.rb +71 -0
  51. data/ext/ffi_c/ffi.c +98 -0
  52. data/ext/ffi_c/libffi.bsd.mk +40 -0
  53. data/ext/ffi_c/libffi.darwin.mk +105 -0
  54. data/ext/ffi_c/libffi.gnu.mk +32 -0
  55. data/ext/ffi_c/libffi.mk +18 -0
  56. data/ext/ffi_c/libffi.vc.mk +26 -0
  57. data/ext/ffi_c/libffi.vc64.mk +26 -0
  58. data/ext/ffi_c/rbffi.h +57 -0
  59. data/ext/ffi_c/rbffi_endian.h +59 -0
  60. data/ext/ffi_c/win32/stdbool.h +8 -0
  61. data/ext/ffi_c/win32/stdint.h +201 -0
  62. data/ffi.gemspec +23 -0
  63. data/gen/Rakefile +30 -0
  64. data/lib/ffi.rb +20 -0
  65. data/lib/ffi/autopointer.rb +203 -0
  66. data/lib/ffi/buffer.rb +4 -0
  67. data/lib/ffi/callback.rb +4 -0
  68. data/lib/ffi/enum.rb +296 -0
  69. data/lib/ffi/errno.rb +43 -0
  70. data/lib/ffi/ffi.rb +44 -0
  71. data/lib/ffi/io.rb +62 -0
  72. data/lib/ffi/library.rb +590 -0
  73. data/lib/ffi/managedstruct.rb +84 -0
  74. data/lib/ffi/memorypointer.rb +1 -0
  75. data/lib/ffi/platform.rb +164 -0
  76. data/lib/ffi/platform/aarch64-linux/types.conf +104 -0
  77. data/lib/ffi/platform/arm-linux/types.conf +104 -0
  78. data/lib/ffi/platform/i386-cygwin/types.conf +3 -0
  79. data/lib/ffi/platform/i386-darwin/types.conf +100 -0
  80. data/lib/ffi/platform/i386-freebsd/types.conf +152 -0
  81. data/lib/ffi/platform/i386-gnu/types.conf +107 -0
  82. data/lib/ffi/platform/i386-linux/types.conf +103 -0
  83. data/lib/ffi/platform/i386-netbsd/types.conf +126 -0
  84. data/lib/ffi/platform/i386-openbsd/types.conf +128 -0
  85. data/lib/ffi/platform/i386-solaris/types.conf +122 -0
  86. data/lib/ffi/platform/i386-windows/types.conf +105 -0
  87. data/lib/ffi/platform/ia64-linux/types.conf +104 -0
  88. data/lib/ffi/platform/mips-linux/types.conf +102 -0
  89. data/lib/ffi/platform/mips64el-linux/types.conf +104 -0
  90. data/lib/ffi/platform/mipsel-linux/types.conf +102 -0
  91. data/lib/ffi/platform/powerpc-aix/types.conf +180 -0
  92. data/lib/ffi/platform/powerpc-darwin/types.conf +100 -0
  93. data/lib/ffi/platform/powerpc-linux/types.conf +100 -0
  94. data/lib/ffi/platform/powerpc64-linux/types.conf +104 -0
  95. data/lib/ffi/platform/s390-linux/types.conf +102 -0
  96. data/lib/ffi/platform/s390x-linux/types.conf +102 -0
  97. data/lib/ffi/platform/sparc-linux/types.conf +102 -0
  98. data/lib/ffi/platform/sparc-solaris/types.conf +128 -0
  99. data/lib/ffi/platform/sparc64-linux/types.conf +102 -0
  100. data/lib/ffi/platform/sparcv9-solaris/types.conf +128 -0
  101. data/lib/ffi/platform/x86_64-cygwin/types.conf +3 -0
  102. data/lib/ffi/platform/x86_64-darwin/types.conf +126 -0
  103. data/lib/ffi/platform/x86_64-freebsd/types.conf +128 -0
  104. data/lib/ffi/platform/x86_64-linux/types.conf +102 -0
  105. data/lib/ffi/platform/x86_64-netbsd/types.conf +128 -0
  106. data/lib/ffi/platform/x86_64-openbsd/types.conf +134 -0
  107. data/lib/ffi/platform/x86_64-solaris/types.conf +122 -0
  108. data/lib/ffi/platform/x86_64-windows/types.conf +120 -0
  109. data/lib/ffi/pointer.rb +161 -0
  110. data/lib/ffi/struct.rb +371 -0
  111. data/lib/ffi/struct_layout_builder.rb +227 -0
  112. data/lib/ffi/tools/const_generator.rb +229 -0
  113. data/lib/ffi/tools/generator.rb +60 -0
  114. data/lib/ffi/tools/generator_task.rb +36 -0
  115. data/lib/ffi/tools/struct_generator.rb +194 -0
  116. data/lib/ffi/tools/types_generator.rb +134 -0
  117. data/lib/ffi/types.rb +194 -0
  118. data/lib/ffi/union.rb +43 -0
  119. data/lib/ffi/variadic.rb +78 -0
  120. data/lib/ffi/version.rb +4 -0
  121. data/libtest/Benchmark.c +52 -0
  122. data/libtest/BoolTest.c +34 -0
  123. data/libtest/BufferTest.c +31 -0
  124. data/libtest/ClosureTest.c +205 -0
  125. data/libtest/EnumTest.c +51 -0
  126. data/libtest/FunctionTest.c +70 -0
  127. data/libtest/GNUmakefile +149 -0
  128. data/libtest/GlobalVariable.c +62 -0
  129. data/libtest/LastErrorTest.c +21 -0
  130. data/libtest/NumberTest.c +132 -0
  131. data/libtest/PointerTest.c +63 -0
  132. data/libtest/ReferenceTest.c +23 -0
  133. data/libtest/StringTest.c +34 -0
  134. data/libtest/StructTest.c +243 -0
  135. data/libtest/UnionTest.c +43 -0
  136. data/libtest/VariadicTest.c +99 -0
  137. data/spec/ffi/LICENSE.SPECS +22 -0
  138. data/spec/ffi/async_callback_spec.rb +35 -0
  139. data/spec/ffi/bitmask_spec.rb +575 -0
  140. data/spec/ffi/bool_spec.rb +32 -0
  141. data/spec/ffi/buffer_spec.rb +279 -0
  142. data/spec/ffi/callback_spec.rb +773 -0
  143. data/spec/ffi/custom_param_type.rb +37 -0
  144. data/spec/ffi/custom_type_spec.rb +74 -0
  145. data/spec/ffi/dup_spec.rb +52 -0
  146. data/spec/ffi/enum_spec.rb +423 -0
  147. data/spec/ffi/errno_spec.rb +20 -0
  148. data/spec/ffi/ffi_spec.rb +28 -0
  149. data/spec/ffi/fixtures/Benchmark.c +52 -0
  150. data/spec/ffi/fixtures/BitmaskTest.c +51 -0
  151. data/spec/ffi/fixtures/BoolTest.c +34 -0
  152. data/spec/ffi/fixtures/BufferTest.c +31 -0
  153. data/spec/ffi/fixtures/ClosureTest.c +205 -0
  154. data/spec/ffi/fixtures/EnumTest.c +51 -0
  155. data/spec/ffi/fixtures/FunctionTest.c +142 -0
  156. data/spec/ffi/fixtures/GNUmakefile +149 -0
  157. data/spec/ffi/fixtures/GlobalVariable.c +62 -0
  158. data/spec/ffi/fixtures/LastErrorTest.c +21 -0
  159. data/spec/ffi/fixtures/NumberTest.c +132 -0
  160. data/spec/ffi/fixtures/PipeHelper.h +21 -0
  161. data/spec/ffi/fixtures/PipeHelperPosix.c +41 -0
  162. data/spec/ffi/fixtures/PipeHelperWindows.c +72 -0
  163. data/spec/ffi/fixtures/PointerTest.c +63 -0
  164. data/spec/ffi/fixtures/ReferenceTest.c +23 -0
  165. data/spec/ffi/fixtures/StringTest.c +34 -0
  166. data/spec/ffi/fixtures/StructTest.c +243 -0
  167. data/spec/ffi/fixtures/UnionTest.c +43 -0
  168. data/spec/ffi/fixtures/VariadicTest.c +99 -0
  169. data/spec/ffi/fixtures/classes.rb +438 -0
  170. data/spec/ffi/function_spec.rb +97 -0
  171. data/spec/ffi/io_spec.rb +16 -0
  172. data/spec/ffi/library_spec.rb +286 -0
  173. data/spec/ffi/long_double.rb +30 -0
  174. data/spec/ffi/managed_struct_spec.rb +68 -0
  175. data/spec/ffi/memorypointer_spec.rb +78 -0
  176. data/spec/ffi/number_spec.rb +247 -0
  177. data/spec/ffi/platform_spec.rb +114 -0
  178. data/spec/ffi/pointer_spec.rb +285 -0
  179. data/spec/ffi/rbx/attach_function_spec.rb +34 -0
  180. data/spec/ffi/rbx/memory_pointer_spec.rb +198 -0
  181. data/spec/ffi/rbx/spec_helper.rb +6 -0
  182. data/spec/ffi/rbx/struct_spec.rb +18 -0
  183. data/spec/ffi/spec_helper.rb +93 -0
  184. data/spec/ffi/string_spec.rb +118 -0
  185. data/spec/ffi/strptr_spec.rb +50 -0
  186. data/spec/ffi/struct_by_ref_spec.rb +43 -0
  187. data/spec/ffi/struct_callback_spec.rb +69 -0
  188. data/spec/ffi/struct_initialize_spec.rb +35 -0
  189. data/spec/ffi/struct_packed_spec.rb +50 -0
  190. data/spec/ffi/struct_spec.rb +882 -0
  191. data/spec/ffi/typedef_spec.rb +91 -0
  192. data/spec/ffi/union_spec.rb +67 -0
  193. data/spec/ffi/variadic_spec.rb +132 -0
  194. data/spec/spec.opts +4 -0
  195. metadata +309 -0
@@ -0,0 +1,50 @@
1
+ #
2
+ # This file is part of ruby-ffi.
3
+ # For licensing, see LICENSE.SPECS
4
+ #
5
+
6
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
7
+
8
+ describe "functions returning :strptr" do
9
+
10
+ it "can attach function with :strptr return type" do
11
+ expect do
12
+ Module.new do
13
+ extend FFI::Library
14
+ ffi_lib FFI::Library::LIBC
15
+ if !FFI::Platform.windows?
16
+ attach_function :strdup, [ :string ], :strptr
17
+ else
18
+ attach_function :_strdup, [ :string ], :strptr
19
+ end
20
+ end
21
+ end.not_to raise_error
22
+ end
23
+
24
+ module StrPtr
25
+ extend FFI::Library
26
+ ffi_lib FFI::Library::LIBC
27
+ attach_function :free, [ :pointer ], :void
28
+ if !FFI::Platform.windows?
29
+ attach_function :strdup, [ :string ], :strptr
30
+ else
31
+ attach_function :strdup, :_strdup, [ :string ], :strptr
32
+ end
33
+ end
34
+
35
+ it "should return [ String, Pointer ]" do
36
+ result = StrPtr.strdup("test")
37
+ expect(result[0].is_a?(String)).to be true
38
+ expect(result[1].is_a?(FFI::Pointer)).to be true
39
+ end
40
+
41
+ it "should return the correct value" do
42
+ result = StrPtr.strdup("test")
43
+ expect(result[0]).to eq("test")
44
+ end
45
+
46
+ it "should return non-NULL pointer" do
47
+ result = StrPtr.strdup("test")
48
+ expect(result[1]).not_to be_null
49
+ end
50
+ end
@@ -0,0 +1,43 @@
1
+ #
2
+ # This file is part of ruby-ffi.
3
+ # For licensing, see LICENSE.SPECS
4
+ #
5
+
6
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
7
+
8
+ describe FFI::Struct, ' by_ref' do
9
+ before :all do
10
+ @struct_class = struct_class = Class.new(FFI::Struct) do
11
+ layout :a, :pointer
12
+ end
13
+
14
+ @api = Module.new do
15
+ extend FFI::Library
16
+ ffi_lib TestLibrary::PATH
17
+ fn = FFI::Type::POINTER.size == FFI::Type::LONG.size ? :ret_ulong : :ret_u64
18
+ attach_function :struct_test, fn, [ struct_class.by_ref ], :pointer
19
+ end
20
+ end
21
+
22
+ it "should accept instances of exact struct class" do
23
+ s = @struct_class.new
24
+ expect(@api.struct_test(s)).to eq(s.pointer)
25
+ end
26
+
27
+ it "should accept nil" do
28
+ expect(@api.struct_test(nil)).to be_null
29
+ end
30
+
31
+ it "should reject other types" do
32
+ expect { expect(@api.struct_test('test')).to be_nil }.to raise_error(TypeError)
33
+ end
34
+
35
+ it "should reject instances of other struct classes" do
36
+ other_class = Class.new(FFI::Struct) do
37
+ layout :a, :pointer
38
+ end
39
+
40
+ expect { @api.struct_test(other_class.new) }.to raise_error(TypeError)
41
+ end
42
+ end
43
+
@@ -0,0 +1,69 @@
1
+ #
2
+ # This file is part of ruby-ffi.
3
+ # For licensing, see LICENSE.SPECS
4
+ #
5
+
6
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
7
+
8
+ describe FFI::Struct, ' with inline callback functions' do
9
+ it 'should be able to define inline callback field' do
10
+ expect(module CallbackMember1
11
+ extend FFI::Library
12
+ ffi_lib TestLibrary::PATH
13
+ DUMMY_CB = callback :dummy_cb, [ :int ], :int
14
+ class TestStruct < FFI::Struct
15
+ layout \
16
+ :add, callback([ :int, :int ], :int),
17
+ :sub, callback([ :int, :int ], :int),
18
+ :cb_with_cb_parameter, callback([ DUMMY_CB, :int ], :int)
19
+ end
20
+ attach_function :struct_call_add_cb, [TestStruct, :int, :int], :int
21
+ attach_function :struct_call_sub_cb, [TestStruct, :int, :int], :int
22
+ end).to be_an_instance_of FFI::Function
23
+ end
24
+
25
+ it 'should take methods as callbacks' do
26
+ module CallbackMember2
27
+ extend FFI::Library
28
+ ffi_lib TestLibrary::PATH
29
+ class TestStruct < FFI::Struct
30
+ layout \
31
+ :add, callback([ :int, :int ], :int),
32
+ :sub, callback([ :int, :int ], :int)
33
+ end
34
+ attach_function :struct_call_add_cb, [TestStruct, :int, :int], :int
35
+ attach_function :struct_call_sub_cb, [TestStruct, :int, :int], :int
36
+ end
37
+ module StructCallbacks
38
+ def self.add a, b
39
+ a+b
40
+ end
41
+ end
42
+
43
+ ts = CallbackMember2::TestStruct.new
44
+ ts[:add] = StructCallbacks.method(:add)
45
+
46
+ expect(CallbackMember2.struct_call_add_cb(ts, 1, 2)).to eq(3)
47
+ end
48
+
49
+ it 'should return callable object from []' do
50
+ module CallbackMember3
51
+ extend FFI::Library
52
+ ffi_lib TestLibrary::PATH
53
+ class TestStruct < FFI::Struct
54
+ layout \
55
+ :add, callback([ :int, :int ], :int),
56
+ :sub, callback([ :int, :int ], :int)
57
+ end
58
+ attach_function :struct_call_add_cb, [TestStruct, :int, :int], :int
59
+ attach_function :struct_call_sub_cb, [TestStruct, :int, :int], :int
60
+ end
61
+
62
+ s = CallbackMember3::TestStruct.new
63
+ add = Proc.new { |a,b| a+b}
64
+ s[:add] = add
65
+ fn = s[:add]
66
+ expect(fn.respond_to?(:call)).to be true
67
+ expect(fn.call(1, 2)).to eq(3)
68
+ end
69
+ end
@@ -0,0 +1,35 @@
1
+ #
2
+ # This file is part of ruby-ffi.
3
+ # For licensing, see LICENSE.SPECS
4
+ #
5
+
6
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
7
+
8
+ describe FFI::Struct, ' with an initialize function' do
9
+ it "should call the initialize function" do
10
+ class StructWithInitialize < FFI::Struct
11
+ layout :string, :string
12
+ attr_accessor :magic
13
+ def initialize
14
+ super
15
+ self.magic = 42
16
+ end
17
+ end
18
+ expect(StructWithInitialize.new.magic).to eq(42)
19
+ end
20
+ end
21
+
22
+ describe FFI::ManagedStruct, ' with an initialize function' do
23
+ it "should call the initialize function" do
24
+ class ManagedStructWithInitialize < FFI::ManagedStruct
25
+ layout :string, :string
26
+ attr_accessor :magic
27
+ def initialize
28
+ super FFI::MemoryPointer.new(:pointer).put_int(0, 0x1234).get_pointer(0)
29
+ self.magic = 42
30
+ end
31
+ def self.release;end
32
+ end
33
+ expect(ManagedStructWithInitialize.new.magic).to eq(42)
34
+ end
35
+ end
@@ -0,0 +1,50 @@
1
+ #
2
+ # This file is part of ruby-ffi.
3
+ # For licensing, see LICENSE.SPECS
4
+ #
5
+
6
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
7
+
8
+ describe FFI::Struct do
9
+ it "packed :char followed by :int should have size of 5" do
10
+ expect(Class.new(FFI::Struct) do
11
+ packed
12
+ layout :c, :char, :i, :int
13
+ end.size).to eq(5)
14
+ end
15
+
16
+ it "packed :char followed by :int should have alignment of 1" do
17
+ expect(Class.new(FFI::Struct) do
18
+ packed
19
+ layout :c, :char, :i, :int
20
+ end.alignment).to eq(1)
21
+ end
22
+
23
+ it "packed(2) :char followed by :int should have size of 6" do
24
+ expect(Class.new(FFI::Struct) do
25
+ packed 2
26
+ layout :c, :char, :i, :int
27
+ end.size).to eq(6)
28
+ end
29
+
30
+ it "packed(2) :char followed by :int should have alignment of 2" do
31
+ expect(Class.new(FFI::Struct) do
32
+ packed 2
33
+ layout :c, :char, :i, :int
34
+ end.alignment).to eq(2)
35
+ end
36
+
37
+ it "packed :short followed by int should have size of 6" do
38
+ expect(Class.new(FFI::Struct) do
39
+ packed
40
+ layout :s, :short, :i, :int
41
+ end.size).to eq(6)
42
+ end
43
+
44
+ it "packed :short followed by int should have alignment of 1" do
45
+ expect(Class.new(FFI::Struct) do
46
+ packed
47
+ layout :s, :short, :i, :int
48
+ end.alignment).to eq(1)
49
+ end
50
+ end
@@ -0,0 +1,882 @@
1
+ #
2
+ # This file is part of ruby-ffi.
3
+ # For licensing, see LICENSE.SPECS
4
+ #
5
+
6
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
7
+
8
+ describe "Struct aligns fields correctly" do
9
+ it "char, followed by an int" do
10
+ class CIStruct < FFI::Struct
11
+ layout :c => :char, :i => :int
12
+ end
13
+ expect(CIStruct.size).to eq(8)
14
+ end
15
+
16
+ it "short, followed by an int" do
17
+ class SIStruct < FFI::Struct
18
+ layout :s => :short, :i => :int
19
+ end
20
+ expect(SIStruct.size).to eq(8)
21
+ end
22
+
23
+ it "int, followed by an int" do
24
+ class IIStruct < FFI::Struct
25
+ layout :i1 => :int, :i => :int
26
+ end
27
+ expect(IIStruct.size).to eq(8)
28
+ end
29
+
30
+ it "long long, followed by an int" do
31
+ class LLIStruct < FFI::Struct
32
+ layout :l => :long_long, :i => :int
33
+ end
34
+ expect(LLIStruct.size).to eq(FFI::TYPE_UINT64.alignment == 4 ? 12 : 16)
35
+ end
36
+ end
37
+
38
+ describe "Struct tests" do
39
+ StructTypes = {
40
+ 's8' => :char,
41
+ 's16' => :short,
42
+ 's32' => :int,
43
+ 's64' => :long_long,
44
+ 'long' => :long,
45
+ 'f32' => :float,
46
+ 'f64' => :double
47
+ }
48
+ module LibTest
49
+ extend FFI::Library
50
+ ffi_lib TestLibrary::PATH
51
+ attach_function :ptr_ret_pointer, [ :pointer, :int], :string
52
+ begin
53
+ attach_function :ptr_ret_int32_t, [ :pointer, :int ], :int
54
+ rescue FFI::NotFoundError
55
+ # NetBSD uses #define instead of typedef for these
56
+ attach_function :ptr_ret_int32_t, :ptr_ret___int32_t, [ :pointer, :int ], :int
57
+ end
58
+ attach_function :ptr_from_address, [ :ulong ], :pointer
59
+ attach_function :string_equals, [ :string, :string ], :int
60
+ [ 's8', 's16', 's32', 's64', 'f32', 'f64', 'long' ].each do |t|
61
+ attach_function "struct_align_#{t}", [ :pointer ], StructTypes[t]
62
+ end
63
+ end
64
+ class PointerMember < FFI::Struct
65
+ layout :pointer, :pointer
66
+ end
67
+ class StringMember < FFI::Struct
68
+ layout :string, :string
69
+ end
70
+
71
+ it "Struct#[:pointer]" do
72
+ magic = 0x12345678
73
+ mp = FFI::MemoryPointer.new :long
74
+ mp.put_long(0, magic)
75
+ smp = FFI::MemoryPointer.new :pointer
76
+ smp.put_pointer(0, mp)
77
+ s = PointerMember.new smp
78
+ expect(s[:pointer]).to eq(mp)
79
+ end
80
+
81
+ it "Struct#[:pointer].nil? for NULL value" do
82
+ magic = 0x12345678
83
+ mp = FFI::MemoryPointer.new :long
84
+ mp.put_long(0, magic)
85
+ smp = FFI::MemoryPointer.new :pointer
86
+ smp.put_pointer(0, nil)
87
+ s = PointerMember.new smp
88
+ expect(s[:pointer].null?).to be true
89
+ end
90
+
91
+ it "Struct#[:pointer]=" do
92
+ magic = 0x12345678
93
+ mp = FFI::MemoryPointer.new :long
94
+ mp.put_long(0, magic)
95
+ smp = FFI::MemoryPointer.new :pointer
96
+ s = PointerMember.new smp
97
+ s[:pointer] = mp
98
+ expect(smp.get_pointer(0)).to eq(mp)
99
+ end
100
+
101
+ it "Struct#[:pointer]=struct" do
102
+ smp = FFI::MemoryPointer.new :pointer
103
+ s = PointerMember.new smp
104
+ expect { s[:pointer] = s }.not_to raise_error Exception
105
+ expect { s[:pointer].nil? }.not_to raise_error Exception
106
+ end
107
+
108
+ it "Struct#[:pointer]=nil" do
109
+ smp = FFI::MemoryPointer.new :pointer
110
+ s = PointerMember.new smp
111
+ s[:pointer] = nil
112
+ expect(smp.get_pointer(0)).to be_null
113
+ end
114
+
115
+ it "Struct#[:string]" do
116
+ magic = "test"
117
+ mp = FFI::MemoryPointer.new 1024
118
+ mp.put_string(0, magic)
119
+ smp = FFI::MemoryPointer.new :pointer
120
+ smp.put_pointer(0, mp)
121
+ s = StringMember.new smp
122
+ expect(s[:string]).to eq(magic)
123
+ end
124
+
125
+ it "Struct#[:string].nil? for NULL value" do
126
+ smp = FFI::MemoryPointer.new :pointer
127
+ smp.put_pointer(0, nil)
128
+ s = StringMember.new smp
129
+ expect(s[:string]).to be_nil
130
+ end
131
+
132
+ it "Struct#layout works with :name, :type pairs" do
133
+ class PairLayout < FFI::Struct
134
+ layout :a, :int, :b, :long_long
135
+ end
136
+ ll_off = (FFI::TYPE_UINT64.alignment == 4 ? 4 : 8)
137
+ expect(PairLayout.size).to eq((ll_off + 8))
138
+ mp = FFI::MemoryPointer.new(PairLayout.size)
139
+ s = PairLayout.new mp
140
+ s[:a] = 0x12345678
141
+ expect(mp.get_int(0)).to eq(0x12345678)
142
+ s[:b] = 0xfee1deadbeef
143
+ expect(mp.get_int64(ll_off)).to eq(0xfee1deadbeef)
144
+ end
145
+
146
+ it "Struct#layout works with :name, :type, offset tuples" do
147
+ class PairLayout < FFI::Struct
148
+ layout :a, :int, 0, :b, :long_long, 4
149
+ end
150
+ expect(PairLayout.size).to eq((FFI::TYPE_UINT64.alignment == 4 ? 12 : 16))
151
+ mp = FFI::MemoryPointer.new(PairLayout.size)
152
+ s = PairLayout.new mp
153
+ s[:a] = 0x12345678
154
+ expect(mp.get_int(0)).to eq(0x12345678)
155
+ s[:b] = 0xfee1deadbeef
156
+ expect(mp.get_int64(4)).to eq(0xfee1deadbeef)
157
+ end
158
+
159
+ it "Struct#layout works with mixed :name,:type and :name,:type,offset" do
160
+ class MixedLayout < FFI::Struct
161
+ layout :a, :int, :b, :long_long, 4
162
+ end
163
+ expect(MixedLayout.size).to eq((FFI::TYPE_UINT64.alignment == 4 ? 12 : 16))
164
+ mp = FFI::MemoryPointer.new(MixedLayout.size)
165
+ s = MixedLayout.new mp
166
+ s[:a] = 0x12345678
167
+ expect(mp.get_int(0)).to eq(0x12345678)
168
+ s[:b] = 0xfee1deadbeef
169
+ expect(mp.get_int64(4)).to eq(0xfee1deadbeef)
170
+ end
171
+
172
+ rb_maj, rb_min = RUBY_VERSION.split('.')
173
+ if rb_maj.to_i >= 1 && rb_min.to_i >= 9 || RUBY_PLATFORM =~ /java/
174
+ it "Struct#layout withs with a hash of :name => type" do
175
+ class HashLayout < FFI::Struct
176
+ layout :a => :int, :b => :long_long
177
+ end
178
+ ll_off = (FFI::TYPE_UINT64.alignment == 4 ? 4 : 8)
179
+ expect(HashLayout.size).to eq(ll_off + 8)
180
+ mp = FFI::MemoryPointer.new(HashLayout.size)
181
+ s = HashLayout.new mp
182
+ s[:a] = 0x12345678
183
+ expect(mp.get_int(0)).to eq(0x12345678)
184
+ s[:b] = 0xfee1deadbeef
185
+ expect(mp.get_int64(ll_off)).to eq(0xfee1deadbeef)
186
+ end
187
+ end
188
+
189
+ it "subclass overrides initialize without calling super" do
190
+ class InitializeWithoutSuper < FFI::Struct
191
+ layout :a, :int, :b, :long_long, :d, [:double, 2]
192
+
193
+ def initialize(a, b)
194
+ self[:a] = a
195
+ self[:b] = b
196
+ self[:d][0] = 1.2
197
+ self[:d][1] = 3.4
198
+ end
199
+
200
+ end
201
+ s = InitializeWithoutSuper.new(0x1eefbeef, 0xdeadcafebabe)
202
+ expect(s[:a]).to eq(0x1eefbeef)
203
+ expect(s[:b]).to eq(0xdeadcafebabe)
204
+ end
205
+
206
+ it "Can use Struct subclass as parameter type" do
207
+ expect(module StructParam
208
+ extend FFI::Library
209
+ ffi_lib TestLibrary::PATH
210
+ class TestStruct < FFI::Struct
211
+ layout :c, :char
212
+ end
213
+ attach_function :struct_field_s8, [ TestStruct.in ], :char
214
+ end).to be_an_instance_of FFI::Function
215
+ end
216
+
217
+ it "Can use Struct subclass as IN parameter type" do
218
+ expect(module StructParam2
219
+ extend FFI::Library
220
+ ffi_lib TestLibrary::PATH
221
+ class TestStruct < FFI::Struct
222
+ layout :c, :char
223
+ end
224
+ attach_function :struct_field_s8, [ TestStruct.in ], :char
225
+ end).to be_an_instance_of FFI::Function
226
+ end
227
+
228
+ it "Can use Struct subclass as OUT parameter type" do
229
+ expect(module StructParam3
230
+ extend FFI::Library
231
+ ffi_lib TestLibrary::PATH
232
+ class TestStruct < FFI::Struct
233
+ layout :c, :char
234
+ end
235
+ attach_function :struct_field_s8, [ TestStruct.out ], :char
236
+ end).to be_an_instance_of FFI::Function
237
+ end
238
+
239
+ it "can be passed directly as a :pointer parameter" do
240
+ class TestStruct < FFI::Struct
241
+ layout :i, :int
242
+ end
243
+ s = TestStruct.new
244
+ s[:i] = 0x12
245
+ expect(LibTest.ptr_ret_int32_t(s, 0)).to eq(0x12)
246
+ end
247
+
248
+ it ":char member aligned correctly" do
249
+ class AlignChar < FFI::Struct
250
+ layout :c, :char, :v, :char
251
+ end
252
+ s = AlignChar.new
253
+ s[:v] = 0x12
254
+ expect(LibTest.struct_align_s8(s.pointer)).to eq(0x12)
255
+ end
256
+
257
+ it ":short member aligned correctly" do
258
+ class AlignShort < FFI::Struct
259
+ layout :c, :char, :v, :short
260
+ end
261
+ s = AlignShort.alloc_in
262
+ s[:v] = 0x1234
263
+ expect(LibTest.struct_align_s16(s.pointer)).to eq(0x1234)
264
+ end
265
+
266
+ it ":int member aligned correctly" do
267
+ class AlignInt < FFI::Struct
268
+ layout :c, :char, :v, :int
269
+ end
270
+ s = AlignInt.alloc_in
271
+ s[:v] = 0x12345678
272
+ expect(LibTest.struct_align_s32(s.pointer)).to eq(0x12345678)
273
+ end
274
+
275
+ it ":long_long member aligned correctly" do
276
+ class AlignLongLong < FFI::Struct
277
+ layout :c, :char, :v, :long_long
278
+ end
279
+ s = AlignLongLong.alloc_in
280
+ s[:v] = 0x123456789abcdef0
281
+ expect(LibTest.struct_align_s64(s.pointer)).to eq(0x123456789abcdef0)
282
+ end
283
+
284
+ it ":long member aligned correctly" do
285
+ class AlignLong < FFI::Struct
286
+ layout :c, :char, :v, :long
287
+ end
288
+ s = AlignLong.alloc_in
289
+ s[:v] = 0x12345678
290
+ expect(LibTest.struct_align_long(s.pointer)).to eq(0x12345678)
291
+ end
292
+
293
+ it ":float member aligned correctly" do
294
+ class AlignFloat < FFI::Struct
295
+ layout :c, :char, :v, :float
296
+ end
297
+ s = AlignFloat.alloc_in
298
+ s[:v] = 1.23456
299
+ expect((LibTest.struct_align_f32(s.pointer) - 1.23456).abs).to be < 0.00001
300
+ end
301
+
302
+ it ":double member aligned correctly" do
303
+ class AlignDouble < FFI::Struct
304
+ layout :c, :char, :v, :double
305
+ end
306
+ s = AlignDouble.alloc_in
307
+ s[:v] = 1.23456789
308
+ expect((LibTest.struct_align_f64(s.pointer) - 1.23456789).abs).to be < 0.00000001
309
+ end
310
+
311
+ it ":ulong, :pointer struct" do
312
+ class ULPStruct < FFI::Struct
313
+ layout :ul, :ulong, :p, :pointer
314
+ end
315
+ s = ULPStruct.alloc_in
316
+ s[:ul] = 0xdeadbeef
317
+ s[:p] = LibTest.ptr_from_address(0x12345678)
318
+ expect(s.pointer.get_ulong(0)).to eq(0xdeadbeef)
319
+ end
320
+ def test_num_field(type, v)
321
+ klass = Class.new(FFI::Struct)
322
+ klass.layout :v, type, :dummy, :long
323
+
324
+ s = klass.new
325
+ s[:v] = v
326
+ expect(s.pointer.send("get_#{type.to_s}", 0)).to eq(v)
327
+ s.pointer.send("put_#{type.to_s}", 0, 0)
328
+ expect(s[:v]).to eq(0)
329
+ end
330
+ def self.int_field_test(type, values)
331
+ values.each do |v|
332
+ it "#{type} field r/w (#{v.to_s(16)})" do
333
+ test_num_field(type, v)
334
+ end
335
+ end
336
+ end
337
+ int_field_test(:char, [ 0, 127, -128, -1 ])
338
+ int_field_test(:uchar, [ 0, 0x7f, 0x80, 0xff ])
339
+ int_field_test(:short, [ 0, 0x7fff, -0x8000, -1 ])
340
+ int_field_test(:ushort, [ 0, 0x7fff, 0x8000, 0xffff ])
341
+ int_field_test(:int, [ 0, 0x7fffffff, -0x80000000, -1 ])
342
+ int_field_test(:uint, [ 0, 0x7fffffff, 0x80000000, 0xffffffff ])
343
+ int_field_test(:long_long, [ 0, 0x7fffffffffffffff, -0x8000000000000000, -1 ])
344
+ int_field_test(:ulong_long, [ 0, 0x7fffffffffffffff, 0x8000000000000000, 0xffffffffffffffff ])
345
+ if FFI::Platform::LONG_SIZE == 32
346
+ int_field_test(:long, [ 0, 0x7fffffff, -0x80000000, -1 ])
347
+ int_field_test(:ulong, [ 0, 0x7fffffff, 0x80000000, 0xffffffff ])
348
+ else
349
+ int_field_test(:long, [ 0, 0x7fffffffffffffff, -0x8000000000000000, -1 ])
350
+ int_field_test(:ulong, [ 0, 0x7fffffffffffffff, 0x8000000000000000, 0xffffffffffffffff ])
351
+ end
352
+
353
+ it ":float field r/w" do
354
+ klass = Class.new(FFI::Struct)
355
+ klass.layout :v, :float, :dummy, :long
356
+
357
+ s = klass.new
358
+ value = 1.23456
359
+ s[:v] = value
360
+ expect((s.pointer.get_float(0) - value).abs).to be < 0.0001
361
+ end
362
+
363
+ it ":double field r/w" do
364
+ klass = Class.new(FFI::Struct)
365
+ klass.layout :v, :double, :dummy, :long
366
+
367
+ s = klass.new
368
+ value = 1.23456
369
+ s[:v] = value
370
+ expect((s.pointer.get_double(0) - value).abs).to be < 0.0001
371
+ end
372
+ module EnumFields
373
+ extend FFI::Library
374
+ TestEnum = enum :test_enum, [:c1, 10, :c2, 20, :c3, 30, :c4, 40]
375
+ class TestStruct < FFI::Struct
376
+ layout :a, :int, :c, :test_enum,
377
+ :d, [ TestEnum, TestEnum.symbols.length ]
378
+ end
379
+ end
380
+
381
+ it ":enum field r/w" do
382
+ s = EnumFields::TestStruct.new
383
+ s[:c] = :c3
384
+
385
+ expect(s.pointer.get_uint(FFI::Type::INT32.size)).to eq(30)
386
+ expect(s[:c]).to eq(:c3)
387
+ end
388
+
389
+ it "array of :enum field" do
390
+ s = EnumFields::TestStruct.new
391
+ EnumFields::TestEnum.symbols.each_with_index do |val, i|
392
+ s[:d][i] = val
393
+ end
394
+
395
+ EnumFields::TestEnum.symbols.each_with_index do |val, i|
396
+ expect(s.pointer.get_uint(FFI::Type::INT32.size * (2 + i))).to eq(EnumFields::TestEnum[val])
397
+ end
398
+
399
+ s[:d].each_with_index do |val, i|
400
+ expect(val).to eq(EnumFields::TestEnum.symbols[i])
401
+ end
402
+ end
403
+
404
+ module CallbackMember
405
+ extend FFI::Library
406
+ ffi_lib TestLibrary::PATH
407
+ callback :add, [ :int, :int ], :int
408
+ callback :sub, [ :int, :int ], :int
409
+ class TestStruct < FFI::Struct
410
+ layout :add, :add,
411
+ :sub, :sub
412
+ end
413
+ attach_function :struct_call_add_cb, [TestStruct.in, :int, :int], :int
414
+ attach_function :struct_call_sub_cb, [TestStruct.in, :int, :int], :int
415
+ end
416
+
417
+ it "Can have CallbackInfo struct field" do
418
+ s = CallbackMember::TestStruct.new
419
+ add_proc = lambda { |a, b| a+b }
420
+ sub_proc = lambda { |a, b| a-b }
421
+ s[:add] = add_proc
422
+ s[:sub] = sub_proc
423
+ expect(CallbackMember.struct_call_add_cb(s, 40, 2)).to eq(42)
424
+ expect(CallbackMember.struct_call_sub_cb(s, 44, 2)).to eq(42)
425
+ end
426
+
427
+ it "Can return its members as a list" do
428
+ class TestStruct < FFI::Struct
429
+ layout :a, :int, :b, :int, :c, :int
430
+ end
431
+ expect(TestStruct.members).to include(:a, :b, :c)
432
+ end
433
+
434
+ it "Can return its instance members and values as lists" do
435
+ class TestStruct < FFI::Struct
436
+ layout :a, :int, :b, :int, :c, :int
437
+ end
438
+ s = TestStruct.new
439
+ expect(s.members).to include(:a, :b, :c)
440
+ s[:a] = 1
441
+ s[:b] = 2
442
+ s[:c] = 3
443
+ expect(s.values).to include(1, 2, 3)
444
+ end
445
+
446
+ it 'should return an ordered field/offset pairs array' do
447
+ class TestStruct < FFI::Struct
448
+ layout :a, :int, :b, :int, :c, :int
449
+ end
450
+ s = TestStruct.new
451
+ expect(s.offsets).to eq([[:a, 0], [:b, 4], [:c, 8]])
452
+ expect(TestStruct.offsets).to eq([[:a, 0], [:b, 4], [:c, 8]])
453
+ end
454
+
455
+ it "Struct#offset_of returns offset of field within struct" do
456
+ class TestStruct < FFI::Struct
457
+ layout :a, :int, :b, :int, :c, :int
458
+ end
459
+ expect(TestStruct.offset_of(:a)).to eq(0)
460
+ expect(TestStruct.offset_of(:b)).to eq(4)
461
+ expect(TestStruct.offset_of(:c)).to eq(8)
462
+ end
463
+ end
464
+
465
+ describe FFI::Struct, ".layout" do
466
+ module FFISpecs
467
+ module LibTest
468
+ extend FFI::Library
469
+ ffi_lib TestLibrary::PATH
470
+ begin
471
+ attach_function :ptr_ret_int32_t, [ :pointer, :int ], :int
472
+ rescue FFI::NotFoundError
473
+ # NetBSD uses #define instead of typedef for these
474
+ attach_function :ptr_ret_int32_t, :ptr_ret___int32_t, [ :pointer, :int ], :int
475
+ end
476
+ end
477
+ end
478
+
479
+ describe "when derived class is not assigned to any constant" do
480
+ it "resolves a built-in type" do
481
+ klass = Class.new FFI::Struct
482
+ klass.layout :number, :int
483
+
484
+ instance = klass.new
485
+ instance[:number] = 0xA1
486
+ expect(FFISpecs::LibTest.ptr_ret_int32_t(instance, 0)).to eq(0xA1)
487
+ end
488
+ end
489
+
490
+ describe "when derived class is assigned to a constant" do
491
+ it "resolves a built-in type" do
492
+ class FFISpecs::TestStruct < FFI::Struct
493
+ layout :number, :int
494
+ end
495
+
496
+ instance = FFISpecs::TestStruct.new
497
+ instance[:number] = 0xA1
498
+ expect(FFISpecs::LibTest.ptr_ret_int32_t(instance, 0)).to eq(0xA1)
499
+ end
500
+
501
+ it "resolves a type from the enclosing module" do
502
+ module FFISpecs::LibTest
503
+ typedef :uint, :custom_int
504
+
505
+ class TestStruct < FFI::Struct
506
+ layout :number, :custom_int
507
+ end
508
+ end
509
+
510
+ instance = FFISpecs::LibTest::TestStruct.new
511
+ instance[:number] = 0xA1
512
+ expect(FFISpecs::LibTest.ptr_ret_int32_t(instance, 0)).to eq(0xA1)
513
+ end
514
+ end
515
+ end
516
+
517
+ describe FFI::Struct, ' with a nested struct field' do
518
+ module LibTest
519
+ extend FFI::Library
520
+ ffi_lib TestLibrary::PATH
521
+ class NestedStruct < FFI::Struct
522
+ layout :i, :int
523
+ end
524
+ class ContainerStruct < FFI::Struct
525
+ layout :first, :char, :ns, NestedStruct
526
+ end
527
+ attach_function :struct_align_nested_struct, [ :pointer ], :int
528
+ attach_function :struct_make_container_struct, [ :int ], :pointer
529
+ end
530
+ before do
531
+ @cs = LibTest::ContainerStruct.new
532
+ end
533
+
534
+ it 'should align correctly nested struct field' do
535
+ @cs[:ns][:i] = 123
536
+ expect(LibTest.struct_align_nested_struct(@cs.to_ptr)).to eq(123)
537
+ end
538
+
539
+ it 'should correctly calculate Container size (in bytes)' do
540
+ expect(LibTest::ContainerStruct.size).to eq(8)
541
+ end
542
+
543
+ it 'should return a Struct object when the field is accessed' do
544
+ expect(@cs[:ns].is_a?(FFI::Struct)).to be true
545
+ end
546
+
547
+ it 'should read a value from memory' do
548
+ @cs = LibTest::ContainerStruct.new(LibTest.struct_make_container_struct(123))
549
+ expect(@cs[:ns][:i]).to eq(123)
550
+ end
551
+
552
+ it 'should write a value to memory' do
553
+ @cs = LibTest::ContainerStruct.new(LibTest.struct_make_container_struct(123))
554
+ @cs[:ns][:i] = 456
555
+ expect(LibTest.struct_align_nested_struct(@cs.to_ptr)).to eq(456)
556
+ end
557
+
558
+ it 'should be able to assign struct instance to nested field' do
559
+ cs = LibTest::ContainerStruct.new(LibTest.struct_make_container_struct(123))
560
+ ns = LibTest::NestedStruct.new
561
+ ns[:i] = 567
562
+ cs[:ns] = ns
563
+ expect(cs[:ns][:i]).to eq(567)
564
+ expect(LibTest.struct_align_nested_struct(cs.to_ptr)).to eq(567)
565
+ end
566
+ end
567
+
568
+ describe FFI::Struct, ' with a nested array of structs' do
569
+ module InlineArrayOfStructs
570
+ extend FFI::Library
571
+ ffi_lib TestLibrary::PATH
572
+ class NestedStruct < FFI::Struct
573
+ layout :i, :int
574
+ end
575
+ class ContainerStruct < FFI::Struct
576
+ layout :first, :char, :ns, [ NestedStruct, 1 ]
577
+ end
578
+ attach_function :struct_align_nested_struct, [ :pointer ], :int
579
+ attach_function :struct_make_container_struct, [ :int ], :pointer
580
+ end
581
+
582
+ before do
583
+ @cs = InlineArrayOfStructs::ContainerStruct.new
584
+ end
585
+
586
+ it 'should align correctly nested struct field' do
587
+ @cs[:ns][0][:i] = 123
588
+ expect(InlineArrayOfStructs.struct_align_nested_struct(@cs.to_ptr)).to eq(123)
589
+ end
590
+
591
+ it 'should correctly calculate Container size (in bytes)' do
592
+ expect(InlineArrayOfStructs::ContainerStruct.size).to eq(8)
593
+ end
594
+
595
+ it 'should return a Struct object when the field is accessed' do
596
+ expect(@cs[:ns][0].is_a?(FFI::Struct)).to be true
597
+ end
598
+
599
+ it 'should read a value from memory' do
600
+ @cs = InlineArrayOfStructs::ContainerStruct.new(InlineArrayOfStructs.struct_make_container_struct(123))
601
+ expect(@cs[:ns][0][:i]).to eq(123)
602
+ end
603
+
604
+ it 'should write a value to memory' do
605
+ @cs = InlineArrayOfStructs::ContainerStruct.new(InlineArrayOfStructs.struct_make_container_struct(123))
606
+ @cs[:ns][0][:i] = 456
607
+ expect(InlineArrayOfStructs.struct_align_nested_struct(@cs.to_ptr)).to eq(456)
608
+ end
609
+
610
+ it 'should support Enumerable#each' do
611
+ @cs = InlineArrayOfStructs::ContainerStruct.new(InlineArrayOfStructs.struct_make_container_struct(123))
612
+ ints = []
613
+ @cs[:ns].each { |s| ints << s[:i] }
614
+ expect(ints[0]).to eq(123)
615
+ end
616
+ end
617
+
618
+ describe FFI::Struct, ' by value' do
619
+ module LibTest
620
+ extend FFI::Library
621
+ ffi_lib TestLibrary::PATH
622
+
623
+ class S8S32 < FFI::Struct
624
+ layout :s8, :char, :s32, :int
625
+ end
626
+
627
+ class StructString < FFI::Struct
628
+ layout :bytes, :string, :len, :int
629
+ end
630
+
631
+ attach_function :struct_return_s8s32, [ ], S8S32.by_value
632
+ attach_function :struct_s8s32_set, [ :char, :int ], S8S32.by_value
633
+ attach_function :struct_s8s32_get_s8, [ S8S32.by_value ], :char
634
+ attach_function :struct_s8s32_get_s32, [ S8S32.by_value ], :int
635
+ attach_function :struct_s8s32_s32_ret_s32, [ S8S32.by_value, :int ], :int
636
+ attach_function :struct_s8s32_s64_ret_s64, [ S8S32.by_value, :long_long ], :long_long
637
+ attach_function :struct_s8s32_ret_s8s32, [ S8S32.by_value ], S8S32.by_value
638
+ attach_function :struct_s32_ptr_s32_s8s32_ret_s32, [ :int, :pointer, :int, S8S32.by_value ], :int
639
+ attach_function :struct_varargs_ret_struct_string, [ :int, :varargs ], StructString.by_value
640
+ end
641
+
642
+ it 'return using pre-set values' do
643
+ s = LibTest.struct_return_s8s32
644
+ expect(s[:s8]).to eq(0x7f)
645
+ expect(s[:s32]).to eq(0x12345678)
646
+ end
647
+
648
+ it 'return using passed in values' do
649
+ s = LibTest.struct_s8s32_set(123, 456789)
650
+ expect(s[:s8]).to eq(123)
651
+ expect(s[:s32]).to eq(456789)
652
+ end
653
+
654
+ it 'parameter' do
655
+ s = LibTest::S8S32.new
656
+ s[:s8] = 0x12
657
+ s[:s32] = 0x34567890
658
+ expect(LibTest.struct_s8s32_get_s8(s)).to eq(0x12)
659
+ expect(LibTest.struct_s8s32_get_s32(s)).to eq(0x34567890)
660
+ end
661
+
662
+ it 'parameter with following s32' do
663
+ s = LibTest::S8S32.new
664
+ s[:s8] = 0x12
665
+ s[:s32] = 0x34567890
666
+
667
+ expect(LibTest.struct_s8s32_s32_ret_s32(s, 0x1eefdead)).to eq(0x1eefdead)
668
+ end
669
+
670
+ # it 'parameter with following s64' do
671
+ # s = LibTest::S8S64.new
672
+ # s[:s8] = 0x12
673
+ # s[:s64] = 0x34567890
674
+ #
675
+ #
676
+ # LibTest.struct_s8s64_s64_ret_s64(s, 0x1eefdead1eefdead).should == 0x1eefdead1eefdead
677
+ # end
678
+
679
+ it 'parameter with preceding s32,ptr,s32' do
680
+ s = LibTest::S8S32.new
681
+ s[:s8] = 0x12
682
+ s[:s32] = 0x34567890
683
+ out = LibTest::S8S32.new
684
+ expect(LibTest.struct_s32_ptr_s32_s8s32_ret_s32(0x1000000, out, 0x1eafbeef, s)).to eq(0x34567890)
685
+ expect(out[:s8]).to eq(s[:s8])
686
+ expect(out[:s32]).to eq(s[:s32])
687
+ end
688
+
689
+ it 'parameter with preceding s32,string,s32' do
690
+ s = LibTest::S8S32.new
691
+ s[:s8] = 0x12
692
+ s[:s32] = 0x34567890
693
+ out = 0.chr * 32
694
+ expect(LibTest.struct_s32_ptr_s32_s8s32_ret_s32(0x1000000, out, 0x1eafbeef, s)).to eq(0x34567890)
695
+ end
696
+
697
+ it 'parameter, returning struct by value' do
698
+ s = LibTest::S8S32.new
699
+ s[:s8] = 0x12
700
+ s[:s32] = 0x34567890
701
+
702
+ ret = LibTest.struct_s8s32_ret_s8s32(s)
703
+ expect(ret[:s8]).to eq(s[:s8])
704
+ expect(ret[:s32]).to eq(s[:s32])
705
+ end
706
+
707
+ it 'varargs returning a struct' do
708
+ string = "test"
709
+ s = LibTest.struct_varargs_ret_struct_string(4, :string, string)
710
+ expect(s[:len]).to eq(string.length)
711
+ expect(s[:bytes]).to eq(string)
712
+ end
713
+ end
714
+
715
+ describe FFI::Struct, ' with an array field' do
716
+ module LibTest
717
+ extend FFI::Library
718
+ ffi_lib TestLibrary::PATH
719
+ class StructWithArray < FFI::Struct
720
+ layout :first, :char, :a, [:int, 5]
721
+ end
722
+ attach_function :struct_make_struct_with_array, [:int, :int, :int, :int, :int], :pointer
723
+ attach_function :struct_field_array, [:pointer], :pointer
724
+ end
725
+ before do
726
+ @s = LibTest::StructWithArray.new
727
+ end
728
+
729
+ it 'should correctly calculate StructWithArray size (in bytes)' do
730
+ expect(LibTest::StructWithArray.size).to eq(24)
731
+ end
732
+
733
+ it 'should read values from memory' do
734
+ @s = LibTest::StructWithArray.new(LibTest.struct_make_struct_with_array(0, 1, 2, 3, 4))
735
+ expect(@s[:a].to_a).to eq([0, 1, 2, 3, 4])
736
+ end
737
+ # it 'should cache array object for successive calls' do
738
+ # @s[:a].object_id.should == @s[:a].object_id
739
+ # end
740
+
741
+ it 'should return the number of elements in the array field' do
742
+ @s = LibTest::StructWithArray.new(LibTest.struct_make_struct_with_array(0, 1, 2, 3, 4))
743
+ expect(@s[:a].size).to eq(5)
744
+ end
745
+
746
+ it 'should allow iteration through the array elements' do
747
+ @s = LibTest::StructWithArray.new(LibTest.struct_make_struct_with_array(0, 1, 2, 3, 4))
748
+ @s[:a].each_with_index { |elem, i| expect(elem).to eq(i) }
749
+ end
750
+
751
+ it 'should return the pointer to the array' do
752
+ @s = LibTest::StructWithArray.new(LibTest.struct_make_struct_with_array(0, 1, 2, 3, 4))
753
+ expect(@s[:a].to_ptr).to eq(LibTest::struct_field_array(@s.to_ptr))
754
+ end
755
+ end
756
+
757
+ describe 'BuggedStruct' do
758
+ module LibTest
759
+ extend FFI::Library
760
+ ffi_lib TestLibrary::PATH
761
+ class BuggedStruct < FFI::Struct
762
+ layout :visible, :uchar,
763
+ :x, :uint,
764
+ :y, :uint,
765
+ :rx, :short,
766
+ :ry, :short,
767
+ :order, :uchar,
768
+ :size, :uchar
769
+ end
770
+ attach_function :bugged_struct_size, [], :uint
771
+ end
772
+
773
+ it 'should return its correct size' do
774
+ expect(LibTest::BuggedStruct.size).to eq(LibTest.bugged_struct_size)
775
+ end
776
+
777
+ it "offsets within struct should be correct" do
778
+ expect(LibTest::BuggedStruct.offset_of(:visible)).to eq(0)
779
+ expect(LibTest::BuggedStruct.offset_of(:x)).to eq(4)
780
+ expect(LibTest::BuggedStruct.offset_of(:y)).to eq(8)
781
+ expect(LibTest::BuggedStruct.offset_of(:rx)).to eq(12)
782
+ expect(LibTest::BuggedStruct.offset_of(:ry)).to eq(14)
783
+ expect(LibTest::BuggedStruct.offset_of(:order)).to eq(16)
784
+ expect(LibTest::BuggedStruct.offset_of(:size)).to eq(17)
785
+ end
786
+
787
+ it 'should return correct field/offset pairs' do
788
+ expect(LibTest::BuggedStruct.offsets.sort do |a, b|
789
+ a[1] <=> b[1]
790
+ end).to eq([[:visible, 0], [:x, 4], [:y, 8], [:rx, 12], [:ry, 14], [:order, 16], [:size, 17]])
791
+ end
792
+ end
793
+
794
+ describe "Struct allocation" do
795
+ it "MemoryPointer.new(Struct, 2)" do
796
+ class S < FFI::Struct
797
+ layout :i, :uint
798
+ end
799
+ p = FFI::MemoryPointer.new(S, 2)
800
+ expect(p.total).to eq(8)
801
+ expect(p.type_size).to eq(4)
802
+ p.put_uint(4, 0xdeadbeef)
803
+ expect(S.new(p[1])[:i]).to eq(0xdeadbeef)
804
+ expect(p[1].address).to eq((p[0].address + 4))
805
+ end
806
+
807
+ it "Buffer.new(Struct, 2)" do
808
+ class S < FFI::Struct
809
+ layout :i, :uint
810
+ end
811
+ p = FFI::Buffer.new(S, 2)
812
+ expect(p.total).to eq(8)
813
+ expect(p.type_size).to eq(4)
814
+ p.put_uint(4, 0xdeadbeef)
815
+ expect(S.new(p[1])[:i]).to eq(0xdeadbeef)
816
+ end
817
+
818
+ it "null? should be true when initialized with NULL pointer" do
819
+ class S < FFI::Struct
820
+ layout :i, :uint
821
+ end
822
+ expect(S.new(FFI::Pointer::NULL)).to be_null
823
+ end
824
+
825
+ it "null? should be false when initialized with non-NULL pointer" do
826
+ class S < FFI::Struct
827
+ layout :i, :uint
828
+ end
829
+ expect(S.new(FFI::MemoryPointer.new(S))).not_to be_null
830
+ end
831
+
832
+ it "supports :bool as a struct member" do
833
+ expect do
834
+ c = Class.new(FFI::Struct) do
835
+ layout :b, :bool
836
+ end
837
+ struct = c.new
838
+ struct[:b] = ! struct[:b]
839
+ end.not_to raise_error Exception
840
+ end
841
+
842
+ end
843
+
844
+ describe "variable-length arrays" do
845
+ it "zero length array should be accepted as last field" do
846
+ expect {
847
+ Class.new(FFI::Struct) do
848
+ layout :count, :int, :data, [ :char, 0 ]
849
+ end
850
+ }.not_to raise_error Exception
851
+ end
852
+
853
+ it "zero length array before last element should raise error" do
854
+ expect {
855
+ Class.new(FFI::Struct) do
856
+ layout :data, [ :char, 0 ], :count, :int
857
+ end
858
+ }.to raise_error
859
+ end
860
+
861
+ it "can access elements of array" do
862
+ struct_class = Class.new(FFI::Struct) do
863
+ layout :count, :int, :data, [ :long, 0 ]
864
+ end
865
+ s = struct_class.new(FFI::MemoryPointer.new(1024))
866
+ s[:data][0] = 0x1eadbeef
867
+ s[:data][1] = 0x12345678
868
+ expect(s[:data][0]).to eq(0x1eadbeef)
869
+ expect(s[:data][1]).to eq(0x12345678)
870
+ end
871
+
872
+ it "non-variable length array is bounds checked" do
873
+ struct_class = Class.new(FFI::Struct) do
874
+ layout :count, :int, :data, [ :long, 1 ]
875
+ end
876
+ s = struct_class.new(FFI::MemoryPointer.new(1024))
877
+ s[:data][0] = 0x1eadbeef
878
+ expect { s[:data][1] = 0x12345678 }.to raise_error
879
+ expect(s[:data][0]).to eq(0x1eadbeef)
880
+ expect { expect(s[:data][1]).to == 0x12345678 }.to raise_error
881
+ end
882
+ end