ffi_dry 0.1.9 → 0.1.11

Sign up to get free protection for your applications and to get access to all the features.
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