ffi 0.3.1 → 0.4.0
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.
- data/README.rdoc +51 -1
- data/Rakefile +38 -28
- data/ext/ffi_c/AbstractMemory.c +74 -70
- data/ext/ffi_c/AbstractMemory.h +8 -4
- data/ext/ffi_c/AutoPointer.c +8 -9
- data/ext/ffi_c/AutoPointer.h +2 -2
- data/ext/ffi_c/Buffer.c +42 -24
- data/ext/ffi_c/Callback.c +85 -33
- data/ext/ffi_c/Callback.h +11 -5
- data/ext/ffi_c/{NativeLibrary.c → DynamicLibrary.c} +83 -16
- data/ext/ffi_c/{NativeLibrary.h → DynamicLibrary.h} +1 -1
- data/ext/ffi_c/Invoker.c +152 -192
- data/ext/ffi_c/LastError.c +135 -0
- data/ext/ffi_c/LastError.h +18 -0
- data/ext/ffi_c/MemoryPointer.c +27 -20
- data/ext/ffi_c/MemoryPointer.h +3 -3
- data/ext/ffi_c/NullPointer.c +51 -47
- data/ext/ffi_c/Platform.c +9 -10
- data/ext/ffi_c/Platform.h +1 -1
- data/ext/ffi_c/Pointer.c +52 -21
- data/ext/ffi_c/Pointer.h +8 -6
- data/ext/ffi_c/Struct.c +70 -61
- data/ext/ffi_c/Struct.h +2 -2
- data/ext/ffi_c/Type.c +230 -0
- data/ext/ffi_c/Type.h +28 -0
- data/ext/ffi_c/Types.c +48 -6
- data/ext/ffi_c/Types.h +8 -2
- data/ext/ffi_c/endian.h +40 -0
- data/ext/ffi_c/extconf.rb +6 -5
- data/ext/ffi_c/ffi.c +21 -44
- data/ext/ffi_c/libffi.bsd.mk +34 -0
- data/ext/ffi_c/libffi.darwin.mk +30 -10
- data/ext/ffi_c/libffi.gnu.mk +29 -0
- data/ext/ffi_c/libffi.mk +4 -2
- data/ext/ffi_c/rbffi.h +6 -8
- data/gen/Rakefile +12 -0
- data/lib/ffi/autopointer.rb +1 -1
- data/lib/ffi/enum.rb +78 -0
- data/lib/ffi/ffi.rb +5 -6
- data/lib/ffi/io.rb +15 -1
- data/lib/ffi/library.rb +79 -18
- data/lib/ffi/pointer.rb +2 -2
- data/lib/ffi/struct.rb +68 -14
- data/lib/ffi/types.rb +6 -3
- data/lib/ffi/variadic.rb +2 -2
- data/lib/ffi.rb +10 -1
- data/spec/ffi/bool_spec.rb +24 -0
- data/spec/ffi/callback_spec.rb +217 -17
- data/spec/ffi/enum_spec.rb +164 -0
- data/spec/ffi/managed_struct_spec.rb +6 -1
- data/spec/ffi/number_spec.rb +30 -0
- data/spec/ffi/pointer_spec.rb +33 -8
- data/spec/ffi/rbx/memory_pointer_spec.rb +0 -6
- data/spec/ffi/spec_helper.rb +5 -1
- data/spec/ffi/string_spec.rb +65 -4
- data/spec/ffi/struct_callback_spec.rb +41 -0
- data/spec/ffi/struct_initialize_spec.rb +30 -0
- data/spec/ffi/struct_spec.rb +44 -21
- metadata +31 -69
- data/ext/ffi_c/ffi.mk +0 -23
- data/nbproject/Makefile-Default.mk +0 -57
- data/nbproject/Makefile-impl.mk +0 -123
- data/nbproject/Package-Default.bash +0 -72
- data/nbproject/configurations.xml +0 -293
- data/nbproject/private/configurations.xml +0 -22
- data/nbproject/private/private.xml +0 -7
- data/nbproject/project.properties +0 -0
- data/nbproject/project.xml +0 -15
- data/samples/getlogin.rb +0 -7
- data/samples/getpid.rb +0 -7
- data/samples/gettimeofday.rb +0 -17
- data/samples/hello.rb +0 -6
- data/samples/inotify.rb +0 -59
- data/samples/pty.rb +0 -75
- data/samples/qsort.rb +0 -20
- data/samples/sample_helper.rb +0 -6
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
|
|
2
|
+
|
|
3
|
+
module TestEnum0
|
|
4
|
+
extend FFI::Library
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
module TestEnum1
|
|
8
|
+
extend FFI::Library
|
|
9
|
+
ffi_lib TestLibrary::PATH
|
|
10
|
+
|
|
11
|
+
enum [:c1, :c2, :c3, :c4]
|
|
12
|
+
enum [:c5, 42, :c6, :c7, :c8]
|
|
13
|
+
enum [:c9, 42, :c10, :c11, 4242, :c12]
|
|
14
|
+
enum [:c13, 42, :c14, 4242, :c15, 424242, :c16, 42424242]
|
|
15
|
+
|
|
16
|
+
attach_function :test_untagged_enum, [:int], :int
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
module TestEnum3
|
|
20
|
+
extend FFI::Library
|
|
21
|
+
ffi_lib TestLibrary::PATH
|
|
22
|
+
|
|
23
|
+
enum :enum_type1, [:c1, :c2, :c3, :c4]
|
|
24
|
+
enum :enum_type2, [:c5, 42, :c6, :c7, :c8]
|
|
25
|
+
enum :enum_type3, [:c9, 42, :c10, :c11, 4242, :c12]
|
|
26
|
+
enum :enum_type4, [:c13, 42, :c14, 4242, :c15, 424242, :c16, 42424242]
|
|
27
|
+
|
|
28
|
+
attach_function :test_tagged_typedef_enum1, [:enum_type1], :enum_type1
|
|
29
|
+
attach_function :test_tagged_typedef_enum2, [:enum_type2], :enum_type2
|
|
30
|
+
attach_function :test_tagged_typedef_enum3, [:enum_type3], :enum_type3
|
|
31
|
+
attach_function :test_tagged_typedef_enum4, [:enum_type4], :enum_type4
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe "A library with no enum defined" do
|
|
35
|
+
it "returns nil when asked for an enum" do
|
|
36
|
+
TestEnum0.enum_type(:foo).should == nil
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
describe "An untagged enum" do
|
|
41
|
+
it "constants can be used as function parameters and return value" do
|
|
42
|
+
TestEnum1.test_untagged_enum(:c1).should == 0
|
|
43
|
+
TestEnum1.test_untagged_enum(:c2).should == 1
|
|
44
|
+
TestEnum1.test_untagged_enum(:c3).should == 2
|
|
45
|
+
TestEnum1.test_untagged_enum(:c4).should == 3
|
|
46
|
+
TestEnum1.test_untagged_enum(:c5).should == 42
|
|
47
|
+
TestEnum1.test_untagged_enum(:c6).should == 43
|
|
48
|
+
TestEnum1.test_untagged_enum(:c7).should == 44
|
|
49
|
+
TestEnum1.test_untagged_enum(:c8).should == 45
|
|
50
|
+
TestEnum1.test_untagged_enum(:c9).should == 42
|
|
51
|
+
TestEnum1.test_untagged_enum(:c10).should == 43
|
|
52
|
+
TestEnum1.test_untagged_enum(:c11).should == 4242
|
|
53
|
+
TestEnum1.test_untagged_enum(:c12).should == 4243
|
|
54
|
+
TestEnum1.test_untagged_enum(:c13).should == 42
|
|
55
|
+
TestEnum1.test_untagged_enum(:c14).should == 4242
|
|
56
|
+
TestEnum1.test_untagged_enum(:c15).should == 424242
|
|
57
|
+
TestEnum1.test_untagged_enum(:c16).should == 42424242
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
describe "A tagged typedef enum" do
|
|
62
|
+
it "is accessible through its tag" do
|
|
63
|
+
TestEnum3.enum_type(:enum_type1).should_not == nil
|
|
64
|
+
TestEnum3.enum_type(:enum_type2).should_not == nil
|
|
65
|
+
TestEnum3.enum_type(:enum_type3).should_not == nil
|
|
66
|
+
TestEnum3.enum_type(:enum_type4).should_not == nil
|
|
67
|
+
end
|
|
68
|
+
it "contains enum constants" do
|
|
69
|
+
TestEnum3.enum_type(:enum_type1).symbols.length.should == 4
|
|
70
|
+
TestEnum3.enum_type(:enum_type2).symbols.length.should == 4
|
|
71
|
+
TestEnum3.enum_type(:enum_type3).symbols.length.should == 4
|
|
72
|
+
TestEnum3.enum_type(:enum_type4).symbols.length.should == 4
|
|
73
|
+
end
|
|
74
|
+
it "constants can be used as function parameters and return value" do
|
|
75
|
+
TestEnum3.test_tagged_typedef_enum1(:c1).should == :c1
|
|
76
|
+
TestEnum3.test_tagged_typedef_enum1(:c2).should == :c2
|
|
77
|
+
TestEnum3.test_tagged_typedef_enum1(:c3).should == :c3
|
|
78
|
+
TestEnum3.test_tagged_typedef_enum1(:c4).should == :c4
|
|
79
|
+
TestEnum3.test_tagged_typedef_enum2(:c5).should == :c5
|
|
80
|
+
TestEnum3.test_tagged_typedef_enum2(:c6).should == :c6
|
|
81
|
+
TestEnum3.test_tagged_typedef_enum2(:c7).should == :c7
|
|
82
|
+
TestEnum3.test_tagged_typedef_enum2(:c8).should == :c8
|
|
83
|
+
TestEnum3.test_tagged_typedef_enum3(:c9).should == :c9
|
|
84
|
+
TestEnum3.test_tagged_typedef_enum3(:c10).should == :c10
|
|
85
|
+
TestEnum3.test_tagged_typedef_enum3(:c11).should == :c11
|
|
86
|
+
TestEnum3.test_tagged_typedef_enum3(:c12).should == :c12
|
|
87
|
+
TestEnum3.test_tagged_typedef_enum4(:c13).should == :c13
|
|
88
|
+
TestEnum3.test_tagged_typedef_enum4(:c14).should == :c14
|
|
89
|
+
TestEnum3.test_tagged_typedef_enum4(:c15).should == :c15
|
|
90
|
+
TestEnum3.test_tagged_typedef_enum4(:c16).should == :c16
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
describe "All enums" do
|
|
95
|
+
it "have autonumbered constants when defined with names only" do
|
|
96
|
+
TestEnum1.enum_value(:c1).should == 0
|
|
97
|
+
TestEnum1.enum_value(:c2).should == 1
|
|
98
|
+
TestEnum1.enum_value(:c3).should == 2
|
|
99
|
+
TestEnum1.enum_value(:c4).should == 3
|
|
100
|
+
|
|
101
|
+
TestEnum3.enum_value(:c1).should == 0
|
|
102
|
+
TestEnum3.enum_value(:c2).should == 1
|
|
103
|
+
TestEnum3.enum_value(:c3).should == 2
|
|
104
|
+
TestEnum3.enum_value(:c4).should == 3
|
|
105
|
+
end
|
|
106
|
+
it "can have an explicit first constant and autonumbered subsequent constants" do
|
|
107
|
+
TestEnum1.enum_value(:c5).should == 42
|
|
108
|
+
TestEnum1.enum_value(:c6).should == 43
|
|
109
|
+
TestEnum1.enum_value(:c7).should == 44
|
|
110
|
+
TestEnum1.enum_value(:c8).should == 45
|
|
111
|
+
|
|
112
|
+
TestEnum3.enum_value(:c5).should == 42
|
|
113
|
+
TestEnum3.enum_value(:c6).should == 43
|
|
114
|
+
TestEnum3.enum_value(:c7).should == 44
|
|
115
|
+
TestEnum3.enum_value(:c8).should == 45
|
|
116
|
+
end
|
|
117
|
+
it "can have a mix of explicit and autonumbered constants" do
|
|
118
|
+
TestEnum1.enum_value(:c9).should == 42
|
|
119
|
+
TestEnum1.enum_value(:c10).should == 43
|
|
120
|
+
TestEnum1.enum_value(:c11).should == 4242
|
|
121
|
+
TestEnum1.enum_value(:c12).should == 4243
|
|
122
|
+
|
|
123
|
+
TestEnum3.enum_value(:c9).should == 42
|
|
124
|
+
TestEnum3.enum_value(:c10).should == 43
|
|
125
|
+
TestEnum3.enum_value(:c11).should == 4242
|
|
126
|
+
TestEnum3.enum_value(:c12).should == 4243
|
|
127
|
+
end
|
|
128
|
+
it "can have all its constants explicitely valued" do
|
|
129
|
+
TestEnum1.enum_value(:c13).should == 42
|
|
130
|
+
TestEnum1.enum_value(:c14).should == 4242
|
|
131
|
+
TestEnum1.enum_value(:c15).should == 424242
|
|
132
|
+
TestEnum1.enum_value(:c16).should == 42424242
|
|
133
|
+
|
|
134
|
+
TestEnum3.enum_value(:c13).should == 42
|
|
135
|
+
TestEnum3.enum_value(:c14).should == 4242
|
|
136
|
+
TestEnum3.enum_value(:c15).should == 424242
|
|
137
|
+
TestEnum3.enum_value(:c16).should == 42424242
|
|
138
|
+
end
|
|
139
|
+
it "return the constant corresponding to a specific value" do
|
|
140
|
+
enum = TestEnum3.enum_type(:enum_type1)
|
|
141
|
+
enum[0].should == :c1
|
|
142
|
+
enum[1].should == :c2
|
|
143
|
+
enum[2].should == :c3
|
|
144
|
+
enum[3].should == :c4
|
|
145
|
+
|
|
146
|
+
enum = TestEnum3.enum_type(:enum_type2)
|
|
147
|
+
enum[42].should == :c5
|
|
148
|
+
enum[43].should == :c6
|
|
149
|
+
enum[44].should == :c7
|
|
150
|
+
enum[45].should == :c8
|
|
151
|
+
|
|
152
|
+
enum = TestEnum3.enum_type(:enum_type3)
|
|
153
|
+
enum[42].should == :c9
|
|
154
|
+
enum[43].should == :c10
|
|
155
|
+
enum[4242].should == :c11
|
|
156
|
+
enum[4243].should == :c12
|
|
157
|
+
|
|
158
|
+
enum = TestEnum3.enum_type(:enum_type4)
|
|
159
|
+
enum[42].should == :c13
|
|
160
|
+
enum[4242].should == :c14
|
|
161
|
+
enum[424242].should == :c15
|
|
162
|
+
enum[42424242].should == :c16
|
|
163
|
+
end
|
|
164
|
+
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
|
|
2
|
+
require 'java' if RUBY_PLATFORM =~ /java/
|
|
2
3
|
|
|
3
4
|
describe "Managed Struct" do
|
|
4
5
|
include FFI
|
|
@@ -33,7 +34,11 @@ describe "Managed Struct" do
|
|
|
33
34
|
loop = 5
|
|
34
35
|
while loop > 0 && @@count < count
|
|
35
36
|
loop -= 1
|
|
36
|
-
|
|
37
|
+
if RUBY_PLATFORM =~ /java/
|
|
38
|
+
java.lang.System.gc
|
|
39
|
+
else
|
|
40
|
+
GC.start
|
|
41
|
+
end
|
|
37
42
|
sleep 0.05 if @@count < count
|
|
38
43
|
end
|
|
39
44
|
end
|
data/spec/ffi/number_spec.rb
CHANGED
|
@@ -20,6 +20,36 @@ describe "Function with primitive integer arguments" do
|
|
|
20
20
|
attach_function :set_double, [ :double ], :void
|
|
21
21
|
attach_function :get_double, [ ], :double
|
|
22
22
|
end
|
|
23
|
+
it "int8.size" do
|
|
24
|
+
FFI::TYPE_INT8.size.should == 1
|
|
25
|
+
end
|
|
26
|
+
it "uint8.size" do
|
|
27
|
+
FFI::TYPE_UINT8.size.should == 1
|
|
28
|
+
end
|
|
29
|
+
it "int16.size" do
|
|
30
|
+
FFI::TYPE_INT16.size.should == 2
|
|
31
|
+
end
|
|
32
|
+
it "uint16.size" do
|
|
33
|
+
FFI::TYPE_UINT16.size.should == 2
|
|
34
|
+
end
|
|
35
|
+
it "int32.size" do
|
|
36
|
+
FFI::TYPE_INT32.size.should == 4
|
|
37
|
+
end
|
|
38
|
+
it "uint32.size" do
|
|
39
|
+
FFI::TYPE_UINT32.size.should == 4
|
|
40
|
+
end
|
|
41
|
+
it "int64.size" do
|
|
42
|
+
FFI::TYPE_INT64.size.should == 8
|
|
43
|
+
end
|
|
44
|
+
it "uint64.size" do
|
|
45
|
+
FFI::TYPE_UINT64.size.should == 8
|
|
46
|
+
end
|
|
47
|
+
it "float.size" do
|
|
48
|
+
FFI::TYPE_FLOAT32.size.should == 4
|
|
49
|
+
end
|
|
50
|
+
it "double.size" do
|
|
51
|
+
FFI::TYPE_FLOAT64.size.should == 8
|
|
52
|
+
end
|
|
23
53
|
[ 0, 127, -128, -1 ].each do |i|
|
|
24
54
|
it ":char call(:char (#{i}))" do
|
|
25
55
|
LibTest.ret_s8(i).should == i
|
data/spec/ffi/pointer_spec.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
|
|
2
2
|
require 'delegate'
|
|
3
|
+
require 'java' if RUBY_PLATFORM =~ /java/
|
|
4
|
+
|
|
3
5
|
module LibTest
|
|
4
6
|
attach_function :ptr_ret_int32_t, [ :pointer, :int ], :int
|
|
5
7
|
attach_function :ptr_from_address, [ FFI::Platform::ADDRESS_SIZE == 32 ? :uint : :ulong_long ], :pointer
|
|
@@ -25,7 +27,6 @@ describe "Pointer" do
|
|
|
25
27
|
end
|
|
26
28
|
class PointerDelegate < DelegateClass(FFI::Pointer)
|
|
27
29
|
def initialize(ptr)
|
|
28
|
-
super
|
|
29
30
|
@ptr = ptr
|
|
30
31
|
end
|
|
31
32
|
def to_ptr
|
|
@@ -84,7 +85,22 @@ describe "Pointer" do
|
|
|
84
85
|
array[j].address.should == address
|
|
85
86
|
end
|
|
86
87
|
end
|
|
88
|
+
|
|
89
|
+
end
|
|
87
90
|
|
|
91
|
+
describe 'NULL' do
|
|
92
|
+
it 'should be obtained using Pointer::NULL constant' do
|
|
93
|
+
null_ptr = FFI::Pointer::NULL
|
|
94
|
+
null_ptr.null?.should be_true
|
|
95
|
+
end
|
|
96
|
+
it 'should be obtained passing address 0 to constructor' do
|
|
97
|
+
FFI::Pointer.new(0).null?.should be_true
|
|
98
|
+
end
|
|
99
|
+
it 'should raise an error when attempting read/write operations on it' do
|
|
100
|
+
null_ptr = FFI::Pointer::NULL
|
|
101
|
+
lambda { null_ptr.read_int }.should raise_error(FFI::NullPointerError)
|
|
102
|
+
lambda { null_ptr.write_int(0xff1) }.should raise_error(FFI::NullPointerError)
|
|
103
|
+
end
|
|
88
104
|
end
|
|
89
105
|
|
|
90
106
|
end
|
|
@@ -106,7 +122,11 @@ describe "AutoPointer" do
|
|
|
106
122
|
loop = 5
|
|
107
123
|
while @@count < count && loop > 0
|
|
108
124
|
loop -= 1
|
|
109
|
-
|
|
125
|
+
if RUBY_PLATFORM =~ /java/
|
|
126
|
+
java.lang.System.gc
|
|
127
|
+
else
|
|
128
|
+
GC.start
|
|
129
|
+
end
|
|
110
130
|
sleep 0.05 unless @@count == count
|
|
111
131
|
end
|
|
112
132
|
@@count = 0
|
|
@@ -115,15 +135,17 @@ describe "AutoPointer" do
|
|
|
115
135
|
self.method(:release).to_proc
|
|
116
136
|
end
|
|
117
137
|
end
|
|
118
|
-
|
|
138
|
+
class AutoPointerSubclass < FFI::AutoPointer
|
|
139
|
+
def self.release(ptr); end
|
|
140
|
+
end
|
|
119
141
|
it "cleanup via default release method" do
|
|
120
|
-
|
|
142
|
+
AutoPointerSubclass.should_receive(:release).at_least(loop_count-wiggle_room).times
|
|
121
143
|
AutoPointerTestHelper.reset
|
|
122
144
|
loop_count.times do
|
|
123
145
|
# note that if we called
|
|
124
146
|
# AutoPointerTestHelper.method(:release).to_proc inline, we'd
|
|
125
147
|
# have a reference to the pointer and it would never get GC'd.
|
|
126
|
-
ap =
|
|
148
|
+
ap = AutoPointerSubclass.new(LibTest.ptr_from_address(magic))
|
|
127
149
|
end
|
|
128
150
|
AutoPointerTestHelper.gc_everything loop_count
|
|
129
151
|
end
|
|
@@ -157,14 +179,17 @@ describe "AutoPointer" do
|
|
|
157
179
|
end
|
|
158
180
|
end
|
|
159
181
|
describe "AutoPointer#new" do
|
|
182
|
+
class AutoPointerSubclass < FFI::AutoPointer
|
|
183
|
+
def self.release(ptr); end
|
|
184
|
+
end
|
|
160
185
|
it "MemoryPointer argument raises ArgumentError" do
|
|
161
|
-
lambda { FFI::AutoPointer.new(FFI::MemoryPointer.new(:int))}.should raise_error(
|
|
186
|
+
lambda { FFI::AutoPointer.new(FFI::MemoryPointer.new(:int))}.should raise_error(::TypeError)
|
|
162
187
|
end
|
|
163
188
|
it "AutoPointer argument raises ArgumentError" do
|
|
164
|
-
lambda {
|
|
189
|
+
lambda { AutoPointerSubclass.new(AutoPointerSubclass.new(LibTest.ptr_from_address(0))) }.should raise_error(::TypeError)
|
|
165
190
|
end
|
|
166
191
|
it "Buffer argument raises ArgumentError" do
|
|
167
|
-
lambda { FFI::AutoPointer.new(FFI::Buffer.new(:int))}.should raise_error(
|
|
192
|
+
lambda { FFI::AutoPointer.new(FFI::Buffer.new(:int))}.should raise_error(::TypeError)
|
|
168
193
|
end
|
|
169
194
|
|
|
170
195
|
end
|
|
@@ -18,12 +18,6 @@ describe "MemoryPointer" do
|
|
|
18
18
|
m.read_string.should == "FFI is Awesome"
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
it "reads back a string from an Array of ints" do
|
|
22
|
-
m = FFI::MemoryPointer.new(:int, 4)
|
|
23
|
-
m.write_array_of_int([541673030, 1092645737, 1869833591, 538994029])
|
|
24
|
-
m.read_string(16).should == "FFI is Awesome "
|
|
25
|
-
end
|
|
26
|
-
|
|
27
21
|
it "makes a pointer for a certain number of bytes" do
|
|
28
22
|
m = FFI::MemoryPointer.new(8)
|
|
29
23
|
m.write_array_of_int([1,2])
|
data/spec/ffi/spec_helper.rb
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
require 'rubygems'
|
|
2
|
+
require 'rbconfig'
|
|
2
3
|
require 'spec'
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
if ENV["MRI_FFI"]
|
|
6
|
+
$:.unshift File.join(File.dirname(__FILE__), "..", "..", "lib"),
|
|
7
|
+
File.join(File.dirname(__FILE__), "..", "..", "build", "#{Config::CONFIG['host_cpu''arch']}", "ffi_c", RUBY_VERSION)
|
|
8
|
+
end
|
|
5
9
|
require "ffi"
|
|
6
10
|
|
|
7
11
|
module TestLibrary
|
data/spec/ffi/string_spec.rb
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
|
|
2
2
|
describe "String tests" do
|
|
3
3
|
include FFI
|
|
4
|
-
module
|
|
4
|
+
module StrLibTest
|
|
5
5
|
extend FFI::Library
|
|
6
6
|
ffi_lib TestLibrary::PATH
|
|
7
7
|
attach_function :ptr_ret_pointer, [ :pointer, :int], :string
|
|
8
8
|
attach_function :string_equals, [ :string, :string ], :int
|
|
9
9
|
attach_function :string_dummy, [ :string ], :void
|
|
10
|
+
attach_function :string_null, [ ], :string
|
|
10
11
|
end
|
|
11
12
|
it "MemoryPointer#get_string returns a tainted string" do
|
|
12
13
|
mp = MemoryPointer.new 1024
|
|
@@ -19,13 +20,13 @@ describe "String tests" do
|
|
|
19
20
|
sp = MemoryPointer.new 1024
|
|
20
21
|
sp.put_string(0, "test")
|
|
21
22
|
mp.put_pointer(0, sp)
|
|
22
|
-
str =
|
|
23
|
+
str = StrLibTest.ptr_ret_pointer(mp, 0)
|
|
23
24
|
str.should == "test"
|
|
24
25
|
str.tainted?.should == true
|
|
25
26
|
end
|
|
26
27
|
it "Poison null byte raises error" do
|
|
27
28
|
s = "123\0abc"
|
|
28
|
-
lambda {
|
|
29
|
+
lambda { StrLibTest.string_equals(s, s) }.should raise_error
|
|
29
30
|
end
|
|
30
31
|
it "Tainted String parameter should throw a SecurityError" do
|
|
31
32
|
$SAFE = 1
|
|
@@ -37,6 +38,66 @@ describe "String tests" do
|
|
|
37
38
|
end
|
|
38
39
|
end if false
|
|
39
40
|
it "casts nil as NULL pointer" do
|
|
40
|
-
|
|
41
|
+
StrLibTest.string_dummy(nil)
|
|
42
|
+
end
|
|
43
|
+
it "return nil for NULL char*" do
|
|
44
|
+
StrLibTest.string_null.should == nil
|
|
45
|
+
end
|
|
46
|
+
it "reads an array of strings until encountering a NULL pointer" do
|
|
47
|
+
strings = ["foo", "bar", "baz", "testing", "ffi"]
|
|
48
|
+
ptrary = MemoryPointer.new(:pointer, 6)
|
|
49
|
+
ary = strings.inject([]) do |a, str|
|
|
50
|
+
f = MemoryPointer.new(1024)
|
|
51
|
+
f.put_string(0, str)
|
|
52
|
+
a << f
|
|
53
|
+
end
|
|
54
|
+
ary.insert(3, nil)
|
|
55
|
+
ptrary.write_array_of_pointer(ary)
|
|
56
|
+
ptrary.get_array_of_string(0).should == ["foo", "bar", "baz"]
|
|
57
|
+
end
|
|
58
|
+
it "reads an array of strings of the size specified, substituting nil when a pointer is NULL" do
|
|
59
|
+
strings = ["foo", "bar", "baz", "testing", "ffi"]
|
|
60
|
+
ptrary = MemoryPointer.new(:pointer, 6)
|
|
61
|
+
ary = strings.inject([]) do |a, str|
|
|
62
|
+
f = MemoryPointer.new(1024)
|
|
63
|
+
f.put_string(0, str)
|
|
64
|
+
a << f
|
|
65
|
+
end
|
|
66
|
+
ary.insert(2, nil)
|
|
67
|
+
ptrary.write_array_of_pointer(ary)
|
|
68
|
+
ptrary.get_array_of_string(0, 4).should == ["foo", "bar", nil, "baz"]
|
|
69
|
+
end
|
|
70
|
+
it "reads an array of strings, taking a memory offset parameter" do
|
|
71
|
+
strings = ["foo", "bar", "baz", "testing", "ffi"]
|
|
72
|
+
ptrary = MemoryPointer.new(:pointer, 5)
|
|
73
|
+
ary = strings.inject([]) do |a, str|
|
|
74
|
+
f = MemoryPointer.new(1024)
|
|
75
|
+
f.put_string(0, str)
|
|
76
|
+
a << f
|
|
77
|
+
end
|
|
78
|
+
ptrary.write_array_of_pointer(ary)
|
|
79
|
+
ptrary.get_array_of_string(2 * FFI.type_size(:pointer), 3).should == ["baz", "testing", "ffi"]
|
|
80
|
+
end
|
|
81
|
+
it "raises an IndexError when trying to read an array of strings out of bounds" do
|
|
82
|
+
strings = ["foo", "bar", "baz", "testing", "ffi"]
|
|
83
|
+
ptrary = MemoryPointer.new(:pointer, 5)
|
|
84
|
+
ary = strings.inject([]) do |a, str|
|
|
85
|
+
f = MemoryPointer.new(1024)
|
|
86
|
+
f.put_string(0, str)
|
|
87
|
+
a << f
|
|
88
|
+
end
|
|
89
|
+
ptrary.write_array_of_pointer(ary)
|
|
90
|
+
lambda { ptrary.get_array_of_string(0, 6) }.should raise_error
|
|
91
|
+
end
|
|
92
|
+
it "raises an IndexError when trying to read an array of strings using a negative offset" do
|
|
93
|
+
strings = ["foo", "bar", "baz", "testing", "ffi"]
|
|
94
|
+
ptrary = MemoryPointer.new(:pointer, 5)
|
|
95
|
+
ary = strings.inject([]) do |a, str|
|
|
96
|
+
f = MemoryPointer.new(1024)
|
|
97
|
+
f.put_string(0, str)
|
|
98
|
+
a << f
|
|
99
|
+
end
|
|
100
|
+
ptrary.write_array_of_pointer(ary)
|
|
101
|
+
lambda { ptrary.get_array_of_string(-1) }.should raise_error
|
|
41
102
|
end
|
|
42
103
|
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
|
|
2
|
+
|
|
3
|
+
describe FFI::Struct, ' with inline callback functions' do
|
|
4
|
+
it 'should be able to define inline callback field' do
|
|
5
|
+
module CallbackMember
|
|
6
|
+
extend FFI::Library
|
|
7
|
+
ffi_lib TestLibrary::PATH
|
|
8
|
+
class TestStruct < FFI::Struct
|
|
9
|
+
layout \
|
|
10
|
+
:add, callback([ :int, :int ], :int),
|
|
11
|
+
:sub, callback([ :int, :int ], :int)
|
|
12
|
+
end
|
|
13
|
+
attach_function :struct_call_add_cb, [TestStruct, :int, :int], :int
|
|
14
|
+
attach_function :struct_call_sub_cb, [TestStruct, :int, :int], :int
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
it 'should take methods as callbacks' do
|
|
18
|
+
module CallbackMember
|
|
19
|
+
extend FFI::Library
|
|
20
|
+
ffi_lib TestLibrary::PATH
|
|
21
|
+
class TestStruct < FFI::Struct
|
|
22
|
+
layout \
|
|
23
|
+
:add, callback([ :int, :int ], :int),
|
|
24
|
+
:sub, callback([ :int, :int ], :int)
|
|
25
|
+
end
|
|
26
|
+
attach_function :struct_call_add_cb, [TestStruct, :int, :int], :int
|
|
27
|
+
attach_function :struct_call_sub_cb, [TestStruct, :int, :int], :int
|
|
28
|
+
end
|
|
29
|
+
module StructCallbacks
|
|
30
|
+
def self.add a, b
|
|
31
|
+
a+b
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
ts = CallbackMember::TestStruct.new
|
|
36
|
+
ts[:add] = StructCallbacks.method(:add)
|
|
37
|
+
|
|
38
|
+
CallbackMember.struct_call_add_cb(ts, 1, 2).should == 3
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
|
|
2
|
+
|
|
3
|
+
describe FFI::Struct, ' with an initialize function' do
|
|
4
|
+
it "should call the initialize function" do
|
|
5
|
+
class StructWithInitialize < FFI::Struct
|
|
6
|
+
layout :string, :string
|
|
7
|
+
attr_accessor :magic
|
|
8
|
+
def initialize
|
|
9
|
+
super
|
|
10
|
+
self.magic = 42
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
StructWithInitialize.new.magic.should == 42
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe FFI::ManagedStruct, ' with an initialize function' do
|
|
18
|
+
it "should call the initialize function" do
|
|
19
|
+
class ManagedStructWithInitialize < FFI::ManagedStruct
|
|
20
|
+
layout :string, :string
|
|
21
|
+
attr_accessor :magic
|
|
22
|
+
def initialize
|
|
23
|
+
super MemoryPointer.new(:pointer).put_int(0, 0x1234).get_pointer(0)
|
|
24
|
+
self.magic = 42
|
|
25
|
+
end
|
|
26
|
+
def self.release;end
|
|
27
|
+
end
|
|
28
|
+
ManagedStructWithInitialize.new.magic.should == 42
|
|
29
|
+
end
|
|
30
|
+
end
|
data/spec/ffi/struct_spec.rb
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
|
|
2
2
|
describe "Struct tests" do
|
|
3
|
-
include FFI
|
|
4
3
|
StructTypes = {
|
|
5
4
|
's8' => :char,
|
|
6
5
|
's16' => :short,
|
|
@@ -29,54 +28,54 @@ describe "Struct tests" do
|
|
|
29
28
|
end
|
|
30
29
|
it "Struct#[:pointer]" do
|
|
31
30
|
magic = 0x12345678
|
|
32
|
-
mp = MemoryPointer.new :long
|
|
31
|
+
mp = FFI::MemoryPointer.new :long
|
|
33
32
|
mp.put_long(0, magic)
|
|
34
|
-
smp = MemoryPointer.new :pointer
|
|
33
|
+
smp = FFI::MemoryPointer.new :pointer
|
|
35
34
|
smp.put_pointer(0, mp)
|
|
36
35
|
s = PointerMember.new smp
|
|
37
36
|
s[:pointer].should == mp
|
|
38
37
|
end
|
|
39
38
|
it "Struct#[:pointer].nil? for NULL value" do
|
|
40
39
|
magic = 0x12345678
|
|
41
|
-
mp = MemoryPointer.new :long
|
|
40
|
+
mp = FFI::MemoryPointer.new :long
|
|
42
41
|
mp.put_long(0, magic)
|
|
43
|
-
smp = MemoryPointer.new :pointer
|
|
42
|
+
smp = FFI::MemoryPointer.new :pointer
|
|
44
43
|
smp.put_pointer(0, nil)
|
|
45
44
|
s = PointerMember.new smp
|
|
46
45
|
s[:pointer].null?.should == true
|
|
47
46
|
end
|
|
48
47
|
it "Struct#[:pointer]=" do
|
|
49
48
|
magic = 0x12345678
|
|
50
|
-
mp = MemoryPointer.new :long
|
|
49
|
+
mp = FFI::MemoryPointer.new :long
|
|
51
50
|
mp.put_long(0, magic)
|
|
52
|
-
smp = MemoryPointer.new :pointer
|
|
51
|
+
smp = FFI::MemoryPointer.new :pointer
|
|
53
52
|
s = PointerMember.new smp
|
|
54
53
|
s[:pointer] = mp
|
|
55
54
|
smp.get_pointer(0).should == mp
|
|
56
55
|
end
|
|
57
56
|
it "Struct#[:pointer]=struct" do
|
|
58
57
|
magic = 0x12345678
|
|
59
|
-
smp = MemoryPointer.new :pointer
|
|
58
|
+
smp = FFI::MemoryPointer.new :pointer
|
|
60
59
|
s = PointerMember.new smp
|
|
61
60
|
lambda { s[:pointer] = s }.should_not raise_error
|
|
62
61
|
end
|
|
63
62
|
it "Struct#[:pointer]=nil" do
|
|
64
|
-
smp = MemoryPointer.new :pointer
|
|
63
|
+
smp = FFI::MemoryPointer.new :pointer
|
|
65
64
|
s = PointerMember.new smp
|
|
66
65
|
s[:pointer] = nil
|
|
67
66
|
smp.get_pointer(0).null?.should == true
|
|
68
67
|
end
|
|
69
68
|
it "Struct#[:string]" do
|
|
70
69
|
magic = "test"
|
|
71
|
-
mp = MemoryPointer.new 1024
|
|
70
|
+
mp = FFI::MemoryPointer.new 1024
|
|
72
71
|
mp.put_string(0, magic)
|
|
73
|
-
smp = MemoryPointer.new :pointer
|
|
72
|
+
smp = FFI::MemoryPointer.new :pointer
|
|
74
73
|
smp.put_pointer(0, mp)
|
|
75
74
|
s = StringMember.new smp
|
|
76
75
|
s[:string].should == magic
|
|
77
76
|
end
|
|
78
77
|
it "Struct#[:string].nil? for NULL value" do
|
|
79
|
-
smp = MemoryPointer.new :pointer
|
|
78
|
+
smp = FFI::MemoryPointer.new :pointer
|
|
80
79
|
smp.put_pointer(0, nil)
|
|
81
80
|
s = StringMember.new smp
|
|
82
81
|
s[:string].nil?.should == true
|
|
@@ -85,9 +84,9 @@ describe "Struct tests" do
|
|
|
85
84
|
class PairLayout < FFI::Struct
|
|
86
85
|
layout :a, :int, :b, :long_long
|
|
87
86
|
end
|
|
88
|
-
ll_off = (FFI::
|
|
87
|
+
ll_off = (FFI::TYPE_UINT64.alignment == 4 ? 4 : 8)
|
|
89
88
|
PairLayout.size.should == (ll_off + 8)
|
|
90
|
-
mp = MemoryPointer.new(PairLayout.size)
|
|
89
|
+
mp = FFI::MemoryPointer.new(PairLayout.size)
|
|
91
90
|
s = PairLayout.new mp
|
|
92
91
|
s[:a] = 0x12345678
|
|
93
92
|
mp.get_int(0).should == 0x12345678
|
|
@@ -98,8 +97,8 @@ describe "Struct tests" do
|
|
|
98
97
|
class PairLayout < FFI::Struct
|
|
99
98
|
layout :a, :int, 0, :b, :long_long, 4
|
|
100
99
|
end
|
|
101
|
-
PairLayout.size.should == (FFI::
|
|
102
|
-
mp = MemoryPointer.new(PairLayout.size)
|
|
100
|
+
PairLayout.size.should == (FFI::TYPE_UINT64.alignment == 4 ? 12 : 16)
|
|
101
|
+
mp = FFI::MemoryPointer.new(PairLayout.size)
|
|
103
102
|
s = PairLayout.new mp
|
|
104
103
|
s[:a] = 0x12345678
|
|
105
104
|
mp.get_int(0).should == 0x12345678
|
|
@@ -110,8 +109,8 @@ describe "Struct tests" do
|
|
|
110
109
|
class MixedLayout < FFI::Struct
|
|
111
110
|
layout :a, :int, :b, :long_long, 4
|
|
112
111
|
end
|
|
113
|
-
MixedLayout.size.should == (FFI::
|
|
114
|
-
mp = MemoryPointer.new(MixedLayout.size)
|
|
112
|
+
MixedLayout.size.should == (FFI::TYPE_UINT64.alignment == 4 ? 12 : 16)
|
|
113
|
+
mp = FFI::MemoryPointer.new(MixedLayout.size)
|
|
115
114
|
s = MixedLayout.new mp
|
|
116
115
|
s[:a] = 0x12345678
|
|
117
116
|
mp.get_int(0).should == 0x12345678
|
|
@@ -124,9 +123,9 @@ describe "Struct tests" do
|
|
|
124
123
|
class HashLayout < FFI::Struct
|
|
125
124
|
layout :a => :int, :b => :long_long
|
|
126
125
|
end
|
|
127
|
-
ll_off = (FFI::
|
|
126
|
+
ll_off = (FFI::TYPE_UINT64.alignment == 4? 4 : 8)
|
|
128
127
|
HashLayout.size.should == (ll_off + 8)
|
|
129
|
-
mp = MemoryPointer.new(HashLayout.size)
|
|
128
|
+
mp = FFI::MemoryPointer.new(HashLayout.size)
|
|
130
129
|
s = HashLayout.new mp
|
|
131
130
|
s[:a] = 0x12345678
|
|
132
131
|
mp.get_int(0).should == 0x12345678
|
|
@@ -393,7 +392,6 @@ describe FFI::Struct, ' with an array field' do
|
|
|
393
392
|
before do
|
|
394
393
|
@s = LibTest::StructWithArray.new
|
|
395
394
|
end
|
|
396
|
-
it 'should align correctly array field'
|
|
397
395
|
it 'should correctly calculate StructWithArray size (in bytes)' do
|
|
398
396
|
LibTest::StructWithArray.size.should == 24
|
|
399
397
|
end
|
|
@@ -451,3 +449,28 @@ describe 'BuggedStruct' do
|
|
|
451
449
|
end.should == [[:visible, 0], [:x, 4], [:y, 8], [:rx, 12], [:ry, 14], [:order, 16], [:size, 17]]
|
|
452
450
|
end
|
|
453
451
|
end
|
|
452
|
+
|
|
453
|
+
describe "Struct allocation" do
|
|
454
|
+
it "MemoryPointer.new(Struct, 2)" do
|
|
455
|
+
class S < FFI::Struct
|
|
456
|
+
layout :i, :uint
|
|
457
|
+
end
|
|
458
|
+
p = FFI::MemoryPointer.new(S, 2)
|
|
459
|
+
p.total.should == 8
|
|
460
|
+
p.type_size.should == 4
|
|
461
|
+
p.put_uint(4, 0xdeadbeef)
|
|
462
|
+
S.new(p[1])[:i].should == 0xdeadbeef
|
|
463
|
+
p[1].address.should == (p[0].address + 4)
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
it "Buffer.new(Struct, 2)" do
|
|
467
|
+
class S < FFI::Struct
|
|
468
|
+
layout :i, :uint
|
|
469
|
+
end
|
|
470
|
+
p = FFI::Buffer.new(S, 2)
|
|
471
|
+
p.total.should == 8
|
|
472
|
+
p.type_size.should == 4
|
|
473
|
+
p.put_uint(4, 0xdeadbeef)
|
|
474
|
+
S.new(p[1])[:i].should == 0xdeadbeef
|
|
475
|
+
end
|
|
476
|
+
end
|