fiddle 1.0.0 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -10,6 +10,11 @@ module Fiddle
10
10
  # The name of this function
11
11
  attr_reader :name
12
12
 
13
+ # Whether GVL is needed to call this function
14
+ def need_gvl?
15
+ @need_gvl
16
+ end
17
+
13
18
  # The integer memory location of this function
14
19
  def to_i
15
20
  ptr.to_i
@@ -83,11 +83,7 @@ module Fiddle
83
83
  when Importer
84
84
  lib.handlers
85
85
  else
86
- begin
87
- Fiddle.dlopen(lib)
88
- rescue DLError
89
- raise(DLError, "can't load #{lib}")
90
- end
86
+ Fiddle.dlopen(lib)
91
87
  end
92
88
  }.flatten()
93
89
  @handler = CompositeHandler.new(handles)
@@ -115,16 +111,21 @@ module Fiddle
115
111
  return SIZEOF_INT
116
112
  when TYPE_LONG
117
113
  return SIZEOF_LONG
118
- when TYPE_LONG_LONG
119
- return SIZEOF_LONG_LONG
120
114
  when TYPE_FLOAT
121
115
  return SIZEOF_FLOAT
122
116
  when TYPE_DOUBLE
123
117
  return SIZEOF_DOUBLE
124
118
  when TYPE_VOIDP
125
119
  return SIZEOF_VOIDP
120
+ when TYPE_CONST_STRING
121
+ return SIZEOF_CONST_STRING
126
122
  else
127
- raise(DLError, "unknown type: #{ty}")
123
+ if defined?(TYPE_LONG_LONG) and
124
+ ty == TYPE_LONG_LONG
125
+ return SIZEOF_LONG_LONG
126
+ else
127
+ raise(DLError, "unknown type: #{ty}")
128
+ end
128
129
  end
129
130
  when Class
130
131
  if( ty.instance_methods().include?(:to_ptr) )
@@ -154,7 +155,8 @@ module Fiddle
154
155
  # :stopdoc:
155
156
  CALL_TYPE_TO_ABI = Hash.new { |h, k|
156
157
  raise RuntimeError, "unsupported call type: #{k}"
157
- }.merge({ :stdcall => (Function::STDCALL rescue Function::DEFAULT),
158
+ }.merge({ :stdcall => Function.const_defined?(:STDCALL) ? Function::STDCALL :
159
+ Function::DEFAULT,
158
160
  :cdecl => Function::DEFAULT,
159
161
  nil => Function::DEFAULT
160
162
  }).freeze
@@ -18,7 +18,7 @@ module Fiddle
18
18
  }
19
19
 
