ffi 0.1.1 → 0.2.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.
- data/Rakefile +52 -29
- data/ext/AbstractMemory.c +72 -28
- data/ext/AutoPointer.c +54 -0
- data/ext/AutoPointer.h +18 -0
- data/ext/Buffer.c +21 -17
- data/ext/Callback.c +81 -43
- data/ext/Callback.h +1 -1
- data/ext/Invoker.c +465 -108
- data/ext/MemoryPointer.c +25 -90
- data/ext/NativeLibrary.c +90 -0
- data/ext/NativeLibrary.h +22 -0
- data/ext/Platform.c +21 -2
- data/ext/Pointer.c +107 -0
- data/ext/Pointer.h +21 -0
- data/ext/Types.c +16 -5
- data/ext/Types.h +3 -1
- data/ext/compat.h +14 -0
- data/ext/extconf.rb +13 -1
- data/ext/ffi.c +11 -1
- data/ext/ffi.mk +3 -3
- data/ext/libffi.darwin.mk +19 -8
- data/gen/Rakefile +12 -0
- data/lib/ffi/autopointer.rb +61 -0
- data/lib/ffi/errno.rb +8 -0
- data/lib/ffi/ffi.rb +38 -201
- data/lib/ffi/io.rb +7 -0
- data/lib/ffi/library.rb +116 -0
- data/lib/ffi/managedstruct.rb +55 -0
- data/lib/ffi/memorypointer.rb +3 -96
- data/lib/ffi/platform.rb +8 -5
- data/lib/ffi/pointer.rb +105 -0
- data/lib/ffi/struct.rb +97 -42
- data/lib/ffi/tools/const_generator.rb +177 -0
- data/lib/ffi/tools/generator.rb +58 -0
- data/lib/ffi/tools/generator_task.rb +35 -0
- data/lib/ffi/tools/struct_generator.rb +194 -0
- data/lib/ffi/tools/types_generator.rb +123 -0
- data/lib/ffi/types.rb +150 -0
- data/lib/ffi/variadic.rb +30 -0
- data/nbproject/Makefile-Default.mk +6 -3
- data/nbproject/Makefile-impl.mk +5 -5
- data/nbproject/Package-Default.bash +72 -0
- data/nbproject/configurations.xml +139 -25
- data/nbproject/private/configurations.xml +1 -1
- data/nbproject/project.xml +4 -0
- data/samples/gettimeofday.rb +6 -2
- data/samples/inotify.rb +59 -0
- data/samples/pty.rb +75 -0
- data/specs/buffer_spec.rb +64 -9
- data/specs/callback_spec.rb +308 -4
- data/specs/errno_spec.rb +13 -0
- data/specs/library_spec.rb +55 -0
- data/specs/managed_struct_spec.rb +40 -0
- data/specs/number_spec.rb +183 -0
- data/specs/pointer_spec.rb +126 -0
- data/specs/rbx/memory_pointer_spec.rb +7 -7
- data/specs/spec_helper.rb +7 -0
- data/specs/string_spec.rb +34 -0
- data/specs/struct_spec.rb +223 -0
- data/specs/typedef_spec.rb +48 -0
- data/specs/variadic_spec.rb +84 -0
- metadata +270 -237
data/Rakefile
CHANGED
@@ -1,16 +1,19 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'rake/gempackagetask'
|
3
3
|
require 'rubygems/specification'
|
4
|
-
require "spec/rake/spectask"
|
5
4
|
require 'date'
|
6
5
|
require 'fileutils'
|
6
|
+
require 'rbconfig'
|
7
7
|
|
8
8
|
GEM = "ffi"
|
9
|
-
GEM_VERSION = "0.
|
9
|
+
GEM_VERSION = "0.2.0"
|
10
10
|
AUTHOR = "Wayne Meissner"
|
11
11
|
EMAIL = "wmeissner@gmail.com"
|
12
12
|
HOMEPAGE = "http://kenai.com/projects/ruby-ffi"
|
13
13
|
SUMMARY = "A Ruby foreign function interface (compatible with Rubinius and JRuby FFI)"
|
14
|
+
LIBEXT = Config::CONFIG['host_os'].downcase =~ /darwin/ ? "dylib" : "so"
|
15
|
+
GMAKE = Config::CONFIG['host_os'].downcase =~ /bsd/ ? "gmake" : "make"
|
16
|
+
LIBTEST = "build/libtest.#{LIBEXT}"
|
14
17
|
|
15
18
|
spec = Gem::Specification.new do |s|
|
16
19
|
s.name = GEM
|
@@ -24,41 +27,33 @@ spec = Gem::Specification.new do |s|
|
|
24
27
|
s.email = EMAIL
|
25
28
|
s.homepage = HOMEPAGE
|
26
29
|
s.rubyforge_project = 'ffi'
|
27
|
-
s.extensions = %w(ext/extconf.rb)
|
30
|
+
s.extensions = %w(ext/extconf.rb gen/Rakefile)
|
28
31
|
|
29
32
|
s.require_path = 'lib'
|
30
33
|
s.autorequire = GEM
|
31
34
|
s.files = %w(LICENSE README Rakefile) + Dir.glob("{ext,lib,nbproject,samples,specs}/**/*")
|
32
35
|
end
|
33
|
-
|
34
|
-
desc "Run all specs"
|
35
|
-
Spec::Rake::SpecTask.new("specs") do |t|
|
36
|
-
t.spec_opts = ["--format", "specdoc", "--colour"]
|
37
|
-
t.spec_files = Dir["spec/**/*_spec.rb"].sort
|
38
|
-
end
|
39
|
-
desc "Run rubinius specs"
|
40
|
-
Spec::Rake::SpecTask.new("rbxspecs") do |t|
|
41
|
-
t.spec_opts = ["--format", "specdoc", "--colour"]
|
42
|
-
t.spec_files = Dir["spec/rbx/*_spec.rb"].sort
|
43
|
-
end
|
44
|
-
|
36
|
+
TEST_DEPS = [ LIBTEST ]
|
45
37
|
if RUBY_PLATFORM == "java"
|
46
|
-
desc "Run specs"
|
47
|
-
task :specs do
|
38
|
+
desc "Run all specs"
|
39
|
+
task :specs => TEST_DEPS do
|
48
40
|
sh %{#{Gem.ruby} -S spec #{Dir["specs/**/*_spec.rb"].join(" ")} -fs --color}
|
49
41
|
end
|
50
|
-
|
42
|
+
desc "Run rubinius specs"
|
43
|
+
task :rbxspecs => TEST_DEPS do
|
51
44
|
sh %{#{Gem.ruby} -S spec #{Dir["specs/rbx/**/*_spec.rb"].join(" ")} -fs --color}
|
52
45
|
end
|
53
46
|
else
|
54
|
-
|
55
|
-
|
47
|
+
TEST_DEPS.unshift :compile
|
48
|
+
desc "Run all specs"
|
49
|
+
task :specs => TEST_DEPS do
|
56
50
|
ENV["MRI_FFI"] = "1"
|
57
|
-
sh %{#{Gem.ruby} -S spec #{Dir["specs/**/*_spec.rb"].join(" ")} -fs --color}
|
51
|
+
sh %{#{Gem.ruby} -Ibuild -Ilib -S spec #{Dir["specs/**/*_spec.rb"].join(" ")} -fs --color}
|
58
52
|
end
|
59
|
-
|
53
|
+
desc "Run rubinius specs"
|
54
|
+
task :rbxspecs => TEST_DEPS do
|
60
55
|
ENV["MRI_FFI"] = "1"
|
61
|
-
sh %{#{Gem.ruby} -S spec #{Dir["specs/rbx/**/*_spec.rb"].join(" ")} -fs --color}
|
56
|
+
sh %{#{Gem.ruby} -Ibuild -Ilib -S spec #{Dir["specs/rbx/**/*_spec.rb"].join(" ")} -fs --color}
|
62
57
|
end
|
63
58
|
end
|
64
59
|
|
@@ -77,14 +72,42 @@ task :make_spec do
|
|
77
72
|
file.puts spec.to_ruby
|
78
73
|
end
|
79
74
|
end
|
80
|
-
file "Makefile" do
|
81
|
-
|
75
|
+
file "build/Makefile" do
|
76
|
+
FileUtils.mkdir_p("build") unless File.directory?("build")
|
77
|
+
sh %{cd build && #{Gem.ruby} ../ext/extconf.rb}
|
82
78
|
end
|
83
|
-
|
84
|
-
|
79
|
+
desc "Compile the native module"
|
80
|
+
task :compile => "build/Makefile" do
|
81
|
+
sh %{cd build; make}
|
85
82
|
end
|
83
|
+
desc "Clean all built files"
|
86
84
|
task :clean do
|
87
|
-
sh %{make
|
85
|
+
sh %{cd build;make distclean} if File.exists?("build/Makefile")
|
88
86
|
FileUtils.rm_rf("build")
|
89
|
-
FileUtils.
|
87
|
+
FileUtils.rm_rf("conftest.dSYM")
|
88
|
+
FileUtils.rm_f(Dir["pkg/*.gem"])
|
89
|
+
FileUtils.rm_f("Makefile")
|
90
90
|
end
|
91
|
+
task "build/libtest.#{LIBEXT}" do
|
92
|
+
sh %{#{GMAKE} -f libtest/GNUmakefile}
|
93
|
+
end
|
94
|
+
|
95
|
+
desc "Test the extension"
|
96
|
+
task :test => [ :specs, :rbxspecs ] do
|
97
|
+
|
98
|
+
end
|
99
|
+
namespace :bench do
|
100
|
+
ITER = ENV['ITER'] ? ENV['ITER'].to_i : 100000
|
101
|
+
bench_libs = "-Ibuild -Ilib" unless RUBY_PLATFORM == "java"
|
102
|
+
bench_files = Dir["bench/bench_*.rb"].reject { |f| f == "bench_helper.rb" }
|
103
|
+
bench_files.each do |bench|
|
104
|
+
task File.basename(bench, ".rb")[6..-1] => TEST_DEPS do
|
105
|
+
sh %{#{Gem.ruby} #{bench_libs} #{bench} #{ITER}}
|
106
|
+
end
|
107
|
+
end
|
108
|
+
task :all => TEST_DEPS do
|
109
|
+
bench_files.each do |bench|
|
110
|
+
sh %{#{Gem.ruby} #{bench_libs} #{bench}}
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
data/ext/AbstractMemory.c
CHANGED
@@ -4,8 +4,9 @@
|
|
4
4
|
#include <stdbool.h>
|
5
5
|
#include <ruby.h>
|
6
6
|
#include "rbffi.h"
|
7
|
+
#include "compat.h"
|
7
8
|
#include "AbstractMemory.h"
|
8
|
-
#include "
|
9
|
+
#include "Pointer.h"
|
9
10
|
|
10
11
|
static VALUE memory_put_float32(VALUE self, VALUE offset, VALUE value);
|
11
12
|
static VALUE memory_get_float32(VALUE self, VALUE offset);
|
@@ -17,6 +18,7 @@ static VALUE memory_get_pointer(VALUE self, VALUE offset);
|
|
17
18
|
static inline caddr_t memory_address(VALUE self);
|
18
19
|
VALUE rb_FFI_AbstractMemory_class = Qnil;
|
19
20
|
static VALUE classMemory = Qnil;
|
21
|
+
static ID to_ptr = 0;
|
20
22
|
|
21
23
|
#define ADDRESS(self, offset) (memory_address((self)) + NUM2ULONG(offset))
|
22
24
|
#ifndef RARRAY_LEN
|
@@ -98,19 +100,29 @@ memory_put_pointer(VALUE self, VALUE offset, VALUE value)
|
|
98
100
|
{
|
99
101
|
AbstractMemory* memory = (AbstractMemory *) DATA_PTR(self);
|
100
102
|
long off = NUM2LONG(offset);
|
103
|
+
const int type = TYPE(value);
|
101
104
|
checkBounds(memory, off, sizeof(void *));
|
102
|
-
|
105
|
+
|
106
|
+
if (rb_obj_is_kind_of(value, rb_FFI_Pointer_class) && type == T_DATA) {
|
103
107
|
void* tmp = memory_address(value);
|
104
108
|
memcpy(memory->address + off, &tmp, sizeof(tmp));
|
105
|
-
} else if (
|
109
|
+
} else if (type == T_NIL) {
|
106
110
|
void* tmp = NULL;
|
107
111
|
memcpy(memory->address + off, &tmp, sizeof(tmp));
|
108
|
-
} else if (
|
112
|
+
} else if (type == T_FIXNUM) {
|
109
113
|
uintptr_t tmp = (uintptr_t) FIX2INT(value);
|
110
114
|
memcpy(memory->address + off, &tmp, sizeof(tmp));
|
111
|
-
} else if (
|
115
|
+
} else if (type == T_BIGNUM) {
|
112
116
|
uintptr_t tmp = (uintptr_t) NUM2ULL(value);
|
113
117
|
memcpy(memory->address + off, &tmp, sizeof(tmp));
|
118
|
+
} else if (rb_respond_to(value, to_ptr)) {
|
119
|
+
VALUE ptr = rb_funcall2(value, to_ptr, 0, NULL);
|
120
|
+
if (rb_obj_is_kind_of(ptr, rb_FFI_Pointer_class) && TYPE(ptr) == T_DATA) {
|
121
|
+
void* tmp = memory_address(ptr);
|
122
|
+
memcpy(memory->address + off, &tmp, sizeof(tmp));
|
123
|
+
} else {
|
124
|
+
rb_raise(rb_eArgError, "to_ptr returned an invalid pointer");
|
125
|
+
}
|
114
126
|
} else {
|
115
127
|
rb_raise(rb_eArgError, "value is not a pointer");
|
116
128
|
}
|
@@ -125,7 +137,7 @@ memory_get_pointer(VALUE self, VALUE offset)
|
|
125
137
|
caddr_t tmp;
|
126
138
|
checkBounds(memory, off, sizeof(tmp));
|
127
139
|
memcpy(&tmp, memory->address + off, sizeof(tmp));
|
128
|
-
return
|
140
|
+
return rb_FFI_Pointer_new(tmp);
|
129
141
|
}
|
130
142
|
|
131
143
|
static VALUE
|
@@ -148,44 +160,71 @@ memory_get_string(int argc, VALUE* argv, VALUE self)
|
|
148
160
|
VALUE length = Qnil, offset = Qnil;
|
149
161
|
AbstractMemory* ptr = (AbstractMemory *) DATA_PTR(self);
|
150
162
|
long off, len;
|
163
|
+
caddr_t end;
|
151
164
|
int nargs = rb_scan_args(argc, argv, "11", &offset, &length);
|
152
165
|
|
153
|
-
off = NUM2LONG(offset);
|
154
|
-
|
155
|
-
len = NUM2LONG(length);
|
156
|
-
} else {
|
157
|
-
caddr_t end;
|
158
|
-
checkBounds(ptr, off, 1);
|
159
|
-
end = memchr(ptr->address + off, 0, ptr->size - off);
|
160
|
-
len = ((end != NULL) ? end - ptr->address: ptr->size) - off;
|
161
|
-
}
|
166
|
+
off = NUM2LONG(offset);
|
167
|
+
len = nargs > 1 && length != Qnil ? NUM2LONG(length) : (ptr->size - off);
|
162
168
|
checkBounds(ptr, off, len);
|
163
|
-
|
169
|
+
end = memchr(ptr->address + off, 0, len);
|
170
|
+
return rb_tainted_str_new((char *) ptr->address + off,
|
171
|
+
(end != NULL ? end - ptr->address - off : len));
|
164
172
|
}
|
165
173
|
|
166
174
|
static VALUE
|
167
|
-
memory_put_string(
|
175
|
+
memory_put_string(VALUE self, VALUE offset, VALUE str)
|
168
176
|
{
|
169
177
|
AbstractMemory* ptr = (AbstractMemory *) DATA_PTR(self);
|
170
|
-
VALUE offset = Qnil, str = Qnil, length = Qnil;
|
171
|
-
bool nulTerminate = true;
|
172
178
|
long off, len;
|
173
|
-
|
179
|
+
|
174
180
|
off = NUM2LONG(offset);
|
175
181
|
len = RSTRING_LEN(str);
|
176
|
-
if (nargs > 2 && length != Qnil) {
|
177
|
-
len = MIN(NUM2ULONG(length), len);
|
178
|
-
nulTerminate = false;
|
179
|
-
}
|
180
182
|
checkBounds(ptr, off, len);
|
183
|
+
if (rb_safe_level() >= 1 && OBJ_TAINTED(str)) {
|
184
|
+
rb_raise(rb_eSecurityError, "Writing unsafe string to memory");
|
185
|
+
}
|
181
186
|
memcpy(ptr->address + off, RSTRING_PTR(str), len);
|
187
|
+
*((char *) ptr->address + off + len) = '\0';
|
188
|
+
return self;
|
189
|
+
}
|
190
|
+
|
191
|
+
static VALUE
|
192
|
+
memory_get_bytes(VALUE self, VALUE offset, VALUE length)
|
193
|
+
{
|
194
|
+
AbstractMemory* ptr = (AbstractMemory *) DATA_PTR(self);
|
195
|
+
long off, len;
|
196
|
+
|
197
|
+
off = NUM2LONG(offset);
|
198
|
+
len = NUM2LONG(length);
|
199
|
+
checkBounds(ptr, off, len);
|
200
|
+
return rb_tainted_str_new((char *) ptr->address + off, len);
|
201
|
+
}
|
202
|
+
|
203
|
+
static VALUE
|
204
|
+
memory_put_bytes(int argc, VALUE* argv, VALUE self)
|
205
|
+
{
|
206
|
+
AbstractMemory* ptr = (AbstractMemory *) DATA_PTR(self);
|
207
|
+
VALUE offset = Qnil, str = Qnil, rbIndex = Qnil, rbLength = Qnil;
|
208
|
+
long off, len, idx;
|
209
|
+
int nargs = rb_scan_args(argc, argv, "22", &offset, &str, &rbIndex, &rbLength);
|
182
210
|
|
183
|
-
|
184
|
-
|
185
|
-
|
211
|
+
off = NUM2LONG(offset);
|
212
|
+
idx = nargs > 2 ? NUM2LONG(rbIndex) : 0;
|
213
|
+
if (idx < 0) {
|
214
|
+
rb_raise(rb_eRangeError, "index canot be less than zero");
|
215
|
+
}
|
216
|
+
len = nargs > 3 ? NUM2LONG(rbLength) : (RSTRING_LEN(str) - idx);
|
217
|
+
if ((idx + len) > RSTRING_LEN(str)) {
|
218
|
+
rb_raise(rb_eRangeError, "index+length is greater than size of string");
|
219
|
+
}
|
220
|
+
checkBounds(ptr, off, len);
|
221
|
+
if (rb_safe_level() >= 1 && OBJ_TAINTED(str)) {
|
222
|
+
rb_raise(rb_eSecurityError, "Writing unsafe string to memory");
|
186
223
|
}
|
224
|
+
memcpy(ptr->address + off, RSTRING_PTR(str) + idx, len);
|
187
225
|
return self;
|
188
226
|
}
|
227
|
+
|
189
228
|
static inline caddr_t
|
190
229
|
memory_address(VALUE self)
|
191
230
|
{
|
@@ -250,8 +289,13 @@ rb_FFI_AbstractMemory_Init()
|
|
250
289
|
rb_define_method(classMemory, "put_pointer", memory_put_pointer, 2);
|
251
290
|
rb_define_method(classMemory, "get_pointer", memory_get_pointer, 1);
|
252
291
|
rb_define_method(classMemory, "get_string", memory_get_string, -1);
|
253
|
-
rb_define_method(classMemory, "put_string", memory_put_string,
|
292
|
+
rb_define_method(classMemory, "put_string", memory_put_string, 2);
|
293
|
+
rb_define_method(classMemory, "get_bytes", memory_get_bytes, 2);
|
294
|
+
rb_define_method(classMemory, "put_bytes", memory_put_bytes, -1);
|
295
|
+
|
254
296
|
rb_define_method(classMemory, "clear", memory_clear, 0);
|
255
297
|
rb_define_method(classMemory, "total", memory_size, 0);
|
298
|
+
|
299
|
+
to_ptr = rb_intern("to_ptr");
|
256
300
|
}
|
257
301
|
|
data/ext/AutoPointer.c
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
|
2
|
+
#include <stdbool.h>
|
3
|
+
#include <stdint.h>
|
4
|
+
#include <limits.h>
|
5
|
+
#include <ruby.h>
|
6
|
+
#include "rbffi.h"
|
7
|
+
#include "AbstractMemory.h"
|
8
|
+
#include "Pointer.h"
|
9
|
+
#include "AutoPointer.h"
|
10
|
+
|
11
|
+
typedef struct AutoPointer {
|
12
|
+
AbstractMemory memory;
|
13
|
+
VALUE parent;
|
14
|
+
} AutoPointer;
|
15
|
+
|
16
|
+
VALUE rb_FFI_AutoPointer_class;
|
17
|
+
static VALUE classAutoPointer = Qnil;
|
18
|
+
static void autoptr_mark(AutoPointer* ptr);
|
19
|
+
static void autoptr_free(AutoPointer* ptr);
|
20
|
+
|
21
|
+
static VALUE
|
22
|
+
autoptr_new(VALUE klass, VALUE other)
|
23
|
+
{
|
24
|
+
AutoPointer* p;
|
25
|
+
AbstractMemory* ptr;
|
26
|
+
VALUE retval;
|
27
|
+
|
28
|
+
retval = Data_Make_Struct(klass, AutoPointer, autoptr_mark, autoptr_free, p);
|
29
|
+
ptr = (AbstractMemory *) DATA_PTR(other);
|
30
|
+
p->memory = *ptr;
|
31
|
+
p->parent = other;
|
32
|
+
return retval;
|
33
|
+
}
|
34
|
+
|
35
|
+
static void
|
36
|
+
autoptr_mark(AutoPointer* ptr)
|
37
|
+
{
|
38
|
+
if (ptr->parent != Qnil) {
|
39
|
+
rb_gc_mark(ptr->parent);
|
40
|
+
}
|
41
|
+
}
|
42
|
+
static void
|
43
|
+
autoptr_free(AutoPointer* ptr)
|
44
|
+
{
|
45
|
+
xfree(ptr);
|
46
|
+
}
|
47
|
+
|
48
|
+
void
|
49
|
+
rb_FFI_AutoPointer_Init()
|
50
|
+
{
|
51
|
+
VALUE moduleFFI = rb_define_module("FFI");
|
52
|
+
rb_FFI_AutoPointer_class = classAutoPointer = rb_define_class_under(moduleFFI, "AutoPointer", rb_FFI_Pointer_class);
|
53
|
+
rb_define_singleton_method(classAutoPointer, "__alloc", autoptr_new, 1);
|
54
|
+
}
|
data/ext/AutoPointer.h
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
#ifndef _AUTOPOINTER_H
|
3
|
+
#define _AUTOPOINTER_H
|
4
|
+
|
5
|
+
#ifdef __cplusplus
|
6
|
+
extern "C" {
|
7
|
+
#endif
|
8
|
+
|
9
|
+
extern void rb_FFI_AutoPointer_Init(void);
|
10
|
+
extern VALUE rb_FFI_AutoPointer_class;
|
11
|
+
|
12
|
+
|
13
|
+
#ifdef __cplusplus
|
14
|
+
}
|
15
|
+
#endif
|
16
|
+
|
17
|
+
#endif /* _AUTOPOINTER_H */
|
18
|
+
|
data/ext/Buffer.c
CHANGED
@@ -7,6 +7,7 @@
|
|
7
7
|
|
8
8
|
typedef struct Buffer {
|
9
9
|
AbstractMemory memory;
|
10
|
+
caddr_t address; /* The C heap address */
|
10
11
|
VALUE parent;
|
11
12
|
} Buffer;
|
12
13
|
|
@@ -20,22 +21,24 @@ static VALUE
|
|
20
21
|
buffer_allocate(VALUE self, VALUE size, VALUE count, VALUE clear)
|
21
22
|
{
|
22
23
|
Buffer* p;
|
24
|
+
VALUE retval;
|
25
|
+
unsigned long msize = NUM2LONG(size) * (count == Qnil ? 1 : NUM2LONG(count));
|
26
|
+
caddr_t memory;
|
23
27
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
p->memory.address = p->memory.size > 0 ? malloc(p->memory.size) : NULL;
|
28
|
-
p->parent = Qnil;
|
29
|
-
|
30
|
-
if (p->memory.address == NULL) {
|
31
|
-
int size = p->memory.size;
|
32
|
-
xfree(p);
|
33
|
-
rb_raise(rb_eNoMemError, "Failed to allocate memory size=%u bytes", size);
|
28
|
+
memory = malloc(msize + 7);
|
29
|
+
if (memory == NULL) {
|
30
|
+
rb_raise(rb_eNoMemError, "Failed to allocate memory size=%lu bytes", msize);
|
34
31
|
}
|
35
|
-
|
32
|
+
retval = Data_Make_Struct(classBuffer, Buffer, buffer_mark, buffer_release, p);
|
33
|
+
p->address = memory;
|
34
|
+
p->memory.size = msize;
|
35
|
+
/* ensure the memory is aligned on at least a 8 byte boundary */
|
36
|
+
p->memory.address = (caddr_t) (((uintptr_t) memory + 0x7) & (uintptr_t) ~0x7UL);;
|
37
|
+
p->parent = Qnil;
|
38
|
+
if (TYPE(clear) == T_TRUE && p->memory.size > 0) {
|
36
39
|
memset(p->memory.address, 0, p->memory.size);
|
37
40
|
}
|
38
|
-
return
|
41
|
+
return retval;
|
39
42
|
}
|
40
43
|
|
41
44
|
static VALUE
|
@@ -43,15 +46,15 @@ buffer_plus(VALUE self, VALUE offset)
|
|
43
46
|
{
|
44
47
|
Buffer* ptr = (Buffer *) DATA_PTR(self);
|
45
48
|
Buffer* p;
|
49
|
+
VALUE retval;
|
46
50
|
long off = NUM2LONG(offset);
|
47
51
|
|
48
52
|
checkBounds(&ptr->memory, off, 1);
|
49
|
-
|
50
|
-
memset(p, 0, sizeof(*p));
|
53
|
+
retval = Data_Make_Struct(classBuffer, Buffer, buffer_mark, buffer_release, p);
|
51
54
|
p->memory.address = ptr->memory.address + off;;
|
52
55
|
p->memory.size = ptr->memory.size - off;
|
53
56
|
p->parent = self;
|
54
|
-
return
|
57
|
+
return retval;
|
55
58
|
}
|
56
59
|
|
57
60
|
static VALUE
|
@@ -66,8 +69,9 @@ buffer_inspect(VALUE self)
|
|
66
69
|
static void
|
67
70
|
buffer_release(Buffer* ptr)
|
68
71
|
{
|
69
|
-
if (ptr->parent
|
70
|
-
free(ptr->
|
72
|
+
if (ptr->parent == Qnil && ptr->address != NULL) {
|
73
|
+
free(ptr->address);
|
74
|
+
ptr->address = NULL;
|
71
75
|
}
|
72
76
|
xfree(ptr);
|
73
77
|
|
data/ext/Callback.c
CHANGED
@@ -1,50 +1,58 @@
|
|
1
|
+
#include <sys/param.h>
|
1
2
|
#include <sys/types.h>
|
3
|
+
#include <sys/mman.h>
|
2
4
|
#include <ruby.h>
|
3
|
-
#include
|
5
|
+
#include <ffi.h>
|
4
6
|
#include "AbstractMemory.h"
|
7
|
+
#include "Pointer.h"
|
8
|
+
#include "MemoryPointer.h"
|
5
9
|
#include "Callback.h"
|
6
10
|
#include "Types.h"
|
7
11
|
#include "rbffi.h"
|
12
|
+
#include "compat.h"
|
13
|
+
#include "extconf.h"
|
8
14
|
|
9
15
|
|
16
|
+
static void CallbackInfo_mark(CallbackInfo *);
|
17
|
+
static void CallbackInfo_free(CallbackInfo *);
|
10
18
|
|
11
|
-
|
12
|
-
static void
|
19
|
+
#if defined(HAVE_LIBFFI) && !defined(HAVE_FFI_CLOSURE_ALLOC)
|
20
|
+
static void* ffi_closure_alloc(size_t size, void** code);
|
21
|
+
static void ffi_closure_free(void* ptr);
|
22
|
+
ffi_status ffi_prep_closure_loc(ffi_closure* closure, ffi_cif* cif,
|
23
|
+
void (*fun)(ffi_cif*, void*, void**, void*),
|
24
|
+
void* user_data, void* code);
|
25
|
+
#endif /* HAVE_FFI_CLOSURE_ALLOC */
|
13
26
|
|
14
|
-
static VALUE
|
27
|
+
static VALUE classCallbackInfo = Qnil;
|
15
28
|
static VALUE classNativeCallback = Qnil;
|
16
29
|
static ID callID = Qnil;
|
17
30
|
|
18
|
-
|
19
|
-
VALUE rb_FFI_Callback_class = Qnil;
|
31
|
+
VALUE rb_FFI_CallbackInfo_class = Qnil;
|
20
32
|
|
21
33
|
static VALUE
|
22
|
-
|
34
|
+
CallbackInfo_new(VALUE klass, VALUE rbReturnType, VALUE rbParamTypes)
|
23
35
|
{
|
24
|
-
CallbackInfo *cbInfo
|
36
|
+
CallbackInfo *cbInfo;
|
37
|
+
VALUE retval;
|
25
38
|
int paramCount = RARRAY_LEN(rbParamTypes);
|
26
39
|
ffi_status status;
|
27
40
|
int i;
|
28
41
|
|
42
|
+
retval = Data_Make_Struct(klass, CallbackInfo, CallbackInfo_mark, CallbackInfo_free, cbInfo);
|
29
43
|
cbInfo->parameterCount = paramCount;
|
30
|
-
cbInfo->parameterTypes =
|
31
|
-
cbInfo->ffiParameterTypes =
|
32
|
-
if (cbInfo->parameterTypes == NULL || cbInfo->ffiParameterTypes == NULL) {
|
33
|
-
callback_free(cbInfo);
|
34
|
-
rb_raise(rb_eNoMemError, "Failed to allocate native memory");
|
35
|
-
}
|
44
|
+
cbInfo->parameterTypes = xcalloc(paramCount, sizeof(NativeType));
|
45
|
+
cbInfo->ffiParameterTypes = xcalloc(paramCount, sizeof(ffi_type *));
|
36
46
|
for (i = 0; i < paramCount; ++i) {
|
37
47
|
cbInfo->parameterTypes[i] = FIX2INT(rb_ary_entry(rbParamTypes, i));
|
38
48
|
cbInfo->ffiParameterTypes[i] = rb_FFI_NativeTypeToFFI(cbInfo->parameterTypes[i]);
|
39
49
|
if (cbInfo->ffiParameterTypes[i] == NULL) {
|
40
|
-
callback_free(cbInfo);
|
41
50
|
rb_raise(rb_eArgError, "Unknown argument type: %#x", cbInfo->parameterTypes[i]);
|
42
51
|
}
|
43
52
|
}
|
44
53
|
cbInfo->returnType = FIX2INT(rbReturnType);
|
45
54
|
cbInfo->ffiReturnType = rb_FFI_NativeTypeToFFI(cbInfo->returnType);
|
46
55
|
if (cbInfo->ffiReturnType == NULL) {
|
47
|
-
callback_free(cbInfo);
|
48
56
|
rb_raise(rb_eArgError, "Unknown return type: %#x", cbInfo->returnType);
|
49
57
|
}
|
50
58
|
#ifdef _WIN32
|
@@ -56,34 +64,33 @@ callback_new(VALUE self, VALUE rbReturnType, VALUE rbParamTypes)
|
|
56
64
|
cbInfo->ffiReturnType, cbInfo->ffiParameterTypes);
|
57
65
|
switch (status) {
|
58
66
|
case FFI_BAD_ABI:
|
59
|
-
callback_free(cbInfo);
|
60
67
|
rb_raise(rb_eArgError, "Invalid ABI specified");
|
61
68
|
case FFI_BAD_TYPEDEF:
|
62
|
-
callback_free(cbInfo);
|
63
69
|
rb_raise(rb_eArgError, "Invalid argument type specified");
|
64
70
|
case FFI_OK:
|
65
71
|
break;
|
66
72
|
default:
|
67
|
-
callback_free(cbInfo);
|
68
73
|
rb_raise(rb_eArgError, "Unknown FFI error");
|
69
74
|
}
|
70
|
-
return
|
75
|
+
return retval;
|
71
76
|
}
|
72
77
|
|
73
78
|
static void
|
74
|
-
|
79
|
+
CallbackInfo_mark(CallbackInfo* cbinfo)
|
75
80
|
{
|
76
81
|
}
|
77
82
|
|
78
83
|
static void
|
79
|
-
|
84
|
+
CallbackInfo_free(CallbackInfo* cbInfo)
|
80
85
|
{
|
81
86
|
if (cbInfo != NULL) {
|
82
87
|
if (cbInfo->parameterTypes != NULL) {
|
83
|
-
|
88
|
+
xfree(cbInfo->parameterTypes);
|
89
|
+
cbInfo->parameterTypes = NULL;
|
84
90
|
}
|
85
91
|
if (cbInfo->ffiParameterTypes != NULL) {
|
86
|
-
|
92
|
+
xfree(cbInfo->ffiParameterTypes);
|
93
|
+
cbInfo->ffiParameterTypes = NULL;
|
87
94
|
}
|
88
95
|
xfree(cbInfo);
|
89
96
|
}
|
@@ -120,19 +127,19 @@ native_callback_invoke(ffi_cif* cif, void* retval, void** parameters, void* user
|
|
120
127
|
VALUE param;
|
121
128
|
switch (cbInfo->parameterTypes[i]) {
|
122
129
|
case INT8:
|
123
|
-
param =
|
130
|
+
param = INT2NUM(*(int8_t *) parameters[i]);
|
124
131
|
break;
|
125
132
|
case UINT8:
|
126
133
|
param = UINT2NUM(*(u_int8_t *) parameters[i]);
|
127
134
|
break;
|
128
135
|
case INT16:
|
129
|
-
param =
|
136
|
+
param = INT2NUM(*(int16_t *) parameters[i]);
|
130
137
|
break;
|
131
138
|
case UINT16:
|
132
139
|
param = UINT2NUM(*(u_int16_t *) parameters[i]);
|
133
140
|
break;
|
134
141
|
case INT32:
|
135
|
-
param =
|
142
|
+
param = INT2NUM(*(int32_t *) parameters[i]);
|
136
143
|
break;
|
137
144
|
case UINT32:
|
138
145
|
param = UINT2NUM(*(u_int32_t *) parameters[i]);
|
@@ -150,10 +157,10 @@ native_callback_invoke(ffi_cif* cif, void* retval, void** parameters, void* user
|
|
150
157
|
param = rb_float_new(*(double *) parameters[i]);
|
151
158
|
break;
|
152
159
|
case STRING:
|
153
|
-
param =
|
160
|
+
param = rb_tainted_str_new2(*(char **) parameters[i]);
|
154
161
|
break;
|
155
162
|
case POINTER:
|
156
|
-
param =
|
163
|
+
param = rb_FFI_Pointer_new(*(caddr_t *) parameters[i]);
|
157
164
|
break;
|
158
165
|
default:
|
159
166
|
param = Qnil;
|
@@ -166,22 +173,14 @@ native_callback_invoke(ffi_cif* cif, void* retval, void** parameters, void* user
|
|
166
173
|
memset(retval, 0, cbInfo->ffiReturnType->size);
|
167
174
|
} else switch (cbInfo->returnType) {
|
168
175
|
case INT8:
|
169
|
-
*((int8_t *) retval) = NUM2INT(rbReturnValue);
|
170
|
-
break;
|
171
|
-
case UINT8:
|
172
|
-
*((u_int8_t *) retval) = NUM2UINT(rbReturnValue);
|
173
|
-
break;
|
174
176
|
case INT16:
|
175
|
-
*((int16_t *) retval) = NUM2INT(rbReturnValue);
|
176
|
-
break;
|
177
|
-
case UINT16:
|
178
|
-
*((u_int16_t *) retval) = NUM2UINT(rbReturnValue);
|
179
|
-
break;
|
180
177
|
case INT32:
|
181
|
-
*((
|
178
|
+
*((long *) retval) = NUM2INT(rbReturnValue);
|
182
179
|
break;
|
180
|
+
case UINT8:
|
181
|
+
case UINT16:
|
183
182
|
case UINT32:
|
184
|
-
*((
|
183
|
+
*((unsigned long *) retval) = NUM2UINT(rbReturnValue);
|
185
184
|
break;
|
186
185
|
case INT64:
|
187
186
|
*((int64_t *) retval) = NUM2LL(rbReturnValue);
|
@@ -229,12 +228,51 @@ rb_FFI_NativeCallback_new(VALUE rbCallbackInfo, VALUE rbProc)
|
|
229
228
|
return Data_Wrap_Struct(classNativeCallback, native_callback_mark, native_callback_free, closure);
|
230
229
|
}
|
231
230
|
|
231
|
+
#if defined(HAVE_LIBFFI) && !defined(HAVE_FFI_CLOSURE_ALLOC)
|
232
|
+
/*
|
233
|
+
* versions of ffi_closure_alloc, ffi_closure_free and ffi_prep_closure_loc for older
|
234
|
+
* system libffi versions.
|
235
|
+
*/
|
236
|
+
static void*
|
237
|
+
ffi_closure_alloc(size_t size, void** code)
|
238
|
+
{
|
239
|
+
void* closure;
|
240
|
+
closure = mmap(NULL, size, PROT_READ | PROT_WRITE,
|
241
|
+
MAP_ANON | MAP_PRIVATE, -1, 0);
|
242
|
+
if (closure == (void *) -1) {
|
243
|
+
return NULL;
|
244
|
+
}
|
245
|
+
memset(closure, 0, size);
|
246
|
+
*code = closure;
|
247
|
+
return closure;
|
248
|
+
}
|
249
|
+
|
250
|
+
static void
|
251
|
+
ffi_closure_free(void* ptr)
|
252
|
+
{
|
253
|
+
munmap(ptr, sizeof(ffi_closure));
|
254
|
+
}
|
255
|
+
|
256
|
+
ffi_status
|
257
|
+
ffi_prep_closure_loc(ffi_closure* closure, ffi_cif* cif,
|
258
|
+
void (*fun)(ffi_cif*, void*, void**, void*),
|
259
|
+
void* user_data, void* code)
|
260
|
+
{
|
261
|
+
ffi_status retval = ffi_prep_closure(closure, cif, fun, user_data);
|
262
|
+
if (retval == FFI_OK) {
|
263
|
+
mprotect(closure, sizeof(ffi_closure), PROT_READ | PROT_EXEC);
|
264
|
+
}
|
265
|
+
return retval;
|
266
|
+
}
|
267
|
+
|
268
|
+
#endif /* HAVE_FFI_CLOSURE_ALLOC */
|
269
|
+
|
232
270
|
void
|
233
271
|
rb_FFI_Callback_Init()
|
234
272
|
{
|
235
273
|
VALUE moduleFFI = rb_define_module("FFI");
|
236
|
-
|
237
|
-
rb_define_singleton_method(
|
274
|
+
rb_FFI_CallbackInfo_class = classCallbackInfo = rb_define_class_under(moduleFFI, "CallbackInfo", rb_cObject);
|
275
|
+
rb_define_singleton_method(classCallbackInfo, "new", CallbackInfo_new, 2);
|
238
276
|
classNativeCallback = rb_define_class_under(moduleFFI, "NativeCallback", rb_cObject);
|
239
277
|
callID = rb_intern("call");
|
240
278
|
}
|