ffi 0.6.4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of ffi might be problematic. Click here for more details.

Files changed (92) hide show
  1. data/History.txt +7 -0
  2. data/LICENSE +10 -21
  3. data/README.rdoc +70 -0
  4. data/Rakefile +56 -84
  5. data/ext/ffi_c/AbstractMemory.c +56 -38
  6. data/ext/ffi_c/AbstractMemory.h +15 -22
  7. data/ext/ffi_c/Buffer.c +61 -22
  8. data/ext/ffi_c/Call.c +52 -540
  9. data/ext/ffi_c/Call.h +1 -1
  10. data/ext/ffi_c/DataConverter.c +62 -0
  11. data/ext/ffi_c/DynamicLibrary.c +21 -1
  12. data/ext/ffi_c/Function.c +252 -30
  13. data/ext/ffi_c/MappedType.c +146 -0
  14. data/{libtest/FunctionTest.c → ext/ffi_c/MappedType.h} +32 -25
  15. data/ext/ffi_c/MemoryPointer.c +12 -33
  16. data/ext/ffi_c/Platform.c +2 -0
  17. data/ext/ffi_c/Pointer.c +66 -28
  18. data/ext/ffi_c/Struct.c +19 -306
  19. data/ext/ffi_c/Struct.h +6 -0
  20. data/ext/ffi_c/StructByReference.c +150 -0
  21. data/{libtest/LastErrorTest.c → ext/ffi_c/StructByReference.h} +30 -21
  22. data/ext/ffi_c/StructLayout.c +26 -16
  23. data/ext/ffi_c/Type.c +39 -68
  24. data/ext/ffi_c/Type.h +12 -22
  25. data/ext/ffi_c/Types.c +20 -5
  26. data/ext/ffi_c/Types.h +7 -7
  27. data/ext/ffi_c/Variadic.c +21 -17
  28. data/ext/ffi_c/extconf.rb +4 -0
  29. data/ext/ffi_c/ffi.c +8 -2
  30. data/ext/ffi_c/rbffi.h +1 -0
  31. data/lib/ffi/autopointer.rb +23 -22
  32. data/lib/ffi/enum.rb +36 -21
  33. data/lib/ffi/errno.rb +20 -0
  34. data/lib/ffi/ffi.rb +13 -80
  35. data/lib/ffi/io.rb +12 -20
  36. data/lib/ffi/library.rb +109 -92
  37. data/lib/ffi/managedstruct.rb +1 -1
  38. data/lib/ffi/memorypointer.rb +15 -21
  39. data/lib/ffi/platform.rb +27 -33
  40. data/lib/ffi/pointer.rb +14 -21
  41. data/lib/ffi/struct.rb +98 -49
  42. data/lib/ffi/struct_layout_builder.rb +158 -0
  43. data/lib/ffi/types.rb +99 -128
  44. data/lib/ffi/union.rb +20 -0
  45. data/lib/ffi/variadic.rb +33 -22
  46. data/spec/ffi/async_callback_spec.rb +23 -0
  47. data/spec/ffi/callback_spec.rb +62 -0
  48. data/spec/ffi/custom_param_type.rb +31 -0
  49. data/spec/ffi/custom_type_spec.rb +73 -0
  50. data/spec/ffi/enum_spec.rb +19 -0
  51. data/spec/ffi/ffi_spec.rb +24 -0
  52. data/spec/ffi/pointer_spec.rb +15 -0
  53. data/spec/ffi/rbx/memory_pointer_spec.rb +7 -1
  54. data/spec/ffi/strptr_spec.rb +36 -0
  55. data/spec/ffi/struct_packed_spec.rb +46 -0
  56. data/spec/ffi/struct_spec.rb +19 -5
  57. data/spec/ffi/typedef_spec.rb +14 -0
  58. data/tasks/ann.rake +80 -0
  59. data/tasks/extension.rake +25 -0
  60. data/tasks/gem.rake +200 -0
  61. data/tasks/git.rake +41 -0
  62. data/tasks/notes.rake +27 -0
  63. data/tasks/post_load.rake +34 -0
  64. data/tasks/rdoc.rake +50 -0
  65. data/tasks/rubyforge.rake +55 -0
  66. data/tasks/setup.rb +301 -0
  67. data/tasks/spec.rake +54 -0
  68. data/tasks/svn.rake +47 -0
  69. data/tasks/test.rake +40 -0
  70. metadata +139 -131
  71. data/README.md +0 -109
  72. data/ext/ffi_c/AutoPointer.c +0 -60
  73. data/ext/ffi_c/AutoPointer.h +0 -18
  74. data/ext/ffi_c/Ffi_c.iml +0 -12
  75. data/ffi.gemspec +0 -18
  76. data/gen/log +0 -1
  77. data/lib/Lib.iml +0 -21
  78. data/libtest/Benchmark.c +0 -73
  79. data/libtest/BoolTest.c +0 -52
  80. data/libtest/BufferTest.c +0 -52
  81. data/libtest/ClosureTest.c +0 -173
  82. data/libtest/EnumTest.c +0 -55
  83. data/libtest/GNUmakefile +0 -141
  84. data/libtest/GlobalVariable.c +0 -56
  85. data/libtest/NumberTest.c +0 -145
  86. data/libtest/PointerTest.c +0 -84
  87. data/libtest/ReferenceTest.c +0 -44
  88. data/libtest/StringTest.c +0 -55
  89. data/libtest/StructTest.c +0 -247
  90. data/libtest/UnionTest.c +0 -64
  91. data/libtest/VariadicTest.c +0 -57
  92. data/spec/ffi/Ffi.iml +0 -12
