ffi_dry 0.1.5 → 0.1.7
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.
- data/History.txt +23 -1
- data/VERSION +1 -1
- data/ffi_dry.gemspec +5 -4
- data/lib/ffi/dry.rb +117 -162
- metadata +2 -2
data/History.txt
CHANGED
@@ -1,3 +1,24 @@
|
|
1
|
+
=== 0.1.7 / 2010-2-05
|
2
|
+
* Fixed bugs with FFI::Unions
|
3
|
+
* Revamped the dsl_layout builder so that it just wraps layout() instead of
|
4
|
+
attempting to duplicate its functionlity. This will hopefully stop causing
|
5
|
+
compatibility issues with FFI as it changes its internals and new versions
|
6
|
+
are released.
|
7
|
+
* :p_struct is an attribute that can be used on any field with a type of
|
8
|
+
:pointer.
|
9
|
+
* Added new experimental feature for the DSL where new fields can be defined
|
10
|
+
by their name as a method name in dsl_layout {} instead of having to
|
11
|
+
prefix field/array/struct for each.
|
12
|
+
* A warning is now issued when a struct field name cannot be mapped to an
|
13
|
+
accessor method because it would cause a conflict. These used to just be
|
14
|
+
silently skipped. Now the warning is issued if warnings are enabled.
|
15
|
+
|
16
|
+
=== 0.1.6 / 2010-1-18
|
17
|
+
* support win32 with wsock32.dll for NetEndian helper stuff.
|
18
|
+
* support StructHelper for FFI::Unions
|
19
|
+
* support passing arguments to StructLayoutBuilder in dsl_layout()
|
20
|
+
* allow overriding copy_size to address incorrect alignment issues in FFI
|
21
|
+
|
1
22
|
=== 0.1.5 / 2010-1-4
|
2
23
|
* Added p_struct dsl directive sugar for creating FFI::Struct accessors to
|
3
24
|
handle pointers to structs.
|
@@ -5,5 +26,6 @@
|
|
5
26
|
=== 0.1.4 / 2010-1-1
|
6
27
|
* Support for FFI 0.5.0 final and up.
|
7
28
|
* Added NetStructHelper with accessors to provide automatic network byte
|
8
|
-
|
29
|
+
order conversion on network packet structures.
|
30
|
+
|
9
31
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.7
|
data/ffi_dry.gemspec
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{ffi_dry}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.7"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Eric Monti"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-02-05}
|
13
13
|
s.description = %q{Provides some useful modules, classes, and methods for FFI bindings as well as a DSL-like syntax for FFI::Struct layouts}
|
14
14
|
s.email = %q{emonti@matasano.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -62,3 +62,4 @@ Gem::Specification.new do |s|
|
|
62
62
|
s.add_dependency(%q<yard>, [">= 0"])
|
63
63
|
end
|
64
64
|
end
|
65
|
+
|
data/lib/ffi/dry.rb
CHANGED
@@ -131,7 +131,14 @@ module FFI::DRY
|
|
131
131
|
# which can be used to add to the size of the copied instance as it is
|
132
132
|
# allocated and copied.
|
133
133
|
def copy(grown=0)
|
134
|
-
self.class.new( :raw => self.to_ptr.read_string(self.
|
134
|
+
self.class.new( :raw => self.to_ptr.read_string(self.copy_size+grown) )
|
135
|
+
end
|
136
|
+
|
137
|
+
# This method is called when creating a copy of self. It can be overridden
|
138
|
+
# by derived structures to return another size. This is sometimes done
|
139
|
+
# to account for alignment issues, etc.
|
140
|
+
def copy_size
|
141
|
+
self.size
|
135
142
|
end
|
136
143
|
|
137
144
|
# Returns a pointer to the specified field, which is the name assigned
|
@@ -150,19 +157,33 @@ module FFI::DRY
|
|
150
157
|
@dsl_metadata
|
151
158
|
end
|
152
159
|
|
153
|
-
|
160
|
+
def define_field_accessor name, &block
|
161
|
+
if instance_methods.include?("#{name}")
|
162
|
+
warn "WARNING: The name '#{name}' is in use for class #{self} "+
|
163
|
+
"Skipping automatic method creation in dsl_layout block."
|
164
|
+
else
|
165
|
+
define_method name, &block
|
166
|
+
end
|
167
|
+
end
|
154
168
|
|
155
169
|
# This passes a block to an instance of DSLStructLayoutBuilder, allowing
|
156
|
-
# for a more declarative syntax with
|
170
|
+
# for a more declarative syntax with additional metadata to be included.
|
157
171
|
#
|
158
|
-
#
|
159
|
-
# about structure members locally
|
172
|
+
# dsl_layout() a replacement to layout() and stores the dsl_metadata
|
173
|
+
# gathered about structure members locally and automatically creates
|
174
|
+
# accessor methods for each field in the structure.
|
175
|
+
#
|
176
|
+
# NOTE if a structure field name conflicts with another instance method
|
177
|
+
# already defined in the class, the relevant accessor method is not
|
178
|
+
# created and a warning is issued. This does not apply to methods
|
179
|
+
# defined after the dsl_layout block is called. In other words this
|
180
|
+
# does not affect the overriding of accessor methods in any way.
|
160
181
|
def dsl_layout &block
|
161
182
|
builder = DSLStructLayoutBuilder.new(self)
|
162
183
|
builder.instance_eval(&block)
|
163
|
-
@layout = builder.
|
184
|
+
@layout = self.layout(*(builder.__layout_args))
|
164
185
|
@size = @layout.size
|
165
|
-
_class_meths_from_dsl_metadata( builder.
|
186
|
+
_class_meths_from_dsl_metadata( builder.__metadata )
|
166
187
|
return @layout
|
167
188
|
end
|
168
189
|
|
@@ -170,19 +191,15 @@ module FFI::DRY
|
|
170
191
|
(@dsl_metadata = meta).each do |spec|
|
171
192
|
name = spec[:name]
|
172
193
|
ftype = spec[:type]
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
p.new(self[name]) unless self[name].null?
|
177
|
-
end
|
178
|
-
else
|
179
|
-
define_method(:"#{name}") { self[name] }
|
194
|
+
if p=spec[:p_struct] and p.kind_of?(Class)
|
195
|
+
define_field_accessor(:"#{name}") do
|
196
|
+
p.new(self[name]) unless self[name].null?
|
180
197
|
end
|
198
|
+
else
|
199
|
+
define_field_accessor(:"#{name}") { self[name] }
|
181
200
|
end
|
182
201
|
|
183
|
-
|
184
|
-
define_method(:"#{name}=") {|val| self[name]=val }
|
185
|
-
end
|
202
|
+
define_field_accessor(:"#{name}=") {|val| self[name]=val }
|
186
203
|
end
|
187
204
|
end
|
188
205
|
end
|
@@ -191,73 +208,29 @@ module FFI::DRY
|
|
191
208
|
base.extend(ClassMethods)
|
192
209
|
end
|
193
210
|
|
194
|
-
alias inspect_orig inspect
|
195
|
-
|
196
|
-
# Overrides inspect to show field names and values
|
197
|
-
def inspect
|
198
|
-
ret = "#<#{self.class}"
|
199
|
-
if not @_inspecting_in_progress
|
200
|
-
@_inspecting_in_progress = true
|
201
|
-
ret << " " << members.map {|m| "#{m}=#{self[m].inspect}"}.join(', ')
|
202
|
-
@_inspecting_in_progress = nil
|
203
|
-
end
|
204
|
-
ret << ">"
|
205
|
-
end
|
206
211
|
end # class StructHelper
|
207
212
|
|
208
|
-
# This
|
209
|
-
#
|
210
|
-
#
|
211
|
-
#
|
212
|
-
#
|
213
|
-
#
|
214
|
-
# define a field (for now):
|
215
|
-
#
|
216
|
-
# field()
|
217
|
-
# array()
|
218
|
-
# struct()
|
219
|
-
#
|
220
|
-
# See the individual method descriptions for more info.
|
221
|
-
#
|
213
|
+
# This class provides the DSL for StructHelper.dsl_layout.
|
214
|
+
# You probably don't want to use this directly but if you do, to use the DSL,
|
215
|
+
# you may either pass a structure definition into 'instance_eval' or
|
216
|
+
# call methods on the object. The methods __metadata and __layout_args return
|
217
|
+
# structure information back to the caller, which can use them to create
|
218
|
+
# a new structure.
|
222
219
|
class DSLStructLayoutBuilder
|
223
|
-
attr_reader :
|
220
|
+
attr_reader :__metadata, :__layout_args
|
224
221
|
|
225
|
-
# Initializes the
|
226
|
-
# Instead of duplicating Struct features, we just call back to them.
|
222
|
+
# Initializes the a new builder class.
|
227
223
|
def initialize(pbind)
|
228
224
|
@pbind = pbind
|
229
|
-
@
|
230
|
-
@
|
231
|
-
|
232
|
-
end
|
233
|
-
|
234
|
-
# calls StructLayoutBuider.build() on the bulder and returns its
|
235
|
-
# result.
|
236
|
-
def build
|
237
|
-
@builder.build
|
238
|
-
end
|
239
|
-
|
240
|
-
# Calls StructLayoutBuilder.add_struct() on the builder and stores
|
241
|
-
# a metadata hash entry (the opts hash with name and type overridden)
|
242
|
-
#
|
243
|
-
# struct field_name, RubyClass, { ... metadata ... }
|
244
|
-
#
|
245
|
-
# :offset is a special key in metadata, specifies the offset of the field.
|
246
|
-
def struct(name, klass, o={})
|
247
|
-
unless klass.kind_of?(Class) and klass < ::FFI::Struct
|
248
|
-
raise(::ArgumentError, "klass must be a struct")
|
249
|
-
end
|
250
|
-
|
251
|
-
opts = o.merge(:name => name, :type => klass)
|
252
|
-
offset = opts[:offset]
|
253
|
-
ret=@builder.add_struct(name, klass, offset)
|
254
|
-
@metadata << opts
|
255
|
-
return ret
|
225
|
+
@__layout_args = []
|
226
|
+
@__metadata = []
|
227
|
+
yield self if block_given?
|
256
228
|
end
|
257
229
|
|
258
230
|
# A pointer to a structure. The structure does not allocate the entire
|
259
|
-
# space for the structure, just a pointer. When calling the
|
260
|
-
# a p_struct field, a new instance of the FFI::Struct
|
231
|
+
# space for the structure pointed to, just a pointer. When calling the
|
232
|
+
# accessor for a p_struct field, a new instance of the FFI::Struct type
|
233
|
+
# for the pointer will be returned.
|
261
234
|
def p_struct(name, klass, o={})
|
262
235
|
unless klass.kind_of?(Class)
|
263
236
|
raise(::ArgumentError, "klass must be a Class")
|
@@ -267,67 +240,54 @@ module FFI::DRY
|
|
267
240
|
field(name, :pointer, opts)
|
268
241
|
end
|
269
242
|
|
270
|
-
#
|
271
|
-
# a metadata hash entry (the opts hash with name and type overridden)
|
243
|
+
# Declaratively adds a field to the structure.
|
272
244
|
#
|
273
245
|
# Syntax:
|
274
246
|
#
|
275
|
-
#
|
247
|
+
# field field_name, ctype, { ... metadata ... }
|
276
248
|
#
|
277
249
|
# :offset is a special key in metadata, specifies the offset of the field.
|
278
|
-
def
|
279
|
-
unless type.kind_of?(::Array)
|
280
|
-
raise(::ArgumentError, "type must be an array")
|
281
|
-
end
|
282
|
-
|
250
|
+
def field(name, type, o={})
|
283
251
|
opts = o.merge(:name => name, :type => type)
|
284
252
|
offset = opts[:offset]
|
285
|
-
mod = enclosing_module
|
286
|
-
ret=
|
287
|
-
if @builder.respond_to?(:add_array)
|
288
|
-
@builder.add_array(name, find_type(type[0], mod), type[1], offset)
|
289
|
-
else
|
290
|
-
@builder.add_field(name, type, offset)
|
291
|
-
end
|
292
253
|
|
293
|
-
@
|
294
|
-
|
254
|
+
@__layout_args << name
|
255
|
+
@__layout_args << type
|
256
|
+
@__layout_args << offset if offset
|
257
|
+
|
258
|
+
@__metadata << opts
|
259
|
+
return opts
|
295
260
|
end
|
296
261
|
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
#
|
302
|
-
#
|
303
|
-
|
304
|
-
|
305
|
-
|
262
|
+
alias array field
|
263
|
+
alias struct field
|
264
|
+
alias union field
|
265
|
+
|
266
|
+
# Experimental - Allows specifying structure fields by taking a missing
|
267
|
+
# method name as field name for the structure.
|
268
|
+
def method_missing(name, type, *extra)
|
269
|
+
o={}
|
270
|
+
if extra.size > 1
|
271
|
+
raise(ArgumentError,
|
272
|
+
"Bad field definition. Use: name :type, {optional extra parameters}")
|
273
|
+
elsif h=extra.first
|
274
|
+
if h.kind_of? Hash
|
275
|
+
o=h
|
276
|
+
else
|
277
|
+
raise(ArgumentError, "Options must be provided as a hash.")
|
278
|
+
end
|
279
|
+
end
|
306
280
|
opts = o.merge(:name => name, :type => type)
|
307
281
|
offset = opts[:offset]
|
308
|
-
mod = enclosing_module
|
309
|
-
ret= @builder.add_field(name, find_type(type, mod), offset)
|
310
|
-
@metadata << opts
|
311
|
-
return ret
|
312
|
-
end
|
313
282
|
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
# do this without meddling into back-end FFI implementations.
|
318
|
-
def find_type(*args)
|
319
|
-
@pbind.instance_eval { find_type(*args) }
|
320
|
-
end
|
283
|
+
@__layout_args << name
|
284
|
+
@__layout_args << type
|
285
|
+
@__layout_args << offset if offset
|
321
286
|
|
322
|
-
|
323
|
-
|
324
|
-
# FFI builder. We probably need to figure out another, *supported*, way to
|
325
|
-
# do this without meddling into back-end FFI implementations.
|
326
|
-
def enclosing_module(*args)
|
327
|
-
@pbind.instance_eval { enclosing_module(*args) }
|
287
|
+
@__metadata << opts
|
288
|
+
return opts
|
328
289
|
end
|
329
|
-
|
330
|
-
end
|
290
|
+
end # DSLStructLayoutBuilder
|
331
291
|
|
332
292
|
# ConstMap can be used to organize and lookup value to constant mappings and
|
333
293
|
# vice versa. Constants can be imported from a namespace based on a regular
|
@@ -362,25 +322,24 @@ module FFI::DRY
|
|
362
322
|
constants.inject({}){|h,c| h.merge! c => const_get(c) }
|
363
323
|
end
|
364
324
|
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
const_set c, v
|
381
|
-
end
|
325
|
+
# When called from a module definition or class method, this method
|
326
|
+
# imports all the constants from # namespace 'nspace' that start with
|
327
|
+
# into the local namespace as constants named with whatever follows the
|
328
|
+
# prefix. Only constant names that match [A-Z][A-Z0-9_]+ are imported,
|
329
|
+
# the rest are ignored.
|
330
|
+
#
|
331
|
+
# This method also yields the (short) constant name and value to a block
|
332
|
+
# if one is provided. The block works like [...].select {|c,v| ... } in
|
333
|
+
# that the value is not mapped if the block returns nil or false.
|
334
|
+
def slurp_constants(nspace, prefix)
|
335
|
+
nspace.constants.grep(/^(#{prefix}([A-Z][A-Z0-9_]+))$/) do
|
336
|
+
c = $2
|
337
|
+
v = nspace.const_get($1)
|
338
|
+
next if block_given? and not yield(c,v)
|
339
|
+
const_set c, v
|
382
340
|
end
|
383
|
-
|
341
|
+
end
|
342
|
+
end # ConstMap
|
384
343
|
|
385
344
|
# Behaves just like ConstFlags, except that it returns a list
|
386
345
|
# of names for the flags. Name string lookups work same way as
|
@@ -411,12 +370,17 @@ module FFI::DRY
|
|
411
370
|
list[arg.to_s.upcase]
|
412
371
|
end
|
413
372
|
end
|
414
|
-
end
|
373
|
+
end # ConstFlagsMap
|
415
374
|
|
416
375
|
|
417
376
|
module NetEndian
|
418
377
|
extend ::FFI::Library
|
419
378
|
|
379
|
+
begin
|
380
|
+
ffi_lib 'wsock32'
|
381
|
+
rescue LoadError
|
382
|
+
end
|
383
|
+
|
420
384
|
attach_function :htons, [:uint16], :uint16
|
421
385
|
attach_function :ntohs, [:uint16], :uint16
|
422
386
|
attach_function :htonl, [:uint32], :uint32
|
@@ -431,7 +395,7 @@ module FFI::DRY
|
|
431
395
|
::FFI.find_type(:int32) => I32_convert,
|
432
396
|
::FFI.find_type(:uint32) => I32_convert,
|
433
397
|
}
|
434
|
-
end
|
398
|
+
end # NetEndian
|
435
399
|
|
436
400
|
|
437
401
|
# A special helper for network packet structures that use big-endian or
|
@@ -440,8 +404,12 @@ module FFI::DRY
|
|
440
404
|
# for 'reading' a 16/32 bit field, and htons/htonl for writing to one.
|
441
405
|
#
|
442
406
|
# NOTE this helper does not currently do anything special for 64-bit or
|
443
|
-
# higher values.
|
407
|
+
# higher values but this might be added at some point if the need arises.
|
444
408
|
#
|
409
|
+
# NOTE unlike the StructHelper module, no special relevance is given
|
410
|
+
# to fields with a ":p_struct" option or defined with the p_struct DSL
|
411
|
+
# method. These are ignored and treated like any other field. A net struct
|
412
|
+
# generally doesn't contain pointers into native memory anyway.
|
445
413
|
module NetStructHelper
|
446
414
|
def self.included(base)
|
447
415
|
base.instance_eval { include StructHelper }
|
@@ -460,31 +428,18 @@ module FFI::DRY
|
|
460
428
|
# Create endian swapper accessors methods for each applicable
|
461
429
|
# field
|
462
430
|
if( type.kind_of?(Symbol) and
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
define_method(:"#{name}"){ cnv[0].call(self[name]) }
|
467
|
-
end
|
468
|
-
unless instance_methods.include?(:"#{name}=")
|
469
|
-
define_method(:"#{name}="){|val| self[name] = cnv[1].call(val) }
|
470
|
-
end
|
471
|
-
|
431
|
+
cnv=NetEndian::ENDIAN_METHS[ ::FFI.find_type(type) ] )
|
432
|
+
define_method(:"#{name}"){ cnv[0].call(self[name]) }
|
433
|
+
define_method(:"#{name}="){|val| self[name] = cnv[1].call(val) }
|
472
434
|
else
|
473
|
-
|
474
|
-
|
475
|
-
define_method(:"#{name}"){ self[name] }
|
476
|
-
end
|
477
|
-
unless instance_methods.include?(:"#{name}=")
|
478
|
-
define_method(:"#{name}="){|val| self[name]=val }
|
479
|
-
end
|
480
|
-
|
435
|
+
define_field_accessor(:"#{name}"){ self[name] }
|
436
|
+
define_field_accessor(:"#{name}="){|val| self[name]=val }
|
481
437
|
end
|
482
438
|
|
483
439
|
end
|
484
440
|
end
|
485
441
|
end
|
486
|
-
end
|
487
|
-
|
488
|
-
end
|
442
|
+
end # NetStructHelper
|
489
443
|
|
444
|
+
end # FFI::DRY
|
490
445
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ffi_dry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Monti
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-
|
12
|
+
date: 2010-02-05 00:00:00 -06:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|