ffi_dry 0.1.9 → 0.1.11

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.
Files changed (6) hide show
  1. data/History.txt +7 -0
  2. data/Rakefile +0 -2
  3. data/VERSION +1 -1
  4. data/ffi_dry.gemspec +4 -20
  5. data/lib/ffi/dry.rb +396 -381
  6. metadata +22 -59
@@ -1,3 +1,10 @@
1
+ === 0.1.11 / 2010-6-2
2
+ * Added attach_optional_function to FFI::Library for dealing with functions
3
+ that are not always present.
4
+
5
+ === 0.1.10 / 2010-6-2
6
+ *** YANKED due to typo in gem
7
+
1
8
  === 0.1.9 / 2010-3-5
2
9
  * Workaround peculiar FFI::MemoryPointer.from_string() bugs when using
3
10
  :raw => buf in ruby 1.9.x
data/Rakefile CHANGED
@@ -12,8 +12,6 @@ begin
12
12
  gem.homepage = "http://github.com/emonti/ffi_dry"
13
13
  gem.authors = ["Eric Monti"]
14
14
  gem.add_dependency "ffi", ">= 0.5.0"
15
- gem.add_development_dependency "rspec"
16
- gem.add_development_dependency "yard"
17
15
  end
18
16
  rescue LoadError
19
17
  puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.9
1
+ 0.1.11
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{ffi_dry}
8
- s.version = "0.1.9"
8
+ s.version = "0.1.11"
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-03-05}
12
+ s.date = %q{2010-06-02}
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 = [
@@ -33,11 +33,10 @@ Gem::Specification.new do |s|
33
33
  "spec/ffi_dry_spec.rb",
34
34
  "spec/spec_helper.rb"
35
35
  ]
