fiddle 1.1.2 → 1.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,627 @@
1
+ # This is based on JRuby's FFI-based fiddle implementation.
2
+
3
+ require 'ffi'
4
+
5
+ module Fiddle
6
+ def self.malloc(size)
7
+ Fiddle::Pointer.malloc(size)
8
+ end
9
+
10
+ def self.free(ptr)
11
+ Fiddle::Pointer::LibC::FREE.call(ptr)
12
+ nil
13
+ end
14
+
15
+ def self.dlwrap(val)
16
+ Pointer.to_ptr(val)
17
+ end
18
+
19
+ module Types
20
+ VOID = 0
21
+ VOIDP = 1
22
+ CHAR = 2
23
+ UCHAR = -CHAR
24
+ SHORT = 3
25
+ USHORT = -SHORT
26
+ INT = 4
27
+ UINT = -INT
28
+ LONG = 5
29
+ ULONG = -LONG
30
+ LONG_LONG = 6
31
+ ULONG_LONG = -LONG_LONG
32
+ FLOAT = 7
33
+ DOUBLE = 8
34
+ VARIADIC = 9
35
+ CONST_STRING = 10
36
+ BOOL = 11
37
+ INT8_T = CHAR
38
+ UINT8_T = UCHAR
39
+ if FFI::Type::Builtin::SHORT.size == 2
40
+ INT16_T = SHORT
41
+ UINT16_T = USHORT
42
+ elsif FFI::Type::Builtin::INT.size == 2
43
+ INT16_T = INT
44
+ UINT16_T = UINT
45
+ end
46
+ if FFI::Type::Builtin::SHORT.size == 4
47
+ INT32_T = SHORT
48
+ UINT32_T = USHORT
49
+ elsif FFI::Type::Builtin::INT.size == 4
50
+ INT32_T = INT
51
+ UINT32_T = UINT
52
+ elsif FFI::Type::Builtin::LONG.size == 4
53
+ INT32_T = LONG
54
+ UINT32_T = ULONG
55
+ end
56
+ if FFI::Type::Builtin::INT.size == 8
57
+ INT64_T = INT
58
+ UINT64_T = UINT
59
+ elsif FFI::Type::Builtin::LONG.size == 8
60
+ INT64_T = LONG
61
+ UINT64_T = ULONG
62
+ else
63
+ INT64_T = LONG_LONG
64
+ UINT64_T = ULONG_LONG
65
+ end
66
+
67
+ # FIXME: platform specific values
68
+ SSIZE_T = INT64_T
69
+ SIZE_T = -SSIZE_T
70
+ PTRDIFF_T = SSIZE_T
71
+ INTPTR_T = INT64_T
72
+ UINTPTR_T = -INTPTR_T
73
+ end
74
+
75
+ WINDOWS = FFI::Platform.windows?
76
+
77
+ module FFIBackend
78
+ FFITypes = {
79
+ 'c' => FFI::Type::INT8,
80
+ 'h' => FFI::Type::INT16,
81
+ 'i' => FFI::Type::INT32,
82
+ 'l' => FFI::Type::LONG,
83
+ 'f' => FFI::Type::FLOAT32,
84
+ 'd' => FFI::Type::FLOAT64,
85
+ 'p' => FFI::Type::POINTER,
86
+ 's' => FFI::Type::STRING,
87
+
88
+ Types::VOID => FFI::Type::Builtin::VOID,
89
+ Types::VOIDP => FFI::Type::Builtin::POINTER,
90
+ Types::CHAR => FFI::Type::Builtin::CHAR,
91
+ Types::UCHAR => FFI::Type::Builtin::UCHAR,
92
+ Types::SHORT => FFI::Type::Builtin::SHORT,
93
+ Types::USHORT => FFI::Type::Builtin::USHORT,
94
+ Types::INT => FFI::Type::Builtin::INT,
95
+ Types::UINT => FFI::Type::Builtin::UINT,
96
+ Types::LONG => FFI::Type::Builtin::LONG,
97
+ Types::ULONG => FFI::Type::Builtin::ULONG,
98
+ Types::LONG_LONG => FFI::Type::Builtin::LONG_LONG,
99
+ Types::ULONG_LONG => FFI::Type::Builtin::ULONG_LONG,
100
+ Types::FLOAT => FFI::Type::Builtin::FLOAT,
101
+ Types::DOUBLE => FFI::Type::Builtin::DOUBLE,
102
+ Types::BOOL => FFI::Type::Builtin::BOOL,
103
+ Types::CONST_STRING => FFI::Type::Builtin::POINTER,
104
+ Types::VARIADIC => FFI::Type::Builtin::VARARGS,
105
+ }
106
+
107
+ def self.to_ffi_type(fiddle_type)
108
+ if fiddle_type.is_a?(Symbol)
109
+ fiddle_type = Types.const_get(fiddle_type.to_s.upcase)
110
+ end
111
+ if !fiddle_type.is_a?(Integer) && fiddle_type.respond_to?(:to_int)
112
+ fiddle_type = fiddle_type.to_int
113
+ end
114
+ ffi_type = FFITypes[fiddle_type]
115
+ ffi_type = FFITypes[-fiddle_type] if ffi_type.nil? && fiddle_type.is_a?(Integer) && fiddle_type < 0
116
+ raise TypeError.new("cannot convert #{fiddle_type} to ffi") unless ffi_type
117
+ ffi_type
118
+ end
119
+ end
120
+
121
+ class Function
122
+ DEFAULT = "default"
123
+ STDCALL = "stdcall"
124
+
125
+ def initialize(ptr, args, return_type, abi = DEFAULT, kwargs = nil)
126
+ if kwargs.nil?
127
+ if abi.kind_of? Hash
128
+ kwargs = abi
129
+ abi = DEFAULT
130
+ end
131
+ end
132
+ @name = kwargs[:name] if kwargs.kind_of? Hash
133
+ @ptr, @args, @return_type, @abi = ptr, args, return_type, abi
134
+ raise TypeError.new "invalid argument types" unless args.is_a?(Array)
135
+
136
+ ffi_return_type = Fiddle::FFIBackend.to_ffi_type(@return_type)
137
+ ffi_args = @args.map { |t| Fiddle::FFIBackend.to_ffi_type(t) }
138
+ pointer = FFI::Pointer.new(ptr.to_i)
139
+ options = {convention: @abi}
140
+ if ffi_args.last == FFI::Type::Builtin::VARARGS
141
+ @function = FFI::VariadicInvoker.new(
142
+ pointer,
143
+ ffi_args,
144
+ ffi_return_type,
145
+ options
146
+ )
147
+ else
148
+ @function = FFI::Function.new(ffi_return_type, ffi_args, pointer, options)
149
+ end
150
+ end
151
+
152
+ def call(*args, &block)
153
+ if @function.is_a?(FFI::VariadicInvoker)
154
+ n_fixed_args = @args.size - 1
155
+ n_fixed_args.step(args.size - 1, 2) do |i|
156
+ if args[i] == :const_string || args[i] == Types::CONST_STRING
157
+ args[i + 1] = String.try_convert(args[i + 1]) || args[i + 1]
158
+ end
159
+ args[i] = Fiddle::FFIBackend.to_ffi_type(args[i])
160
+ end
161
+ else
162
+ @args.each_with_index do |arg_type, i|
163
+ next unless arg_type == Types::VOIDP
164
+
165
+ src = args[i]
166
+ next if src.nil?
167
+ next if src.is_a?(String)
168
+ next if src.is_a?(FFI::AbstractMemory)
169
+ next if src.is_a?(FFI::Struct)
170
+
171
+ args[i] = Pointer[src]
172
+ end
173
+ end
174
+ result = @function.call(*args, &block)
175
+ result = Pointer.new(result) if result.is_a?(FFI::Pointer)
176
+ result
177
+ end
178
+ end
179
+
180
+ class Closure
181
+ def initialize(ret, args, abi = Function::DEFAULT)
182
+ raise TypeError.new "invalid argument types" unless args.is_a?(Array)
183
+
184
+ @ctype, @args = ret, args
185
+ ffi_args = @args.map { |t| Fiddle::FFIBackend.to_ffi_type(t) }
186
+ if ffi_args.size == 1 && ffi_args[0] == FFI::Type::Builtin::VOID
187
+ ffi_args = []
188
+ end
189
+ return_type = Fiddle::FFIBackend.to_ffi_type(@ctype)
190
+ raise "#{self.class} must implement #call" unless respond_to?(:call)
191
+ callable = method(:call)
192
+ @function = FFI::Function.new(return_type, ffi_args, callable, convention: abi)
193
+ @freed = false
194
+ end
195
+
196
+ def to_ptr
197
+ @function
198
+ end
199
+
200
+ def to_i
201
+ @function.to_i
202
+ end
203
+
204
+ def free
205
+ return if @freed
206
+ @function.free
207
+ @freed = true
208
+ end
209
+
210
+ def freed?
211
+ @freed
212
+ end
213
+ end
214
+
215
+ class Error < StandardError; end
216
+ class DLError < Error; end
217
+ class ClearedReferenceError < Error; end
218
+
219
+ class Pointer
220
+ attr_reader :ffi_ptr
221
+ extend FFI::DataConverter
222
+ native_type FFI::Type::Builtin::POINTER
223
+
224
+ def self.to_native(value, ctx)
225
+ if value.is_a?(Pointer)
226
+ value.ffi_ptr
227
+
228
+ elsif value.is_a?(Integer)
229
+ FFI::Pointer.new(value)
230
+
231
+ elsif value.is_a?(String)
232
+ value
233
+ end
234
+ end
235
+
236
+ def self.from_native(value, ctx)
237
+ self.new(value)
238
+ end
239
+
240
+ def self.to_ptr(value)
241
+ if value.is_a?(String)
242
+ cptr = Pointer.malloc(value.bytesize)
243
+ cptr.ffi_ptr.put_string(0, value)
244
+ cptr
245
+
246
+ elsif value.is_a?(Array)
247
+ raise NotImplementedError, "array ptr"
248
+
249
+ elsif value.respond_to?(:to_ptr)
250
+ ptr = value.to_ptr
251
+ case ptr
252
+ when Pointer
253
+ ptr
254
+ when FFI::Pointer
255
+ Pointer.new(ptr)
256
+ else
257
+ raise DLError.new("to_ptr should return a Fiddle::Pointer object, was #{ptr.class}")
258
+ end
259
+
260
+ else
261
+ Pointer.new(value)
262
+ end
263
+ end
264
+
265
+ def self.write(addr, bytes)
266
+ FFI::Pointer.new(addr).write_bytes(bytes)
267
+ end
268
+
269
+ def self.read(addr, len)
270
+ FFI::Pointer.new(addr).read_bytes(len)
271
+ end
272
+
273
+ class << self
274
+ alias [] to_ptr
275
+ end
276
+
277
+ def []=(*args, value)
278
+ if args.size == 2
279
+ if value.is_a?(Integer)
280
+ value = self.class.new(value)
281
+ end
282
+ if value.is_a?(Fiddle::Pointer)
283
+ value = value.to_str(args[1])
284
+ end
285
+
286
+ @ffi_ptr.put_bytes(args[0], value, 0, args[1])
287
+ elsif args.size == 1
288
+ if value.is_a?(Fiddle::Pointer)
289
+ value = value.to_str(args[0] + 1)
290
+ else
291
+ value = value.chr
292
+ end
293
+
294
+ @ffi_ptr.put_bytes(args[0], value, 0, 1)
295
+ end
296
+ rescue FFI::NullPointerError
297
+ raise DLError.new("NULL pointer access")
298
+ end
299
+
300
+ def initialize(addr, size = nil, free = nil)
301
+ ptr = if addr.is_a?(FFI::Pointer)
302
+ addr
303
+
304
+ elsif addr.is_a?(Integer)
305
+ FFI::Pointer.new(addr)
306
+
307
+ elsif addr.respond_to?(:to_ptr)
308
+ fiddle_ptr = addr.to_ptr
309
+ if fiddle_ptr.is_a?(Pointer)
310
+ fiddle_ptr.ffi_ptr
311
+ elsif fiddle_ptr.is_a?(FFI::AutoPointer)
312
+ addr.ffi_ptr
313
+ elsif fiddle_ptr.is_a?(FFI::Pointer)
314
+ fiddle_ptr
315
+ else
316
+ raise DLError.new("to_ptr should return a Fiddle::Pointer object, was #{fiddle_ptr.class}")
317
+ end
318
+ elsif addr.is_a?(IO)
319
+ raise NotImplementedError, "IO ptr isn't supported"
320
+ else
321
+ FFI::Pointer.new(Integer(addr))
322
+ end
323
+
324
+ @size = size ? size : ptr.size
325
+ @free = free
326
+ @ffi_ptr = ptr
327
+ @freed = false
328
+ end
329
+
330
+ module LibC
331
+ extend FFI::Library
332
+ ffi_lib FFI::Library::LIBC
333
+ MALLOC = attach_function :malloc, [ :size_t ], :pointer
334
+ REALLOC = attach_function :realloc, [ :pointer, :size_t ], :pointer
335
+ FREE = attach_function :free, [ :pointer ], :void
336
+ end
337
+
338
+ def self.malloc(size, free = nil)
339
+ if block_given? and free.nil?
340
+ message = "a free function must be supplied to #{self}.malloc " +
341
+ "when it is called with a block"
342
+ raise ArgumentError, message
343
+ end
344
+
345
+ pointer = new(LibC.malloc(size), size, free)
346
+ if block_given?
347
+ begin
348
+ yield(pointer)
349
+ ensure
350
+ pointer.call_free
351
+ end
352
+ else
353
+ pointer
354
+ end
355
+ end
356
+
357
+ def null?
358
+ @ffi_ptr.null?
359
+ end
360
+
361
+ def to_ptr
362
+ @ffi_ptr
363
+ end
364
+
365
+ def size
366
+ defined?(@layout) ? @layout.size : @size
367
+ end
368
+
369
+ def free
370
+ @free
371
+ end
372
+
373
+ def free=(free)
374
+ @free = free
375
+ end
376
+
377
+ def call_free
378
+ return if @free.nil?
379
+ return if @freed
380
+ if @free == RUBY_FREE
381
+ LibC::FREE.call(@ffi_ptr)
382
+ else
383
+ @free.call(@ffi_ptr)
384
+ end
385
+ @freed = true
386
+ end
387
+
388
+ def freed?
389
+ @freed
390
+ end
391
+
392
+ def size=(size)
393
+ @size = size
394
+ end
395
+
396
+ def [](index, length = nil)
397
+ if length
398
+ ffi_ptr.get_bytes(index, length)
399
+ else
400
+ ffi_ptr.get_char(index)
401
+ end
402
+ rescue FFI::NullPointerError
403
+ raise DLError.new("NULL pointer dereference")
404
+ end
405
+
406
+ def to_i
407
+ ffi_ptr.to_i
408
+ end
409
+ alias to_int to_i
410
+
411
+ # without \0
412
+ def to_s(len = nil)
413
+ if len
414
+ ffi_ptr.get_string(0, len)
415
+ else
416
+ ffi_ptr.get_string(0)
417
+ end
418
+ rescue FFI::NullPointerError
419
+ raise DLError.new("NULL pointer access")
420
+ end
421
+
422
+ def to_str(len = nil)
423
+ if len
424
+ ffi_ptr.read_string(len)
425
+ else
426
+ ffi_ptr.read_string(@size)
427
+ end
428
+ rescue FFI::NullPointerError
429
+ raise DLError.new("NULL pointer access")
430
+ end
431
+
432
+ def to_value
433
+ raise NotImplementedError, "to_value isn't supported"
434
+ end
435
+
436
+ def inspect
437
+ "#<#{self.class.name} ptr=#{to_i.to_s(16)} size=#{@size} free=#{@free.inspect}>"
438
+ end
439
+
440
+ def +(delta)
441
+ self.class.new(to_i + delta, @size - delta)
442
+ end
443
+
444
+ def -(delta)
445
+ self.class.new(to_i - delta, @size + delta)
446
+ end
447
+
448
+ def <=>(other)
449
+ return unless other.is_a?(Pointer)
450
+ diff = self.to_i - other.to_i
451
+ return 0 if diff == 0
452
+ diff > 0 ? 1 : -1
453
+ end
454
+
455
+ def eql?(other)
456
+ return unless other.is_a?(Pointer)
457
+ self.to_i == other.to_i
458
+ end
459
+
460
+ def ==(other)
461
+ eql?(other)
462
+ end
463
+
464
+ def ptr
465
+ Pointer.new(ffi_ptr.get_pointer(0))
466
+ end
467
+
468
+ def +@
469
+ ptr
470
+ end
471
+
472
+ def -@
473
+ ref
474
+ end
475
+
476
+ def ref
477
+ cptr = Pointer.malloc(FFI::Type::POINTER.size, RUBY_FREE)
478
+ cptr.ffi_ptr.put_pointer(0, ffi_ptr)
479
+ cptr
480
+ end
481
+ end
482
+
483
+ class Handle
484
+ RTLD_GLOBAL = FFI::DynamicLibrary::RTLD_GLOBAL
485
+ RTLD_LAZY = FFI::DynamicLibrary::RTLD_LAZY
486
+ RTLD_NOW = FFI::DynamicLibrary::RTLD_NOW
487
+
488
+ def initialize(libname = nil, flags = RTLD_LAZY | RTLD_GLOBAL)
489
+ begin
490
+ @lib = FFI::DynamicLibrary.open(libname, flags)
491
+ rescue LoadError, RuntimeError # LoadError for JRuby, RuntimeError for TruffleRuby
492
+ raise DLError, "Could not open #{libname}"
493
+ end
494
+
495
+ @open = true
496
+
497
+ begin
498
+ yield(self)
499
+ ensure
500
+ self.close
501
+ end if block_given?
502
+ end
503
+
504
+ def close
505
+ raise DLError.new("closed handle") unless @open
506
+ @open = false
507
+ 0
508
+ end
509
+
510
+ def self.sym(func)
511
+ DEFAULT.sym(func)
512
+ end
513
+
514
+ def sym(func)
515
+ raise TypeError.new("invalid function name") unless func.is_a?(String)
516
+ raise DLError.new("closed handle") unless @open
517
+ address = @lib.find_function(func)
518
+ raise DLError.new("unknown symbol #{func}") if address.nil? || address.null?
519
+ address.to_i
520
+ end
521
+
522
+ def self.sym_defined?(func)
523
+ DEFAULT.sym_defined?(func)
524
+ end
525
+
526
+ def sym_defined?(func)
527
+ raise TypeError.new("invalid function name") unless func.is_a?(String)
528
+ raise DLError.new("closed handle") unless @open
529
+ address = @lib.find_function(func)
530
+ !address.nil? && !address.null?
531
+ end
532
+
533
+ def self.[](func)
534
+ self.sym(func)
535
+ end
536
+
537
+ def [](func)
538
+ sym(func)
539
+ end
540
+
541
+ def enable_close
542
+ @enable_close = true
543
+ end
544
+
545
+ def close_enabled?
546
+ @enable_close
547
+ end
548
+
549
+ def disable_close
550
+ @enable_close = false
551
+ end
552
+
553
+ DEFAULT = new
554
+ end
555
+
556
+ class Pinned
557
+ def initialize(object)
558
+ @object = object
559
+ end
560
+
561
+ def ref
562
+ if @object.nil?
563
+ raise ClearedReferenceError, "`ref` called on a cleared object"
564
+ end
565
+ @object
566
+ end
567
+
568
+ def clear
569
+ @object = nil
570
+ end
571
+
572
+ def cleared?
573
+ @object.nil?
574
+ end
575
+ end
576
+
577
+ RUBY_FREE = Fiddle::Pointer::LibC::FREE.address
578
+ NULL = Fiddle::Pointer.new(0)
579
+
580
+ ALIGN_VOIDP = Fiddle::FFIBackend::FFITypes[Types::VOIDP].alignment
581
+ ALIGN_CHAR = Fiddle::FFIBackend::FFITypes[Types::CHAR].alignment
582
+ ALIGN_SHORT = Fiddle::FFIBackend::FFITypes[Types::SHORT].alignment
583
+ ALIGN_INT = Fiddle::FFIBackend::FFITypes[Types::INT].alignment
584
+ ALIGN_LONG = Fiddle::FFIBackend::FFITypes[Types::LONG].alignment
585
+ ALIGN_LONG_LONG = Fiddle::FFIBackend::FFITypes[Types::LONG_LONG].alignment
586
+ ALIGN_INT8_T = Fiddle::FFIBackend::FFITypes[Types::INT8_T].alignment
587
+ ALIGN_INT16_T = Fiddle::FFIBackend::FFITypes[Types::INT16_T].alignment
588
+ ALIGN_INT32_T = Fiddle::FFIBackend::FFITypes[Types::INT32_T].alignment
589
+ ALIGN_INT64_T = Fiddle::FFIBackend::FFITypes[Types::INT64_T].alignment
590
+ ALIGN_FLOAT = Fiddle::FFIBackend::FFITypes[Types::FLOAT].alignment
591
+ ALIGN_DOUBLE = Fiddle::FFIBackend::FFITypes[Types::DOUBLE].alignment
592
+ ALIGN_BOOL = Fiddle::FFIBackend::FFITypes[Types::BOOL].alignment
593
+ ALIGN_SIZE_T = Fiddle::FFIBackend::FFITypes[Types::SIZE_T].alignment
594
+ ALIGN_SSIZE_T = ALIGN_SIZE_T
595
+ ALIGN_PTRDIFF_T = Fiddle::FFIBackend::FFITypes[Types::PTRDIFF_T].alignment
596
+ ALIGN_INTPTR_T = Fiddle::FFIBackend::FFITypes[Types::INTPTR_T].alignment
597
+ ALIGN_UINTPTR_T = Fiddle::FFIBackend::FFITypes[Types::UINTPTR_T].alignment
598
+
599
+ SIZEOF_VOIDP = Fiddle::FFIBackend::FFITypes[Types::VOIDP].size
600
+ SIZEOF_CHAR = Fiddle::FFIBackend::FFITypes[Types::CHAR].size
601
+ SIZEOF_UCHAR = Fiddle::FFIBackend::FFITypes[Types::UCHAR].size
602
+ SIZEOF_SHORT = Fiddle::FFIBackend::FFITypes[Types::SHORT].size
603
+ SIZEOF_USHORT = Fiddle::FFIBackend::FFITypes[Types::USHORT].size
604
+ SIZEOF_INT = Fiddle::FFIBackend::FFITypes[Types::INT].size
605
+ SIZEOF_UINT = Fiddle::FFIBackend::FFITypes[Types::UINT].size
606
+ SIZEOF_LONG = Fiddle::FFIBackend::FFITypes[Types::LONG].size
607
+ SIZEOF_ULONG = Fiddle::FFIBackend::FFITypes[Types::ULONG].size
608
+ SIZEOF_LONG_LONG = Fiddle::FFIBackend::FFITypes[Types::LONG_LONG].size
609
+ SIZEOF_ULONG_LONG = Fiddle::FFIBackend::FFITypes[Types::ULONG_LONG].size
610
+ SIZEOF_INT8_T = Fiddle::FFIBackend::FFITypes[Types::INT8_T].size
611
+ SIZEOF_UINT8_T = Fiddle::FFIBackend::FFITypes[Types::UINT8_T].size
612
+ SIZEOF_INT16_T = Fiddle::FFIBackend::FFITypes[Types::INT16_T].size
613
+ SIZEOF_UINT16_T = Fiddle::FFIBackend::FFITypes[Types::UINT16_T].size
614
+ SIZEOF_INT32_T = Fiddle::FFIBackend::FFITypes[Types::INT32_T].size
615
+ SIZEOF_UINT32_T = Fiddle::FFIBackend::FFITypes[Types::UINT32_T].size
616
+ SIZEOF_INT64_T = Fiddle::FFIBackend::FFITypes[Types::INT64_T].size
617
+ SIZEOF_UINT64_T = Fiddle::FFIBackend::FFITypes[Types::UINT64_T].size
618
+ SIZEOF_FLOAT = Fiddle::FFIBackend::FFITypes[Types::FLOAT].size
619
+ SIZEOF_DOUBLE = Fiddle::FFIBackend::FFITypes[Types::DOUBLE].size
620
+ SIZEOF_BOOL = Fiddle::FFIBackend::FFITypes[Types::BOOL].size
621
+ SIZEOF_SIZE_T = Fiddle::FFIBackend::FFITypes[Types::SIZE_T].size
622
+ SIZEOF_SSIZE_T = SIZEOF_SIZE_T
623
+ SIZEOF_PTRDIFF_T = Fiddle::FFIBackend::FFITypes[Types::PTRDIFF_T].size
624
+ SIZEOF_INTPTR_T = Fiddle::FFIBackend::FFITypes[Types::INTPTR_T].size
625
+ SIZEOF_UINTPTR_T = Fiddle::FFIBackend::FFITypes[Types::UINTPTR_T].size
626
+ SIZEOF_CONST_STRING = Fiddle::FFIBackend::FFITypes[Types::VOIDP].size
627
+ end
data/lib/fiddle/pack.rb CHANGED
@@ -41,6 +41,12 @@ module Fiddle
41
41
  when SIZEOF_LONG
