ffi-struct_ex 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 640a03806e45782319f75058970cf5c999927d03
4
- data.tar.gz: f5f3247405c6243e66a4d958d356787cb231c7d8
3
+ metadata.gz: f351f025099f8a13f3c2aeba992b7fd67d8dd90b
4
+ data.tar.gz: 198834f90618214c7ffb3a811d3de12c114dd4db
5
5
  SHA512:
6
- metadata.gz: 57dcf4abe3d98e2fe8c0cbcbaed6e17e2ac2a8d877f1c57d53da3f97666099e73de65cca320714451e535ab0a5724361d6c3bbc965838d50d7a58209a4779867
7
- data.tar.gz: ece4cba95fbf5095e5151432f072957ed6d3d8eaf4b6e56ace473d745dfa48ccd12c4531464a6cb185698012b1cb942769181e2bcb92649f83023c76378ef8b0
6
+ metadata.gz: 31828f46959ae2d4a4686b787f0555b3f320c12bfb80d7375920d1a45e8e620a27dc1f75cf13a19233ea157ac208e22f7e1909305cf156ca0b66894e256e22c5
7
+ data.tar.gz: daf79112bfa648f3aa9343319cf5e21d2cae9668ef1df5a98544e4af347279408032267184325fa080c7bd031446fcc8583327de83bf6b259cbd84d3b3281b39
data/README.md CHANGED
@@ -18,22 +18,24 @@ Or install it yourself as:
18
18
 
19
19
  ## Usage
20
20
 
21
- * Struct (only support "unsigned" field)
21
+ * Struct (default type is "unsigned char/short/int" depending on bits size)
22
22
 
23
23
  ```ruby
24
24
  require 'ffi/struct_ex'
25
25
 
26
26
  class Subject < FFI::StructEx
27
- layout :bits_0_2, 3,
28
- :bit_3, 1,
27
+ layout :bits_0_2, 'uint8: 3',
28
+ :bit_3, 'char: 1',
29
29
  :bit_4, 1,
30
- :bits_5_7, 3
30
+ :bits_5_7, 'char: 3'
31
31
  end
32
32
 
33
- subject = Subject.new(bits_0_2: 0b001, bit_3: 0b1, bit_4: 0b0, bits_5_7: 0b011)
33
+ Subject.size #=> 1
34
34
 
35
- subject[:bits_0_2] #=> 0b101
36
- subject.read #=> 0b0110_1001
35
+ subject = Subject.new(bits_0_2: 0b001, bit_3: 0b1, bit_4: 0b1, bits_5_7: 0b100)
36
+
37
+ subject[:bits_0_2] #=> 1
38
+ subject[:bits_5_7] #=> -4
37
39
  ```
38
40
 
39
41
  * Struct (embedded bit fields)
@@ -9,6 +9,10 @@ class Integer
9
9
 
10
10
  bytes
11
11
  end
12
+
13
+ def to_signed(bits_size)
14
+ self & (1 << bits_size - 1) != 0 ? self - (1 << bits_size) : self
15
+ end
12
16
  end
13
17
 
14
18
  class String
@@ -26,67 +30,140 @@ end
26
30
 
27
31
  module FFI
28
32
  class StructEx < FFI::Struct
33
+ SIGNED_NUMBER_TYPES = [FFI::Type::INT8, FFI::Type::INT16, FFI::Type::INT32, FFI::Type::INT64]
34
+ UNSIGNED_NUMBER_TYPES = [FFI::Type::UINT8, FFI::Type::UINT16, FFI::Type::UINT32, FFI::Type::UINT64]
35
+
29
36
  class << self
30
37
  def bit_fields(*field_specs)
31
- struct_class = Class.new(StructEx) do
32
- layout(*field_specs)
33
- end
38
+ Class.new(FFI::StructLayout::Field) do
39
+ class << self
40
+ attr_accessor :struct_class
41
+
42
+ def alignment; struct_class.alignment; end
43
+ def size; struct_class.size; end
44
+ end
45
+
46
+ self.struct_class = Class.new(StructEx) do
47
+ layout(*field_specs)
48
+ end
34
49
 
35
- bit_fields_class = Class.new(FFI::StructLayout::Field) do
36
50
  def initialize(name, offset, type)
37
51
  super(name, offset, FFI::Type::Struct.new(self.class.struct_class))
38
52
  end
39
53
 
40
54
  def get(ptr)
41
- #self.class.struct_class == type.struct_class
42
- self.class.struct_class.new(ptr.slice(self.offset, self.size))
55
+ type.struct_class.new(ptr.slice(offset, size))
43
56
  end
