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/lib/ffi/io.rb
ADDED
data/lib/ffi/library.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
module FFI::Library
|
2
|
+
DEFAULT = FFI::DynamicLibrary.open(nil, FFI::DynamicLibrary::RTLD_LAZY)
|
3
|
+
|
4
|
+
# TODO: Rubinius does *names here and saves the array. Multiple libs?
|
5
|
+
def ffi_lib(*names)
|
6
|
+
mapped_names = names.map { |name| FFI.map_library_name(name) }
|
7
|
+
errors = Hash.new
|
8
|
+
ffi_libs = mapped_names.map do |name|
|
9
|
+
begin
|
10
|
+
FFI::DynamicLibrary.open(name, FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_LOCAL)
|
11
|
+
rescue LoadError => ex
|
12
|
+
errors[name] = ex
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
end.compact
|
16
|
+
raise LoadError, "Could not open any of [#{mapped_names.join(", ")}]" if ffi_libs.empty?
|
17
|
+
@ffi_libs = ffi_libs
|
18
|
+
end
|
19
|
+
def ffi_convention(convention)
|
20
|
+
@ffi_convention = convention
|
21
|
+
end
|
22
|
+
##
|
23
|
+
# Attach C function +name+ to this module.
|
24
|
+
#
|
25
|
+
# If you want to provide an alternate name for the module function, supply
|
26
|
+
# it after the +name+, otherwise the C function name will be used.#
|
27
|
+
#
|
28
|
+
# After the +name+, the C function argument types are provided as an Array.
|
29
|
+
#
|
30
|
+
# The C function return type is provided last.
|
31
|
+
|
32
|
+
def attach_function(mname, a3, a4, a5=nil)
|
33
|
+
cname, arg_types, ret_type = a5 ? [ a3, a4, a5 ] : [ mname.to_s, a3, a4 ]
|
34
|
+
libraries = defined?(@ffi_libs) ? @ffi_libs : [ DEFAULT ]
|
35
|
+
convention = defined?(@ffi_convention) ? @ffi_convention : :default
|
36
|
+
|
37
|
+
# Convert :foo to the native type
|
38
|
+
callback_count = 0
|
39
|
+
arg_types.map! { |e|
|
40
|
+
begin
|
41
|
+
find_type(e)
|
42
|
+
rescue FFI::TypeError => ex
|
43
|
+
if defined?(@ffi_callbacks) && @ffi_callbacks.has_key?(e)
|
44
|
+
callback_count += 1
|
45
|
+
@ffi_callbacks[e]
|
46
|
+
elsif e.is_a?(Class) && e < FFI::Struct
|
47
|
+
FFI::NativeType::POINTER
|
48
|
+
else
|
49
|
+
raise ex
|
50
|
+
end
|
51
|
+
end
|
52
|
+
}
|
53
|
+
options = Hash.new
|
54
|
+
options[:convention] = convention
|
55
|
+
options[:type_map] = @ffi_typedefs if defined?(@ffi_typedefs)
|
56
|
+
# Try to locate the function in any of the libraries
|
57
|
+
invokers = []
|
58
|
+
libraries.each do |lib|
|
59
|
+
begin
|
60
|
+
invokers << FFI.create_invoker(lib, cname.to_s, arg_types, find_type(ret_type), options)
|
61
|
+
rescue LoadError => ex
|
62
|
+
end if invokers.empty?
|
63
|
+
end
|
64
|
+
invoker = invokers.compact.shift
|
65
|
+
raise FFI::NotFoundError.new(cname.to_s, libraries.map { |lib| lib.name }) unless invoker
|
66
|
+
|
67
|
+
# Setup the parameter list for the module function as (a1, a2)
|
68
|
+
arity = arg_types.length
|
69
|
+
params = (1..arity).map {|i| "a#{i}" }.join(",")
|
70
|
+
|
71
|
+
# Always use rest args for functions with callback parameters
|
72
|
+
if callback_count > 0 || invoker.kind_of?(FFI::VariadicInvoker)
|
73
|
+
params = "*args, &block"
|
74
|
+
end
|
75
|
+
call = arity <= 3 && callback_count < 1 && !invoker.kind_of?(FFI::VariadicInvoker)? "call#{arity}" : "call"
|
76
|
+
|
77
|
+
#
|
78
|
+
# Attach the invoker to this module as 'mname'.
|
79
|
+
#
|
80
|
+
self.module_eval <<-code
|
81
|
+
@@#{mname} = invoker
|
82
|
+
def self.#{mname}(#{params})
|
83
|
+
@@#{mname}.#{call}(#{params})
|
84
|
+
end
|
85
|
+
def #{mname}(#{params})
|
86
|
+
@@#{mname}.#{call}(#{params})
|
87
|
+
end
|
88
|
+
code
|
89
|
+
invoker
|
90
|
+
end
|
91
|
+
def callback(name, args, ret)
|
92
|
+
@ffi_callbacks = Hash.new unless defined?(@ffi_callbacks)
|
93
|
+
@ffi_callbacks[name] = FFI::CallbackInfo.new(find_type(ret), args.map { |e| find_type(e) })
|
94
|
+
end
|
95
|
+
def typedef(current, add)
|
96
|
+
@ffi_typedefs = Hash.new unless defined?(@ffi_typedefs)
|
97
|
+
if current.kind_of? Integer
|
98
|
+
code = current
|
99
|
+
else
|
100
|
+
code = @ffi_typedefs[current] || FFI.find_type(current)
|
101
|
+
end
|
102
|
+
|
103
|
+
@ffi_typedefs[add] = code
|
104
|
+
end
|
105
|
+
def find_type(name)
|
106
|
+
code = if defined?(@ffi_typedefs)
|
107
|
+
@ffi_typedefs[name]
|
108
|
+
end
|
109
|
+
code = name if !code && name.kind_of?(FFI::CallbackInfo)
|
110
|
+
if code.nil? || code.kind_of?(Symbol)
|
111
|
+
FFI.find_type(name)
|
112
|
+
else
|
113
|
+
code
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module FFI
|
2
|
+
#
|
3
|
+
# FFI::ManagedStruct allows custom garbage-collection of your FFI::Structs.
|
4
|
+
#
|
5
|
+
# The typical use case would be when interacting with a library
|
6
|
+
# that has a nontrivial memory management design, such as a linked
|
7
|
+
# list or a binary tree.
|
8
|
+
#
|
9
|
+
# When the Struct instance is garbage collected, FFI::ManagedStruct will
|
10
|
+
# invoke the class's release() method during object finalization.
|
11
|
+
#
|
12
|
+
# Example usage:
|
13
|
+
# module MyLibrary
|
14
|
+
# ffi_lib "libmylibrary"
|
15
|
+
# attach_function :new_dlist, [], :pointer
|
16
|
+
# attach_function :destroy_dlist, [:pointer], :void
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# class DoublyLinkedList < FFI::ManagedStruct
|
20
|
+
# @@@
|
21
|
+
# struct do |s|
|
22
|
+
# s.name 'struct dlist'
|
23
|
+
# s.include 'dlist.h'
|
24
|
+
# s.field :head, :pointer
|
25
|
+
# s.field :tail, :pointer
|
26
|
+
# end
|
27
|
+
# @@@
|
28
|
+
#
|
29
|
+
# def self.release ptr
|
30
|
+
# MyLibrary.destroy_dlist(ptr)
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# begin
|
35
|
+
# ptr = DoublyLinkedList.new(MyLibrary.new_dlist)
|
36
|
+
# # do something with the list
|
37
|
+
# end
|
38
|
+
# # struct is out of scope, and will be GC'd using DoublyLinkedList#release
|
39
|
+
#
|
40
|
+
#
|
41
|
+
class ManagedStruct < FFI::Struct
|
42
|
+
|
43
|
+
# call-seq:
|
44
|
+
# ManagedStruct.new(pointer)
|
45
|
+
# ManagedStruct.new
|
46
|
+
#
|
47
|
+
# When passed a pointer, create a new ManagedStruct which will invoke the class method release() on
|
48
|
+
def initialize(pointer=nil)
|
49
|
+
raise NoMethodError, "release() not implemented for class #{self}" unless self.class.respond_to? :release
|
50
|
+
raise ArgumentError, "Must supply a pointer to memory for the Struct" unless pointer
|
51
|
+
super FFI::AutoPointer.new(pointer, self.class.method(:release))
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
data/lib/ffi/memorypointer.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'ffi/pointer'
|
1
2
|
module FFI
|
2
3
|
class MemoryPointer
|
3
4
|
# call-seq:
|
@@ -69,101 +70,7 @@ module FFI
|
|
69
70
|
raise ArgumentError, "unknown type size" unless @type_size
|
70
71
|
self + (which * @type_size)
|
71
72
|
end
|
72
|
-
|
73
|
-
def write_int(obj)
|
74
|
-
put_int32(0, obj)
|
75
|
-
end
|
76
|
-
|
77
|
-
# Read a C int from the memory pointed to.
|
78
|
-
def read_int
|
79
|
-
get_int32(0)
|
80
|
-
end
|
81
|
-
|
82
|
-
# Write +obj+ as a C long at the memory pointed to.
|
83
|
-
def write_long(obj)
|
84
|
-
put_long(0, obj)
|
85
|
-
end
|
86
|
-
|
87
|
-
# Read a C long from the memory pointed to.
|
88
|
-
def read_long
|
89
|
-
get_long(0)
|
90
|
-
end
|
91
|
-
# Write +obj+ as a C long long at the memory pointed to.
|
92
|
-
def write_long_long(obj)
|
93
|
-
put_int64(0, obj)
|
94
|
-
end
|
95
|
-
|
96
|
-
# Read a C long long from the memory pointed to.
|
97
|
-
def read_long_long
|
98
|
-
get_int64(0)
|
99
|
-
end
|
100
|
-
def read_pointer
|
101
|
-
get_pointer(0)
|
102
|
-
end
|
103
|
-
def read_float
|
104
|
-
get_float32(0)
|
105
|
-
end
|
106
|
-
def write_float(obj)
|
107
|
-
put_float32(0, obj)
|
108
|
-
end
|
109
|
-
|
110
|
-
def read_string(len=nil)
|
111
|
-
if len
|
112
|
-
get_string(0, len)
|
113
|
-
else
|
114
|
-
get_string(0)
|
115
|
-
end
|
116
|
-
end
|
117
|
-
def read_string_length(len)
|
118
|
-
get_string(0, len)
|
119
|
-
end
|
120
|
-
def read_string_to_null
|
121
|
-
get_string(0)
|
122
|
-
end
|
123
|
-
def write_string_length(str, len)
|
124
|
-
put_string(0, str, len)
|
125
|
-
end
|
126
|
-
def write_string(str, len=nil)
|
127
|
-
len = str.size unless len
|
128
|
-
# Write the string data without NUL termination
|
129
|
-
put_string(0, str, len)
|
130
|
-
end
|
131
|
-
def read_array_of_type(type, reader, length)
|
132
|
-
ary = []
|
133
|
-
size = FFI.type_size(type)
|
134
|
-
tmp = self
|
135
|
-
length.times {
|
136
|
-
ary << tmp.send(reader)
|
137
|
-
tmp += size
|
138
|
-
}
|
139
|
-
ary
|
140
|
-
end
|
141
|
-
|
142
|
-
def write_array_of_type(type, writer, ary)
|
143
|
-
size = FFI.type_size(type)
|
144
|
-
tmp = self
|
145
|
-
ary.each {|i|
|
146
|
-
tmp.send(writer, i)
|
147
|
-
tmp += size
|
148
|
-
}
|
149
|
-
self
|
150
|
-
end
|
151
|
-
def read_array_of_int(length)
|
152
|
-
get_array_of_int32(0, length)
|
153
|
-
end
|
154
|
-
|
155
|
-
def write_array_of_int(ary)
|
156
|
-
put_array_of_int32(0, ary)
|
157
|
-
end
|
158
|
-
|
159
|
-
def read_array_of_long(length)
|
160
|
-
get_array_of_long(0, length)
|
161
|
-
end
|
162
|
-
|
163
|
-
def write_array_of_long(ary)
|
164
|
-
put_array_of_long(0, ary)
|
165
|
-
end
|
166
|
-
end
|
73
|
+
end
|
167
74
|
end
|
168
75
|
|
169
|
-
MemoryPointer = FFI::MemoryPointer
|
76
|
+
MemoryPointer = FFI::MemoryPointer
|
data/lib/ffi/platform.rb
CHANGED
@@ -20,10 +20,12 @@ module FFI
|
|
20
20
|
raise FFI::PlatformError, "Unknown operating system: #{OS_}"
|
21
21
|
end
|
22
22
|
ARCH = case CPU.downcase
|
23
|
-
when /i?86|x86|i86pc/
|
24
|
-
"i386"
|
25
23
|
when /amd64|x86_64/
|
26
24
|
"x86_64"
|
25
|
+
when /i?86|x86|i86pc/
|
26
|
+
"i386"
|
27
|
+
when /ppc|powerpc/
|
28
|
+
"powerpc"
|
27
29
|
else
|
28
30
|
raise FFI::PlatformError, "Unknown cpu architecture: #{ARCH_}"
|
29
31
|
end
|
@@ -39,6 +41,7 @@ module FFI
|
|
39
41
|
IS_OPENBSD = is_os("openbsd")
|
40
42
|
IS_WINDOWS = is_os("windows")
|
41
43
|
IS_BSD = IS_MAC || IS_FREEBSD || IS_OPENBSD
|
44
|
+
CONF_DIR = File.dirname(__FILE__)
|
42
45
|
public
|
43
46
|
LIBC = if IS_WINDOWS
|
44
47
|
"msvcrt"
|
@@ -50,11 +53,11 @@ module FFI
|
|
50
53
|
LIBPREFIX = IS_WINDOWS ? '' : 'lib'
|
51
54
|
LIBSUFFIX = case OS
|
52
55
|
when /darwin/
|
53
|
-
'
|
56
|
+
'dylib'
|
54
57
|
when /linux|bsd|solaris/
|
55
|
-
'
|
58
|
+
'so'
|
56
59
|
when /win/
|
57
|
-
'
|
60
|
+
'dll'
|
58
61
|
else
|
59
62
|
raise PlatformError, "Cannot determine shared library extension for #{OS}"
|
60
63
|
end
|
data/lib/ffi/pointer.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'ffi/platform'
|
2
|
+
module FFI
|
3
|
+
class Pointer
|
4
|
+
SIZE = Platform::ADDRESS_SIZE / 8
|
5
|
+
|
6
|
+
# Return the size of a pointer on the current platform, in bytes
|
7
|
+
def self.size
|
8
|
+
SIZE
|
9
|
+
end
|
10
|
+
# Write +obj+ as a C int at the memory pointed to.
|
11
|
+
def write_int(obj)
|
12
|
+
put_int32(0, obj)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Read a C int from the memory pointed to.
|
16
|
+
def read_int
|
17
|
+
get_int32(0)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Write +obj+ as a C long at the memory pointed to.
|
21
|
+
def write_long(obj)
|
22
|
+
put_long(0, obj)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Read a C long from the memory pointed to.
|
26
|
+
def read_long
|
27
|
+
get_long(0)
|
28
|
+
end
|
29
|
+
# Write +obj+ as a C long long at the memory pointed to.
|
30
|
+
def write_long_long(obj)
|
31
|
+
put_int64(0, obj)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Read a C long long from the memory pointed to.
|
35
|
+
def read_long_long
|
36
|
+
get_int64(0)
|
37
|
+
end
|
38
|
+
def read_pointer
|
39
|
+
get_pointer(0)
|
40
|
+
end
|
41
|
+
def read_float
|
42
|
+
get_float32(0)
|
43
|
+
end
|
44
|
+
def write_float(obj)
|
45
|
+
put_float32(0, obj)
|
46
|
+
end
|
47
|
+
|
48
|
+
def read_string(len=nil)
|
49
|
+
if len
|
50
|
+
get_string(0, len)
|
51
|
+
else
|
52
|
+
get_string(0)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
def read_string_length(len)
|
56
|
+
get_string(0, len)
|
57
|
+
end
|
58
|
+
def read_string_to_null
|
59
|
+
get_string(0)
|
60
|
+
end
|
61
|
+
def write_string_length(str, len)
|
62
|
+
put_string(0, str, len)
|
63
|
+
end
|
64
|
+
def write_string(str, len=nil)
|
65
|
+
len = str.size unless len
|
66
|
+
# Write the string data without NUL termination
|
67
|
+
put_string(0, str, len)
|
68
|
+
end
|
69
|
+
def read_array_of_type(type, reader, length)
|
70
|
+
ary = []
|
71
|
+
size = FFI.type_size(type)
|
72
|
+
tmp = self
|
73
|
+
length.times {
|
74
|
+
ary << tmp.send(reader)
|
75
|
+
tmp += size
|
76
|
+
}
|
77
|
+
ary
|
78
|
+
end
|
79
|
+
|
80
|
+
def write_array_of_type(type, writer, ary)
|
81
|
+
size = FFI.type_size(type)
|
82
|
+
tmp = self
|
83
|
+
ary.each {|i|
|
84
|
+
tmp.send(writer, i)
|
85
|
+
tmp += size
|
86
|
+
}
|
87
|
+
self
|
88
|
+
end
|
89
|
+
def read_array_of_int(length)
|
90
|
+
get_array_of_int32(0, length)
|
91
|
+
end
|
92
|
+
|
93
|
+
def write_array_of_int(ary)
|
94
|
+
put_array_of_int32(0, ary)
|
95
|
+
end
|
96
|
+
|
97
|
+
def read_array_of_long(length)
|
98
|
+
get_array_of_long(0, length)
|
99
|
+
end
|
100
|
+
|
101
|
+
def write_array_of_long(ary)
|
102
|
+
put_array_of_long(0, ary)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
data/lib/ffi/struct.rb
CHANGED
@@ -14,10 +14,6 @@ module FFI
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
class StructLayoutBuilder
|
17
|
-
LONG_ALIGN = Platform::ARCH =~ /sparc.*/ ? 64 : Platform::LONG_SIZE
|
18
|
-
ADDRESS_ALIGN = Platform::ARCH =~ /sparc.*/ ? 64 : Platform::ADDRESS_SIZE
|
19
|
-
FLOAT_ALIGN = Platform::ARCH =~ /sparc.*/ ? 64 : Platform::ADDRESS_SIZE
|
20
|
-
DOUBLE_ALIGN = Platform::ARCH =~ /sparc.*/ ? 64 : Platform::ADDRESS_SIZE
|
21
17
|
class Field
|
22
18
|
def initialize(off)
|
23
19
|
@off = off
|
@@ -38,6 +34,7 @@ module FFI
|
|
38
34
|
end
|
39
35
|
class Signed8 < Field
|
40
36
|
def self.size; 8; end
|
37
|
+
def self.align; Platform::INT8_ALIGN; end
|
41
38
|
def put(ptr, val)
|
42
39
|
ptr.put_int8(@off, val)
|
43
40
|
end
|
@@ -47,6 +44,7 @@ module FFI
|
|
47
44
|
end
|
48
45
|
class Unsigned8 < Field
|
49
46
|
def self.size; 8; end
|
47
|
+
def self.align; Platform::INT8_ALIGN; end
|
50
48
|
def put(ptr, val)
|
51
49
|
ptr.put_uint8(@off, val)
|
52
50
|
end
|
@@ -56,6 +54,7 @@ module FFI
|
|
56
54
|
end
|
57
55
|
class Signed16 < Field
|
58
56
|
def self.size; 16; end
|
57
|
+
def self.align; Platform::INT16_ALIGN; end
|
59
58
|
def put(ptr, val)
|
60
59
|
ptr.put_int16(@off, val)
|
61
60
|
end
|
@@ -65,6 +64,7 @@ module FFI
|
|
65
64
|
end
|
66
65
|
class Unsigned16 < Field
|
67
66
|
def self.size; 16; end
|
67
|
+
def self.align; Platform::INT16_ALIGN; end
|
68
68
|
def put(ptr, val)
|
69
69
|
ptr.put_uint16(@off, val)
|
70
70
|
end
|
@@ -74,6 +74,7 @@ module FFI
|
|
74
74
|
end
|
75
75
|
class Signed32 < Field
|
76
76
|
def self.size; 32; end
|
77
|
+
def self.align; Platform::INT32_ALIGN; end
|
77
78
|
def put(ptr, val)
|
78
79
|
ptr.put_int32(@off, val)
|
79
80
|
end
|
@@ -83,6 +84,7 @@ module FFI
|
|
83
84
|
end
|
84
85
|
class Unsigned32 < Field
|
85
86
|
def self.size; 32; end
|
87
|
+
def self.align; Platform::INT32_ALIGN; end
|
86
88
|
def put(ptr, val)
|
87
89
|
ptr.put_uint32(@off, val)
|
88
90
|
end
|
@@ -92,7 +94,7 @@ module FFI
|
|
92
94
|
end
|
93
95
|
class Signed64 < Field
|
94
96
|
def self.size; 64; end
|
95
|
-
def self.align;
|
97
|
+
def self.align; Platform::INT64_ALIGN; end
|
96
98
|
def put(ptr, val)
|
97
99
|
ptr.put_int64(@off, val)
|
98
100
|
end
|
@@ -102,7 +104,7 @@ module FFI
|
|
102
104
|
end
|
103
105
|
class Unsigned64 < Field
|
104
106
|
def self.size; 64; end
|
105
|
-
def self.align;
|
107
|
+
def self.align; Platform::INT64_ALIGN; end
|
106
108
|
def put(ptr, val)
|
107
109
|
ptr.put_uint64(@off, val)
|
108
110
|
end
|
@@ -110,6 +112,47 @@ module FFI
|
|
110
112
|
ptr.get_uint64(@off)
|
111
113
|
end
|
112
114
|
end
|
115
|
+
class FloatField < Field
|
116
|
+
def self.size; Platform::FLOAT_SIZE; end
|
117
|
+
def self.align; Platform::FLOAT_ALIGN; end
|
118
|
+
def put(ptr, val)
|
119
|
+
ptr.put_float32(@off, val)
|
120
|
+
end
|
121
|
+
def get(ptr)
|
122
|
+
ptr.get_float32(@off)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
class DoubleField < Field
|
126
|
+
def self.size; Platform::DOUBLE_SIZE; end
|
127
|
+
def self.align; Platform::DOUBLE_ALIGN; end
|
128
|
+
def put(ptr, val)
|
129
|
+
ptr.put_float64(@off, val)
|
130
|
+
end
|
131
|
+
def get(ptr)
|
132
|
+
ptr.get_float64(@off)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
class PointerField < Field
|
136
|
+
def self.size; Platform::ADDRESS_SIZE; end
|
137
|
+
def self.align; Platform::ADDRESS_ALIGN; end
|
138
|
+
def put(ptr, val)
|
139
|
+
ptr.put_pointer(@off, val)
|
140
|
+
end
|
141
|
+
def get(ptr)
|
142
|
+
ptr.get_pointer(@off)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
class StringField < Field
|
146
|
+
def self.size; Platform::ADDRESS_SIZE; end
|
147
|
+
def self.align; Platform::ADDRESS_ALIGN; end
|
148
|
+
def put(ptr, val)
|
149
|
+
raise ArgumentError, "Cannot set :string fields"
|
150
|
+
end
|
151
|
+
def get(ptr)
|
152
|
+
strp = ptr.get_pointer(@off)
|
153
|
+
(strp.nil? || strp.null?) ? nil : strp.get_string(0)
|
154
|
+
end
|
155
|
+
end
|
113
156
|
def initialize
|
114
157
|
@fields = {}
|
115
158
|
@size = 0
|
@@ -136,6 +179,14 @@ module FFI
|
|
136
179
|
Signed64
|
137
180
|
when :ulong_long, NativeType::UINT64
|
138
181
|
Unsigned64
|
182
|
+
when :float, NativeType::FLOAT32
|
183
|
+
FloatField
|
184
|
+
when :double, NativeType::FLOAT64
|
185
|
+
DoubleField
|
186
|
+
when :pointer, NativeType::POINTER
|
187
|
+
PointerField
|
188
|
+
when :string, NativeType::STRING
|
189
|
+
StringField
|
139
190
|
else
|
140
191
|
raise ArgumentError, "Unknown type: #{type}"
|
141
192
|
end
|
@@ -202,62 +253,66 @@ module FFI
|
|
202
253
|
@pointer.clear
|
203
254
|
self
|
204
255
|
end
|
256
|
+
def to_ptr
|
257
|
+
@pointer
|
258
|
+
end
|
259
|
+
def self.in
|
260
|
+
:buffer_in
|
261
|
+
end
|
262
|
+
def self.out
|
263
|
+
:buffer_out
|
264
|
+
end
|
205
265
|
end
|
206
266
|
class Struct < BaseStruct
|
207
|
-
|
267
|
+
private
|
268
|
+
def self.enclosing_module
|
269
|
+
begin
|
270
|
+
mod = self.name.split("::")[0..-2].inject(Object) { |obj, c| obj.const_get(c) }
|
271
|
+
mod.respond_to?(:find_type) ? mod : nil
|
272
|
+
rescue Exception => ex
|
273
|
+
nil
|
274
|
+
end
|
275
|
+
end
|
276
|
+
def self.find_type(type, mod = nil)
|
277
|
+
return mod ? mod.find_type(type) : FFI.find_type(type)
|
278
|
+
end
|
279
|
+
def self.hash_layout(spec)
|
208
280
|
raise "Ruby version not supported" if RUBY_VERSION =~ /1.8.*/
|
209
281
|
builder = FFI::StructLayoutBuilder.new
|
282
|
+
mod = enclosing_module
|
210
283
|
spec[0].each do |name,type|
|
211
|
-
builder.add_field(name,
|
284
|
+
builder.add_field(name, find_type(type, mod))
|
212
285
|
end
|
213
286
|
builder.build
|
214
287
|
end
|
215
|
-
def self.
|
288
|
+
def self.array_layout(spec)
|
216
289
|
builder = FFI::StructLayoutBuilder.new
|
290
|
+
mod = enclosing_module
|
217
291
|
i = 0
|
218
292
|
while i < spec.size
|
219
|
-
name, type
|
220
|
-
|
221
|
-
code =
|
222
|
-
|
223
|
-
i
|
293
|
+
name, type = spec[i, 2]
|
294
|
+
i += 2
|
295
|
+
code = find_type(type, mod)
|
296
|
+
# If the next param is a Fixnu, it specifies the offset
|
297
|
+
if spec[i].kind_of?(Fixnum)
|
298
|
+
offset = spec[i]
|
299
|
+
i += 1
|
300
|
+
builder.add_field(name, code, offset)
|
301
|
+
else
|
302
|
+
builder.add_field(name, code)
|
303
|
+
end
|
224
304
|
end
|
225
305
|
builder.build
|
226
306
|
end
|
307
|
+
public
|
227
308
|
def self.layout(*spec)
|
228
|
-
|
309
|
+
|
229
310
|
return @layout if spec.size == 0
|
230
|
-
cspec = spec[0].kind_of?(Hash) ?
|
311
|
+
cspec = spec[0].kind_of?(Hash) ? hash_layout(spec) : array_layout(spec)
|
231
312
|
|
232
313
|
@layout = cspec unless self == FFI::Struct
|
233
314
|
@size = cspec.size
|
234
315
|
return cspec
|
235
316
|
end
|
236
|
-
def self.config(base, *fields)
|
237
|
-
config = Config::CONFIG
|
238
|
-
@size = config["#{base}.sizeof"]
|
239
|
-
|
240
|
-
builder = StructLayoutBuilder.new
|
241
|
-
|
242
|
-
fields.each do |field|
|
243
|
-
offset = config["#{base}.#{field}.offset"]
|
244
|
-
size = config["#{base}.#{field}.size"]
|
245
|
-
type = config["#{base}.#{field}.type"]
|
246
|
-
type = type ? type.to_sym : FFI.size_to_type(size)
|
247
|
-
|
248
|
-
code = FFI.find_type type
|
249
|
-
if (code == NativeType::CHAR_ARRAY)
|
250
|
-
builder.add_char_array(field.to_s, size, offset)
|
251
|
-
else
|
252
|
-
builder.add_field(field.to_s, code, offset)
|
253
|
-
end
|
254
|
-
end
|
255
|
-
cspec = builder.build
|
256
|
-
|
257
|
-
@layout = cspec
|
258
|
-
@size = cspec.size if @size < cspec.size
|
259
|
-
|
260
|
-
return cspec
|
261
|
-
end
|
262
317
|
end
|
263
318
|
end
|