ffi 0.4.0 → 0.5.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 (55) hide show
  1. data/README.rdoc +15 -15
  2. data/Rakefile +57 -8
  3. data/ext/ffi_c/AbstractMemory.c +101 -24
  4. data/ext/ffi_c/AbstractMemory.h +98 -6
  5. data/ext/ffi_c/ArrayType.c +129 -0
  6. data/ext/ffi_c/ArrayType.h +58 -0
  7. data/ext/ffi_c/AutoPointer.c +1 -0
  8. data/ext/ffi_c/Buffer.c +59 -43
  9. data/ext/ffi_c/Call.c +853 -0
  10. data/ext/ffi_c/Call.h +86 -0
  11. data/ext/ffi_c/ClosurePool.c +302 -0
  12. data/ext/ffi_c/ClosurePool.h +29 -0
  13. data/ext/ffi_c/DynamicLibrary.c +3 -0
  14. data/ext/ffi_c/Function.c +478 -0
  15. data/ext/ffi_c/Function.h +80 -0
  16. data/ext/ffi_c/FunctionInfo.c +221 -0
  17. data/ext/ffi_c/LastError.c +30 -6
  18. data/ext/ffi_c/MemoryPointer.c +50 -28
  19. data/ext/ffi_c/MethodHandle.c +346 -0
  20. data/ext/ffi_c/MethodHandle.h +53 -0
  21. data/ext/ffi_c/Pointer.c +73 -13
  22. data/ext/ffi_c/Pointer.h +31 -7
  23. data/ext/ffi_c/Struct.c +517 -224
  24. data/ext/ffi_c/Struct.h +60 -6
  25. data/ext/ffi_c/StructByValue.c +140 -0
  26. data/ext/ffi_c/StructByValue.h +53 -0
  27. data/ext/ffi_c/StructLayout.c +450 -0
  28. data/ext/ffi_c/Type.c +121 -22
  29. data/ext/ffi_c/Type.h +37 -8
  30. data/ext/ffi_c/Types.c +46 -61
  31. data/ext/ffi_c/Types.h +38 -7
  32. data/ext/ffi_c/Variadic.c +260 -0
  33. data/ext/ffi_c/compat.h +53 -3
  34. data/ext/ffi_c/extconf.rb +6 -7
  35. data/ext/ffi_c/ffi.c +45 -39
  36. data/ext/ffi_c/libffi.darwin.mk +2 -2
  37. data/ext/ffi_c/rbffi.h +3 -0
  38. data/lib/ffi/ffi.rb +7 -4
  39. data/lib/ffi/library.rb +34 -59
  40. data/lib/ffi/platform.rb +14 -4
  41. data/lib/ffi/struct.rb +110 -281
  42. data/lib/ffi/union.rb +4 -9
  43. data/lib/ffi/variadic.rb +1 -6
  44. data/spec/ffi/buffer_spec.rb +6 -0
  45. data/spec/ffi/callback_spec.rb +34 -3
  46. data/spec/ffi/function_spec.rb +73 -0
  47. data/spec/ffi/library_spec.rb +56 -52
  48. data/spec/ffi/pointer_spec.rb +3 -3
  49. data/spec/ffi/struct_callback_spec.rb +26 -3
  50. data/spec/ffi/struct_spec.rb +56 -3
  51. metadata +42 -11
  52. data/ext/ffi_c/Callback.c +0 -374
  53. data/ext/ffi_c/Callback.h +0 -47
  54. data/ext/ffi_c/Invoker.c +0 -962
  55. data/ext/ffi_c/NullPointer.c +0 -143
@@ -47,9 +47,9 @@ build_ffi = \
47
47
 
48
48
  $(LIBFFI):
49
49
  @for arch in $(ARCHES); do $(call build_ffi,$$arch);done
50
- # Assemble into a FAT (i386, ppc) library
50
+ # Assemble into a FAT (x86_64, i386, ppc) library
51
51
  @mkdir -p $(BUILD_DIR)/libffi/.libs