44
57
 
45
58
  def put(ptr, value)
46
- self.class.struct_class.new(ptr.slice(self.offset, self.size)).write(value)
59
+ type.struct_class.new(ptr.slice(offset, size)).write(value)
47
60
  end
61
+ end
62
+ end
48
63
 
64
+ def bit_field(type, bits_size, bits_offset)
65
+ Class.new(FFI::StructLayout::Field) do
49
66
  class << self
50
- attr_accessor :struct_class
67
+ attr_accessor :type, :bits_size, :bits_offset, :struct_class
68
+ #no need to implement alignment b/c we always provide offset when adding this field to struct_layout_builder
69
+ end
51
70
 
52
- def alignment
53
- struct_class.alignment
54
- end
71
+ self.struct_class = Class.new(Struct) do
72
+ layout('', type)
73
+ end
55
74
 
56
- def size
57
- struct_class.size
58
- end
75
+ self.type, self.bits_size, self.bits_offset = type, bits_size, bits_offset
76
+
77
+ def initialize(name, offset, type)
78
+ super(name, offset, FFI::Type::Struct.new(self.class.struct_class))
59
79
  end
60
80
 
61
- self.struct_class = struct_class
81
+ def read(ptr)
82
+ ptr.slice(offset, size).send("read_uint#{size * 8}".to_sym)
83
+ end
84
+
85
+ def write(ptr, value)
86
+ ptr.slice(offset, size).send("write_uint#{size * 8}".to_sym, value)
87
+ end
88
+
89
+ def get(ptr)
90
+ mask = (1 << self.class.bits_size) - 1
91
+ value = (read(ptr) >> self.class.bits_offset) & mask
92
+
93
+ SIGNED_NUMBER_TYPES.include?(self.class.type) ? value.to_signed(self.class.bits_size) : value
94
+ end
95
+
96
+ def put(ptr, value)
97
+ mask = ((1 << self.class.bits_size) - 1) << self.class.bits_offset
98
+ write(ptr, (read(ptr) & ~mask) | ((value << self.class.bits_offset) & mask))
99
+ end
62
100
  end
63
101
  end
64
102
 
65
- attr_reader :bits_size, :field_specs, :has_bit_field
103
+ attr_reader :field_specs
66
104
 
67
- def layout(*field_specs)
68
- return super if field_specs.size == 0
105
+ private
106
+ def array_layout(builder, field_specs)
107
+ @field_specs = {}
69
108
 
70
- field_spec_class = ::Struct.new(:name, :type, :bits_offset, :descriptors)
109
+ field_spec_class = ::Struct.new(:name, :type, :descriptors)
71
110
 
72
- @field_specs = {}
111
+ current_allocation_unit = nil
73
112
 
74
- i = bits_offset = 0
113
+ offset = i = 0
75
114
 
76
115
  while i < field_specs.size
77
- field_name, type = field_specs[i, 2]
116
+ name, type = field_specs[i, 2]
78
117
  i += 2
79
118
 
80
- unless type.is_a?(Integer)
81
- type = find_field_type(type)
82
- bits_size = type.size * 8
83
-
119
+ unless type.is_a?(Integer) || type.is_a?(String)
120
+ # If the next param is a Integer, it specifies the offset
84
121
  if field_specs[i].is_a?(Integer)
85
- bits_offset = field_specs[i] * 8
122
+ offset = field_specs[i]
86
123
  i += 1
124
+ else
125
+ offset = nil
87
126
  end
127
+
128
+ type = find_field_type(type)
129
+ builder.add name, type, offset
130
+
131
+ current_allocation_unit = nil
88
132
  else
