fiddle 1.1.2 → 1.1.3

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,613 @@
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.map! do |arg|
163
+ if arg.respond_to?(:to_ptr)
164
+ begin
165
+ arg = arg.to_ptr
166
+ end until arg.is_a?(FFI::Pointer) || !arg.respond_to?(:to_ptr)
167
+ arg
168
+ else
169
+ arg
170
+ end
171
+ end
172
+ end
173
+ result = @function.call(*args, &block)
174
+ result = Pointer.new(result) if result.is_a?(FFI::Pointer)
175
+ result
176
+ end
177
+ end
178
+
179
+ class Closure
180
+ def initialize(ret, args, abi = Function::DEFAULT)
181
+ raise TypeError.new "invalid argument types" unless args.is_a?(Array)
182
+
183
+ @ctype, @args = ret, args
184
+ ffi_args = @args.map { |t| Fiddle::FFIBackend.to_ffi_type(t) }
185
+ if ffi_args.size == 1 && ffi_args[0] == FFI::Type::Builtin::VOID
186
+ ffi_args = []
187
+ end
188
+ return_type = Fiddle::FFIBackend.to_ffi_type(@ctype)
189
+ raise "#{self.class} must implement #call" unless respond_to?(:call)
190
+ callable = method(:call)
191
+ @function = FFI::Function.new(return_type, ffi_args, callable, convention: abi)
192
+ @freed = false
193
+ end
194
+
195
+ def to_ptr
196
+ @function
197
+ end
198
+
199
+ def to_i
200
+ @function.to_i
201
+ end
202
+
203
+ def free
204
+ return if @freed
205
+ @function.free
206
+ @freed = true
207
+ end
208
+
209
+ def freed?
210
+ @freed
211
+ end
212
+ end
213
+
214
+ class Error < StandardError; end
215
+ class DLError < Error; end
216
+ class ClearedReferenceError < Error; end
217
+
218
+ class Pointer
219
+ attr_reader :ffi_ptr
220
+ extend FFI::DataConverter
221
+ native_type FFI::Type::Builtin::POINTER
222
+
223
+ def self.to_native(value, ctx)
224
+ if value.is_a?(Pointer)
225
+ value.ffi_ptr
226
+
227
+ elsif value.is_a?(Integer)
228
+ FFI::Pointer.new(value)
229
+
230
+ elsif value.is_a?(String)
231
+ value
232
+ end
233
+ end
234
+
235
+ def self.from_native(value, ctx)
236
+ self.new(value)
237
+ end
238
+
239
+ def self.to_ptr(value)
240
+ if value.is_a?(String)
241
+ cptr = Pointer.malloc(value.bytesize)
242
+ cptr.ffi_ptr.put_string(0, value)
243
+ cptr
244
+
245
+ elsif value.is_a?(Array)
246
+ raise NotImplementedError, "array ptr"
247
+
248
+ elsif value.respond_to?(:to_ptr)
249
+ ptr = value.to_ptr
250
+ case ptr
251
+ when Pointer
252
+ ptr
253
+ when FFI::Pointer
254
+ Pointer.new(ptr)
255
+ else
256
+ raise DLError.new("to_ptr should return a Fiddle::Pointer object, was #{ptr.class}")
257
+ end
258
+
259
+ else
260
+ Pointer.new(value)
261
+ end
262
+ end
263
+
264
+ class << self
265
+ alias [] to_ptr
266
+ end
267
+
268
+ def []=(*args, value)
269
+ if args.size == 2
270
+ if value.is_a?(Integer)
271
+ value = self.class.new(value)
272
+ end
273
+ if value.is_a?(Fiddle::Pointer)
274
+ value = value.to_str(args[1])
275
+ end
276
+
277
+ @ffi_ptr.put_bytes(args[0], value, 0, args[1])
278
+ elsif args.size == 1
279
+ if value.is_a?(Fiddle::Pointer)
280
+ value = value.to_str(args[0] + 1)
281
+ else
282
+ value = value.chr
283
+ end
284
+
285
+ @ffi_ptr.put_bytes(args[0], value, 0, 1)
286
+ end
287
+ rescue FFI::NullPointerError
288
+ raise DLError.new("NULL pointer access")
289
+ end
290
+
291
+ def initialize(addr, size = nil, free = nil)
292
+ ptr = if addr.is_a?(FFI::Pointer)
293
+ addr
294
+
295
+ elsif addr.is_a?(Integer)
296
+ FFI::Pointer.new(addr)
297
+
298
+ elsif addr.respond_to?(:to_ptr)
299
+ fiddle_ptr = addr.to_ptr
300
+ if fiddle_ptr.is_a?(Pointer)
301
+ fiddle_ptr.ffi_ptr
302
+ elsif fiddle_ptr.is_a?(FFI::AutoPointer)
303
+ addr.ffi_ptr
304
+ elsif fiddle_ptr.is_a?(FFI::Pointer)
305
+ fiddle_ptr
306
+ else
307
+ raise DLError.new("to_ptr should return a Fiddle::Pointer object, was #{fiddle_ptr.class}")
308
+ end
309
+ elsif addr.is_a?(IO)
310
+ raise NotImplementedError, "IO ptr isn't supported"
311
+ end
312
+
313
+ @size = size ? size : ptr.size
314
+ @free = free
315
+ @ffi_ptr = ptr
316
+ @freed = false
317
+ end
318
+
319
+ module LibC
320
+ extend FFI::Library
321
+ ffi_lib FFI::Library::LIBC
322
+ MALLOC = attach_function :malloc, [ :size_t ], :pointer
323
+ REALLOC = attach_function :realloc, [ :pointer, :size_t ], :pointer
324
+ FREE = attach_function :free, [ :pointer ], :void
325
+ end
326
+
327
+ def self.malloc(size, free = nil)
328
+ if block_given? and free.nil?
329
+ message = "a free function must be supplied to #{self}.malloc " +
330
+ "when it is called with a block"
331
+ raise ArgumentError, message
332
+ end
333
+
334
+ pointer = new(LibC.malloc(size), size, free)
335
+ if block_given?
336
+ begin
337
+ yield(pointer)
338
+ ensure
339
+ pointer.call_free
340
+ end
341
+ else
342
+ pointer
343
+ end
344
+ end
345
+
346
+ def null?
347
+ @ffi_ptr.null?
348
+ end
349
+
350
+ def to_ptr
351
+ @ffi_ptr
352
+ end
353
+
354
+ def size
355
+ defined?(@layout) ? @layout.size : @size
356
+ end
357
+
358
+ def free
359
+ @free
360
+ end
361
+
362
+ def free=(free)
363
+ @free = free
364
+ end
365
+
366
+ def call_free
367
+ return if @free.nil?
368
+ return if @freed
369
+ if @free == RUBY_FREE
370
+ LibC::FREE.call(@ffi_ptr)
371
+ else
372
+ @free.call(@ffi_ptr)
373
+ end
374
+ @freed = true
375
+ end
376
+
377
+ def freed?
378
+ @freed
379
+ end
380
+
381
+ def size=(size)
382
+ @size = size
383
+ end
384
+
385
+ def [](index, length = nil)
386
+ if length
387
+ ffi_ptr.get_bytes(index, length)
388
+ else
389
+ ffi_ptr.get_char(index)
390
+ end
391
+ rescue FFI::NullPointerError
392
+ raise DLError.new("NULL pointer dereference")
393
+ end
394
+
395
+ def to_i
396
+ ffi_ptr.to_i
397
+ end
398
+ alias to_int to_i
399
+
400
+ # without \0
401
+ def to_s(len = nil)
402
+ if len
403
+ ffi_ptr.get_string(0, len)
404
+ else
405
+ ffi_ptr.get_string(0)
406
+ end
407
+ rescue FFI::NullPointerError
408
+ raise DLError.new("NULL pointer access")
409
+ end
410
+
411
+ def to_str(len = nil)
412
+ if len
413
+ ffi_ptr.read_string(len)
414
+ else
415
+ ffi_ptr.read_string(@size)
416
+ end
417
+ rescue FFI::NullPointerError
418
+ raise DLError.new("NULL pointer access")
419
+ end
420
+
421
+ def to_value
422
+ raise NotImplementedError, "to_value isn't supported"
423
+ end
424
+
425
+ def inspect
426
+ "#<#{self.class.name} ptr=#{to_i.to_s(16)} size=#{@size} free=#{@free.inspect}>"
427
+ end
428
+
429
+ def +(delta)
430
+ self.class.new(to_i + delta, @size - delta)
431
+ end
432
+
433
+ def -(delta)
434
+ self.class.new(to_i - delta, @size + delta)
435
+ end
436
+
437
+ def <=>(other)
438
+ return unless other.is_a?(Pointer)
439
+ diff = self.to_i - other.to_i
440
+ return 0 if diff == 0
441
+ diff > 0 ? 1 : -1
442
+ end
443
+
444
+ def eql?(other)
445
+ return unless other.is_a?(Pointer)
446
+ self.to_i == other.to_i
447
+ end
448
+
449
+ def ==(other)
450
+ eql?(other)
451
+ end
452
+
453
+ def ptr
454
+ Pointer.new(ffi_ptr.get_pointer(0))
455
+ end
456
+
457
+ def +@
458
+ ptr
459
+ end
460
+
461
+ def -@
462
+ ref
463
+ end
464
+
465
+ def ref
466
+ cptr = Pointer.malloc(FFI::Type::POINTER.size, RUBY_FREE)
467
+ cptr.ffi_ptr.put_pointer(0, ffi_ptr)
468
+ cptr
469
+ end
470
+ end
471
+
472
+ class Handle
473
+ RTLD_GLOBAL = FFI::DynamicLibrary::RTLD_GLOBAL
474
+ RTLD_LAZY = FFI::DynamicLibrary::RTLD_LAZY
475
+ RTLD_NOW = FFI::DynamicLibrary::RTLD_NOW
476
+
477
+ def initialize(libname = nil, flags = RTLD_LAZY | RTLD_GLOBAL)
478
+ @lib = FFI::DynamicLibrary.open(libname, flags) rescue LoadError
479
+ raise DLError.new("Could not open #{libname}") unless @lib
480
+
481
+ @open = true
482
+
483
+ begin
484
+ yield(self)
485
+ ensure
486
+ self.close
487
+ end if block_given?
488
+ end
489
+
490
+ def close
491
+ raise DLError.new("closed handle") unless @open
492
+ @open = false
493
+ 0
494
+ end
495
+
496
+ def self.sym(func)
497
+ DEFAULT.sym(func)
498
+ end
499
+
500
+ def sym(func)
501
+ raise TypeError.new("invalid function name") unless func.is_a?(String)
502
+ raise DLError.new("closed handle") unless @open
503
+ address = @lib.find_function(func)
504
+ raise DLError.new("unknown symbol #{func}") if address.nil? || address.null?
505
+ address.to_i
506
+ end
507
+
508
+ def self.sym_defined?(func)
509
+ DEFAULT.sym_defined?(func)
510
+ end
511
+
512
+ def sym_defined?(func)
513
+ raise TypeError.new("invalid function name") unless func.is_a?(String)
514
+ raise DLError.new("closed handle") unless @open
515
+ address = @lib.find_function(func)
516
+ !address.nil? && !address.null?
517
+ end
518
+
519
+ def self.[](func)
520
+ self.sym(func)
521
+ end
522
+
523
+ def [](func)
524
+ sym(func)
525
+ end
526
+
527
+ def enable_close
528
+ @enable_close = true
529
+ end
530
+
531
+ def close_enabled?
532
+ @enable_close
533
+ end
534
+
535
+ def disable_close
536
+ @enable_close = false
537
+ end
538
+
539
+ DEFAULT = new
540
+ end
541
+
542
+ class Pinned
543
+ def initialize(object)
544
+ @object = object
545
+ end
546
+
547
+ def ref
548
+ if @object.nil?
549
+ raise ClearedReferenceError, "`ref` called on a cleared object"
550
+ end
551
+ @object
552
+ end
553
+
554
+ def clear
555
+ @object = nil
556
+ end
557
+
558
+ def cleared?
559
+ @object.nil?
560
+ end
561
+ end
562
+
563
+ RUBY_FREE = Fiddle::Pointer::LibC::FREE.address
564
+ NULL = Fiddle::Pointer.new(0)
565
+
566
+ ALIGN_VOIDP = Fiddle::FFIBackend::FFITypes[Types::VOIDP].alignment
567
+ ALIGN_CHAR = Fiddle::FFIBackend::FFITypes[Types::CHAR].alignment
568
+ ALIGN_SHORT = Fiddle::FFIBackend::FFITypes[Types::SHORT].alignment
569
+ ALIGN_INT = Fiddle::FFIBackend::FFITypes[Types::INT].alignment
570
+ ALIGN_LONG = Fiddle::FFIBackend::FFITypes[Types::LONG].alignment
571
+ ALIGN_LONG_LONG = Fiddle::FFIBackend::FFITypes[Types::LONG_LONG].alignment
572
+ ALIGN_INT8_T = Fiddle::FFIBackend::FFITypes[Types::INT8_T].alignment
573
+ ALIGN_INT16_T = Fiddle::FFIBackend::FFITypes[Types::INT16_T].alignment
574
+ ALIGN_INT32_T = Fiddle::FFIBackend::FFITypes[Types::INT32_T].alignment
575
+ ALIGN_INT64_T = Fiddle::FFIBackend::FFITypes[Types::INT64_T].alignment
576
+ ALIGN_FLOAT = Fiddle::FFIBackend::FFITypes[Types::FLOAT].alignment
577
+ ALIGN_DOUBLE = Fiddle::FFIBackend::FFITypes[Types::DOUBLE].alignment
578
+ ALIGN_BOOL = Fiddle::FFIBackend::FFITypes[Types::BOOL].alignment
579
+ ALIGN_SIZE_T = Fiddle::FFIBackend::FFITypes[Types::SIZE_T].alignment
580
+ ALIGN_SSIZE_T = ALIGN_SIZE_T
581
+ ALIGN_PTRDIFF_T = Fiddle::FFIBackend::FFITypes[Types::PTRDIFF_T].alignment
582
+ ALIGN_INTPTR_T = Fiddle::FFIBackend::FFITypes[Types::INTPTR_T].alignment
583
+ ALIGN_UINTPTR_T = Fiddle::FFIBackend::FFITypes[Types::UINTPTR_T].alignment
584
+
585
+ SIZEOF_VOIDP = Fiddle::FFIBackend::FFITypes[Types::VOIDP].size
586
+ SIZEOF_CHAR = Fiddle::FFIBackend::FFITypes[Types::CHAR].size
587
+ SIZEOF_UCHAR = Fiddle::FFIBackend::FFITypes[Types::UCHAR].size
588
+ SIZEOF_SHORT = Fiddle::FFIBackend::FFITypes[Types::SHORT].size
589
+ SIZEOF_USHORT = Fiddle::FFIBackend::FFITypes[Types::USHORT].size
590
+ SIZEOF_INT = Fiddle::FFIBackend::FFITypes[Types::INT].size
591
+ SIZEOF_UINT = Fiddle::FFIBackend::FFITypes[Types::UINT].size
592
+ SIZEOF_LONG = Fiddle::FFIBackend::FFITypes[Types::LONG].size
593
+ SIZEOF_ULONG = Fiddle::FFIBackend::FFITypes[Types::ULONG].size
594
+ SIZEOF_LONG_LONG = Fiddle::FFIBackend::FFITypes[Types::LONG_LONG].size
595
+ SIZEOF_ULONG_LONG = Fiddle::FFIBackend::FFITypes[Types::ULONG_LONG].size
596
+ SIZEOF_INT8_T = Fiddle::FFIBackend::FFITypes[Types::INT8_T].size
597
+ SIZEOF_UINT8_T = Fiddle::FFIBackend::FFITypes[Types::UINT8_T].size
598
+ SIZEOF_INT16_T = Fiddle::FFIBackend::FFITypes[Types::INT16_T].size
599
+ SIZEOF_UINT16_T = Fiddle::FFIBackend::FFITypes[Types::UINT16_T].size
600
+ SIZEOF_INT32_T = Fiddle::FFIBackend::FFITypes[Types::INT32_T].size
601
+ SIZEOF_UINT32_T = Fiddle::FFIBackend::FFITypes[Types::UINT32_T].size
602
+ SIZEOF_INT64_T = Fiddle::FFIBackend::FFITypes[Types::INT64_T].size
603
+ SIZEOF_UINT64_T = Fiddle::FFIBackend::FFITypes[Types::UINT64_T].size
604
+ SIZEOF_FLOAT = Fiddle::FFIBackend::FFITypes[Types::FLOAT].size
605
+ SIZEOF_DOUBLE = Fiddle::FFIBackend::FFITypes[Types::DOUBLE].size
606
+ SIZEOF_BOOL = Fiddle::FFIBackend::FFITypes[Types::BOOL].size
607
+ SIZEOF_SIZE_T = Fiddle::FFIBackend::FFITypes[Types::SIZE_T].size
608
+ SIZEOF_SSIZE_T = SIZEOF_SIZE_T
609
+ SIZEOF_PTRDIFF_T = Fiddle::FFIBackend::FFITypes[Types::PTRDIFF_T].size
610
+ SIZEOF_INTPTR_T = Fiddle::FFIBackend::FFITypes[Types::INTPTR_T].size
611
+ SIZEOF_UINTPTR_T = Fiddle::FFIBackend::FFITypes[Types::UINTPTR_T].size
612
+ SIZEOF_CONST_STRING = Fiddle::FFIBackend::FFITypes[Types::VOIDP].size
613
+ 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.3"
3
3
  end