emonti-ffi_dry 0.1.0 → 0.1.1
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/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
|