89
- bits_size = type
133
+ if type.is_a?(Integer)
134
+ ffi_type, bits_size = UNSIGNED_NUMBER_TYPES.find {|ffi_type| type <= ffi_type.size * 8}, type
135
+ raise "Unrecognized format #{type}" unless ffi_type
136
+ elsif type.is_a?(String)
137
+ m = /^(?<ffi_type>[\w_]+)\s*:\s*(?<bits_size>\d+)$/.match(type.strip)
138
+ raise "Unrecognized format #{type}" unless m
139
+
140
+ ffi_type, bits_size = find_field_type(m[:ffi_type].to_sym), m[:bits_size].to_i
141
+ raise "Unrecognized type #{type}" unless UNSIGNED_NUMBER_TYPES.include?(ffi_type) || SIGNED_NUMBER_TYPES.include?(ffi_type)
142
+ end
143
+
144
+ raise "Illegal format #{type}" if bits_size > ffi_type.size * 8
145
+
146
+ unless current_allocation_unit
147
+ current_allocation_unit = {ffi_type: ffi_type, bits_size: bits_size}
148
+ offset = builder.send(:align, builder.size, [@min_alignment || 1, ffi_type.alignment].max)
149
+ else
150
+ # Adjacent bit fields are packed into the same 1-, 2-, or 4-byte allocation unit if the integral types are the same size
151
+ # and if the next bit field fits into the current allocation unit without crossing the boundary
152
+ # imposed by the common alignment requirements of the bit fields.
153
+ if ffi_type.size == current_allocation_unit[:ffi_type].size
154
+ if current_allocation_unit[:bits_size] + bits_size <= ffi_type.size * 8
155
+ current_allocation_unit[:bits_size] += bits_size
156
+ else
157
+ offset = builder.send(:align, builder.size, [@min_alignment || 1, ffi_type.alignment].max)
158
+ current_allocation_unit[:bits_size] = bits_size
159
+ end
160
+ else
161
+ offset = builder.send(:align, builder.size, [@min_alignment || 1, ffi_type.alignment].max)
162
+ current_allocation_unit = {ffi_type: ffi_type, bits_size: bits_size}
163
+ end
164
+ end
165
+
166
+ builder.add name, find_field_type(bit_field(ffi_type, bits_size, current_allocation_unit[:bits_size] - bits_size)), offset
90
167
  end
91
168
 
92
169
  if field_specs[i].is_a?(Hash)
@@ -96,18 +173,7 @@ module FFI
96
173
  descriptors = {}
97
174
  end
98
175
 
99
- @field_specs[field_name] = field_spec_class.new(field_name, type, bits_offset, descriptors)
100
- bits_offset += bits_size
101
- end
102
-
103
- @has_bit_field = @field_specs.any? {|field_name, field_spec| field_spec.type.is_a?(Integer)}
104
-
105
- if @has_bit_field
106
- #FIXME consider 24 bits situation or larger than 32 bits
107
- #FIXME remove dummy field or have a better name for this field
108
- super(:dummy, "uint#{(bits_offset + 7) & (-1 << 3)}".to_sym)
109
- else
110
- super(*field_specs.reject {|field_spec| field_spec.is_a?(Hash)})
176
+ @field_specs[name] = field_spec_class.new(name, type, descriptors)
111
177
  end
112
178
  end
113
179
  end
@@ -121,25 +187,9 @@ module FFI
121
187
  end
122
188
  end
123
189
 
124
- def [](field_name)
125
- return super unless self.class.has_bit_field
126
-
127
- field_spec = self.class.field_specs[field_name]
128
- mask = ((1 << field_spec.type) - 1) << field_spec.bits_offset
129
-
130
- (self.read & mask) >> field_spec.bits_offset
131
- end
132
-
133
190
  # Set field value
134
191
  def []=(field_name, value)
135
- value = map_field_value(field_name, value)
136
-
137
- return super(field_name, value) unless self.class.has_bit_field
138
-
139
- field_spec = self.class.field_specs[field_name]
140
- mask = ((1 << field_spec.type) - 1) << field_spec.bits_offset
141
-
142
- self.write((self.read & (-1 - mask)) | ((value << field_spec.bits_offset) & mask))
192
+ super(field_name, map_field_value(field_name, value))
143
193
  end
144
194
 
145
195
  def write(value)
@@ -185,7 +235,7 @@ module FFI
185
235
  return field_spec.descriptors[descriptor_key] if field_spec.descriptors.has_key?(descriptor_key)
186
236
 
187
237
  type = field_spec.type
188
- return value.to_dec if (type.is_a?(Integer) || FFI::StructLayoutBuilder::NUMBER_TYPES.include?(type)) && value.is_a?(String)
238
+ return value.to_dec if (type.is_a?(Integer) || type.is_a?(String) || FFI::StructLayoutBuilder::NUMBER_TYPES.include?(type)) && value.is_a?(String)
189
239
 
190
240
  value
191
241
  end
@@ -1,5 +1,5 @@
1
1
  module Ffi
2
2
  module StructEx
3
- VERSION = "0.0.3"
3
+ VERSION = "0.1.0"
4
4
  end
5
5
  end
@@ -5,11 +5,11 @@ require 'ffi/struct_ex'
5
5
  class TestStructEx < Test::Unit::TestCase
6
6
  def test_bit_fields
7
7
  subject_class = Class.new(FFI::StructEx) do