@@ -0,0 +1,31 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
2
+
3
+ describe "functions with custom parameter types" do
4
+ before :each do
5
+
6
+ Custom_enum = Class.new do
7
+ extend FFI::DataConverter
8
+ ToNativeMap= { :a => 1, :b => 2 }
9
+ FromNativeMap = { 1 => :a, 2 => :b }
10
+
11
+ def self.native_type
12
+ @native_type_called = true
13
+ FFI::Type::INT32
14
+ end
15
+
16
+ def self.to_native(val, ctx)
17
+ @to_native_called = true
18
+ ToNativeMap[val]
19
+ end
20
+
21
+ def self.from_native(val, ctx)
22
+ @from_native_called = true
23
+ FromNativeMap[val]
24
+ end
25
+ def self.native_type_called?; @native_type_called; end
26
+ def self.from_native_called?; @from_native_called; end
27
+ def self.to_native_called?; @to_native_called; end
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,73 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
2
+
3
+ describe "functions with custom types" do
4
+ before :each do
5
+
6
+ Custom_enum = Class.new do
7
+ extend FFI::DataConverter
8
+ ToNativeMap= { :a => 1, :b => 2, :c => 3 }
9
+ FromNativeMap = { 1 => :a, 2 => :b, 3 => :c }
10
+
11
+ def self.native_type
12
+ @native_type_called = true
13
+ FFI::Type::INT32
14
+ end
15
+
16
+ def self.to_native(val, ctx)
17
+ @to_native_called = true
18
+ ToNativeMap[val]
19
+ end
20
+
21
+ def self.from_native(val, ctx)
22
+ @from_native_called = true
23
+ FromNativeMap[val]
24
+ end
25
+ def self.native_type_called?; @native_type_called; end
26
+ def self.from_native_called?; @from_native_called; end
27
+ def self.to_native_called?; @to_native_called; end
28
+ end
29
+
30
+ end
31
+
32
+ it "can attach with custom return type" do
33
+ lambda do
34
+ m = Module.new do
35
+ extend FFI::Library
36
+ ffi_lib TestLibrary::PATH
37
+ attach_function :ret_s32, [ :int ], Custom_enum
38
+ end
39
+ end.should_not raise_error
40
+ end
41
+
42
+ it "should return object of correct type" do
43
+
44
+ m = Module.new do
45
+
46
+ extend FFI::Library
47
+ ffi_lib TestLibrary::PATH
48
+ attach_function :ret_s32, [ :int ], Custom_enum
49
+ end
50
+
51
+ m.ret_s32(1).is_a?(Symbol).should be_true
52
+ end
53
+
54
+ it "from_native should be called for result" do
55
+ m = Module.new do
56
+ extend FFI::Library
57
+ ffi_lib TestLibrary::PATH
58
+ attach_function :ret_s32, [ :int ], Custom_enum
59
+ end
60
+ m.ret_s32(1)
61
+ Custom_enum.from_native_called?.should be_true
62
+ end
63
+
64
+ it "to_native should be called for parameter" do
65
+ m = Module.new do
66
+ extend FFI::Library
67
+ ffi_lib TestLibrary::PATH
68
+ attach_function :ret_s32, [ Custom_enum ], :int
69
+ end
70
+ m.ret_s32(:a)
71
+ Custom_enum.to_native_called?.should be_true
72
+ end
73
+ end
@@ -89,6 +89,25 @@ describe "A tagged typedef enum" do
89
89
  TestEnum3.test_tagged_typedef_enum4(:c15).should == :c15