42
42
  PACK_MAP[TYPE_BOOL] = PACK_MAP[TYPE_ULONG]
43
43
  end
44
+ if RUBY_ENGINE == "jruby" and WINDOWS and [0].pack("l!").size == 8
45
+ # JRuby's 'l!' pack string doesn't use 32-bit on Windows.
46
+ # See https://github.com/jruby/jruby/issues/8357 for details
47
+ PACK_MAP[TYPE_LONG] = PACK_MAP[TYPE_INT]
48
+ PACK_MAP[TYPE_ULONG] = PACK_MAP[TYPE_UINT]
49
+ end
44
50
 
45
51
  SIZE_MAP = {
46
52
  TYPE_VOIDP => SIZEOF_VOIDP,
data/lib/fiddle/struct.rb CHANGED
@@ -290,15 +290,28 @@ module Fiddle
290
290
  # Allocates a C struct with the +types+ provided.
291
291
  #
292
292
  # See Fiddle::Pointer.malloc for memory management issues.
293
- def CStructEntity.malloc(types, func = nil, size = size(types), &block)
293
+ def CStructEntity.malloc(types, func = nil, size = size(types))
294
+ if block_given? and func.nil?
295
+ message = "a free function must be supplied to #{self}.malloc " +
296
+ "when it is called with a block"
297
+ raise ArgumentError, message
298
+ end
299
+
300
+ pointer = Pointer.malloc(size)
301
+ begin
302
+ struct = new(pointer, types, func)
303
+ rescue
304
+ pointer.free = func
305
+ pointer.call_free
306
+ raise
307
+ end
294
308
  if block_given?
295
- super(size, func) do |struct|
296
- struct.set_ctypes types
297
- yield struct
309
+ begin
310
+ yield(struct)
311
+ ensure
312
+ struct.call_free
298
313
  end
299
314
  else
300
- struct = super(size, func)
301
- struct.set_ctypes types
302
315
  struct
303
316
  end
304
317
  end
@@ -505,6 +518,14 @@ module Fiddle
505
518
  def to_s() # :nodoc:
506
519
  super(@size)
507
520
  end
521
+
522
+ def +(delta)
523
+ Pointer.new(to_i + delta, @size - delta)
524
+ end
525
+
526
+ def -(delta)
527
+ Pointer.new(to_i - delta, @size + delta)
528
+ end
508
529
  end
509
530
 
510
531
  # A pointer to a C union
@@ -1,3 +1,3 @@
1
1
  module Fiddle
2
- VERSION = "1.1.2"
2
+ VERSION = "1.1.6"
3
3
  end