8
- layout :field_0, bit_fields(:bits_0_2, 3,
9
- :bit_3, 1,
10
- :bit_4, 1,
11
- :bits_5_7, 3,
12
- :bits_8_15, 8),
8
+ layout :field_0, bit_fields(:bits_0_2, 'uint16: 3',
9
+ :bit_3, 'uint16: 1',
10
+ :bit_4, 'uint16: 1',
11
+ :bits_5_7, 'uint16: 3',
12
+ :bits_8_15, 'uint16: 8'),
13
13
  :field_1, :uint8,
14
14
  :field_2, :uint8,
15
15
  :field_3, :uint8
@@ -23,6 +23,7 @@ class TestStructEx < Test::Unit::TestCase
23
23
 
24
24
  assert_equal(FFI::StructEx, subject[:field_0].class.superclass)
25
25
  assert_equal(2, subject[:field_0].size)
26
+ assert_equal(2, subject[:field_0].alignment)
26
27
 
27
28
  subject[:field_0] = 0b0110_1001
28
29
  assert_equal(0b0110_1001, subject[:field_0].read)
@@ -122,8 +123,8 @@ class TestStructEx < Test::Unit::TestCase
122
123
 
123
124
  assert_equal(FFI::StructEx, subject[:field_0].class.superclass)
124
125
  assert_equal(1, subject[:field_0].size)
125
- assert(subject[:field_0] == 0b0110_1001)
126
- assert(subject[:field_1] == subject.map_field_value(:field_1, '0x1'))
126
+ assert_equal(0b0110_1001, subject[:field_0])
127
+ assert_equal(subject[:field_1], subject.map_field_value(:field_1, '0x1'))
127
128
  end
128
129
 
129
130
  def test_descriptors
@@ -166,12 +167,181 @@ class TestStructEx < Test::Unit::TestCase
166
167
  assert_equal(0x00, subject[:field_1])
167
168
  end
168
169
 