90
90
  TestEnum3.test_tagged_typedef_enum4(:c16).should == :c16
91
91
  end
92
+
93
+ it "integers can be used instead of constants" do
94
+ TestEnum3.test_tagged_typedef_enum1(0).should == :c1
95
+ TestEnum3.test_tagged_typedef_enum1(1).should == :c2
96
+ TestEnum3.test_tagged_typedef_enum1(2).should == :c3
97
+ TestEnum3.test_tagged_typedef_enum1(3).should == :c4
98
+ TestEnum3.test_tagged_typedef_enum2(42).should == :c5
99
+ TestEnum3.test_tagged_typedef_enum2(43).should == :c6
100
+ TestEnum3.test_tagged_typedef_enum2(44).should == :c7
101
+ TestEnum3.test_tagged_typedef_enum2(45).should == :c8
102
+ TestEnum3.test_tagged_typedef_enum3(42).should == :c9
103
+ TestEnum3.test_tagged_typedef_enum3(43).should == :c10
104
+ TestEnum3.test_tagged_typedef_enum3(4242).should == :c11
105
+ TestEnum3.test_tagged_typedef_enum3(4243).should == :c12
106
+ TestEnum3.test_tagged_typedef_enum4(42).should == :c13
107
+ TestEnum3.test_tagged_typedef_enum4(4242).should == :c14
108
+ TestEnum3.test_tagged_typedef_enum4(424242).should == :c15
109
+ TestEnum3.test_tagged_typedef_enum4(42424242).should == :c16
110
+ end
92
111
  end
93
112
 
94
113
  describe "All enums" do
@@ -0,0 +1,24 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
2
+
3
+ describe "FFI" do
4
+
5
+ describe ".map_library_name" do
6
+
7
+ let(:prefix) { FFI::Platform::LIBPREFIX }
8
+ let(:suffix) { FFI::Platform::LIBSUFFIX }
9
+
10
+ it "should add platform library extension if not present" do
11
+ FFI.map_library_name("#{prefix}dummy").should == "#{prefix}dummy.#{suffix}"
12
+ end
13
+
14
+ it "should add platform library extension even if lib suffix is present in name" do
15
+ FFI.map_library_name("#{prefix}dummy_with_#{suffix}").should == "#{prefix}dummy_with_#{suffix}.#{suffix}"
16
+ end
17
+
18
+ it "should return Platform::LIBC when called with 'c'" do
19
+ FFI.map_library_name('c').should == FFI::Library::LIBC
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -177,7 +177,21 @@ describe "AutoPointer" do
177
177
  end
178
178
  AutoPointerTestHelper.gc_everything loop_count
179
179
  end
180
+
181
+ it "can be used as the return type of a function" do
182
+ lambda do
183
+ Module.new do
184
+ extend FFI::Library
185
+ ffi_lib TestLibrary::PATH
186
+ class CustomAutoPointer < FFI::AutoPointer
187
+ def self.release(ptr); end
188
+ end
189
+ attach_function :ptr_from_address, [ FFI::Platform::ADDRESS_SIZE == 32 ? :uint : :ulong_long ], CustomAutoPointer
190
+ end
191
+ end.should_not raise_error
192
+ end
180
193
  end