52
- env MACOSX_DEPLOYMENT_TARGET=10.4 /usr/bin/libtool -static -o $@ \
52
+ /usr/bin/libtool -static -o $@ \
53
53
  $(foreach arch, $(ARCHES),$(BUILD_DIR)/libffi-$(arch)/.libs/libffi_convenience.a)
54
54
  @mkdir -p $(LIBFFI_BUILD_DIR)/include
55
55
  $(RM) $(LIBFFI_BUILD_DIR)/include/ffi.h
data/ext/ffi_c/rbffi.h CHANGED
@@ -9,9 +9,12 @@ extern "C" {
9
9
 
10
10
  #define MAX_PARAMETERS (32)
11
11
 
12
+ extern VALUE rbffi_FFIModule;
13
+
12
14
  extern void rbffi_Type_Init(VALUE ffiModule);
13
15
  extern void rbffi_Buffer_Init(VALUE ffiModule);
14
16
  extern void rbffi_Invoker_Init(VALUE ffiModule);
17
+ extern void rbffi_Variadic_Init(VALUE ffiModule);
15
18
  extern VALUE rbffi_AbstractMemoryClass, rbffi_InvokerClass;
16
19
  extern int rbffi_type_size(VALUE type);
17
20
 
data/lib/ffi/ffi.rb CHANGED
@@ -66,7 +66,9 @@ module FFI
66
66
  end
67
67
  lib
68
68
  end
69
- def self.create_invoker(lib, name, args, ret_type, native_ret_type, options = { :convention => :default })
69
+
70
+
71
+ def self.create_invoker(lib, name, args, ret_type, options = { :convention => :default })
70
72
  # Current artificial limitation based on JRuby::FFI limit
71
73
  raise SignatureError, 'FFI functions may take max 32 arguments!' if args.size > 32
72
74
 
@@ -85,12 +87,13 @@ module FFI
85
87
  raise NotFoundError.new(name, library.name) unless function
86
88
 
87
89
  args = args.map {|e| find_type(e) }
88
- if args.length > 0 && args[args.length - 1] == FFI::NativeType::VARARGS
89
- invoker = FFI::VariadicInvoker.new(function, args, ret_type, find_type(native_ret_type), options)
90
+ invoker = if args.length > 0 && args[args.length - 1] == FFI::NativeType::VARARGS
91
+ FFI::VariadicInvoker.new(function, args, find_type(ret_type), options)
90
92
  else
91
- invoker = FFI::Invoker.new(function, args, ret_type, find_type(native_ret_type), options[:convention].to_s, options[:enums])
93
+ FFI::Function.new(find_type(ret_type), args, function, options)
92
94
  end
93
95
  raise NotFoundError.new(name, library.name) unless invoker
96
+
94
97
  return invoker
95
98
  end
96
99
  end
data/lib/ffi/library.rb CHANGED
@@ -28,6 +28,22 @@ module FFI::Library
28
28
  def ffi_convention(convention)
29
29
  @ffi_convention = convention
30
30
  end
31
+
32
+ def ffi_libraries
33
+ unless defined?(@ffi_libs) or self.name.nil?
34
+ libs = []
35
+ # Try the exact name (e.g. User32) and all lower case (e.g. LibC -> libc)
36
+ [ self.name, self.name.downcase ].each do |name|
37
+ begin
38
+ libs << FFI::DynamicLibrary.open(name, FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_GLOBAL)
39
+ rescue Exception
40
+ end
41
+ end
42
+ @ffi_libs = libs unless libs.empty?
43
+ end
44
+ defined?(@ffi_libs) ? @ffi_libs : [ DEFAULT ]
45
+ end
46
+
31
47
  ##
32
48
  # Attach C function +name+ to this module.
33
49
  #
@@ -51,15 +67,14 @@ module FFI::Library
51
67
 
52
68
  # Try to locate the function in any of the libraries
53
69
  invokers = []
54
- libraries = defined?(@ffi_libs) ? @ffi_libs : [ DEFAULT ]
55
- libraries.each do |lib|
70
+ ffi_libraries.each do |lib|
56
71
  begin
57
- invokers << FFI.create_invoker(lib, cname.to_s, arg_types, ret_type, find_type(ret_type), options)
72
+ invokers << FFI.create_invoker(lib, cname.to_s, arg_types, find_type(ret_type), options)
58
73
  rescue LoadError => ex
59
74
  end if invokers.empty?
60
75
  end
61
76
  invoker = invokers.compact.shift
62
- raise FFI::NotFoundError.new(cname.to_s, libraries.map { |lib| lib.name }) unless invoker
77
+ raise FFI::NotFoundError.new(cname.to_s, ffi_libraries.map { |lib| lib.name }) unless invoker
63
78
 
64
79
  # Setup the parameter list for the module function as (a1, a2)
65
80
  arity = arg_types.length
@@ -91,71 +106,31 @@ module FFI::Library
91
106
  end
92
107
  def attach_variable(mname, a1, a2 = nil)
93
108
  cname, type = a2 ? [ a1, a2 ] : [ mname.to_s, a1 ]
94
- libraries = defined?(@ffi_libs) ? @ffi_libs : [ DEFAULT ]
95
109
  address = nil
96
- libraries.each do |lib|
110
+ ffi_libraries.each do |lib|
97
111
  begin
98
112
  address = lib.find_variable(cname.to_s)
99
113
  break unless address.nil?
100
114
  rescue LoadError
101
115
  end
102
116
  end
103
- raise FFI::NotFoundError.new(cname, libraries) if address.nil?
104
- case ffi_type = find_type(type)
105
- when :pointer, FFI::NativeType::POINTER
106
- op = :pointer
107
- when :char, FFI::NativeType::INT8
108
- op = :int8
109
- when :uchar, FFI::NativeType::UINT8
110
- op = :uint8
111
- when :short, FFI::NativeType::INT16
112
- op = :int16
113
- when :ushort, FFI::NativeType::UINT16
114
- op = :uint16
115
- when :int, FFI::NativeType::INT32
116
- op = :int32
117
- when :uint, FFI::NativeType::UINT32
118
- op = :uint32
119
- when :long, FFI::NativeType::LONG
120
- op = :long
121
- when :ulong, FFI::NativeType::ULONG
122
- op = :ulong
123
- when :long_long, FFI::NativeType::INT64
124
- op = :int64
125
- when :ulong_long, FFI::NativeType::UINT64
126
- op = :uint64
127
- else
128
- if ffi_type.is_a?(FFI::CallbackInfo)
129
- op = :callback
130
- else
131
- raise TypeError, "Cannot access library variable of type #{type}"
132
- end
133
- end
117
+
118
+ raise FFI::NotFoundError.new(cname, ffi_libraries) if address.nil? || address.null?
119
+ s = FFI::Struct.new(address, :gvar, find_type(type))
120
+
134
121
  #
135
122
  # Attach to this module as mname/mname=
136
123
  #
137
- if op == :callback
138
- self.module_eval <<-code
139
- @@ffi_gvar_#{mname} = address
140
- @@ffi_gvar_#{mname}_cbinfo = ffi_type
141
- def self.#{mname}
142
- raise ArgError, "Cannot get callback fields"
143
- end
144
- def self.#{mname}=(value)
145
- @@ffi_gvar_#{mname}.put_callback(0, value, @@ffi_gvar_#{mname}_cbinfo)
146
- end
147
- code
148
- else
149
- self.module_eval <<-code
150
- @@ffi_gvar_#{mname} = address
151
- def self.#{mname}
152
- @@ffi_gvar_#{mname}.get_#{op.to_s}(0)
153
- end
154
- def self.#{mname}=(value)
155
- @@ffi_gvar_#{mname}.put_#{op.to_s}(0, value)
156
- end
157
- code
158
- end
124
+ self.module_eval <<-code
125
+ @@ffi_gvar_#{mname} = s
126
+ def self.#{mname}
127
+ @@ffi_gvar_#{mname}[:gvar]
128
+ end
129
+ def self.#{mname}=(value)
130
+ @@ffi_gvar_#{mname}[:gvar] = value
131
+ end
132
+ code
133
+
159
134
  address
160
135
  end
161
136
 
data/lib/ffi/platform.rb CHANGED
@@ -17,8 +17,9 @@ module FFI
17
17
  when /win|mingw/
18
18
  "windows"
19
19
  else
20
- raise FFI::PlatformError, "Unknown operating system: #{Config::CONFIG['host_os']}"
20
+ Config::CONFIG['host_os'].downcase
21
21
  end
22
+
22
23
  ARCH = case CPU.downcase
23
24
  when /amd64|x86_64/
24
25
  "x86_64"
@@ -27,8 +28,9 @@ module FFI
27
28
  when /ppc|powerpc/
28
29
  "powerpc"
29
30
  else
30
- raise FFI::PlatformError, "Unknown cpu architecture: #{ARCH_}"
31
+ Config::CONFIG['host_cpu']
31
32
  end
33
+
32
34
  private
33
35
  def self.is_os(os)
34
36
  OS == os
@@ -43,6 +45,7 @@ module FFI
43
45
  IS_BSD = IS_MAC || IS_FREEBSD || IS_OPENBSD
44
46
  CONF_DIR = File.dirname(__FILE__)
45
47
  public
48
+
46
49
  LIBC = if IS_WINDOWS
47
50
  "msvcrt"
48
51
  elsif IS_LINUX
@@ -50,26 +53,33 @@ module FFI
50
53
  else
51
54
  "c"
52
55
  end
56
+
53
57
  LIBPREFIX = IS_WINDOWS ? '' : 'lib'
58
+
54
59
  LIBSUFFIX = case OS
55
60
  when /darwin/
56
61
  'dylib'
57
62
  when /linux|bsd|solaris/
58
63
  'so'
59
- when /win/
64
+ when /windows/
60
65
  'dll'
61
66
  else
62
- raise PlatformError, "Cannot determine shared library extension for #{OS}"
67
+ # Punt and just assume a sane unix (i.e. anything but AIX)
68
+ 'so'
63
69
  end
70
+
64
71
  def self.bsd?
65
72
  IS_BSD
66
73
  end
74
+
67
75
  def self.windows?
68
76
  IS_WINDOWS
69
77
  end
78
+
70
79
  def self.mac?
71
80
  IS_MAC
72
81
  end
82
+
73
83
  def self.unix?
74
84
  !IS_WINDOWS
75
85
  end
data/lib/ffi/struct.rb CHANGED
@@ -2,353 +2,182 @@ require 'ffi/platform'
2
2
  module FFI
3
3
 
4
4
  class StructLayout
5
- attr_reader :size, :align
6
5
 
7
- def members
8
- @field_names
9
- end
10
6
  def offsets
11
- @fields.map { |name, field| [name, field.offset] }.sort { |a, b| a[1] <=> b[1] }
7
+ members.map { |m| [ m, self[m].offset ] }
12
8
  end
9
+
13
10
  def offset_of(field_name)
14
- @fields[field_name].offset
11
+ self[field_name].offset
15
12
  end
16
13
  end
17
14
 
18
- class StructLayoutBuilder
19
- class Field
20
- def size
21
- self.class.size
22
- end
23
- def align
24
- self.class.align
25
- end
26
- def offset
27
- @off
28
- end
29
- def self.size
30
- const_get(:SIZE)
31
- end
32
- def self.align
33
- const_get(:ALIGN)
34
- end
35
- end
36
-
37
- def self.struct_field_class_from(type)
38
- klass_name = type.name.split('::').last
39
- code = <<-code
40
- class StructField_#{klass_name} < Field
41
- @info = #{type}
42
- class << self
43
- attr_reader :info
44
- def size
45
- #{type.size}
46
- end
47
- def align
48
- #{type.align}
49
- end
50
- end
51
- def get(ptr)
52
- self.class.info.new(ptr + @off)
53
- end
54
- end
55
- StructField_#{klass_name}
56
- code
57
- self.module_eval(code)
58
- end
59
-
60
- def self.array_field_class_from(type, num)
61
- klass_name = type.name.split('::').last
62
- code = <<-code
63
- class ArrayField_#{klass_name}_#{num} < Field
64
- @info = #{type}
65
- @num = #{num}
66
- class << self
67
- attr_reader :info, :num
68
- def size
69
- #{type.size} * #{num}
70
- end
71
- def align
72
- #{type.align}
73
- end
74
- end
75
- def get(ptr)
76
- @array ? @array : get_array_data(ptr)
77
- end
78
- private
79
- def get_array_data(ptr)
80
- @array = FFI::Struct::Array.new(ptr + @off, self.class.info, self.class.num)
81
- end
82
- end
83
- ArrayField_#{klass_name}_#{num}
84
- code
85
- self.module_eval(code)
86
- end
87
-
88
- class CallbackField < Field
89
- def self.size
90
- FFI::Type::POINTER.size
91
- end
92
-
93
- def self.align
94
- FFI::Type::POINTER.alignment
95
- end
96
-
97
- def put(ptr, proc)
98
- ptr.put_callback(@off, proc, @info)
99
- end
15
+
16
+ class Struct
100
17
 
101
- def get(ptr)
102
- raise ArgumentError, "Cannot get callback fields"
103
- end
18
+ def size
19
+ self.class.size
104
20
  end
105
21
 
106
- def initialize
107
- @field_names = []
108
- @fields = {}
109
- @size = 0
110
- @min_align = 1
22
+ def alignment
23
+ self.class.alignment
111
24
  end
25
+ alias_method :align, :alignment
112
26
 
113
- def native_field_class_from(type)
114
- case type
115
- when :char, NativeType::INT8
116
- Signed8
117
- when :uchar, NativeType::UINT8
118
- Unsigned8
119
- when :short, NativeType::INT16
120
- Signed16
121
- when :ushort, NativeType::UINT16
122
- Unsigned16
123
- when :long, NativeType::LONG
124
- FFI::Platform::LONG_SIZE == 32 ? Signed32 : Signed64
125
- when :ulong, NativeType::ULONG
126
- FFI::Platform::LONG_SIZE == 32 ? Unsigned32 : Unsigned64
127
- when :int, NativeType::INT32
128
- Signed32
129
- when :uint, NativeType::UINT32
130
- Unsigned32
131
- when :long_long, NativeType::INT64
132
- Signed64
133
- when :ulong_long, NativeType::UINT64
134
- Unsigned64
135
- when :float, NativeType::FLOAT32
136
- FloatField
137
- when :double, NativeType::FLOAT64
138
- DoubleField
139
- when :pointer, NativeType::POINTER
140
- PointerField
141
- when :string, NativeType::STRING
142
- StringField
143
- end
27
+ def offset_of(name)
28
+ self.class.offset_of(name)
144
29
  end
145
30
 
146
- def callback_field_class_from(type)
147
- return CallbackField, type if type.is_a?(FFI::CallbackInfo)
31
+ def members
32
+ self.class.members
148
33
  end
149
34
 
150
- def struct_field_class_from(type)
151
- self.class.struct_field_class_from(type) if type.is_a?(Class) and type < FFI::Struct
35
+ def values
36
+ members.map { |m| self[m] }
152
37
  end
153
38
 
154
- def array_field_class_from(type)
155
- self.class.array_field_class_from(field_class_from(type[0]), type[1]) if type.is_a?(Array)
39
+ def offsets
40
+ self.class.offsets
156
41
  end
157
42
 
158
- def field_class_from(type)
159
- field_class = native_field_class_from(type) ||
160
- callback_field_class_from(type) ||
161
- array_field_class_from(type) ||
162
- struct_field_class_from(type)
163
- field_class or raise ArgumentError, "Unknown type: #{type}"
43
+ def clear
44
+ pointer.clear
45
+ self
164
46
  end
165
47
 
166
- def add_field(name, type, offset = nil)
167
- field_class, info = field_class_from(type)
168
- off = calc_alignment_of(field_class, offset)
169
- calc_current_size(off, field_class.size)
170
- @field_names << name
171
- @fields[name] = field_class.new(off, info)
172
- @min_align = field_class.align if field_class.align > @min_align
48
+ def to_ptr
49
+ pointer
173
50
  end
174
51
 
175
- def build
176
- StructLayout.new(@field_names, @fields, align(@size, @min_align), @min_align)
52
+ def self.size
53
+ defined?(@layout) ? @layout.size : defined?(@size) ? @size : 0
177
54
  end
178
55
 
179
- def align(offset, align)
180
- align + ((offset - 1) & ~(align - 1))
56
+ def self.size=(size)
57
+ raise ArgumentError, "Size already set" if defined?(@size) || defined?(@layout)
58
+ @size = size
181
59
  end
182
60
 
183
- private
184
- def calc_alignment_of(field_class, offset)
185
- offset ? offset.to_i : align(@size, field_class.align)
186
- end
187
- def calc_current_size(offset, size)
188
- @size = offset + size
61
+ def self.alignment
62
+ @layout.alignment
189
63
  end
190
- end
191
64
 
192
- class Struct
193
- class Array
194
- include Enumerable
195
-
196
- def initialize(ptr, type, num)
197
- @pointer, @type, @num = ptr, type, num
198
- end
199
-
200
- def to_ptr
201
- @pointer
202
- end
203
-
204
- def to_a
205
- get_array_data(@pointer)
206
- end
207
-
208
- def size
209
- @num * @type.size
210
- end
211
-
212
- def each(&blk)
213
- to_a.each(&blk)
214
- end
215
-
216
- private
217
- def get_array_data(ptr)
218
- (0..@num - 1).inject([]) do |array, index|
219
- array << @type.new(0).get(ptr + index * @type.size)
220
- end
221
- end
222
- end
223
-
224
- def self.size
225
- @size
65
+ def self.align
66
+ @layout.alignment
226
67
  end
227
68
 
228
69
  def self.members
229
70
  @layout.members
230
71
  end
231
72
 
232
- def self.align
233
- @layout.align
234
- end
235
-
236
73
  def self.offsets
237
74
  @layout.offsets
238
75
  end
239
76
 
240
- def self.offset_of(field_name)
241
- @layout.offset_of(field_name)
77
+ def self.offset_of(name)
78
+ @layout.offset_of(name)
242
79
  end
243
80
 
244
- def size
245
- self.class.size
81
+ def self.in
82
+ :buffer_in
246
83
  end
247
84
 
248
- def align
249
- self.class.align
85
+ def self.out
86
+ :buffer_out
250
87
  end
251
88
 
252
- def members
253
- layout.members
89
+ def self.by_value
90
+ ::FFI::StructByValue.new(self)
254
91
  end
255
92
 
256
- def values
257
- layout.members.map { |m| self[m] }
258
- end
259
- def offsets
260
- self.class.offsets
261
- end
262
93
 
263
- def offset_of(field_name)
264
- self.class.offset_of(field_name)
265
- end
266
94
 
267
- def clear
268
- pointer.clear
269
- self
270
- end
95
+ class << self
96
+ public
271
97
 
272
- def to_ptr
273
- pointer
274
- end
98
+ def layout(*spec)
99
+ return @layout if spec.size == 0
275
100
 
276
- def self.in
277
- :buffer_in
278
- end
101
+ builder = FFI::StructLayoutBuilder.new
102
+ builder.union = self < Union
103
+ if spec[0].kind_of?(Hash)
104
+ hash_layout(builder, spec)
105
+ else
106
+ array_layout(builder, spec)
107
+ end
108
+ builder.size = @size if defined?(@size) && @size > builder.size
109
+ cspec = builder.build
110
+ @layout = cspec unless self == FFI::Struct
111
+ @size = cspec.size
112
+ return cspec
113
+ end
279
114
 
280
- def self.out
281
- :buffer_out
282
- end
283
115
 
284
- protected
116
+ protected
285
117
 
286
- def self.callback(params, ret)
287
- mod = enclosing_module
288
- FFI::CallbackInfo.new(find_type(ret, mod), params.map { |e| find_type(e, mod) })
289
- end
118
+ def callback(params, ret)
119
+ mod = enclosing_module
120
+ FFI::CallbackInfo.new(find_type(ret, mod), params.map { |e| find_type(e, mod) })
121
+ end
290
122
 
291
- private
292
123
 
293
- def self.builder
294
- StructLayoutBuilder.new
295
- end
124
+ def enclosing_module
125
+ begin
126
+ mod = self.name.split("::")[0..-2].inject(Object) { |obj, c| obj.const_get(c) }
127
+ mod.respond_to?(:find_type) ? mod : nil
128
+ rescue Exception => ex
129
+ nil
130
+ end
131
+ end
296
132
 
297
- def self.enclosing_module
298
- begin
299
- mod = self.name.split("::")[0..-2].inject(Object) { |obj, c| obj.const_get(c) }
300
- mod.respond_to?(:find_type) ? mod : nil
301
- rescue Exception => ex
302
- nil
133
+ def find_type(type, mod = nil)
134
+ if (type.kind_of?(Class) && type < FFI::Struct) || type.is_a?(::Array)
135
+ type
136
+ elsif mod
137
+ mod.find_type(type)
138
+ end || FFI.find_type(type)
303
139
  end
304
- end
305
140
 
306
- def self.is_a_struct?(type)
307
- type.is_a?(Class) and type < Struct
308
- end
309
141
 
310
- def self.find_type(type, mod = nil)
311
- return type if is_a_struct?(type) or type.is_a?(::Array)
312
- mod ? mod.find_type(type) : FFI.find_type(type)
313
- end
142
+ private
314
143
 
315
- def self.hash_layout(spec)
316
- raise "Ruby version not supported" if RUBY_VERSION =~ /1.8.*/
317
- builder = self.builder
318
- mod = enclosing_module
319
- spec[0].each do |name,type|
320
- builder.add_field(name, find_type(type, mod))
144
+ def hash_layout(builder, spec)
145
+ raise "Ruby version not supported" if RUBY_VERSION =~ /1.8.*/
146
+ mod = enclosing_module
147
+ spec[0].each do |name,type|
148
+ if type.kind_of?(Class) && type < Struct
149
+ builder.add_struct(name, type)
150
+ elsif type.kind_of?(::Array)
151
+ builder.add_array(name, find_type(type[0], mod), type[1])
152
+ else
153
+ builder.add_field(name, find_type(type, mod))
154
+ end
155
+ end
321
156
  end
322
- builder.build
323
- end
324
157
 
325
- def self.array_layout(spec)
326
- builder = self.builder
327
- mod = enclosing_module
328
- i = 0
329
- while i < spec.size
330
- name, type = spec[i, 2]
331
- i += 2
332
- code = find_type(type, mod)
333
- # If the next param is a Fixnum, it specifies the offset
334
- if spec[i].kind_of?(Fixnum)
335
- offset = spec[i]
336
- i += 1
337
- builder.add_field(name, code, offset)
338
- else
339
- builder.add_field(name, code)
158
+ def array_layout(builder, spec)
159
+ mod = enclosing_module
160
+ i = 0
161
+ while i < spec.size
162
+ name, type = spec[i, 2]
163
+ i += 2
164
+
165
+ # If the next param is a Integer, it specifies the offset
166
+ if spec[i].kind_of?(Integer)
167
+ offset = spec[i]
168
+ i += 1
169
+ else
170
+ offset = nil
171
+ end
172
+ if type.kind_of?(Class) && type < Struct
173
+ builder.add_struct(name, type, offset)
174
+ elsif type.kind_of?(::Array)
175
+ builder.add_array(name, find_type(type[0], mod), type[1], offset)
176
+ else
177
+ builder.add_field(name, find_type(type, mod), offset)
178
+ end
340
179
  end
341
180
  end
342
- builder.build
343
- end
344
-
345
- public
346
- def self.layout(*spec)
347
- return @layout if spec.size == 0
348
- cspec = spec[0].kind_of?(Hash) ? hash_layout(spec) : array_layout(spec)
349
- @layout = cspec unless self == FFI::Struct
350
- @size = cspec.size
351
- return cspec
352
181
  end
353
182
  end
354
183
  end