emonti-ffi_dry 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +104 -21
- data/VERSION +1 -1
- data/lib/ffi/dry.rb +6 -4
- data/samples/basic.rb +54 -34
- data/samples/describer.rb +118 -47
- metadata +3 -2
data/README.rdoc
CHANGED
@@ -10,8 +10,9 @@ things and add support for some uncommon ones.
|
|
10
10
|
|
11
11
|
== Synopsis
|
12
12
|
|
13
|
+
(samples/ in the package for code)
|
13
14
|
|
14
|
-
|
15
|
+
A major feature is a DSL"-like" syntax for declaring structure members
|
15
16
|
in FFI::Struct or FFI::ManagedStruct definitions.
|
16
17
|
|
17
18
|
require 'rubygems'
|
@@ -22,27 +23,32 @@ in FFI::Struct or FFI::ManagedStruct definitions.
|
|
22
23
|
include FFI::DRY::StructHelper
|
23
24
|
|
24
25
|
# we get a new way of specifying layouts with a 'dsl'-like syntax
|
25
|
-
# The :desc => ...
|
26
|
+
# The hash containing {:desc => ... } can contain arbitrary keys which
|
27
|
+
# can be used however we like. dsl_metadata will contain all these
|
28
|
+
# in the class and instance.
|
26
29
|
dsl_layout do
|
27
30
|
field :field1, :uint16, :desc => 'this is field 1'
|
28
31
|
field :field2, :uint16, :desc => 'this is field 2'
|
29
32
|
end
|
30
33
|
end
|
31
34
|
|
32
|
-
|
33
35
|
ss0=SomeStruct.new
|
34
36
|
|
35
|
-
With the
|
36
|
-
|
37
|
+
With the declarations above, we specified :desc hash value in metadata
|
38
|
+
Let's check out in our instance.
|
37
39
|
|
38
40
|
pp ss0.dsl_metadata
|
39
41
|
[{:type=>:uint16, :name=>:field1, :desc=>"this is field 1"},
|
40
42
|
{:type=>:uint16, :name=>:field2, :desc=>"this is field 2"}]
|
41
43
|
# => nil
|
42
44
|
|
45
|
+
Or class.
|
46
|
+
|
47
|
+
pp SomeStruct.dsl_metadata
|
48
|
+
#...
|
43
49
|
|
44
|
-
|
45
|
-
initialization. (The FFI standard ways still work too)
|
50
|
+
We get some additional ways of instantiating and declaring values for free
|
51
|
+
during initialization. (The FFI standard ways still work too)
|
46
52
|
|
47
53
|
raw_data = "\x00\x00\xff\xff"
|
48
54
|
|
@@ -55,10 +61,19 @@ initialization. (The FFI standard ways still work too)
|
|
55
61
|
ss1,
|
56
62
|
ss2,
|
57
63
|
ss3,
|
58
|
-
ss4].each_with_index {|x,i| p ["ss#{i}",[x.field1, x.field2]]}
|
64
|
+
ss4 ].each_with_index {|x,i| p ["ss#{i}",[x.field1, x.field2]]}
|
65
|
+
|
66
|
+
# which will produce...
|
67
|
+
# ["ss0", [0, 0]]
|
68
|
+
# ["ss1", [0, 65535]]
|
69
|
+
# ["ss2", [1, 2]]
|
70
|
+
# ["ss3", [1, 0]]
|
71
|
+
# ["ss4", [1, 65535]]
|
72
|
+
|
59
73
|
|
60
|
-
Here's
|
61
|
-
"neighborly" way.
|
74
|
+
Here's a broader example which utilizes that arbitrary ':desc' parameter in a
|
75
|
+
"neighborly" way. This also demonstrates superclasses to add common struct
|
76
|
+
features, declaring array fields, as well as nesting other structs.
|
62
77
|
|
63
78
|
require 'rubygems'
|
64
79
|
require 'ffi'
|
@@ -68,7 +83,7 @@ Here's an example which utilizes that arbitrary ':desc' parameter in a
|
|
68
83
|
include ::FFI::DRY::StructHelper
|
69
84
|
|
70
85
|
def self.describe
|
71
|
-
print "Struct: #{self.
|
86
|
+
print "Struct: #{self.name}"
|
72
87
|
dsl_metadata().each_with_index do |spec, i|
|
73
88
|
print " Field #{i}\n"
|
74
89
|
print " name: #{spec[:name].inspect}\n"
|
@@ -96,7 +111,8 @@ Here's an example which utilizes that arbitrary ':desc' parameter in a
|
|
96
111
|
:desc => "a string up to 255 bytes bound by :len"
|
97
112
|
end
|
98
113
|
|
99
|
-
# override kind getter method
|
114
|
+
# override kind getter method with our own
|
115
|
+
# resolves kind to some kind of type array for example...
|
100
116
|
def kind
|
101
117
|
[:default, :bar, :baz][ self[:kind] ]
|
102
118
|
end
|
@@ -105,16 +121,78 @@ Here's an example which utilizes that arbitrary ':desc' parameter in a
|
|
105
121
|
s1=TestStruct.new
|
106
122
|
s2=SomeStruct.new
|
107
123
|
|
124
|
+
# check out that 'kind' override:
|
125
|
+
s2.kind
|
126
|
+
# => :default
|
127
|
+
|
128
|
+
# oh and the regular FFI way is always intact
|
129
|
+
s2[:kind]
|
130
|
+
# => 0
|
131
|
+
|
132
|
+
s2[:kind]=1
|
133
|
+
s2.kind
|
134
|
+
# => :bar
|
135
|
+
|
136
|
+
s2.kind=3
|
137
|
+
s2.kind
|
138
|
+
# => :baz
|
139
|
+
|
108
140
|
puts "*"*70
|
109
141
|
s1.describe
|
110
|
-
|
142
|
+
## we get a dump of metadata
|
143
|
+
# **********************************************************************
|
144
|
+
# Struct: TestStruct
|
145
|
+
# Field 0
|
146
|
+
# name: :field1
|
147
|
+
# type: :uint8
|
148
|
+
# desc: test field 1
|
149
|
+
#
|
150
|
+
# Field 1
|
151
|
+
# name: :field2
|
152
|
+
# type: :uint8
|
153
|
+
# desc: test field 2
|
154
|
+
|
111
155
|
puts "*"*70
|
112
156
|
s2.describe
|
113
|
-
|
157
|
+
## we get a dump of metadata
|
158
|
+
# Struct: SomeStruct Field 0
|
159
|
+
# name: :kind
|
160
|
+
# type: :uint8
|
161
|
+
# desc: a type identifier
|
162
|
+
#
|
163
|
+
# Field 1
|
164
|
+
# name: :tst
|
165
|
+
# type: TestStruct
|
166
|
+
# desc: a nested TestStruct
|
167
|
+
#
|
168
|
+
# Field 2
|
169
|
+
# name: :len
|
170
|
+
# type: :uint8
|
171
|
+
# desc: 8-bit size value (>= self.size+2)
|
172
|
+
#
|
173
|
+
# Field 3
|
174
|
+
# name: :str
|
175
|
+
# type: [:char, 255]
|
176
|
+
# desc: a string up to 255 bytes bound by :len
|
114
177
|
|
178
|
+
puts "*"*70
|
179
|
+
s2.tst.describe
|
180
|
+
## same as s1.describe
|
181
|
+
# **********************************************************************
|
182
|
+
# Struct: TestStruct
|
183
|
+
# Field 0
|
184
|
+
# name: :field1
|
185
|
+
# type: :uint8
|
186
|
+
# desc: test field 1
|
187
|
+
#
|
188
|
+
# Field 1
|
189
|
+
# name: :field2
|
190
|
+
# type: :uint8
|
191
|
+
# desc: test field 2
|
115
192
|
|
116
193
|
There's also some helpers for collecting lookup maps for constants, a common
|
117
|
-
and handy thing when porting various libraries.
|
194
|
+
and handy thing when porting various libraries. We use Socket here just for
|
195
|
+
example purposes, you can 'slurp' constants form any namespace this way.
|
118
196
|
|
119
197
|
require 'ffi/dry'
|
120
198
|
require 'socket'
|
@@ -122,29 +200,34 @@ and handy thing when porting various libraries.
|
|
122
200
|
module AddressFamily
|
123
201
|
include FFI::DRY::ConstMap
|
124
202
|
slurp_constants ::Socket, "AF_"
|
125
|
-
def list ; @@list ||= super() ; end
|
203
|
+
def list ; @@list ||= super() ; end # only generate the hash once
|
126
204
|
end
|
127
205
|
|
128
|
-
AddressFamily now has all the constants it found for Socket::AF_*
|
206
|
+
AddressFamily now has all the constants it found for Socket::AF_* minus the
|
207
|
+
prefix.
|
129
208
|
|
130
209
|
AddressFamily::INET
|
131
210
|
AddressFamily::LINK
|
132
211
|
AddressFamily::INET6
|
212
|
+
|
133
213
|
etc...
|
134
214
|
|
135
|
-
We can do type
|
215
|
+
We can do type or value lookups using []
|
216
|
+
|
136
217
|
AddressFamily[2] # => "INET"
|
137
218
|
AddressFamily["INET"] # => 2
|
138
219
|
|
139
|
-
We can get a hash of all
|
220
|
+
We can get a hash of all constant->value pairs with .list
|
221
|
+
|
140
222
|
AddressFamily.list
|
141
223
|
# => {"NATM"=>31, "DLI"=>13, "UNIX"=>1, "NETBIOS"=>33, ...}
|
142
224
|
|
143
|
-
...
|
225
|
+
... and invert for a reverse mapping
|
226
|
+
|
144
227
|
AddressFamily.list.invert
|
145
228
|
# => {16=>"APPLETALK", 5=>"CHAOS", 27=>"NDRV", 0=>"UNSPEC", ...}
|
146
229
|
|
147
230
|
|
148
|
-
==
|
231
|
+
== License
|
149
232
|
|
150
233
|
Copyright (c) 2009 Eric Monti. See LICENSE for details.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.1
|
data/lib/ffi/dry.rb
CHANGED
@@ -217,6 +217,7 @@ module FFI::DRY
|
|
217
217
|
#
|
218
218
|
# struct field_name, RubyClass, { ... metadata ... }
|
219
219
|
#
|
220
|
+
# :offset is a special key in metadata, specifies the offset of the field.
|
220
221
|
def struct(name, klass, o={})
|
221
222
|
unless klass.kind_of?(Class) and klass < ::FFI::Struct
|
222
223
|
raise(::ArgumentError, "klass must be a struct")
|
@@ -236,6 +237,7 @@ module FFI::DRY
|
|
236
237
|
#
|
237
238
|
# array field_name, [ctype, N], { ... metadata ... }
|
238
239
|
#
|
240
|
+
# :offset is a special key in metadata, specifies the offset of the field.
|
239
241
|
def array(name, type, o={})
|
240
242
|
unless type.kind_of?(::Array)
|
241
243
|
raise(::ArgumentError, "type must be an array")
|
@@ -256,6 +258,7 @@ module FFI::DRY
|
|
256
258
|
#
|
257
259
|
# field field_name, ctype, { ... metadata ... }
|
258
260
|
#
|
261
|
+
# :offset is a special key in metadata, specifies the offset of the field.
|
259
262
|
def field(name, type, o={})
|
260
263
|
opts = o.merge(:name => name, :type => type)
|
261
264
|
offset = opts[:offset]
|
@@ -275,8 +278,7 @@ module FFI::DRY
|
|
275
278
|
|
276
279
|
end
|
277
280
|
|
278
|
-
# Used for creating various value <=> constant mapping modules
|
279
|
-
# Ip::Hdr::Proto for IP protocols.
|
281
|
+
# Used for creating various value <=> constant mapping namespace modules.
|
280
282
|
module ConstMap
|
281
283
|
|
282
284
|
def self.included(klass)
|
@@ -333,8 +335,8 @@ module FFI::DRY
|
|
333
335
|
end
|
334
336
|
|
335
337
|
# A flexible lookup. Takes 'arg' as a Symbol or String as a name to lookup
|
336
|
-
# a value, or an Integer to lookup a corresponding names for the
|
337
|
-
# present in it.
|
338
|
+
# a bit-flag value, or an Integer to lookup a corresponding names for the
|
339
|
+
# flags present in it.
|
338
340
|
def [](arg)
|
339
341
|
if arg.is_a? Integer
|
340
342
|
ret = []
|
data/samples/basic.rb
CHANGED
@@ -1,35 +1,55 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# One major feature is a dsl"-like" syntax for declaring structure members
|
3
|
+
# in FFI::Struct or FFI::ManagedStruct definitions.
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'ffi'
|
7
|
+
require 'ffi/dry'
|
8
|
+
|
9
|
+
class SomeStruct < FFI::Struct
|
10
|
+
include FFI::DRY::StructHelper
|
11
|
+
|
12
|
+
# we get a new way of specifying layouts with a 'dsl'-like syntax
|
13
|
+
# The hash containing {:desc => ... } can contain arbitrary keys which
|
14
|
+
# can be used however we like. dsl_metadata will contain all these
|
15
|
+
# in the class and instance.
|
16
|
+
dsl_layout do
|
17
|
+
field :field1, :uint16, :desc => 'this is field 1'
|
18
|
+
field :field2, :uint16, :desc => 'this is field 2'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
ss0=SomeStruct.new
|
23
|
+
|
24
|
+
# With the declaration above, we specified :desc hash value, which was stored
|
25
|
+
# in metadata along with the field name and type.
|
26
|
+
|
27
|
+
pp ss0.dsl_metadata
|
28
|
+
[{:type=>:uint16, :name=>:field1, :desc=>"this is field 1"},
|
29
|
+
{:type=>:uint16, :name=>:field2, :desc=>"this is field 2"}]
|
30
|
+
# => nil
|
31
|
+
|
32
|
+
|
33
|
+
# We get some free additional ways of instantiating and declaring values during
|
34
|
+
# initialization. (The FFI standard ways still work too)
|
35
|
+
|
36
|
+
raw_data = "\x00\x00\xff\xff"
|
37
|
+
|
38
|
+
ss1=SomeStruct.new :raw => raw_data
|
39
|
+
ss2=SomeStruct.new :raw => raw_data, :field1 => 1, :field2 => 2
|
40
|
+
ss3=SomeStruct.new {|x| x.field1=1 }
|
41
|
+
ss4=SomeStruct.new(:raw => raw_data) {|x| x.field1=1 }
|
42
|
+
|
43
|
+
[ ss0,
|
44
|
+
ss1,
|
45
|
+
ss2,
|
46
|
+
ss3,
|
47
|
+
ss4 ].each_with_index {|x,i| p ["ss#{i}",[x.field1, x.field2]]}
|
48
|
+
|
49
|
+
# which should produce...
|
50
|
+
# ["ss0", [0, 0]]
|
51
|
+
# ["ss1", [0, 65535]]
|
52
|
+
# ["ss2", [1, 2]]
|
53
|
+
# ["ss3", [1, 0]]
|
54
|
+
# ["ss4", [1, 65535]]
|
35
55
|
|
data/samples/describer.rb
CHANGED
@@ -1,48 +1,119 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
1
|
+
# Here's a broader example which utilizes that arbitrary ':desc' parameter in a
|
2
|
+
# "neighborly" way. This also demonstrates superclasses to add common struct
|
3
|
+
# features, declaring array fields, as well as nesting other structs.
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'ffi'
|
7
|
+
require 'ffi/dry'
|
8
|
+
|
9
|
+
class NeighborlyStruct < ::FFI::Struct
|
10
|
+
include ::FFI::DRY::StructHelper
|
11
|
+
|
12
|
+
def self.describe
|
13
|
+
print "Struct: #{self.name}"
|
14
|
+
dsl_metadata().each_with_index do |spec, i|
|
15
|
+
print " Field #{i}\n"
|
16
|
+
print " name: #{spec[:name].inspect}\n"
|
17
|
+
print " type: #{spec[:type].inspect}\n"
|
18
|
+
print " desc: #{spec[:desc]}\n\n"
|
19
|
+
end
|
20
|
+
print "\n"
|
21
|
+
end
|
22
|
+
def describe; self.class.describe; end
|
23
|
+
end
|
24
|
+
|
25
|
+
class TestStruct < NeighborlyStruct
|
26
|
+
dsl_layout do
|
27
|
+
field :field1, :uint8, :desc => "test field 1"
|
28
|
+
field :field2, :uint8, :desc => "test field 2"
|
29
|
+
end
|
15
30
|
end
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
s2.
|
31
|
+
|
32
|
+
class SomeStruct < NeighborlyStruct
|
33
|
+
dsl_layout do
|
34
|
+
field :kind, :uint8, :desc => "a type identifier"
|
35
|
+
struct :tst, TestStruct, :desc => "a nested TestStruct"
|
36
|
+
field :len, :uint8, :desc => "8-bit size value (>= self.size+2)"
|
37
|
+
array :str, [:char,255],
|
38
|
+
:desc => "a string up to 255 bytes bound by :len"
|
39
|
+
end
|
40
|
+
|
41
|
+
# override kind getter method with our own
|
42
|
+
# resolves kind to some kind of type array for example...
|
43
|
+
def kind
|
44
|
+
[:default, :bar, :baz][ self[:kind] ]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
s1=TestStruct.new
|
49
|
+
s2=SomeStruct.new
|
50
|
+
|
51
|
+
# check out that 'kind' override:
|
52
|
+
s2.kind
|
53
|
+
# => :default
|
54
|
+
|
55
|
+
# oh and the regular FFI way is always intact
|
56
|
+
s2[:kind]
|
57
|
+
# => 0
|
58
|
+
|
59
|
+
s2[:kind]=1
|
60
|
+
s2.kind
|
61
|
+
# => :bar
|
62
|
+
|
63
|
+
s2.kind=3
|
64
|
+
s2.kind
|
65
|
+
# => :baz
|
66
|
+
|
67
|
+
puts "*"*70
|
68
|
+
s1.describe
|
69
|
+
## we get a dump of metadata
|
70
|
+
# **********************************************************************
|
71
|
+
# Struct: TestStruct
|
72
|
+
# Field 0
|
73
|
+
# name: :field1
|
74
|
+
# type: :uint8
|
75
|
+
# desc: test field 1
|
76
|
+
#
|
77
|
+
# Field 1
|
78
|
+
# name: :field2
|
79
|
+
# type: :uint8
|
80
|
+
# desc: test field 2
|
81
|
+
|
82
|
+
puts "*"*70
|
83
|
+
s2.describe
|
84
|
+
## we get a dump of metadata
|
85
|
+
# Struct: SomeStruct Field 0
|
86
|
+
# name: :kind
|
87
|
+
# type: :uint8
|
88
|
+
# desc: a type identifier
|
89
|
+
#
|
90
|
+
# Field 1
|
91
|
+
# name: :tst
|
92
|
+
# type: TestStruct
|
93
|
+
# desc: a nested TestStruct
|
94
|
+
#
|
95
|
+
# Field 2
|
96
|
+
# name: :len
|
97
|
+
# type: :uint8
|
98
|
+
# desc: 8-bit size value (>= self.size+2)
|
99
|
+
#
|
100
|
+
# Field 3
|
101
|
+
# name: :str
|
102
|
+
# type: [:char, 255]
|
103
|
+
# desc: a string up to 255 bytes bound by :len
|
104
|
+
|
105
|
+
puts "*"*70
|
106
|
+
s2.tst.describe
|
107
|
+
## same as s1.describe
|
108
|
+
# **********************************************************************
|
109
|
+
# Struct: TestStruct
|
110
|
+
# Field 0
|
111
|
+
# name: :field1
|
112
|
+
# type: :uint8
|
113
|
+
# desc: test field 1
|
114
|
+
#
|
115
|
+
# Field 1
|
116
|
+
# name: :field2
|
117
|
+
# type: :uint8
|
118
|
+
# desc: test field 2
|
119
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: emonti-ffi_dry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Monti
|
@@ -67,6 +67,7 @@ files:
|
|
67
67
|
- spec/spec_helper.rb
|
68
68
|
has_rdoc: false
|
69
69
|
homepage: http://github.com/emonti/ffi_dry
|
70
|
+
licenses:
|
70
71
|
post_install_message:
|
71
72
|
rdoc_options:
|
72
73
|
- --charset=UTF-8
|
@@ -87,7 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
87
88
|
requirements: []
|
88
89
|
|
89
90
|
rubyforge_project:
|
90
|
-
rubygems_version: 1.
|
91
|
+
rubygems_version: 1.3.5
|
91
92
|
signing_key:
|
92
93
|
specification_version: 3
|
93
94
|
summary: Syntactic sugar and helper utilities for FFI
|