194
+
181
195
  describe "AutoPointer#new" do
182
196
  class AutoPointerSubclass < FFI::AutoPointer
183
197
  def self.release(ptr); end
@@ -193,3 +207,4 @@ describe "AutoPointer#new" do
193
207
  end
194
208
 
195
209
  end
210
+
@@ -1,3 +1,4 @@
1
+ # coding: utf-8
1
2
  require "rubygems"
2
3
  require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
3
4
 
@@ -13,7 +14,12 @@ describe "MemoryPointer" do
13
14
  m = FFI::MemoryPointer.from_string("FFI is Awesome")
14
15
  m.type_size.should == 15
15
16
  end
16
-
17
+
18
+ it "makes a pointer from a string with multibyte characters" do
19
+ m = FFI::MemoryPointer.from_string("ぱんだ")
20
+ m.type_size.should == 10
21
+ end
22
+
17
23
  it "reads back a string" do
18
24
  m = FFI::MemoryPointer.from_string("FFI is Awesome")
19
25
  m.read_string.should == "FFI is Awesome"
@@ -0,0 +1,36 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
2
+
3
+ describe "functions returning :strptr" do
4
+ it "can attach function with :strptr return type" do
5
+ lambda do
6
+ m = Module.new do
7
+ extend FFI::Library
8
+ ffi_lib FFI::Library::LIBC
9
+ attach_function :strdup, [ :string ], :strptr
10
+ end
11
+ end.should_not raise_error
12
+ end
13
+
14
+ module StrPtr
15
+ extend FFI::Library
16
+ ffi_lib FFI::Library::LIBC
17
+ attach_function :strdup, [ :string ], :strptr
18
+ attach_function :free, [ :pointer ], :void
19
+ end
20
+
21
+ it "should return [ String, Pointer ]" do
22
+ result = StrPtr.strdup("test")
23
+ result[0].is_a?(String).should be_true
24
+ result[1].is_a?(FFI::Pointer).should be_true
25
+ end
26
+
27
+ it "should return the correct value" do
28
+ result = StrPtr.strdup("test")
29
+ result[0].should == "test"
30
+ end
31
+
32
+ it "should return non-NULL pointer" do
33
+ result = StrPtr.strdup("test")
34
+ result[1].null?.should be_false
35
+ end
36
+ end
@@ -0,0 +1,46 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
2
+
3
+ describe FFI::Struct do
4
+ it "packed :char followed by :int should have size of 5" do
5
+ Class.new(FFI::Struct) do
6
+ packed
7
+ layout :c, :char, :i, :int
8
+ end.size.should == 5
9
+ end
10
+
11
+ it "packed :char followed by :int should have alignment of 1" do
12
+ Class.new(FFI::Struct) do
13
+ packed
14
+ layout :c, :char, :i, :int
15
+ end.alignment.should == 1
16
+ end
17
+
18
+ it "packed(2) :char followed by :int should have size of 6" do
19
+ Class.new(FFI::Struct) do
20
+ packed 2
21
+ layout :c, :char, :i, :int
22
+ end.size.should == 6
23
+ end
24
+
25
+ it "packed(2) :char followed by :int should have alignment of 2" do
26
+ Class.new(FFI::Struct) do
27
+ packed 2
28
+ layout :c, :char, :i, :int
29
+ end.alignment.should == 2
30
+ end
31
+
32
+ it "packed :short followed by int should have size of 6" do
33
+ Class.new(FFI::Struct) do
34
+ packed
35
+ layout :s, :short, :i, :int
36
+ end.size.should == 6
37
+ end
38
+
39
+ it "packed :short followed by int should have alignment of 1" do
40
+ Class.new(FFI::Struct) do
41
+ packed
42
+ layout :s, :short, :i, :int
43
+ end.alignment.should == 1
44
+ end
45
+
46
+ end
@@ -158,7 +158,7 @@ describe "Struct tests" do
158
158
  class TestStruct < FFI::Struct
159
159
  layout :c, :char
160
160
  end
161
- attach_function :struct_field_s8, [ TestStruct ], :char
161
+ attach_function :struct_field_s8, [ TestStruct.in ], :char
162
162
  end
163
163
  end
164
164
  it "Can use Struct subclass as IN parameter type" do
@@ -327,8 +327,8 @@ describe "Struct tests" do
327
327
  layout :add, :add,
328
328
  :sub, :sub
329
329
  end
330
- attach_function :struct_call_add_cb, [TestStruct, :int, :int], :int
331
- attach_function :struct_call_sub_cb, [TestStruct, :int, :int], :int
330
+ attach_function :struct_call_add_cb, [TestStruct.in, :int, :int], :int
331
+ attach_function :struct_call_sub_cb, [TestStruct.in, :int, :int], :int
332
332
  end
333
333
  it "Can have CallbackInfo struct field" do
334
334
  s = CallbackMember::TestStruct.new
@@ -336,8 +336,8 @@ describe "Struct tests" do
336
336
  sub_proc = lambda { |a, b| a-b }
337
337
  s[:add] = add_proc
338
338
  s[:sub] = sub_proc
339
- CallbackMember.struct_call_add_cb(s.pointer, 40, 2).should == 42
340
- CallbackMember.struct_call_sub_cb(s.pointer, 44, 2).should == 42
339
+ CallbackMember.struct_call_add_cb(s, 40, 2).should == 42
340
+ CallbackMember.struct_call_sub_cb(s, 44, 2).should == 42
341
341
  end
342
342
  it "Can return its members as a list" do
343
343
  class TestStruct < FFI::Struct
@@ -621,4 +621,18 @@ describe "Struct allocation" do
621
621
  p.put_uint(4, 0xdeadbeef)
622
622
  S.new(p[1])[:i].should == 0xdeadbeef
623
623
  end
624
+
625
+ it "null? should be true when initialized with NULL pointer" do
626
+ class S < FFI::Struct
627
+ layout :i, :uint
628
+ end
629
+ S.new(Pointer::NULL).null?.should be_true
630
+ end
631
+
632
+ it "null? should be false when initialized with non-NULL pointer" do
633
+ class S < FFI::Struct
634
+ layout :i, :uint
635
+ end
636
+ S.new(MemoryPointer.new(S)).null?.should be_false
637
+ end
624
638
  end
@@ -45,4 +45,18 @@ describe "Custom type definitions" do
45
45
  s[:a] = 0x12345678
46
46
  s.pointer.get_uint(0).should == 0x12345678
47
47
  end
48
+
49
+ it "attach_function after a typedef should not reject normal types" do
50
+ lambda do
51
+ Module.new do
52
+ extend FFI::Library
53
+ # enum() will insert a custom typedef called :foo for the enum
54
+ enum :foo, [ :a, :b ]
55
+ typedef :int, :bar
56
+
57
+ ffi_lib TestLibrary::PATH
58
+ attach_function :ptr_ret_int32_t, [ :string, :foo ], :bar
59
+ end
60
+ end.should_not raise_error
61
+ end
48
62
  end