169
- # def test_sizeof
170
- # subject_class = Class.new(FFI::StructEx) do
171
- # layout :field_0, 2,
172
- # :field_1, 31
173
- # end
174
- #
175
- # assert_equal(8, subject_class.size)
176
- # end
170
+ def test_sizeof
171
+ assert_equal(4, Class.new(FFI::StructEx) do
172
+ layout :field_0, :short,
173
+ :field_1, :char,
174
+ :field_2, :char
175
+ end.size)
176
+
177
+ assert_equal(8, Class.new(FFI::StructEx) do
178
+ layout :field_0, 31,
179
+ :field_1, 31
180
+ end.size)
181
+
182
+ assert_equal(8, Class.new(FFI::StructEx) do
183
+ layout :field_0, 31,
184
+ :field_1, :uint8
185
+ end.size)
186
+
187
+ assert_equal(8, Class.new(FFI::StructEx) do
188
+ layout :field_0, 1,
189
+ :field_1, :uint32
190
+ end.size)
191
+
192
+ assert_equal(3, Class.new(FFI::StructEx) do
193
+ layout :field_0, 4,
194
+ :field_1, 4,
195
+ :field_2, 8,
196
+ :field_3, :uint8
197
+ end.size)
198
+
199
+ assert_equal(2, Class.new(FFI::StructEx) do
200
+ layout :field_0, 1,
201
+ :field_1, 1,
202
+ :field_2, :uint8
203
+ end.size)
204
+
205
+ assert_equal(3, Class.new(FFI::StructEx) do
206
+ layout :field_0, 1,
207
+ :field_1, 1,
208
+ :field_2, :uint8,
209
+ :field_3, 1,
210
+ :field_4, 1
211
+ end.size)
212
+
213
+ assert_equal(6, Class.new(FFI::StructEx) do
214
+ layout :field_0, 1,
215
+ :field_1, 1,
216
+ :field_2, :uint16,
217
+ :field_3, 1,
218
+ :field_4, 1
219
+ end.size)
220
+
221
+ assert_equal(6, Class.new(FFI::StructEx) do
222
+ layout :field_0, 1,
223
+ :field_1, 1,
224
+ :field_2, :uint16,
225
+ :field_3, 1,
226
+ :field_4, 8
227
+ end.size)
228
+
229
+ assert_equal(4, Class.new(FFI::StructEx) do
230
+ layout :field_0, 8,
231
+ :field_1, 16
232
+ end.size)
233
+
234
+ assert_equal(4, Class.new(FFI::StructEx) do
235
+ layout :field_0, 16,
236
+ :field_1, 8
237
+ end.size)
238
+
239
+ assert_equal(6, Class.new(FFI::StructEx) do
240
+ layout :field_0, 1,
241
+ :field_1, 16,
242
+ :field_2, 1
243
+ end.size)
244
+
245
+ assert_equal(1, Class.new(FFI::StructEx) do
246
+ layout :field_0, 'uint8: 1'
247
+ end.size)
248
+
249
+ assert_equal(2, Class.new(FFI::StructEx) do
250
+ layout :field_0, 'uint16: 1'
251
+ end.size)
252
+
253
+ assert_equal(4, Class.new(FFI::StructEx) do
254
+ layout :field_0, 'uint32: 1'
255
+ end.size)
256
+
257
+ assert_equal(2, Class.new(FFI::StructEx) do
258
+ layout :bits_0_2, 'uint16: 3',
259
+ :bit_3, 'uint16: 1',
260
+ :bit_4, 'uint16: 1',
261
+ :bits_5_7, 'uint16: 3',
262
+ :bits_8_15, 'uint16: 8'
263
+ end.size)
264
+
265
+ assert_equal(1, Class.new(FFI::StructEx) do
266
+ layout :field_0, 'uint8: 1',
267
+ :field_1, 'uint8: 1'
268
+ end.size)
269
+
270
+ assert_equal(1, Class.new(FFI::StructEx) do
271
+ layout :field_0, 'uint8: 1',
272
+ :field_1, 'int8: 1'
273
+ end.size)
274
+
275
+ assert_equal(8, Class.new(FFI::StructEx) do
276
+ layout :field_0, 'uint32: 1',
277
+ :field_1, :uint16
278
+ end.size)
279
+
280
+ assert_equal(8, Class.new(FFI::StructEx) do
281
+ layout :field_0, 'uint8: 1',
282
+ :field_1, :uint32
283
+ end.size)
284
+
285
+ assert_equal(8, Class.new(FFI::StructEx) do
286
+ layout :field_0, 'uint8: 1',
287
+ :field_1, 'uint32: 1'
288
+ end.size)
289
+
290
+ assert_equal(4, Class.new(FFI::StructEx) do
291
+ layout :field_0, :short,
292
+ :field_1, :char
293
+ end.size)
294
+
295
+ assert_equal(4, Class.new(FFI::StructEx) do
296
+ layout :field_0, :short,
297
+ :field_1, 'char: 1'
298
+ end.size)
299
+
300
+ subject_class = Class.new(FFI::StructEx) do
301
+ layout :field_0, :char,
302
+ :field_1, 'short: 1'
303
+ end
304
+ assert_equal(4, subject_class.size)
305
+
306
+ subject_class = Class.new(FFI::StructEx) do
307
+ layout :field_0, 'uint: 8',
308
+ :field_1, 'int: 1'
309
+ end
310
+ assert_equal(4, subject_class.size)
311
+ subject = subject_class.new
312
+ subject[:field_0] = 0b0110_1001
313
+ assert_equal(0b0110_1001, subject[:field_0])
314
+ subject[:field_0] = 0b1111_1111
315
+ assert_equal(0b1111_1111, subject[:field_0])
316
+
317
+ subject[:field_1] = 1
318
+ assert_equal(-1, subject[:field_1])
319
+ subject[:field_1] = 0
320
+ assert_equal(0, subject[:field_1])
321
+ subject[:field_1] = -1
322
+ assert_equal(-1, subject[:field_1])
323
+
324
+ #Check no impact for typedef type
325
+ FFI.typedef :uint8, :UINT8
326
+ subject_class = Class.new(FFI::StructEx) do
327
+ layout :field_0, 'UINT8: 8',
328
+ :field_1, 'int: 1'
329
+ end
330
+
331
+ subject_class = Class.new(FFI::StructEx) do
332
+ layout :bits_0_2, 'uint8: 3',
333
+ :bit_3, 'char: 1',
334
+ :bit_4, 'uint8: 1',
335
+ :bits_5_7, 'char: 3'
336
+ end
337
+
338
+ assert_equal(1, subject_class.size)
339
+
340
+ subject = subject_class.new(bits_0_2: 0b001, bit_3: 0b1, bit_4: 0b1, bits_5_7: 0b100)
341
+ assert_equal(1, subject[:bits_0_2])
342
+ assert_equal(-1, subject[:bit_3])
343
+ assert_equal(1, subject[:bit_4])
344
+ assert_equal(-4, subject[:bits_5_7])
345
+
346
+ end
177
347
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ffi-struct_ex
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ruijia Li
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-22 00:00:00.000000000 Z
11
+ date: 2014-05-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi