ffi 0.3.0 → 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 +47 -28
- data/ext/ffi_c/AbstractMemory.c +149 -65
- data/ext/ffi_c/AbstractMemory.h +34 -4
- data/ext/ffi_c/AutoPointer.c +15 -18
- data/ext/ffi_c/AutoPointer.h +2 -2
- data/ext/ffi_c/Buffer.c +79 -44
- data/ext/ffi_c/Callback.c +133 -62
- data/ext/ffi_c/Callback.h +11 -5
- data/ext/ffi_c/{NativeLibrary.c → DynamicLibrary.c} +88 -24
- data/ext/ffi_c/{NativeLibrary.h → DynamicLibrary.h} +1 -1
- data/ext/ffi_c/Invoker.c +154 -190
- data/ext/ffi_c/LastError.c +135 -0
- data/ext/ffi_c/LastError.h +18 -0
- data/ext/ffi_c/MemoryPointer.c +30 -20
- data/ext/ffi_c/MemoryPointer.h +3 -3
- data/ext/ffi_c/NullPointer.c +67 -28
- data/ext/ffi_c/Platform.c +9 -10
- data/ext/ffi_c/Platform.h +1 -1
- data/ext/ffi_c/Pointer.c +67 -30
- data/ext/ffi_c/Pointer.h +8 -6
- data/ext/ffi_c/Struct.c +202 -267
- 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
data/README.rdoc
CHANGED
|
@@ -4,16 +4,66 @@ ruby-ffi
|
|
|
4
4
|
|
|
5
5
|
== DESCRIPTION:
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Ruby-FFI is a ruby extension for programmatically loading dynamic
|
|
8
|
+
libraries, binding functions within them, and calling those functions
|
|
9
|
+
from Ruby code. Moreover, a Ruby-FFI extension works without changes
|
|
10
|
+
on Ruby and JRuby. Discover why should you write your next extension
|
|
11
|
+
using Ruby-FFI {here}[http://kenai.com/projects/ruby-ffi/pages/WhyUseFFI].
|
|
8
12
|
|
|
9
13
|
== FEATURES/PROBLEMS:
|
|
10
14
|
|
|
15
|
+
* It has a very intuitive DSL
|
|
16
|
+
* It supports all C native types
|
|
17
|
+
* It supports C structs (also nested), enums and global variables
|
|
18
|
+
* It supports callbacks
|
|
19
|
+
* It has smart methods to handle memory management of pointers and structs
|
|
20
|
+
|
|
11
21
|
== SYNOPSIS:
|
|
12
22
|
|
|
23
|
+
require 'ffi'
|
|
24
|
+
|
|
25
|
+
module MyLib
|
|
26
|
+
extend FFI::Library
|
|
27
|
+
attach_function :puts, [ :string ], :int
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
MyLib.puts 'Hello boys using libc!'
|
|
31
|
+
|
|
32
|
+
For less minimalistic and more sane examples you may look at:
|
|
33
|
+
|
|
34
|
+
* the samples/ folder
|
|
35
|
+
* the examples on the Kenai {wiki}[http://kenai.com/projects/ruby-ffi/pages/Home]
|
|
36
|
+
* the projects using FFI listed on this {page}[http://kenai.com/projects/ruby-ffi/pages/ProjectsUsingFFI]
|
|
37
|
+
|
|
13
38
|
== REQUIREMENTS:
|
|
14
39
|
|
|
40
|
+
* You need a sane building environment in order to compile the extension.
|
|
41
|
+
|
|
15
42
|
== DOWNLOAD/INSTALL:
|
|
16
43
|
|
|
44
|
+
From rubyforge:
|
|
45
|
+
|
|
46
|
+
[sudo] gem install ffi
|
|
47
|
+
|
|
48
|
+
or from the mercurial repository on Kenai:
|
|
49
|
+
|
|
50
|
+
hg clone https://kenai.com/hg/ruby-ffi~mercurial ruby-ffi
|
|
51
|
+
cd ruby-ffi
|
|
52
|
+
rake gem:install
|
|
53
|
+
|
|
54
|
+
== CREDITS:
|
|
55
|
+
|
|
56
|
+
Special thanks to:
|
|
57
|
+
|
|
58
|
+
Yehuda Katz
|
|
59
|
+
Luc Heinrich
|
|
60
|
+
Andrea Fazzi
|
|
61
|
+
Mike Dalessio
|
|
62
|
+
Hongli Lai
|
|
63
|
+
Evan Phoenix
|
|
64
|
+
Aman Gupta
|
|
65
|
+
Beoran
|
|
66
|
+
|
|
17
67
|
== LICENSE:
|
|
18
68
|
|
|
19
69
|
See LICENSE file.
|
data/Rakefile
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
require 'rubygems'
|
|
2
|
+
require 'rbconfig'
|
|
2
3
|
|
|
3
|
-
USE_RAKE_COMPILER = (
|
|
4
|
+
USE_RAKE_COMPILER = (RUBY_PLATFORM =~ /java/) ? false : true
|
|
4
5
|
if USE_RAKE_COMPILER
|
|
5
|
-
gem 'rake-compiler', '>=0.
|
|
6
|
+
gem 'rake-compiler', '>=0.6.0'
|
|
6
7
|
require 'rake/extensiontask'
|
|
8
|
+
ENV['RUBY_CC_VERSION'] = '1.8.6:1.9.1'
|
|
7
9
|
end
|
|
8
10
|
|
|
9
11
|
require 'date'
|
|
@@ -24,34 +26,44 @@ end
|
|
|
24
26
|
LIBEXT = Config::CONFIG['host_os'].downcase =~ /darwin/ ? "dylib" : "so"
|
|
25
27
|
GMAKE = Config::CONFIG['host_os'].downcase =~ /bsd/ ? "gmake" : "make"
|
|
26
28
|
LIBTEST = "build/libtest.#{LIBEXT}"
|
|
27
|
-
BUILD_DIR = "build
|
|
29
|
+
BUILD_DIR = "build"
|
|
30
|
+
BUILD_EXT_DIR = File.join(BUILD_DIR, "#{Config::CONFIG['arch']}", 'ffi_c', RUBY_VERSION)
|
|
28
31
|
|
|
29
32
|
# Project general information
|
|
30
33
|
PROJ.name = 'ffi'
|
|
31
34
|
PROJ.authors = 'Wayne Meissner'
|
|
32
35
|
PROJ.email = 'wmeissner@gmail.com'
|
|
33
36
|
PROJ.url = 'http://kenai.com/projects/ruby-ffi'
|
|
34
|
-
PROJ.version = '0.
|
|
37
|
+
PROJ.version = '0.4.0'
|
|
35
38
|
PROJ.rubyforge.name = 'ffi'
|
|
36
|
-
|
|
37
39
|
PROJ.readme_file = 'README.rdoc'
|
|
38
40
|
|
|
39
41
|
# Annoucement
|
|
40
|
-
PROJ.ann.paragraphs
|
|
41
|
-
|
|
42
|
+
PROJ.ann.paragraphs << 'FEATURES' << 'SYNOPSIS' << 'REQUIREMENTS' << 'DOWNLOAD/INSTALL' << 'CREDITS' << 'LICENSE'
|
|
43
|
+
|
|
44
|
+
PROJ.ann.email[:from] = 'andrea.fazzi@alcacoop.it'
|
|
42
45
|
PROJ.ann.email[:to] << 'dev@ruby-ffi.kenai.com' << 'users@ruby-ffi.kenai.com'
|
|
43
46
|
PROJ.ann.email[:server] = 'smtp.gmail.com'
|
|
44
47
|
|
|
45
48
|
# Gem specifications
|
|
46
49
|
PROJ.gem.need_tar = false
|
|
47
|
-
PROJ.gem.files = %w(LICENSE README.rdoc Rakefile) + Dir.glob("{ext,lib,
|
|
50
|
+
PROJ.gem.files = %w(LICENSE README.rdoc Rakefile) + Dir.glob("{ext,gen,lib,spec}/**/*")
|
|
48
51
|
PROJ.gem.platform = Gem::Platform::RUBY
|
|
49
|
-
|
|
52
|
+
|
|
53
|
+
# Override Mr. Bones autogenerated extensions and force ours in
|
|
54
|
+
PROJ.gem.extras['extensions'] = %w(ext/ffi_c/extconf.rb gen/Rakefile)
|
|
50
55
|
|
|
51
56
|
# RDoc
|
|
52
57
|
PROJ.rdoc.exclude << '^ext\/'
|
|
53
58
|
PROJ.rdoc.opts << '-x' << 'ext'
|
|
54
59
|
|
|
60
|
+
# Ruby
|
|
61
|
+
PROJ.ruby_opts = []
|
|
62
|
+
PROJ.ruby_opts << '-I' << BUILD_EXT_DIR unless RUBY_PLATFORM == "java"
|
|
63
|
+
|
|
64
|
+
#RSpec
|
|
65
|
+
PROJ.spec.opts << '--color' << '-fs'
|
|
66
|
+
|
|
55
67
|
TEST_DEPS = [ LIBTEST ]
|
|
56
68
|
if RUBY_PLATFORM == "java"
|
|
57
69
|
desc "Run all specs"
|
|
@@ -67,12 +79,12 @@ else
|
|
|
67
79
|
desc "Run all specs"
|
|
68
80
|
task :specs => TEST_DEPS do
|
|
69
81
|
ENV["MRI_FFI"] = "1"
|
|
70
|
-
sh %{#{Gem.ruby} -Ilib -I#{
|
|
82
|
+
sh %{#{Gem.ruby} -Ilib -I#{BUILD_EXT_DIR} -S spec #{Dir["spec/ffi/*_spec.rb"].join(" ")} -fs --color}
|
|
71
83
|
end
|
|
72
84
|
desc "Run rubinius specs"
|
|
73
85
|
task :rbxspecs => TEST_DEPS do
|
|
74
86
|
ENV["MRI_FFI"] = "1"
|
|
75
|
-
sh %{#{Gem.ruby} -Ilib -I#{
|
|
87
|
+
sh %{#{Gem.ruby} -Ilib -I#{BUILD_EXT_DIR} -S spec #{Dir["spec/ffi/rbx/*_spec.rb"].join(" ")} -fs --color}
|
|
76
88
|
end
|
|
77
89
|
end
|
|
78
90
|
|
|
@@ -82,30 +94,31 @@ task :package => 'gem:package'
|
|
|
82
94
|
desc "Install the gem locally"
|
|
83
95
|
task :install => 'gem:install'
|
|
84
96
|
|
|
85
|
-
|
|
86
|
-
file "#{BUILD_DIR}/Makefile" do
|
|
87
|
-
FileUtils.mkdir_p(BUILD_DIR) unless File.directory?(BUILD_DIR)
|
|
88
|
-
sh %{cd "#{BUILD_DIR}" && #{Gem.ruby} #{Dir.pwd}/ext/ffi_c/extconf.rb}
|
|
89
|
-
end
|
|
90
|
-
desc "Compile the native module"
|
|
91
|
-
task :compile => "#{BUILD_DIR}/Makefile" do
|
|
92
|
-
sh %{cd "#{BUILD_DIR}"; make}
|
|
93
|
-
end
|
|
94
|
-
end
|
|
97
|
+
|
|
95
98
|
desc "Clean all built files"
|
|
96
|
-
task :
|
|
97
|
-
FileUtils.rm_rf(
|
|
98
|
-
FileUtils.rm_rf(
|
|
99
|
-
FileUtils.
|
|
99
|
+
task :distclean => :clobber do
|
|
100
|
+
FileUtils.rm_rf('build')
|
|
101
|
+
FileUtils.rm_rf(Dir['lib/**/ffi_c.so'])
|
|
102
|
+
FileUtils.rm_rf('lib/1.8')
|
|
103
|
+
FileUtils.rm_rf('lib/1.9')
|
|
104
|
+
FileUtils.rm_rf('conftest.dSYM')
|
|
105
|
+
FileUtils.rm_rf('pkg')
|
|
100
106
|
end
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
desc "Build the native test lib"
|
|
101
110
|
task "build/libtest.#{LIBEXT}" do
|
|
102
|
-
sh %{#{GMAKE} -f libtest/GNUmakefile}
|
|
111
|
+
sh %{#{GMAKE} -f libtest/GNUmakefile CPU=#{Config::CONFIG['host_cpu']}}
|
|
103
112
|
end
|
|
113
|
+
|
|
114
|
+
|
|
104
115
|
desc "Build test helper lib"
|
|
105
116
|
task :libtest => "build/libtest.#{LIBEXT}"
|
|
117
|
+
|
|
106
118
|
desc "Test the extension"
|
|
107
|
-
task :test => [ :specs, :rbxspecs ]
|
|
108
|
-
|
|
119
|
+
task :test => [ :specs, :rbxspecs ]
|
|
120
|
+
|
|
121
|
+
|
|
109
122
|
namespace :bench do
|
|
110
123
|
ITER = ENV['ITER'] ? ENV['ITER'].to_i : 100000
|
|
111
124
|
bench_libs = "-Ilib -I#{BUILD_DIR}" unless RUBY_PLATFORM == "java"
|
|
@@ -121,3 +134,9 @@ namespace :bench do
|
|
|
121
134
|
end
|
|
122
135
|
end
|
|
123
136
|
end
|
|
137
|
+
|
|
138
|
+
task 'spec:run' => TEST_DEPS
|
|
139
|
+
task 'spec:specdoc' => TEST_DEPS
|
|
140
|
+
|
|
141
|
+
task :default => :specs
|
|
142
|
+
|
data/ext/ffi_c/AbstractMemory.c
CHANGED
|
@@ -9,41 +9,60 @@
|
|
|
9
9
|
#include "Pointer.h"
|
|
10
10
|
#include "Callback.h"
|
|
11
11
|
|
|
12
|
-
static VALUE memory_put_float32(VALUE self, VALUE offset, VALUE value);
|
|
13
|
-
static VALUE memory_get_float32(VALUE self, VALUE offset);
|
|
14
|
-
static VALUE memory_put_float64(VALUE self, VALUE offset, VALUE value);
|
|
15
|
-
static VALUE memory_get_float64(VALUE self, VALUE offset);
|
|
16
|
-
static VALUE memory_put_pointer(VALUE self, VALUE offset, VALUE value);
|
|
17
|
-
static VALUE memory_get_pointer(VALUE self, VALUE offset);
|
|
18
12
|
|
|
19
13
|
static inline char* memory_address(VALUE self);
|
|
20
|
-
VALUE
|
|
21
|
-
static
|
|
22
|
-
static ID
|
|
14
|
+
VALUE rbffi_AbstractMemoryClass = Qnil;
|
|
15
|
+
static ID id_to_ptr = 0;
|
|
16
|
+
static ID id_call = 0;
|
|
17
|
+
|
|
18
|
+
static VALUE
|
|
19
|
+
memory_allocate(VALUE klass)
|
|
20
|
+
{
|
|
21
|
+
AbstractMemory* memory;
|
|
22
|
+
VALUE obj;
|
|
23
|
+
obj = Data_Make_Struct(klass, AbstractMemory, NULL, -1, memory);
|
|
24
|
+
memory->ops = &rbffi_AbstractMemoryOps;
|
|
25
|
+
|
|
26
|
+
return obj;
|
|
27
|
+
}
|
|
23
28
|
|
|
24
29
|
#define NUM_OP(name, type, toNative, fromNative) \
|
|
25
|
-
static
|
|
26
|
-
static
|
|
27
|
-
|
|
30
|
+
static void memory_op_put_##name(AbstractMemory* memory, long off, VALUE value); \
|
|
31
|
+
static void \
|
|
32
|
+
memory_op_put_##name(AbstractMemory* memory, long off, VALUE value) \
|
|
28
33
|
{ \
|
|
29
|
-
long off = NUM2LONG(offset); \
|
|
30
|
-
AbstractMemory* memory = MEMORY(self); \
|
|
31
34
|
type tmp = (type) toNative(value); \
|
|
32
35
|
checkBounds(memory, off, sizeof(type)); \
|
|
33
36
|
memcpy(memory->address + off, &tmp, sizeof(tmp)); \
|
|
37
|
+
} \
|
|
38
|
+
static VALUE memory_put_##name(VALUE self, VALUE offset, VALUE value); \
|
|
39
|
+
static VALUE \
|
|
40
|
+
memory_put_##name(VALUE self, VALUE offset, VALUE value) \
|
|
41
|
+
{ \
|
|
42
|
+
AbstractMemory* memory; \
|
|
43
|
+
Data_Get_Struct(self, AbstractMemory, memory); \
|
|
44
|
+
memory_op_put_##name(memory, NUM2LONG(offset), value); \
|
|
34
45
|
return self; \
|
|
35
46
|
} \
|
|
36
|
-
static VALUE
|
|
47
|
+
static VALUE memory_op_get_##name(AbstractMemory* memory, long off); \
|
|
37
48
|
static VALUE \
|
|
38
|
-
|
|
49
|
+
memory_op_get_##name(AbstractMemory* memory, long off) \
|
|
39
50
|
{ \
|
|
40
|
-
long off = NUM2LONG(offset); \
|
|
41
|
-
AbstractMemory* memory = MEMORY(self); \
|
|
42
51
|
type tmp; \
|
|
43
52
|
checkBounds(memory, off, sizeof(type)); \
|
|
44
53
|
memcpy(&tmp, memory->address + off, sizeof(tmp)); \
|
|
45
54
|
return fromNative(tmp); \
|
|
46
55
|
} \
|
|
56
|
+
static VALUE memory_get_##name(VALUE self, VALUE offset); \
|
|
57
|
+
static VALUE \
|
|
58
|
+
memory_get_##name(VALUE self, VALUE offset) \
|
|
59
|
+
{ \
|
|
60
|
+
AbstractMemory* memory; \
|
|
61
|
+
Data_Get_Struct(self, AbstractMemory, memory); \
|
|
62
|
+
return memory_op_get_##name(memory, NUM2LONG(offset)); \
|
|
63
|
+
} \
|
|
64
|
+
static MemoryOp memory_op_##name = { memory_op_get_##name, memory_op_put_##name }; \
|
|
65
|
+
\
|
|
47
66
|
static VALUE memory_put_array_of_##name(VALUE self, VALUE offset, VALUE ary); \
|
|
48
67
|
static VALUE \
|
|
49
68
|
memory_put_array_of_##name(VALUE self, VALUE offset, VALUE ary) \
|
|
@@ -88,46 +107,26 @@ NUM_OP(uint64, uint64_t, NUM2ULL, ULL2NUM);
|
|
|
88
107
|
NUM_OP(float32, float, NUM2DBL, rb_float_new);
|
|
89
108
|
NUM_OP(float64, double, NUM2DBL, rb_float_new);
|
|
90
109
|
|
|
91
|
-
static
|
|
92
|
-
|
|
93
|
-
{
|
|
94
|
-
AbstractMemory* memory = MEMORY(self);
|
|
95
|
-
long off = NUM2LONG(offset);
|
|
110
|
+
static inline void*
|
|
111
|
+
get_pointer_value(VALUE value)
|
|
112
|
+
{
|
|
96
113
|
const int type = TYPE(value);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if (rb_obj_is_kind_of(value, rb_FFI_Pointer_class) && type == T_DATA) {
|
|
100
|
-
void* tmp = memory_address(value);
|
|
101
|
-
memcpy(memory->address + off, &tmp, sizeof(tmp));
|
|
114
|
+
if (type == T_DATA && rb_obj_is_kind_of(value, rbffi_PointerClass)) {
|
|
115
|
+
return memory_address(value);
|
|
102
116
|
} else if (type == T_NIL) {
|
|
103
|
-
|
|
104
|
-
memcpy(memory->address + off, &tmp, sizeof(tmp));
|
|
117
|
+
return NULL;
|
|
105
118
|
} else if (type == T_FIXNUM) {
|
|
106
|
-
|
|
107
|
-
memcpy(memory->address + off, &tmp, sizeof(tmp));
|
|
119
|
+
return (void *) (uintptr_t) FIX2INT(value);
|
|
108
120
|
} else if (type == T_BIGNUM) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
VALUE ptr = rb_funcall2(value, to_ptr, 0, NULL);
|
|
113
|
-
void* tmp = MEMORY_PTR(ptr);
|
|
114
|
-
memcpy(memory->address + off, &tmp, sizeof(tmp));
|
|
121
|
+
return (void *) (uintptr_t) NUM2ULL(value);
|
|
122
|
+
} else if (rb_respond_to(value, id_to_ptr)) {
|
|
123
|
+
return MEMORY_PTR(rb_funcall2(value, id_to_ptr, 0, NULL));
|
|
115
124
|
} else {
|
|
116
125
|
rb_raise(rb_eArgError, "value is not a pointer");
|
|
117
126
|
}
|
|
118
|
-
return self;
|
|
119
127
|
}
|
|
120
128
|
|
|
121
|
-
|
|
122
|
-
memory_get_pointer(VALUE self, VALUE offset)
|
|
123
|
-
{
|
|
124
|
-
AbstractMemory* memory = MEMORY(self);
|
|
125
|
-
long off = NUM2LONG(offset);
|
|
126
|
-
void* tmp;
|
|
127
|
-
checkBounds(memory, off, sizeof(tmp));
|
|
128
|
-
memcpy(&tmp, memory->address + off, sizeof(tmp));
|
|
129
|
-
return rb_FFI_Pointer_new(tmp);
|
|
130
|
-
}
|
|
129
|
+
NUM_OP(pointer, void *, get_pointer_value, rbffi_Pointer_NewInstance);
|
|
131
130
|
|
|
132
131
|
static VALUE
|
|
133
132
|
memory_put_callback(VALUE self, VALUE offset, VALUE proc, VALUE cbInfo)
|
|
@@ -136,8 +135,8 @@ memory_put_callback(VALUE self, VALUE offset, VALUE proc, VALUE cbInfo)
|
|
|
136
135
|
long off = NUM2LONG(offset);
|
|
137
136
|
checkBounds(memory, off, sizeof(void *));
|
|
138
137
|
|
|
139
|
-
if (rb_obj_is_kind_of(proc, rb_cProc)) {
|
|
140
|
-
VALUE callback =
|
|
138
|
+
if (rb_obj_is_kind_of(proc, rb_cProc) || rb_respond_to(proc, id_call)) {
|
|
139
|
+
VALUE callback = rbffi_NativeCallback_ForProc(proc, cbInfo);
|
|
141
140
|
void* code = ((NativeCallback *) DATA_PTR(callback))->code;
|
|
142
141
|
memcpy(memory->address + off, &code, sizeof(code));
|
|
143
142
|
} else {
|
|
@@ -178,15 +177,56 @@ memory_get_string(int argc, VALUE* argv, VALUE self)
|
|
|
178
177
|
(end != NULL ? end - ptr->address - off : len));
|
|
179
178
|
}
|
|
180
179
|
|
|
180
|
+
static VALUE
|
|
181
|
+
memory_get_array_of_string(int argc, VALUE* argv, VALUE self)
|
|
182
|
+
{
|
|
183
|
+
VALUE offset = Qnil, countnum = Qnil, retVal = Qnil;
|
|
184
|
+
AbstractMemory* ptr;
|
|
185
|
+
long off;
|
|
186
|
+
int count;
|
|
187
|
+
|
|
188
|
+
rb_scan_args(argc, argv, "11", &offset, &countnum);
|
|
189
|
+
off = NUM2LONG(offset);
|
|
190
|
+
count = (countnum == Qnil ? 0 : NUM2INT(countnum));
|
|
191
|
+
retVal = rb_ary_new2(count);
|
|
192
|
+
|
|
193
|
+
Data_Get_Struct(self, AbstractMemory, ptr);
|
|
194
|
+
|
|
195
|
+
if (countnum != Qnil) {
|
|
196
|
+
int i;
|
|
197
|
+
|
|
198
|
+
checkBounds(ptr, off, count * sizeof (char*));
|
|
199
|
+
|
|
200
|
+
for (i = 0; i < count; ++i) {
|
|
201
|
+
const char* strptr = *((const char**) (ptr->address + off) + i);
|
|
202
|
+
rb_ary_push(retVal, (strptr == NULL ? Qnil : rb_tainted_str_new2(strptr)));
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
} else {
|
|
206
|
+
checkBounds(ptr, off, sizeof (char*));
|
|
207
|
+
for ( ; off < ptr->size - sizeof (void *); off += sizeof (void *)) {
|
|
208
|
+
const char* strptr = *(const char**) (ptr->address + off);
|
|
209
|
+
if (strptr == NULL) {
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
rb_ary_push(retVal, rb_tainted_str_new2(strptr));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return retVal;
|
|
217
|
+
}
|
|
218
|
+
|
|
181
219
|
static VALUE
|
|
182
220
|
memory_put_string(VALUE self, VALUE offset, VALUE str)
|
|
183
221
|
{
|
|
184
222
|
AbstractMemory* ptr = MEMORY(self);
|
|
185
223
|
long off, len;
|
|
186
224
|
|
|
225
|
+
Check_Type(str, T_STRING);
|
|
187
226
|
off = NUM2LONG(offset);
|
|
188
227
|
len = RSTRING_LEN(str);
|
|
189
|
-
|
|
228
|
+
|
|
229
|
+
checkBounds(ptr, off, len + 1);
|
|
190
230
|
if (rb_safe_level() >= 1 && OBJ_TAINTED(str)) {
|
|
191
231
|
rb_raise(rb_eSecurityError, "Writing unsafe string to memory");
|
|
192
232
|
}
|
|
@@ -215,6 +255,8 @@ memory_put_bytes(int argc, VALUE* argv, VALUE self)
|
|
|
215
255
|
long off, len, idx;
|
|
216
256
|
int nargs = rb_scan_args(argc, argv, "22", &offset, &str, &rbIndex, &rbLength);
|
|
217
257
|
|
|
258
|
+
Check_Type(str, T_STRING);
|
|
259
|
+
|
|
218
260
|
off = NUM2LONG(offset);
|
|
219
261
|
idx = nargs > 2 ? NUM2LONG(rbIndex) : 0;
|
|
220
262
|
if (idx < 0) {
|
|
@@ -239,21 +281,64 @@ memory_address(VALUE obj)
|
|
|
239
281
|
}
|
|
240
282
|
|
|
241
283
|
AbstractMemory*
|
|
242
|
-
|
|
284
|
+
rbffi_AbstractMemory_Cast(VALUE obj, VALUE klass)
|
|
243
285
|
{
|
|
244
286
|
if (rb_obj_is_kind_of(obj, klass)) {
|
|
245
287
|
AbstractMemory* memory;
|
|
246
288
|
Data_Get_Struct(obj, AbstractMemory, memory);
|
|
247
289
|
return memory;
|
|
248
290
|
}
|
|
291
|
+
|
|
249
292
|
rb_raise(rb_eArgError, "Invalid Memory object");
|
|
293
|
+
return NULL;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
static VALUE
|
|
297
|
+
memory_op_get_strptr(AbstractMemory* ptr, long offset)
|
|
298
|
+
{
|
|
299
|
+
void* tmp = NULL;
|
|
300
|
+
|
|
301
|
+
if (ptr != NULL && ptr->address != NULL) {
|
|
302
|
+
checkBounds(ptr, offset, sizeof(tmp));
|
|
303
|
+
memcpy(&tmp, ptr->address + offset, sizeof(tmp));
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return tmp != NULL ? rb_tainted_str_new2(tmp) : Qnil;
|
|
250
307
|
}
|
|
251
308
|
|
|
309
|
+
static void
|
|
310
|
+
memory_op_put_strptr(AbstractMemory* ptr, long offset, VALUE value)
|
|
311
|
+
{
|
|
312
|
+
rb_raise(rb_eArgError, "Cannot set :string fields");
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
static MemoryOp memory_op_strptr = { memory_op_get_strptr, memory_op_put_strptr };
|
|
316
|
+
|
|
317
|
+
//static MemoryOp memory_op_pointer = { memory_op_get_pointer, memory_op_put_pointer };
|
|
318
|
+
|
|
319
|
+
MemoryOps rbffi_AbstractMemoryOps = {
|
|
320
|
+
.int8 = &memory_op_int8,
|
|
321
|
+
.uint8 = &memory_op_uint8,
|
|
322
|
+
.int16 = &memory_op_int16,
|
|
323
|
+
.uint16 = &memory_op_uint16,
|
|
324
|
+
.int32 = &memory_op_int32,
|
|
325
|
+
.uint32 = &memory_op_uint32,
|
|
326
|
+
.int64 = &memory_op_int64,
|
|
327
|
+
.uint64 = &memory_op_uint64,
|
|
328
|
+
.float32 = &memory_op_float32,
|
|
329
|
+
.float64 = &memory_op_float64,
|
|
330
|
+
.pointer = &memory_op_pointer,
|
|
331
|
+
.strptr = &memory_op_strptr,
|
|
332
|
+
};
|
|
333
|
+
|
|
252
334
|
void
|
|
253
|
-
|
|
335
|
+
rbffi_AbstractMemory_Init(VALUE moduleFFI)
|
|
254
336
|
{
|
|
255
|
-
VALUE
|
|
256
|
-
|
|
337
|
+
VALUE classMemory = rb_define_class_under(moduleFFI, "AbstractMemory", rb_cObject);
|
|
338
|
+
rbffi_AbstractMemoryClass = classMemory;
|
|
339
|
+
rb_global_variable(&rbffi_AbstractMemoryClass);
|
|
340
|
+
|
|
341
|
+
rb_define_alloc_func(classMemory, memory_allocate);
|
|
257
342
|
#undef INT
|
|
258
343
|
#define INT(type) \
|
|
259
344
|
rb_define_method(classMemory, "put_" #type, memory_put_##type, 2); \
|
|
@@ -286,16 +371,11 @@ rb_FFI_AbstractMemory_Init()
|
|
|
286
371
|
ALIAS(long_long, int64);
|
|
287
372
|
|
|
288
373
|
if (sizeof(long) == 4) {
|
|
289
|
-
|
|
290
|
-
rb_define_alias(classMemory, "put_ulong", "put_uint32");
|
|
291
|
-
rb_define_alias(classMemory, "get_long", "get_int32");
|
|
292
|
-
rb_define_alias(classMemory, "get_ulong", "get_uint32");
|
|
374
|
+
ALIAS(long, int32);
|
|
293
375
|
} else {
|
|
294
|
-
|
|
295
|
-
rb_define_alias(classMemory, "put_ulong", "put_uint64");
|
|
296
|
-
rb_define_alias(classMemory, "get_long", "get_int64");
|
|
297
|
-
rb_define_alias(classMemory, "get_ulong", "get_uint64");
|
|
376
|
+
ALIAS(long, int64);
|
|
298
377
|
}
|
|
378
|
+
|
|
299
379
|
rb_define_method(classMemory, "put_float32", memory_put_float32, 2);
|
|
300
380
|
rb_define_method(classMemory, "get_float32", memory_get_float32, 1);
|
|
301
381
|
rb_define_alias(classMemory, "put_float", "put_float32");
|
|
@@ -314,15 +394,19 @@ rb_FFI_AbstractMemory_Init()
|
|
|
314
394
|
rb_define_alias(classMemory, "get_array_of_double", "get_array_of_float64");
|
|
315
395
|
rb_define_method(classMemory, "put_pointer", memory_put_pointer, 2);
|
|
316
396
|
rb_define_method(classMemory, "get_pointer", memory_get_pointer, 1);
|
|
397
|
+
rb_define_method(classMemory, "put_array_of_pointer", memory_put_array_of_pointer, 2);
|
|
398
|
+
rb_define_method(classMemory, "get_array_of_pointer", memory_get_array_of_pointer, 2);
|
|
317
399
|
rb_define_method(classMemory, "put_callback", memory_put_callback, 3);
|
|
318
400
|
rb_define_method(classMemory, "get_string", memory_get_string, -1);
|
|
319
401
|
rb_define_method(classMemory, "put_string", memory_put_string, 2);
|
|
320
402
|
rb_define_method(classMemory, "get_bytes", memory_get_bytes, 2);
|
|
321
403
|
rb_define_method(classMemory, "put_bytes", memory_put_bytes, -1);
|
|
404
|
+
rb_define_method(classMemory, "get_array_of_string", memory_get_array_of_string, -1);
|
|
322
405
|
|
|
323
406
|
rb_define_method(classMemory, "clear", memory_clear, 0);
|
|
324
407
|
rb_define_method(classMemory, "total", memory_size, 0);
|
|
325
408
|
|
|
326
|
-
|
|
409
|
+
id_to_ptr = rb_intern("to_ptr");
|
|
410
|
+
id_call = rb_intern("call");
|
|
327
411
|
}
|
|
328
412
|
|
data/ext/ffi_c/AbstractMemory.h
CHANGED
|
@@ -5,14 +5,40 @@
|
|
|
5
5
|
#include <sys/types.h>
|
|
6
6
|
#include <stdint.h>
|
|
7
7
|
|
|
8
|
+
#include "compat.h"
|
|
9
|
+
|
|
8
10
|
#ifdef __cplusplus
|
|
9
11
|
extern "C" {
|
|
10
12
|
#endif
|
|
11
13
|
|
|
14
|
+
typedef struct AbstractMemory_ AbstractMemory;
|
|
15
|
+
|
|
16
|
+
typedef struct {
|
|
17
|
+
VALUE (*get)(AbstractMemory* ptr, long offset);
|
|
18
|
+
void (*put)(AbstractMemory* ptr, long offset, VALUE value);
|
|
19
|
+
} MemoryOp;
|
|
20
|
+
|
|
12
21
|
typedef struct {
|
|
22
|
+
MemoryOp* int8;
|
|
23
|
+
MemoryOp* uint8;
|
|
24
|
+
MemoryOp* int16;
|
|
25
|
+
MemoryOp* uint16;
|
|
26
|
+
MemoryOp* int32;
|
|
27
|
+
MemoryOp* uint32;
|
|
28
|
+
MemoryOp* int64;
|
|
29
|
+
MemoryOp* uint64;
|
|
30
|
+
MemoryOp* float32;
|
|
31
|
+
MemoryOp* float64;
|
|
32
|
+
MemoryOp* pointer;
|
|
33
|
+
MemoryOp* strptr;
|
|
34
|
+
} MemoryOps;
|
|
35
|
+
|
|
36
|
+
struct AbstractMemory_ {
|
|
13
37
|
char* address; // Use char* instead of void* to ensure adding to it works correctly
|
|
14
38
|
long size;
|
|
15
|
-
|
|
39
|
+
MemoryOps* ops;
|
|
40
|
+
};
|
|
41
|
+
|
|
16
42
|
|
|
17
43
|
static inline void
|
|
18
44
|
checkBounds(AbstractMemory* mem, long off, long len)
|
|
@@ -23,13 +49,17 @@ checkBounds(AbstractMemory* mem, long off, long len)
|
|
|
23
49
|
}
|
|
24
50
|
}
|
|
25
51
|
|
|
26
|
-
#define MEMORY(obj)
|
|
52
|
+
#define MEMORY(obj) rbffi_AbstractMemory_Cast((obj), rbffi_AbstractMemoryClass)
|
|
27
53
|
#define MEMORY_PTR(obj) MEMORY((obj))->address
|
|
28
54
|
#define MEMORY_LEN(obj) MEMORY((obj))->size
|
|
29
55
|
|
|
30
|
-
extern
|
|
56
|
+
extern void rbffi_AbstractMemory_Init(VALUE ffiModule);
|
|
57
|
+
|
|
58
|
+
extern AbstractMemory* rbffi_AbstractMemory_Cast(VALUE obj, VALUE klass);
|
|
59
|
+
|
|
60
|
+
extern VALUE rbffi_AbstractMemoryClass;
|
|
61
|
+
extern MemoryOps rbffi_AbstractMemoryOps;
|
|
31
62
|
|
|
32
|
-
extern VALUE rb_FFI_AbstractMemory_class;
|
|
33
63
|
#ifdef __cplusplus
|
|
34
64
|
}
|
|
35
65
|
#endif
|
data/ext/ffi_c/AutoPointer.c
CHANGED
|
@@ -13,17 +13,20 @@ typedef struct AutoPointer {
|
|
|
13
13
|
VALUE parent;
|
|
14
14
|
} AutoPointer;
|
|
15
15
|
|
|
16
|
-
VALUE rb_FFI_AutoPointer_class;
|
|
17
|
-
static VALUE classAutoPointer = Qnil;
|
|
18
16
|
static void autoptr_mark(AutoPointer* ptr);
|
|
19
|
-
static
|
|
17
|
+
static VALUE autoptr_allocate(VALUE klass);
|
|
18
|
+
static VALUE autoptr_set_parent(VALUE self, VALUE parent);
|
|
19
|
+
|
|
20
|
+
VALUE rbffi_AutoPointerClass = Qnil;
|
|
20
21
|
|
|
21
22
|
static VALUE
|
|
22
23
|
autoptr_allocate(VALUE klass)
|
|
23
24
|
{
|
|
24
25
|
AutoPointer* p;
|
|
25
|
-
VALUE obj = Data_Make_Struct(klass, AutoPointer, autoptr_mark,
|
|
26
|
+
VALUE obj = Data_Make_Struct(klass, AutoPointer, autoptr_mark, -1, p);
|
|
26
27
|
p->parent = Qnil;
|
|
28
|
+
p->memory.ops = &rbffi_AbstractMemoryOps;
|
|
29
|
+
|
|
27
30
|
return obj;
|
|
28
31
|
}
|
|
29
32
|
|
|
@@ -31,7 +34,7 @@ static VALUE
|
|
|
31
34
|
autoptr_set_parent(VALUE self, VALUE parent)
|
|
32
35
|
{
|
|
33
36
|
AutoPointer* p;
|
|
34
|
-
AbstractMemory* ptr =
|
|
37
|
+
AbstractMemory* ptr = rbffi_AbstractMemory_Cast(parent, rbffi_PointerClass);
|
|
35
38
|
|
|
36
39
|
Data_Get_Struct(self, AutoPointer, p);
|
|
37
40
|
p->memory = *ptr;
|
|
@@ -43,21 +46,15 @@ autoptr_set_parent(VALUE self, VALUE parent)
|
|
|
43
46
|
static void
|
|
44
47
|
autoptr_mark(AutoPointer* ptr)
|
|
45
48
|
{
|
|
46
|
-
|
|
47
|
-
rb_gc_mark(ptr->parent);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
static void
|
|
51
|
-
autoptr_free(AutoPointer* ptr)
|
|
52
|
-
{
|
|
53
|
-
xfree(ptr);
|
|
49
|
+
rb_gc_mark(ptr->parent);
|
|
54
50
|
}
|
|
55
51
|
|
|
56
52
|
void
|
|
57
|
-
|
|
53
|
+
rbffi_AutoPointer_Init(VALUE moduleFFI)
|
|
58
54
|
{
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
55
|
+
rbffi_AutoPointerClass = rb_define_class_under(moduleFFI, "AutoPointer", rbffi_PointerClass);
|
|
56
|
+
rb_global_variable(&rbffi_AutoPointerClass);
|
|
57
|
+
|
|
58
|
+
rb_define_alloc_func(rbffi_AutoPointerClass, autoptr_allocate);
|
|
59
|
+
rb_define_protected_method(rbffi_AutoPointerClass, "parent=", autoptr_set_parent, 1);
|
|
63
60
|
}
|
data/ext/ffi_c/AutoPointer.h
CHANGED