fiddle 1.1.2 → 1.1.6

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.
@@ -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