data/tasks/ann.rake ADDED
@@ -0,0 +1,80 @@
1
+
2
+ begin
3
+ require 'bones/smtp_tls'
4
+ rescue LoadError
5
+ require 'net/smtp'
6
+ end
7
+ require 'time'
8
+
9
+ namespace :ann do
10
+
11
+ # A prerequisites task that all other tasks depend upon
12
+ task :prereqs
13
+
14
+ file PROJ.ann.file do
15
+ ann = PROJ.ann
16
+ puts "Generating #{ann.file}"
17
+ File.open(ann.file,'w') do |fd|
18
+ fd.puts("#{PROJ.name} version #{PROJ.version}")
19
+ fd.puts(" by #{Array(PROJ.authors).first}") if PROJ.authors
20
+ fd.puts(" #{PROJ.url}") if PROJ.url.valid?
21
+ fd.puts(" (the \"#{PROJ.release_name}\" release)") if PROJ.release_name
22
+ fd.puts
23
+ fd.puts("== DESCRIPTION")
24
+ fd.puts
25
+ fd.puts(PROJ.description)
26
+ fd.puts
27
+ fd.puts(PROJ.changes.sub(%r/^.*$/, '== CHANGES'))
28
+ fd.puts
29
+ ann.paragraphs.each do |p|
30
+ fd.puts "== #{p.upcase}"
31
+ fd.puts
32
+ fd.puts paragraphs_of(PROJ.readme_file, p).join("\n\n")
33
+ fd.puts
34
+ end
35
+ fd.puts ann.text if ann.text
36
+ end
37
+ end
38
+
39
+ desc "Create an announcement file"
40
+ task :announcement => ['ann:prereqs', PROJ.ann.file]
41
+
42
+ desc "Send an email announcement"
43
+ task :email => ['ann:prereqs', PROJ.ann.file] do
44
+ ann = PROJ.ann
45
+ from = ann.email[:from] || Array(PROJ.authors).first || PROJ.email
46
+ to = Array(ann.email[:to])
47
+
48
+ ### build a mail header for RFC 822
49
+ rfc822msg = "From: #{from}\n"
50
+ rfc822msg << "To: #{to.join(',')}\n"
51
+ rfc822msg << "Subject: [ANN] #{PROJ.name} #{PROJ.version}"
52
+ rfc822msg << " (#{PROJ.release_name})" if PROJ.release_name
53
+ rfc822msg << "\n"
54
+ rfc822msg << "Date: #{Time.new.rfc822}\n"
55
+ rfc822msg << "Message-Id: "
56
+ rfc822msg << "<#{"%.8f" % Time.now.to_f}@#{ann.email[:domain]}>\n\n"
57
+ rfc822msg << File.read(ann.file)
58
+
59
+ params = [:server, :port, :domain, :acct, :passwd, :authtype].map do |key|
60
+ ann.email[key]
61
+ end
62
+
63
+ params[3] = (PROJ.ann.email[:from] || PROJ.email) if params[3].nil?
64
+
65
+ if params[4].nil?
66
+ STDOUT.write "Please enter your e-mail password (#{params[3]}): "
67
+ params[4] = STDIN.gets.chomp
68
+ end
69
+
70
+ ### send email
71
+ Net::SMTP.start(*params) {|smtp| smtp.sendmail(rfc822msg, from, to)}
72
+ end
73
+ end # namespace :ann
74
+
75
+ desc 'Alias to ann:announcement'
76
+ task :ann => 'ann:announcement'
77
+
78
+ CLOBBER << PROJ.ann.file
79
+
80
+ # EOF
@@ -0,0 +1,25 @@
1
+ spec = Gem::Specification.new do |s|
2
+ s.name = PROJ.name
3
+ s.version = PROJ.version
4
+ s.platform = Gem::Platform::RUBY
5
+ s.has_rdoc = true
6
+ s.extra_rdoc_files = ["README.rdoc", "LICENSE"]
7
+ s.summary = PROJ.summary
8
+ s.description = PROJ.description
9
+ s.authors = Array(PROJ.authors)
10
+ s.email = PROJ.email
11
+ s.homepage = Array(PROJ.url).first
12
+ s.rubyforge_project = PROJ.rubyforge.name
13
+ s.extensions = %w(ext/ffi_c/extconf.rb gen/Rakefile)
14
+ s.require_path = 'lib'
15
+ s.files = PROJ.gem.files
16
+ s.add_dependency *PROJ.gem.dependencies.flatten
17
+ end
18
+
19
+ Rake::ExtensionTask.new('ffi_c', spec) do |ext|
20
+ ext.name = 'ffi_c' # indicate the name of the extension.
21
+ # ext.lib_dir = BUILD_DIR # put binaries into this folder.
22
+ ext.tmp_dir = BUILD_DIR # temporary folder used during compilation.
23
+ ext.cross_compile = true # enable cross compilation (requires cross compile toolchain)
24
+ ext.cross_platform = ['i386-mingw32', 'i386-mswin32'] # forces the Windows platform instead of the default one
25
+ end if USE_RAKE_COMPILER