36
- s.has_rdoc = true
37
36
  s.homepage = %q{http://github.com/emonti/ffi_dry}
38
37
  s.rdoc_options = ["--charset=UTF-8"]
39
38
  s.require_paths = ["lib"]
40
- s.rubygems_version = %q{1.3.1}
39
+ s.rubygems_version = %q{1.3.6}
41
40
  s.summary = %q{Syntactic sugar and helper utilities for FFI}
42
41
  s.test_files = [
43
42
  "spec/ffi_dry_spec.rb",
@@ -46,30 +45,15 @@ Gem::Specification.new do |s|
46
45
 
47
46
  if s.respond_to? :specification_version then
48
47
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
49
- s.specification_version = 2
48
+ s.specification_version = 3
50
49
 
51
50
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
52
51
  s.add_runtime_dependency(%q<ffi>, [">= 0.5.0"])
53
- s.add_development_dependency(%q<rspec>, [">= 0"])
54
- s.add_development_dependency(%q<yard>, [">= 0"])
55
- s.add_runtime_dependency(%q<ffi>, [">= 0.5.0"])
56
- s.add_development_dependency(%q<rspec>, [">= 0"])
57
- s.add_development_dependency(%q<yard>, [">= 0"])
58
52
  else
59
53
  s.add_dependency(%q<ffi>, [">= 0.5.0"])
60
- s.add_dependency(%q<rspec>, [">= 0"])
61
- s.add_dependency(%q<yard>, [">= 0"])
62
- s.add_dependency(%q<ffi>, [">= 0.5.0"])
63
- s.add_dependency(%q<rspec>, [">= 0"])
64
- s.add_dependency(%q<yard>, [">= 0"])
65
54
  end
66
55
  else
67
56
  s.add_dependency(%q<ffi>, [">= 0.5.0"])
68
- s.add_dependency(%q<rspec>, [">= 0"])
69
- s.add_dependency(%q<yard>, [">= 0"])
70
- s.add_dependency(%q<ffi>, [">= 0.5.0"])
71
- s.add_dependency(%q<rspec>, [">= 0"])
72
- s.add_dependency(%q<yard>, [">= 0"])
73
57
  end
74
58
  end
75
59
 
@@ -6,442 +6,457 @@ unless defined?(FFI::Library::LIBC)
6
6
  FFI::Library::LIBC = (RUBY_PLATFORM == 'mswin32' ? 'msvcrt' : 'c')
7
7
  end
8
8
 
9
- module FFI::DRY
10
-
11
- # A module to add syntactic sugar and some nice automatic getter/setter
12
- # methods to FFI::Struct, FFI::ManagedStruct, etc.
13
- #
14
- # For example:
15
- # require 'rubygems'
16
- # require 'ffi'
17
- # require 'ffi/dry'
18
- # require 'pp'
19
- #
20
- # class SomeStruct < FFI::Struct
21
- # include FFI::DRY::StructHelper
22
- #
23
- # # we get a new way of specifying layouts with a 'dsl'-like syntax
24
- # dsl_layout do
25
- # field :field1, :uint16, :desc => 'this is field 1'
26
- # field :field2, :uint16, :desc => 'this is field 2'
27
- # end
28
- # end
29
- #
30
- # ss0=SomeStruct.new
31
- #
32
- # pp ss0.dsl_metadata # we can look at definition metadata
33
- #
34
- # # produces...
35
- # # [{:type=>:uint16, :name=>:field1, :desc=>"this is field 1"},
36
- # # {:type=>:uint16, :name=>:field2, :desc=>"this is field 2"}]
37
- #
38
- # # And we have additional ways of instantiating and declaring values
39
- # # during initialization. (The FFI standard ways still work too)
40
- #
41
- # raw_data = "\x00\x00\xff\xff"
42
- #
43
- # ss1=SomeStruct.new :raw => raw_data
44
- # ss2=SomeStruct.new :raw => raw_data, :field1 => 1, :field2 => 2
45
- # ss3=SomeStruct.new {|x| x.field1=1 }
46
- # ss4=SomeStruct.new(:raw => raw_data) {|x| x.field1=1 }
47
- #
48
- # [ ss0,
49
- # ss1,
50
- # ss2,
51
- # ss3,
52
- # ss4].each_with_index {|x,i| pp ["ss#{i}",[x.field1, x.field2]]}
53
- #
54
- # # produces...
55
- # # ["ss0", [0, 0]]
56
- # # ["ss1", [0, 65535]]
57
- # # ["ss2", [1, 2]]
58
- # # ["ss3", [1, 0]]
59
- # # ["ss4", [1, 65535]]
60
- #
61
- module StructHelper
62
-
63
- attr_reader :dsl_metadata
64
-
65
- # Allows setting structure fields on initialization to ::FFI::Struct.new
66
- # as well as a "yield(self) if block_given?" at the end.
9
+ module FFI
10
+ module Library
11
+ # Helper method for attaching optional functions which may not be present.
12
+ # Just catches and ignores any FFI::NotFoundError exceptions.
13
+ def attach_optional_function(*args)
14
+ begin
15
+ attach_function(*args)
16
+ rescue FFI::NotFoundError => e
17
+ warn "WARNING: #{e.to_s}" if $DEBUG
18
+ nil
19
+ end
20
+ end
21
+ end
22
+
23
+ module DRY
24
+
25
+ # A module to add syntactic sugar and some nice automatic getter/setter
26
+ # methods to FFI::Struct, FFI::ManagedStruct, etc.
27
+ #
28
+ # For example:
29
+ # require 'rubygems'
30
+ # require 'ffi'
31
+ # require 'ffi/dry'
32
+ # require 'pp'
33
+ #
34
+ # class SomeStruct < FFI::Struct
35
+ # include FFI::DRY::StructHelper
36
+ #
37
+ # # we get a new way of specifying layouts with a 'dsl'-like syntax
38
+ # dsl_layout do
39
+ # field :field1, :uint16, :desc => 'this is field 1'
40
+ # field :field2, :uint16, :desc => 'this is field 2'
41
+ # end
42
+ # end
67
43
  #
68
- # Field initialization happens if there is only one argument and it is
69
- # a Hash.
44
+ # ss0=SomeStruct.new
70
45
  #
71
- # The params hash is taken as a set of values for fields where the hash
72
- # keys specify the field names to set.
46
+ # pp ss0.dsl_metadata # we can look at definition metadata
73
47
  #
74
- # @param [Hash] params
75
- # Note:
76
- # The :raw parameter is a special tag in the hash. The value is taken as a
77
- # string and initialized into a new FFI::MemoryPointer which this Struct
78
- # then overlays.
48
+ # # produces...
49
+ # # [{:type=>:uint16, :name=>:field1, :desc=>"this is field 1"},
50
+ # # {:type=>:uint16, :name=>:field2, :desc=>"this is field 2"}]
79
51
  #
80
- # If your struct layout has a field named :raw field, it won't be
81
- # assignable through the hash argument.
52
+ # # And we have additional ways of instantiating and declaring values
53
+ # # during initialization. (The FFI standard ways still work too)
82
54
  #
83
- # See also: set_fields() which is called automatically on the hash, minus
84
- # the :raw tag.
55
+ # raw_data = "\x00\x00\xff\xff"
85
56
  #
86
- def initialize(*args)
87
- @dsl_metadata = self.class.dsl_metadata
88
- params=nil
89
-
90
- if args.size == 1 and (oparams=args[0]).is_a? Hash
91
- params = oparams.dup
92
- if raw=params.delete(:raw)
93
- super( ::FFI::MemoryPointer.new(raw.size).write_string(raw) )
57
+ # ss1=SomeStruct.new :raw => raw_data
58
+ # ss2=SomeStruct.new :raw => raw_data, :field1 => 1, :field2 => 2
59
+ # ss3=SomeStruct.new {|x| x.field1=1 }
60
+ # ss4=SomeStruct.new(:raw => raw_data) {|x| x.field1=1 }
61
+ #
62
+ # [ ss0,
63
+ # ss1,
64
+ # ss2,
65
+ # ss3,
66
+ # ss4].each_with_index {|x,i| pp ["ss#{i}",[x.field1, x.field2]]}
67
+ #
68
+ # # produces...
69
+ # # ["ss0", [0, 0]]
70
+ # # ["ss1", [0, 65535]]
71
+ # # ["ss2", [1, 2]]
72
+ # # ["ss3", [1, 0]]
73
+ # # ["ss4", [1, 65535]]
74
+ #
75
+ module StructHelper
76
+
77
+ attr_reader :dsl_metadata
78
+
79
+ # Allows setting structure fields on initialization to ::FFI::Struct.new
80
+ # as well as a "yield(self) if block_given?" at the end.
81
+ #
82
+ # Field initialization happens if there is only one argument and it is
83
+ # a Hash.
84
+ #
85
+ # The params hash is taken as a set of values for fields where the hash
86
+ # keys specify the field names to set.
87
+ #
88
+ # @param [Hash] params
89
+ # Note:
90
+ # The :raw parameter is a special tag in the hash. The value is taken as a
91
+ # string and initialized into a new FFI::MemoryPointer which this Struct
92
+ # then overlays.
93
+ #
94
+ # If your struct layout has a field named :raw field, it won't be
95
+ # assignable through the hash argument.
96
+ #
97
+ # See also: set_fields() which is called automatically on the hash, minus
98
+ # the :raw tag.
99
+ #
100
+ def initialize(*args)
101
+ @dsl_metadata = self.class.dsl_metadata
102
+ params=nil
103
+
104
+ if args.size == 1 and (oparams=args[0]).is_a? Hash
105
+ params = oparams.dup
106
+ if raw=params.delete(:raw)
107
+ super( ::FFI::MemoryPointer.new(raw.size).write_string(raw) )
108
+ else
109
+ super()
110
+ end
94
111
  else
95
- super()
112
+ super(*args)
96
113
  end
97
- else
98
- super(*args)
99
- end
100
114
 
101
- set_fields(params)
102
- yield self if block_given?
103
- end
115
+ set_fields(params)
116
+ yield self if block_given?
117
+ end
104
118
 
105
- # Sets field values in the struct specified by their symbolic name from a
106
- # hash of ':field => value' pairs. Uses accessor field wrapper methods
107
- # instead of a direct reference to the field (as in "obj.field1 = x",
108
- # not "obj[:field] = x"). The difference is subtle, but this allows you
109
- # to take advantage of any wrapper methods you override when initializing
110
- # a new object. The only caveat is that the wrapper method must be named
111
- # the same as the field, and the field must be included in members() from
112
- # the layout.
113
- #
114
- # This method is called automatically if you are using the initialize()
115
- # method provided in the Struct class and passing it a Hash as its only
116
- # argument.
117
- def set_fields(params=nil)
118
- (params || {}).keys.each do |p|
119
- if members().include?(p)
120
- self.__send__(:"#{p}=", params[p])
121
- else
122
- raise(::ArgumentError, "#{self.class} does not have a '#{p}' field")
119
+ # Sets field values in the struct specified by their symbolic name from a
120
+ # hash of ':field => value' pairs. Uses accessor field wrapper methods
121
+ # instead of a direct reference to the field (as in "obj.field1 = x",
122
+ # not "obj[:field] = x"). The difference is subtle, but this allows you
123
+ # to take advantage of any wrapper methods you override when initializing
124
+ # a new object. The only caveat is that the wrapper method must be named
125
+ # the same as the field, and the field must be included in members() from
126
+ # the layout.
127
+ #
128
+ # This method is called automatically if you are using the initialize()
129
+ # method provided in the Struct class and passing it a Hash as its only
130
+ # argument.
131
+ def set_fields(params=nil)
132
+ (params || {}).keys.each do |p|
133
+ if members().include?(p)
134
+ self.__send__(:"#{p}=", params[p])
135
+ else
136
+ raise(::ArgumentError, "#{self.class} does not have a '#{p}' field")
137
+ end
123
138
  end
124
139
  end
125
- end
126
140
 
127
- # Returns a new instance of self.class containing a seperately allocated
128
- # copy of all our data. This abstract method should usually be called
129
- # with super() from overridden 'copy' implementations for structures
130
- # containing pointers to other memory or variable length data at the end.
131
- #
132
- # Note also that, by default, this implementation determine's size
133
- # automatically based on the structure size. This is comparable to
134
- # sizeof(some_struct) in C. However, you can supply a 'grown' parameter
135
- # which can be used to add to the size of the copied instance as it is
136
- # allocated and copied.
137
- def copy(grown=0)
138
- self.class.new( :raw => self.to_ptr.read_string(self.copy_size+grown) )
139
- end
141
+ # Returns a new instance of self.class containing a seperately allocated
142
+ # copy of all our data. This abstract method should usually be called
143
+ # with super() from overridden 'copy' implementations for structures
144
+ # containing pointers to other memory or variable length data at the end.
145
+ #
146
+ # Note also that, by default, this implementation determine's size
147
+ # automatically based on the structure size. This is comparable to
148
+ # sizeof(some_struct) in C. However, you can supply a 'grown' parameter
149
+ # which can be used to add to the size of the copied instance as it is
150
+ # allocated and copied.
151
+ def copy(grown=0)
152
+ self.class.new( :raw => self.to_ptr.read_string(self.copy_size+grown) )
153
+ end
140
154
 
141
- # This method is called when creating a copy of self. It can be overridden
142
- # by derived structures to return another size. This is sometimes done
143
- # to account for alignment issues, etc.
144
- def copy_size
145
- self.size
146
- end
155
+ # This method is called when creating a copy of self. It can be overridden
156
+ # by derived structures to return another size. This is sometimes done
157
+ # to account for alignment issues, etc.
158
+ def copy_size
159
+ self.size
160
+ end
147
161
 
148
- # Returns a pointer to the specified field, which is the name assigned
149
- # to a member in the layout.
150
- def ptr_to(field)
151
- x = self[field] # this is actually a test, to raise if missing
152
- return (self.to_ptr + self.offset_of(field))
153
- end
162
+ # Returns a pointer to the specified field, which is the name assigned
163
+ # to a member in the layout.
164
+ def ptr_to(field)
165
+ x = self[field] # this is actually a test, to raise if missing
166
+ return (self.to_ptr + self.offset_of(field))
167
+ end
168
+
169
+ # Contains dsl_layout and some support methods that an 'includee' of
170
+ # DryStructHelper will have available as class methods.
171
+ module ClassMethods
172
+ # returns the structure metadata for this class based on
173
+ # the dsl_layout definitions
174
+ def dsl_metadata
175
+ @dsl_metadata
176
+ end
154
177
 
155
- # Contains dsl_layout and some support methods that an 'includee' of
156
- # DryStructHelper will have available as class methods.
157
- module ClassMethods
158
- # returns the structure metadata for this class based on
159
- # the dsl_layout definitions
160
- def dsl_metadata
161
- @dsl_metadata
178
+ def define_field_accessor name, &block
179
+ if instance_methods.include?("#{name}")
180
+ warn "WARNING: The name '#{name}' is in use for class #{self} "+
181
+ "Skipping automatic method creation in dsl_layout block."
182
+ else
183
+ define_method name, &block
184
+ end
185
+ end
186
+
187
+ # This passes a block to an instance of DSLStructLayoutBuilder, allowing
188
+ # for a more declarative syntax with additional metadata to be included.
189
+ #
190
+ # dsl_layout() a replacement to layout() and stores the dsl_metadata
191
+ # gathered about structure members locally and automatically creates
192
+ # accessor methods for each field in the structure.
193
+ #
194
+ # NOTE if a structure field name conflicts with another instance method
195
+ # already defined in the class, the relevant accessor method is not
196
+ # created and a warning is issued. This does not apply to methods
197
+ # defined after the dsl_layout block is called. In other words this
198
+ # does not affect the overriding of accessor methods in any way.
199
+ def dsl_layout &block
200
+ builder = DSLStructLayoutBuilder.new(self)
201
+ builder.instance_eval(&block)
202
+ @layout = self.layout(*(builder.__layout_args))
203
+ @size = @layout.size
204
+ _class_meths_from_dsl_metadata( builder.__metadata )
205
+ return @layout
206
+ end
207
+
208
+ def _class_meths_from_dsl_metadata(meta)
209
+ (@dsl_metadata = meta).each do |spec|
210
+ name = spec[:name]
211
+ ftype = spec[:type]
212
+ if p=spec[:p_struct] and p.kind_of?(Class)
213
+ define_field_accessor(:"#{name}") do
214
+ p.new(self[name]) unless self[name].null?
215
+ end
216
+ else
217
+ define_field_accessor(:"#{name}") { self[name] }
218
+ end
219
+
220
+ define_field_accessor(:"#{name}=") {|val| self[name]=val }
221
+ end
222
+ end
223
+ end
224
+
225
+ def self.included(base)
226
+ base.extend(ClassMethods)
162
227
  end
163
228
 
164
- def define_field_accessor name, &block
165
- if instance_methods.include?("#{name}")
166
- warn "WARNING: The name '#{name}' is in use for class #{self} "+
167
- "Skipping automatic method creation in dsl_layout block."
168
- else
169
- define_method name, &block
229
+ end # class StructHelper
230
+
231
+ # This class provides the DSL for StructHelper.dsl_layout.
232
+ # You probably don't want to use this directly but if you do, to use the DSL,
233
+ # you may either pass a structure definition into 'instance_eval' or
234
+ # call methods on the object. The methods __metadata and __layout_args return
235
+ # structure information back to the caller, which can use them to create
236
+ # a new structure.
237
+ class DSLStructLayoutBuilder
238
+ attr_reader :__metadata, :__layout_args
239
+
240
+ # Initializes the a new builder class.
241
+ def initialize(pbind)
242
+ @pbind = pbind
243
+ @__layout_args = []
244
+ @__metadata = []
245
+ yield self if block_given?
246
+ end
247
+
248
+ # A pointer to a structure. The structure does not allocate the entire
249
+ # space for the structure pointed to, just a pointer. When calling the
250
+ # accessor for a p_struct field, a new instance of the FFI::Struct type
251
+ # for the pointer will be returned.
252
+ def p_struct(name, klass, o={})
253
+ unless klass.kind_of?(Class)
254
+ raise(TypeError, "klass must be a Class")
170
255
  end
256
+ opts = o.merge(:p_struct => klass)
257
+ offset = opts[:offset]
258
+ field(name, :pointer, opts)
171
259
  end
172
260
 
173
- # This passes a block to an instance of DSLStructLayoutBuilder, allowing
174
- # for a more declarative syntax with additional metadata to be included.
261
+ # Declaratively adds a field to the structure.
262
+ #
263
+ # Syntax:
175
264
  #
176
- # dsl_layout() a replacement to layout() and stores the dsl_metadata
177
- # gathered about structure members locally and automatically creates
178
- # accessor methods for each field in the structure.
265
+ # field field_name, ctype, { ... metadata ... }
179
266
  #
180
- # NOTE if a structure field name conflicts with another instance method
181
- # already defined in the class, the relevant accessor method is not
182
- # created and a warning is issued. This does not apply to methods
183
- # defined after the dsl_layout block is called. In other words this
184
- # does not affect the overriding of accessor methods in any way.
185
- def dsl_layout &block
186
- builder = DSLStructLayoutBuilder.new(self)
187
- builder.instance_eval(&block)
188
- @layout = self.layout(*(builder.__layout_args))
189
- @size = @layout.size
190
- _class_meths_from_dsl_metadata( builder.__metadata )
191
- return @layout
267
+ # :offset is a special key in metadata, specifies the offset of the field.
268
+ def field(name, type, o={})
269
+ opts = o.merge(:name => name, :type => type)
270
+ offset = opts[:offset]
271
+
272
+ @__layout_args << name
273
+ @__layout_args << type
274
+ @__layout_args << offset if offset
275
+
276
+ @__metadata << opts
277
+ return opts
192
278
  end
193
279
 
194
- def _class_meths_from_dsl_metadata(meta)
195
- (@dsl_metadata = meta).each do |spec|
196
- name = spec[:name]
197
- ftype = spec[:type]
198
- if p=spec[:p_struct] and p.kind_of?(Class)
199
- define_field_accessor(:"#{name}") do
200
- p.new(self[name]) unless self[name].null?
201
- end
280
+ alias array field
281
+ alias struct field
282
+ alias union field
283
+
284
+ # Experimental - Allows specifying structure fields by taking a missing
285
+ # method name as field name for the structure.
286
+ def method_missing(name, type, *extra)
287
+ o={}
288
+ if extra.size > 1
289
+ raise(ArgumentError,
290
+ "Bad field syntax. Use: 'name :type, {optional extra parameters}'")
291
+ elsif h=extra.first
292
+ if h.kind_of? Hash
293
+ o=h
202
294
  else
203
- define_field_accessor(:"#{name}") { self[name] }
295
+ raise(TypeError, "Options must be provided as a hash.")
204
296
  end
205
-
206
- define_field_accessor(:"#{name}=") {|val| self[name]=val }
207
297
  end
208
- end
209
- end
210
-
211
- def self.included(base)
212
- base.extend(ClassMethods)
213
- end
298
+ opts = o.merge(:name => name, :type => type)
299
+ offset = opts[:offset]
214
300
 
215
- end # class StructHelper
216
-
217
- # This class provides the DSL for StructHelper.dsl_layout.
218
- # You probably don't want to use this directly but if you do, to use the DSL,
219
- # you may either pass a structure definition into 'instance_eval' or
220
- # call methods on the object. The methods __metadata and __layout_args return
221
- # structure information back to the caller, which can use them to create
222
- # a new structure.
223
- class DSLStructLayoutBuilder
224
- attr_reader :__metadata, :__layout_args
225
-
226
- # Initializes the a new builder class.
227
- def initialize(pbind)
228
- @pbind = pbind
229
- @__layout_args = []
230
- @__metadata = []
231
- yield self if block_given?
232
- end
301
+ @__layout_args << name
302
+ @__layout_args << type
303
+ @__layout_args << offset if offset
233
304
 
234
- # A pointer to a structure. The structure does not allocate the entire
235
- # space for the structure pointed to, just a pointer. When calling the
236
- # accessor for a p_struct field, a new instance of the FFI::Struct type
237
- # for the pointer will be returned.
238
- def p_struct(name, klass, o={})
239
- unless klass.kind_of?(Class)
240
- raise(TypeError, "klass must be a Class")
305
+ @__metadata << opts
306
+ return opts
241
307
  end
242
- opts = o.merge(:p_struct => klass)
243
- offset = opts[:offset]
244
- field(name, :pointer, opts)
245
- end
308
+ end # DSLStructLayoutBuilder
246
309
 
247
- # Declaratively adds a field to the structure.
248
- #
249
- # Syntax:
250
- #
251
- # field field_name, ctype, { ... metadata ... }
252
- #
253
- # :offset is a special key in metadata, specifies the offset of the field.
254
- def field(name, type, o={})
255
- opts = o.merge(:name => name, :type => type)
256
- offset = opts[:offset]
257
-
258
- @__layout_args << name
259
- @__layout_args << type
260
- @__layout_args << offset if offset
310
+ # ConstMap can be used to organize and lookup value to constant mappings and
311
+ # vice versa. Constants can be imported from a namespace based on a regular
312
+ # expression or other means.
313
+ module ConstMap
261
314
 
262
- @__metadata << opts
263
- return opts
264
- end
315
+ def self.included(klass)
316
+ klass.extend(ConstMap)
317
+ end
265
318
 
266
- alias array field
267
- alias struct field
268
- alias union field
269
-
270
- # Experimental - Allows specifying structure fields by taking a missing
271
- # method name as field name for the structure.
272
- def method_missing(name, type, *extra)
273
- o={}
274
- if extra.size > 1
275
- raise(ArgumentError,
276
- "Bad field syntax. Use: 'name :type, {optional extra parameters}'")
277
- elsif h=extra.first
278
- if h.kind_of? Hash
279
- o=h
280
- else
281
- raise(TypeError, "Options must be provided as a hash.")
319
+ # A flexible name to value lookup.
320
+ #
321
+ # @param [String, Symbol, Integer] arg
322
+ # Use a Symbol or String as a name to lookup its value. Use an
323
+ # Integer to lookup the corresponding name.
324
+ #
325
+ def [](arg)
326
+ if arg.is_a? Integer
327
+ list.invert[arg]
328
+ elsif arg.is_a? String or arg.is_a? Symbol
329
+ list[arg.to_s.upcase]
282
330
  end
283
331
  end
284
- opts = o.merge(:name => name, :type => type)
285
- offset = opts[:offset]
286
332
 
287
- @__layout_args << name
288
- @__layout_args << type
289
- @__layout_args << offset if offset
333
+ # Generates a hash of all the constant names mapped to value. Usually,
334
+ # it's a good idea to override this like so to cache results in
335
+ # derived modules:
336
+ #
337
+ # def list; @@list ||= super() ; end
338
+ #
339
+ def list
340
+ constants.inject({}){|h,c| h.merge! c => const_get(c) }
341
+ end
290
342
 
291
- @__metadata << opts
292
- return opts
293
- end
294
- end # DSLStructLayoutBuilder
343
+ # When called from a module definition or class method, this method
344
+ # imports all the constants from # namespace 'nspace' that start with
345
+ # into the local namespace as constants named with whatever follows the
346
+ # prefix. Only constant names that match [A-Z][A-Z0-9_]+ are imported,
347
+ # the rest are ignored.
348
+ #
349
+ # This method also yields the (short) constant name and value to a block
350
+ # if one is provided. The block works like [...].select {|c,v| ... } in
351
+ # that the value is not mapped if the block returns nil or false.
352
+ def slurp_constants(nspace, prefix)
353
+ nspace.constants.grep(/^(#{prefix}([A-Z][A-Z0-9_]+))$/) do
354
+ c = $2
355
+ v = nspace.const_get($1)
356
+ next if block_given? and not yield(c,v)
357
+ const_set c, v
358
+ end
359
+ end
360
+ end # ConstMap
295
361
 
296
- # ConstMap can be used to organize and lookup value to constant mappings and
297
- # vice versa. Constants can be imported from a namespace based on a regular
298
- # expression or other means.
299
- module ConstMap
362
+ # Behaves just like ConstFlags, except that it returns a list
363
+ # of names for the flags. Name string lookups work same way as
364
+ # ConstFlags.
365
+ module ConstFlagsMap
366
+ include ConstMap
300
367
 
301
- def self.included(klass)
302
- klass.extend(ConstMap)
303
- end
368
+ def self.included(klass)
369
+ klass.extend(ConstFlagsMap)
370
+ end
304
371
 
305
- # A flexible name to value lookup.
306
- #
307
- # @param [String, Symbol, Integer] arg
308
- # Use a Symbol or String as a name to lookup its value. Use an
309
- # Integer to lookup the corresponding name.
310
- #
311
- def [](arg)
312
- if arg.is_a? Integer
313
- list.invert[arg]
314
- elsif arg.is_a? String or arg.is_a? Symbol
315
- list[arg.to_s.upcase]
372
+ # A flexible lookup method for name to bit-flag mappings.
373
+ #
374
+ # @param [String, Symbol, Integer] arg
375
+ # Symbol or String as a name to lookup a bit-flag value, or an
376
+ # Integer to lookup a corresponding names for the flags present in it.
377
+ def [](arg)
378
+ if arg.is_a? Integer
379
+ ret = []
380
+ if arg == 0
381
+ n = list.invert[0]
382
+ ret << n if n
383
+ else
384
+ list.invert.sort.each {|v,n| ret << n if v !=0 and (v & arg) == v }
385
+ end
386
+ return ret
387
+ elsif arg.is_a? String or arg.is_a? Symbol
388
+ list[arg.to_s.upcase]
389
+ end
316
390
  end
317
- end
391
+ end # ConstFlagsMap
318
392
 
319
- # Generates a hash of all the constant names mapped to value. Usually,
320
- # it's a good idea to override this like so to cache results in
321
- # derived modules:
322
- #
323
- # def list; @@list ||= super() ; end
324
- #
325
- def list
326
- constants.inject({}){|h,c| h.merge! c => const_get(c) }
327
- end
328
393
 
329
- # When called from a module definition or class method, this method
330
- # imports all the constants from # namespace 'nspace' that start with
331
- # into the local namespace as constants named with whatever follows the
332
- # prefix. Only constant names that match [A-Z][A-Z0-9_]+ are imported,
333
- # the rest are ignored.
334
- #
335
- # This method also yields the (short) constant name and value to a block
336
- # if one is provided. The block works like [...].select {|c,v| ... } in
337
- # that the value is not mapped if the block returns nil or false.
338
- def slurp_constants(nspace, prefix)
339
- nspace.constants.grep(/^(#{prefix}([A-Z][A-Z0-9_]+))$/) do
340
- c = $2
341
- v = nspace.const_get($1)
342
- next if block_given? and not yield(c,v)
343
- const_set c, v
344
- end
345
- end
346
- end # ConstMap
394
+ module NetEndian
395
+ extend ::FFI::Library
347
396
 
348
- # Behaves just like ConstFlags, except that it returns a list
349
- # of names for the flags. Name string lookups work same way as
350
- # ConstFlags.
351
- module ConstFlagsMap
352
- include ConstMap
397
+ ffi_lib FFI::Library::LIBC
398
+ begin; ffi_lib 'wsock32'; rescue LoadError; end
353
399
 
354
- def self.included(klass)
355
- klass.extend(ConstFlagsMap)
356
- end
400
+ attach_function :htons, [:uint16], :uint16
401
+ attach_function :ntohs, [:uint16], :uint16
402
+ attach_function :htonl, [:uint32], :uint32
403
+ attach_function :ntohl, [:uint32], :uint32
357
404
 
358
- # A flexible lookup method for name to bit-flag mappings.
405
+ I16_convert = [method(:ntohs), method(:htons)]
406
+ I32_convert = [method(:ntohl), method(:htonl)]
407
+
408
+ ENDIAN_METHS = {
409
+ ::FFI.find_type(:int16) => I16_convert,
410
+ ::FFI.find_type(:uint16) => I16_convert,
411
+ ::FFI.find_type(:int32) => I32_convert,
412
+ ::FFI.find_type(:uint32) => I32_convert,
413
+ }
414
+ end # NetEndian
415
+
416
+
417
+ # A special helper for network packet structures that use big-endian or
418
+ # "network" byte-order. This helper generates read/write accessors that
419
+ # automatically call the appropriate byte conversion function, ntohs/ntohl
420
+ # for 'reading' a 16/32 bit field, and htons/htonl for writing to one.
359
421
  #
360
- # @param [String, Symbol, Integer] arg
361
- # Symbol or String as a name to lookup a bit-flag value, or an
362
- # Integer to lookup a corresponding names for the flags present in it.
363
- def [](arg)
364
- if arg.is_a? Integer
365
- ret = []
366
- if arg == 0
367
- n = list.invert[0]
368
- ret << n if n
369
- else
370
- list.invert.sort.each {|v,n| ret << n if v !=0 and (v & arg) == v }
371
- end
372
- return ret
373
- elsif arg.is_a? String or arg.is_a? Symbol
374
- list[arg.to_s.upcase]
422
+ # NOTE this helper does not currently do anything special for 64-bit or
423
+ # higher values but this might be added at some point if the need arises.
424
+ #
425
+ # NOTE unlike the StructHelper module, no special relevance is given
426
+ # to fields with a ":p_struct" option or defined with the p_struct DSL
427
+ # method. These are ignored and treated like any other field. A net struct
428
+ # generally doesn't contain pointers into native memory anyway.
429
+ module NetStructHelper
430
+ def self.included(base)
431
+ base.instance_eval { include StructHelper }
432
+ base.extend(ClassMethods)
375
433
  end
376
- end
377
- end # ConstFlagsMap
378
-
379
-
380
- module NetEndian
381
- extend ::FFI::Library
382
-
383
- ffi_lib FFI::Library::LIBC
384
- begin; ffi_lib 'wsock32'; rescue LoadError; end
385
-
386
- attach_function :htons, [:uint16], :uint16
387
- attach_function :ntohs, [:uint16], :uint16
388
- attach_function :htonl, [:uint32], :uint32
389
- attach_function :ntohl, [:uint32], :uint32
390
-
391
- I16_convert = [method(:ntohs), method(:htons)]
392
- I32_convert = [method(:ntohl), method(:htonl)]
393
-
394
- ENDIAN_METHS = {
395
- ::FFI.find_type(:int16) => I16_convert,
396
- ::FFI.find_type(:uint16) => I16_convert,
397
- ::FFI.find_type(:int32) => I32_convert,
398
- ::FFI.find_type(:uint32) => I32_convert,
399
- }
400
- end # NetEndian
401
-
402
-
403
- # A special helper for network packet structures that use big-endian or
404
- # "network" byte-order. This helper generates read/write accessors that
405
- # automatically call the appropriate byte conversion function, ntohs/ntohl
406
- # for 'reading' a 16/32 bit field, and htons/htonl for writing to one.
407
- #
408
- # NOTE this helper does not currently do anything special for 64-bit or
409
- # higher values but this might be added at some point if the need arises.
410
- #
411
- # NOTE unlike the StructHelper module, no special relevance is given
412
- # to fields with a ":p_struct" option or defined with the p_struct DSL
413
- # method. These are ignored and treated like any other field. A net struct
414
- # generally doesn't contain pointers into native memory anyway.
415
- module NetStructHelper
416
- def self.included(base)
417
- base.instance_eval { include StructHelper }
418
- base.extend(ClassMethods)
419
- end
420
434
 
421
- module ClassMethods
435
+ module ClassMethods
422
436
 
423
- private
437
+ private
424
438
 
425
- def _class_meths_from_dsl_metadata(meta)
426
- (@dsl_metadata = meta).each do |spec|
427
- name = spec[:name]
428
- type = spec[:type]
439
+ def _class_meths_from_dsl_metadata(meta)
440
+ (@dsl_metadata = meta).each do |spec|
441
+ name = spec[:name]
442
+ type = spec[:type]
429
443
 
430
- # Create endian swapper accessors methods for each applicable
431
- # field
432
- if( type.kind_of?(Symbol) and
433
- cnv=NetEndian::ENDIAN_METHS[ ::FFI.find_type(type) ] )
434
- define_method(:"#{name}"){ cnv[0].call(self[name]) }
435
- define_method(:"#{name}="){|val| self[name] = cnv[1].call(val) }
436
- else
437
- define_field_accessor(:"#{name}"){ self[name] }
438
- define_field_accessor(:"#{name}="){|val| self[name]=val }
439
- end
444
+ # Create endian swapper accessors methods for each applicable
445
+ # field
446
+ if( type.kind_of?(Symbol) and
447
+ cnv=NetEndian::ENDIAN_METHS[ ::FFI.find_type(type) ] )
448
+ define_method(:"#{name}"){ cnv[0].call(self[name]) }
449
+ define_method(:"#{name}="){|val| self[name] = cnv[1].call(val) }
450
+ else
451
+ define_field_accessor(:"#{name}"){ self[name] }
452
+ define_field_accessor(:"#{name}="){|val| self[name]=val }
453
+ end
440
454
 
455
+ end
441
456
  end
442
457
  end
443
- end
444
- end # NetStructHelper
458
+ end # NetStructHelper
445
459
 
446
- end # FFI::DRY
460
+ end # DRY
461
+ end # FFI
447
462
 
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ffi_dry
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.9
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 11
9
+ version: 0.1.11
5
10
  platform: ruby
6
11
  authors:
7
12
  - Eric Monti
@@ -9,69 +14,23 @@ autorequire:
9
14
  bindir: bin
10
15
  cert_chain: []
11
16
 
12
- date: 2010-03-05 00:00:00 -06:00
17
+ date: 2010-06-02 00:00:00 -05:00
13
18
  default_executable:
14
19
  dependencies:
15
20
  - !ruby/object:Gem::Dependency
16
21
  name: ffi
17
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
20
24
  requirements:
21
25
  - - ">="
22
26
  - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ - 5
30
+ - 0
23
31
  version: 0.5.0
24
- version:
25
- - !ruby/object:Gem::Dependency
26
- name: rspec
27
- type: :development
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: "0"
34
- version:
35
- - !ruby/object:Gem::Dependency
36
- name: yard
37
- type: :development
38
- version_requirement:
39
- version_requirements: !ruby/object:Gem::Requirement
40
- requirements:
41
- - - ">="
42
- - !ruby/object:Gem::Version
43
- version: "0"
44
- version:
45
- - !ruby/object:Gem::Dependency
46
- name: ffi
47
32
  type: :runtime
48
- version_requirement:
49
- version_requirements: !ruby/object:Gem::Requirement
50
- requirements:
51
- - - ">="
52
- - !ruby/object:Gem::Version
53
- version: 0.5.0
54
- version:
55
- - !ruby/object:Gem::Dependency
56
- name: rspec
57
- type: :development
58
- version_requirement:
59
- version_requirements: !ruby/object:Gem::Requirement
60
- requirements:
61
- - - ">="
62
- - !ruby/object:Gem::Version
63
- version: "0"
64
- version:
65
- - !ruby/object:Gem::Dependency
66
- name: yard
67
- type: :development
68
- version_requirement:
69
- version_requirements: !ruby/object:Gem::Requirement
70
- requirements:
71
- - - ">="
72
- - !ruby/object:Gem::Version
73
- version: "0"
74
- version:
33
+ version_requirements: *id001
75
34
  description: Provides some useful modules, classes, and methods for FFI bindings as well as a DSL-like syntax for FFI::Struct layouts
76
35
  email: emonti@matasano.com
77
36
  executables: []
@@ -99,6 +58,8 @@ files:
99
58
  - spec/spec_helper.rb
100
59
  has_rdoc: true
101
60
  homepage: http://github.com/emonti/ffi_dry
61
+ licenses: []
62
+
102
63
  post_install_message:
103
64
  rdoc_options:
104
65
  - --charset=UTF-8
@@ -108,20 +69,22 @@ required_ruby_version: !ruby/object:Gem::Requirement
108
69
  requirements:
109
70
  - - ">="
110
71
  - !ruby/object:Gem::Version
72
+ segments:
73
+ - 0
111
74
  version: "0"
112
- version:
113
75
  required_rubygems_version: !ruby/object:Gem::Requirement
114
76
  requirements:
115
77
  - - ">="
116
78
  - !ruby/object:Gem::Version
79
+ segments:
80
+ - 0
117
81
  version: "0"
118
- version:
119
82
  requirements: []
120
83
 
121
84
  rubyforge_project:
122
- rubygems_version: 1.3.1
85
+ rubygems_version: 1.3.6
123
86
  signing_key:
124
- specification_version: 2
87
+ specification_version: 3
125
88
  summary: Syntactic sugar and helper utilities for FFI
126
89
  test_files:
127
90
  - spec/ffi_dry_spec.rb