20
20
  PACK_MAP = {
21
- TYPE_VOIDP => ((SIZEOF_VOIDP == SIZEOF_LONG_LONG) ? "q" : "l!"),
21
+ TYPE_VOIDP => "l!",
22
22
  TYPE_CHAR => "c",
23
23
  TYPE_SHORT => "s!",
24
24
  TYPE_INT => "i!",
@@ -48,6 +48,7 @@ module Fiddle
48
48
  ALIGN_MAP[TYPE_LONG_LONG] = ALIGN_MAP[-TYPE_LONG_LONG] = ALIGN_LONG_LONG
49
49
  PACK_MAP[TYPE_LONG_LONG] = PACK_MAP[-TYPE_LONG_LONG] = "q"
50
50
  SIZE_MAP[TYPE_LONG_LONG] = SIZE_MAP[-TYPE_LONG_LONG] = SIZEOF_LONG_LONG
51
+ PACK_MAP[TYPE_VOIDP] = "q" if SIZEOF_LONG_LONG == SIZEOF_VOIDP
51
52
  end
52
53
 
53
54
  def align(addr, align)
@@ -80,10 +81,13 @@ module Fiddle
80
81
  case SIZEOF_VOIDP
81
82
  when SIZEOF_LONG
82
83
  ary.pack(@template)
83
- when SIZEOF_LONG_LONG
84
- ary.pack(@template)
85
84
  else
86
- raise(RuntimeError, "sizeof(void*)?")
85
+ if defined?(TYPE_LONG_LONG) and
86
+ SIZEOF_VOIDP == SIZEOF_LONG_LONG
87
+ ary.pack(@template)
88
+ else
89
+ raise(RuntimeError, "sizeof(void*)?")
90
+ end
87
91
  end
88
92
  end
89
93
 
@@ -91,10 +95,13 @@ module Fiddle
91
95
  case SIZEOF_VOIDP
92
96
  when SIZEOF_LONG
93
97
  ary.join().unpack(@template)
94
- when SIZEOF_LONG_LONG
95
- ary.join().unpack(@template)
96
98
  else
97
- raise(RuntimeError, "sizeof(void*)?")
99
+ if defined?(TYPE_LONG_LONG) and
100
+ SIZEOF_VOIDP == SIZEOF_LONG_LONG
101
+ ary.join().unpack(@template)
102
+ else
103
+ raise(RuntimeError, "sizeof(void*)?")
104
+ end
98
105
  end
99
106
  end
100
107
 
@@ -4,15 +4,72 @@ require 'fiddle/value'
4
4
  require 'fiddle/pack'
5
5
 
6
6
  module Fiddle
7
- # C struct shell
7
+ # A base class for objects representing a C structure
8
8
  class CStruct
9
+ include Enumerable
10
+
9
11
  # accessor to Fiddle::CStructEntity
10
12
  def CStruct.entity_class
11
13
  CStructEntity
12
14
  end
15
+
16
+ def each
17
+ return enum_for(__function__) unless block_given?
18
+
19
+ self.class.members.each do |name,|
20
+ yield(self[name])
21
+ end
22
+ end
23
+
24
+ def each_pair
25
+ return enum_for(__function__) unless block_given?
26
+
27
+ self.class.members.each do |name,|
28
+ yield(name, self[name])
29
+ end
30
+ end
31
+
32
+ def to_h
33
+ hash = {}
34
+ each_pair do |name, value|
35
+ hash[name] = unstruct(value)
36
+ end
37
+ hash
38
+ end
39
+
40
+ def replace(another)
41
+ if another.nil?
42
+ self.class.members.each do |name,|
43
+ self[name] = nil
44
+ end
45
+ elsif another.respond_to?(:each_pair)
46
+ another.each_pair do |name, value|
47
+ self[name] = value
48
+ end
49
+ else
50
+ another.each do |name, value|
51
+ self[name] = value
52
+ end
53
+ end
54
+ self
55
+ end
56
+
57
+ private
58
+ def unstruct(value)
59
+ case value
60
+ when CStruct
61
+ value.to_h
62
+ when Array
63
+ value.collect do |v|
64
+ unstruct(v)
65
+ end
66
+ else
67
+ value
68
+ end
69
+ end
13
70
  end
14
71
 
15
- # C union shell
72
+ # A base class for objects representing a C union
16
73
  class CUnion
17
74
  # accessor to Fiddle::CUnionEntity
18
75
  def CUnion.entity_class
@@ -20,6 +77,41 @@ module Fiddle
20
77
  end
21
78
  end
22
79
 
80
+ # Wrapper for arrays within a struct
81
+ class StructArray < Array
82
+ include ValueUtil
83
+
84
+ def initialize(ptr, type, initial_values)
85
+ @ptr = ptr
86
+ @type = type
87
+ @is_struct = @type.respond_to?(:entity_class)
88
+ if @is_struct
89
+ super(initial_values)
90
+ else
91
+ @size = Fiddle::PackInfo::SIZE_MAP[type]
92
+ @pack_format = Fiddle::PackInfo::PACK_MAP[type]
93
+ super(initial_values.collect { |v| unsigned_value(v, type) })
94
+ end
95
+ end
96
+
97
+ def to_ptr
98
+ @ptr
99
+ end
100
+
101
+ def []=(index, value)
102
+ if index < 0 || index >= size
103
+ raise IndexError, 'index %d outside of array bounds 0...%d' % [index, size]
104
+ end
105
+
106
+ if @is_struct
107
+ self[index].replace(value)
108
+ else
109
+ to_ptr[index * @size, @size] = [value].pack(@pack_format)
110
+ super(index, value)
111
+ end
112
+ end
113
+ end
114
+
23
115
  # Used to construct C classes (CUnion, CStruct, etc)
24
116
  #
25
117
  # Fiddle::Importer#struct and Fiddle::Importer#union wrap this functionality in an
@@ -35,7 +127,7 @@ module Fiddle
35
127
  # Fiddle::Importer#struct and Fiddle::Importer#union wrap this functionality in an
36
128
  # easy-to-use manner.
37
129
  #
38
- # Example:
130
+ # Examples:
39
131
  #
40
132
  # require 'fiddle/struct'
41
133
  # require 'fiddle/cparser'
@@ -46,47 +138,98 @@ module Fiddle
46
138
  #
47
139
  # MyStruct = Fiddle::CStructBuilder.create(Fiddle::CUnion, types, members)
48
140
  #
49
- # obj = MyStruct.allocate
141
+ # MyStruct.malloc(Fiddle::RUBY_FREE) do |obj|
142
+ # ...
143
+ # end
144
+ #
145
+ # obj = MyStruct.malloc(Fiddle::RUBY_FREE)
146
+ # begin
147
+ # ...
148
+ # ensure
149
+ # obj.call_free
150
+ # end
151
+ #
152
+ # obj = MyStruct.malloc
153
+ # begin
154
+ # ...
155
+ # ensure
156
+ # Fiddle.free obj.to_ptr
157
+ # end
50
158
  #
51
159
  def create(klass, types, members)
52
160
  new_class = Class.new(klass){
53
- define_method(:initialize){|addr|
54
- @entity = klass.entity_class.new(addr, types)
161
+ define_method(:initialize){|addr, func = nil|
162
+ if addr.is_a?(self.class.entity_class)
163
+ @entity = addr
164
+ else
165
+ @entity = self.class.entity_class.new(addr, types, func)
166
+ end
55
167
  @entity.assign_names(members)
56
168
  }
169
+ define_method(:[]) { |*args| @entity.send(:[], *args) }
170
+ define_method(:[]=) { |*args| @entity.send(:[]=, *args) }
57
171
  define_method(:to_ptr){ @entity }
58
172
  define_method(:to_i){ @entity.to_i }
173
+ define_singleton_method(:types) { types }
174
+ define_singleton_method(:members) { members }
59
175
  members.each{|name|
176
+ name = name[0] if name.is_a?(Array) # name is a nested struct
177
+ next if method_defined?(name)
60
178
  define_method(name){ @entity[name] }
61
179
  define_method(name + "="){|val| @entity[name] = val }
62
180
  }
63
- }
64
- size = klass.entity_class.size(types)
65
- new_class.module_eval(<<-EOS, __FILE__, __LINE__+1)
66
- def new_class.size()
67
- #{size}
181
+ entity_class = klass.entity_class
182
+ alignment = entity_class.alignment(types)
183
+ size = entity_class.size(types)
184
+ define_singleton_method(:alignment) { alignment }
185
+ define_singleton_method(:size) { size }
186
+ define_singleton_method(:malloc) do |func=nil, &block|
187
+ if block
188
+ entity_class.malloc(types, func, size) do |entity|
189
+ block.call(new(entity))
190
+ end
191
+ else
192
+ new(entity_class.malloc(types, func, size))
193
+ end
68
194
  end
69
- def new_class.malloc()
70
- addr = Fiddle.malloc(#{size})
71
- new(addr)
72
- end
73
- EOS
195
+ }
74
196
  return new_class
75
197
  end
76
198
  module_function :create
77
199
  end
78
200
 
79
- # A C struct wrapper
201
+ # A pointer to a C structure
80
202
  class CStructEntity < Fiddle::Pointer
81
203
  include PackInfo
82
204
  include ValueUtil
83
205
 
206
+ def CStructEntity.alignment(types)
207
+ max = 1
208
+ types.each do |type, count = 1|
209
+ if type.respond_to?(:entity_class)
210
+ n = type.alignment
211
+ else
212
+ n = ALIGN_MAP[type]
213
+ end
214
+ max = n if n > max
215
+ end
216
+ max
217
+ end
218
+
84
219
  # Allocates a C struct with the +types+ provided.
85
220
  #
86
- # When the instance is garbage collected, the C function +func+ is called.
87
- def CStructEntity.malloc(types, func = nil)
88
- addr = Fiddle.malloc(CStructEntity.size(types))
89
- CStructEntity.new(addr, types, func)
221
+ # See Fiddle::Pointer.malloc for memory management issues.
222
+ def CStructEntity.malloc(types, func = nil, size = size(types), &block)
223
+ if block_given?
224
+ super(size, func) do |struct|
225
+ struct.set_ctypes types
226
+ yield struct
227
+ end
228
+ else
229
+ struct = super(size, func)
230
+ struct.set_ctypes types
231
+ struct
232
+ end
90
233
  end
91
234
 
92
235
  # Returns the offset for the packed sizes for the given +types+.
@@ -102,9 +245,15 @@ module Fiddle
102
245
  max_align = types.map { |type, count = 1|
103
246
  last_offset = offset
104
247
 
105
- align = PackInfo::ALIGN_MAP[type]
248
+ if type.respond_to?(:entity_class)
249
+ align = type.alignment
250
+ type_size = type.size
251
+ else
252
+ align = PackInfo::ALIGN_MAP[type]
253
+ type_size = PackInfo::SIZE_MAP[type]
254
+ end
106
255
  offset = PackInfo.align(last_offset, align) +
107
- (PackInfo::SIZE_MAP[type] * count)
256
+ (type_size * count)
108
257
 
109
258
  align
110
259
  }.max
@@ -118,13 +267,37 @@ module Fiddle
118
267
  #
119
268
  # See also Fiddle::Pointer.new
120
269
  def initialize(addr, types, func = nil)
270
+ if func && addr.is_a?(Pointer) && addr.free
271
+ raise ArgumentError, 'free function specified on both underlying struct Pointer and when creating a CStructEntity - who do you want to free this?'
272
+ end
121
273
  set_ctypes(types)
122
274
  super(addr, @size, func)
123
275
  end
124
276
 
125
277
  # Set the names of the +members+ in this C struct
126
278
  def assign_names(members)
127
- @members = members
279
+ @members = []
280
+ @nested_structs = {}
281
+ members.each_with_index do |member, index|
282
+ if member.is_a?(Array) # nested struct
283
+ member_name = member[0]
284
+ struct_type, struct_count = @ctypes[index]
285
+ if struct_count.nil?
286
+ struct = struct_type.new(to_i + @offset[index])
287
+ else
288
+ structs = struct_count.times.map do |i|
289
+ struct_type.new(to_i + @offset[index] + i * struct_type.size)
290
+ end
291
+ struct = StructArray.new(to_i + @offset[index],
292
+ struct_type,
293
+ structs)
294
+ end
295
+ @nested_structs[member_name] = struct
296
+ else
297
+ member_name = member
298
+ end
299
+ @members << member_name
300
+ end
128
301
  end
129
302
 
130
303
  # Calculates the offsets and sizes for the given +types+ in the struct.
@@ -135,12 +308,18 @@ module Fiddle
135
308
 
136
309
  max_align = types.map { |type, count = 1|
137
310
  orig_offset = offset
138
- align = ALIGN_MAP[type]
311
+ if type.respond_to?(:entity_class)
312
+ align = type.alignment
313
+ type_size = type.size
314
+ else
315
+ align = ALIGN_MAP[type]
316
+ type_size = SIZE_MAP[type]
317
+ end
139
318
  offset = PackInfo.align(orig_offset, align)
140
319
 
141
320
  @offset << offset
142
321
 
143
- offset += (SIZE_MAP[type] * count)
322
+ offset += (type_size * count)
144
323
 
145
324
  align
146
325
  }.max
@@ -148,15 +327,34 @@ module Fiddle
148
327
  @size = PackInfo.align(offset, max_align)
149
328
  end
150
329
 
151
- # Fetch struct member +name+
152
- def [](name)
330
+ # Fetch struct member +name+ if only one argument is specified. If two
331
+ # arguments are specified, the first is an offset and the second is a
332
+ # length and this method returns the string of +length+ bytes beginning at
333
+ # +offset+.
334
+ #
335
+ # Examples:
336
+ #
337
+ # my_struct = struct(['int id']).malloc
338
+ # my_struct.id = 1
339
+ # my_struct['id'] # => 1
340
+ # my_struct[0, 4] # => "\x01\x00\x00\x00".b
341
+ #
342
+ def [](*args)
343
+ return super(*args) if args.size > 1
344
+ name = args[0]
153
345
  idx = @members.index(name)
154
346
  if( idx.nil? )
155
347
  raise(ArgumentError, "no such member: #{name}")
156
348
  end
157
349
  ty = @ctypes[idx]
158
350
  if( ty.is_a?(Array) )
159
- r = super(@offset[idx], SIZE_MAP[ty[0]] * ty[1])
351
+ if ty.first.respond_to?(:entity_class)
352
+ return @nested_structs[name]
353
+ else
354
+ r = super(@offset[idx], SIZE_MAP[ty[0]] * ty[1])
355
+ end
356
+ elsif ty.respond_to?(:entity_class)
357
+ return @nested_structs[name]
160
358
  else
161
359
  r = super(@offset[idx], SIZE_MAP[ty.abs])
162
360
  end
@@ -176,14 +374,44 @@ module Fiddle
176
374
  if( ty.is_a?(Integer) && (ty < 0) )
177
375
  return unsigned_value(val, ty)
178
376
  elsif( ty.is_a?(Array) && (ty[0] < 0) )
179
- return val.collect{|v| unsigned_value(v,ty[0])}
377
+ return StructArray.new(self + @offset[idx], ty[0], val)
180
378
  else
181
379
  return val
182
380
  end
183
381
  end
184
382
 
185
- # Set struct member +name+, to value +val+
186
- def []=(name, val)
383
+ # Set struct member +name+, to value +val+. If more arguments are
384
+ # specified, writes the string of bytes to the memory at the given
385
+ # +offset+ and +length+.
386
+ #
387
+ # Examples:
388
+ #
389
+ # my_struct = struct(['int id']).malloc
390
+ # my_struct['id'] = 1
391
+ # my_struct[0, 4] = "\x01\x00\x00\x00".b
392
+ # my_struct.id # => 1
393
+ #
394
+ def []=(*args)
395
+ return super(*args) if args.size > 2
396
+ name, val = *args
397
+ name = name.to_s if name.is_a?(Symbol)
398
+ nested_struct = @nested_structs[name]
399
+ if nested_struct
400
+ if nested_struct.is_a?(StructArray)
401
+ if val.nil?
402
+ nested_struct.each do |s|
403
+ s.replace(nil)
404
+ end
405
+ else
406
+ val.each_with_index do |v, i|
407
+ nested_struct[i] = v
408
+ end
409
+ end
410
+ else
411
+ nested_struct.replace(val)
412
+ end
413
+ return val
414
+ end
187
415
  idx = @members.index(name)
188
416
  if( idx.nil? )
189
417
  raise(ArgumentError, "no such member: #{name}")
@@ -202,23 +430,16 @@ module Fiddle
202
430
  end
203
431
  end
204
432
 
433
+ undef_method :size=
205
434
  def to_s() # :nodoc:
206
435
  super(@size)
207
436
  end
208
437
  end
209
438
 
210
- # A C union wrapper
439
+ # A pointer to a C union
211
440
  class CUnionEntity < CStructEntity
212
441
  include PackInfo
213
442
 
214
- # Allocates a C union the +types+ provided.
215
- #
216
- # When the instance is garbage collected, the C function +func+ is called.
217
- def CUnionEntity.malloc(types, func=nil)
218
- addr = Fiddle.malloc(CUnionEntity.size(types))
219
- CUnionEntity.new(addr, types, func)
220
- end
221
-
222
443
  # Returns the size needed for the union with the given +types+.
223
444
  #
224
445
  # Fiddle::CUnionEntity.size(
@@ -228,7 +449,11 @@ module Fiddle
228
449
  # Fiddle::TYPE_VOIDP ]) #=> 8
229
450
  def CUnionEntity.size(types)
230
451
  types.map { |type, count = 1|
231
- PackInfo::SIZE_MAP[type] * count
452
+ if type.respond_to?(:entity_class)
453
+ type.size * count
454
+ else
455
+ PackInfo::SIZE_MAP[type] * count
456
+ end
232
457
  }.max
233
458
  end
234
459
 
@@ -241,4 +466,3 @@ module Fiddle
241
466
  end
242
467
  end
243
468
  end
244
-