ffi-struct_ex 0.0.2 → 0.0.3

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: 6d550f7884cc459c4dbf3f506f6952fc9dc2f345
4
- data.tar.gz: 7087461425de5d646a04f5e8bc75749d908cbb7b
3
+ metadata.gz: 640a03806e45782319f75058970cf5c999927d03
4
+ data.tar.gz: f5f3247405c6243e66a4d958d356787cb231c7d8
5
5
  SHA512:
6
- metadata.gz: 1db936827a6652292e2849fd93ea54ad7f14c5f2c53503c9be3f1cbebec24287541197e66d1162cc30ea0969fb31b8e2a3f996d39bbd34b28ba3f732fd0c8720
7
- data.tar.gz: 4e85fe3b694f1ac30ea118e450f7c0e63df8f295c32fd33f9bb6cff1ddca0a850a8bbb3cd379e9828f5c0bb9c782808a232115f7915ab303140e25bcccb98ab1
6
+ metadata.gz: 57dcf4abe3d98e2fe8c0cbcbaed6e17e2ac2a8d877f1c57d53da3f97666099e73de65cca320714451e535ab0a5724361d6c3bbc965838d50d7a58209a4779867
7
+ data.tar.gz: ece4cba95fbf5095e5151432f072957ed6d3d8eaf4b6e56ace473d745dfa48ccd12c4531464a6cb185698012b1cb942769181e2bcb92649f83023c76378ef8b0
data/README.md CHANGED
@@ -63,7 +63,10 @@ subject[:field_0].read #=> 0b0110_1101
63
63
  subject[:field_0] = {bits_0_2: 0b001, bit_3: 0b1, bit_4: 0b0, bits_5_7: 0b011}
64
64
  subject[:field_0].read #=> 0b0110_1001
65
65
 
66
+ #Equality check
66
67
  subject[:field_0] == {bits_0_2: 0b001, bit_3: 0b1, bit_4: 0b0, bits_5_7: 0b011} #=> true
68
+ subject[:field_0] == 0b0110_1001 #=> true
69
+ subject[:field_0] == '0b0110_1001' #=> true
67
70
  ```
68
71
 
69
72
  ## Contributing
@@ -11,19 +11,29 @@ class Integer
11
11
  end
12
12
  end
13
13
 
14
+ class String
15
+ def to_dec
16
+ case self
17
+ when /^[+-]?\d+$/
18
+ self.to_i
19
+ when /^[+-]?0[xX][\da-fA-F_]+$/
20
+ self.to_i(16)
21
+ when /^[+-]?0[bB][_01]+$/
22
+ self.to_i(2)
23
+ end
24
+ end
25
+ end
26
+
14
27
  module FFI
15
28
  class StructEx < FFI::Struct
16
- BitLayout = ::Struct.new(:name, :bits, :offset, :texts)
17
-
18
29
  class << self
19
- def bit_fields(*descs)
30
+ def bit_fields(*field_specs)
20
31
  struct_class = Class.new(StructEx) do
21
- layout(*descs)
32
+ layout(*field_specs)
22
33
  end
23
34
 
24
35
  bit_fields_class = Class.new(FFI::StructLayout::Field) do
25
36
  def initialize(name, offset, type)
26
- #TODO use a different native_type to avoid dummy field for struct
27
37
  super(name, offset, FFI::Type::Struct.new(self.class.struct_class))
28
38
  end
29
39
 
@@ -42,51 +52,63 @@ module FFI
42
52
  def alignment
43
53
  struct_class.alignment
44
54
  end
55
+
56
+ def size
57
+ struct_class.size
58
+ end
45
59
  end
46
60
 
47
61
  self.struct_class = struct_class
48
62
  end
49
63
  end
50
64
 
51
- attr_reader :bits_size, :bit_layouts
65
+ attr_reader :bits_size, :field_specs, :has_bit_field
52
66
 
53
- def layout(*descs)
54
- if descs.size == 0 || !descs[1].is_a?(Integer)
55
- super(*descs)
56
- #@bits_size = self.size * 8
57
- else
58
- @bit_layouts = {}
67
+ def layout(*field_specs)
68
+ return super if field_specs.size == 0
69
+
70
+ field_spec_class = ::Struct.new(:name, :type, :bits_offset, :descriptors)
71
+
72
+ @field_specs = {}
73
+
74
+ i = bits_offset = 0
59
75
 
60
- index = @bits_size = 0
76
+ while i < field_specs.size
77
+ field_name, type = field_specs[i, 2]
78
+ i += 2
61
79
 
62
- while index < descs.size
63
- bit_field_name, bits, texts = descs[index, 3]
80
+ unless type.is_a?(Integer)
81
+ type = find_field_type(type)
82
+ bits_size = type.size * 8
64
83
 
65
- if texts.kind_of?(Hash)
66
- @bit_layouts[bit_field_name] = BitLayout.new(bit_field_name, bits, @bits_size, texts)
67
- index += 3
68
- else
69
- @bit_layouts[bit_field_name] = BitLayout.new(bit_field_name, bits, @bits_size)
70
- index += 2
84
+ if field_specs[i].is_a?(Integer)
85
+ bits_offset = field_specs[i] * 8
86
+ i += 1
71
87
  end
88
+ else
89
+ bits_size = type
90
+ end
72
91
 
73
- @bits_size += bits
92
+ if field_specs[i].is_a?(Hash)
93
+ descriptors = field_specs[i]
94
+ i += 1
95
+ else
96
+ descriptors = {}
74
97
  end
75
98
 
76
- #FIXME consider 24 bits situation or larger than 32 bits
77
- #FIXME remove dummy field or have a better name for this field
78
- super(:dummy, "uint#{bytes_size * 8}".to_sym)
99
+ @field_specs[field_name] = field_spec_class.new(field_name, type, bits_offset, descriptors)
100
+ bits_offset += bits_size
79
101
  end
80
- end
81
102
 
82
- def bytes_size
83
- (bits_size + 7) >> 3
84
- end
103
+ @has_bit_field = @field_specs.any? {|field_name, field_spec| field_spec.type.is_a?(Integer)}
85
104
 
86
- def alignment
87
- return super unless self.bit_layouts
88
- #FIXME consider 24 bits situation
89
- FFI.find_type("uint#{bytes_size * 8}".to_sym).alignment
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)})
111
+ end
90
112
  end
91
113
  end
92
114
 
@@ -99,77 +121,73 @@ module FFI
99
121
  end
100
122
  end
101
123
 
102
- def [](bit_field_name)
103
- return super unless self.class.bit_layouts && self.class.bit_layouts.keys.include?(bit_field_name)
124
+ def [](field_name)
125
+ return super unless self.class.has_bit_field
104
126
 
105
- bit_layout = self.class.bit_layouts[bit_field_name]
106
- mask = ((1 << bit_layout.bits) - 1) << bit_layout.offset
127
+ field_spec = self.class.field_specs[field_name]
128
+ mask = ((1 << field_spec.type) - 1) << field_spec.bits_offset
107
129
 
108
- (self.read & mask) >> bit_layout.offset
130
+ (self.read & mask) >> field_spec.bits_offset
109
131
  end
110
132
 
111
- def []=(bit_field_name, value)
112
- return super unless self.class.bit_layouts && self.class.bit_layouts.keys.include?(bit_field_name)
133
+ # Set field value
134
+ def []=(field_name, value)
135
+ value = map_field_value(field_name, value)
113
136
 
114
- value = look_for_value(bit_field_name, value)
137
+ return super(field_name, value) unless self.class.has_bit_field
115
138
 
116
- bit_layout = self.class.bit_layouts[bit_field_name]
117
- mask = ((1 << bit_layout.bits) - 1) << bit_layout.offset
139
+ field_spec = self.class.field_specs[field_name]
140
+ mask = ((1 << field_spec.type) - 1) << field_spec.bits_offset
118
141
 
119
- self.write((self.read & (-1 - mask)) | ((value << bit_layout.offset) & mask))
142
+ self.write((self.read & (-1 - mask)) | ((value << field_spec.bits_offset) & mask))
120
143
  end
121
144
 
122
145
  def write(value)
123
146
  if value.is_a?(Integer)
124
- to_ptr.write_array_of_uint8(value.to_bytes(self.class.bytes_size))
147
+ to_ptr.write_array_of_uint8(value.to_bytes(self.class.size))
125
148
  elsif value.is_a?(Hash)
126
- value.each do |bit_field_name, v|
127
- self[bit_field_name] = v if self.class.bit_layouts.keys.include? bit_field_name
149
+ value.each do |field_name, v|
150
+ self[field_name] = v
128
151
  end
129
152
  end
130
153
  end
131
154
 
132
155
  def read
133
- bytes = to_ptr.read_array_of_uint8(self.class.bytes_size)
156
+ bytes = to_ptr.read_array_of_uint8(self.class.size)
134
157
  bytes.reverse.inject(0) {|value, n| (value << 8) | n}
135
158
  end
136
159
 
137
- def size
138
- self.class.bytes_size
139
- end
140
-
141
160
  def ==(other)
142
161
  if other.is_a?(Integer)
143
162
  self.read == other
163
+ elsif other.is_a?(String)
164
+ self.==(other.to_dec)
144
165
  elsif other.is_a?(Hash)
145
- other.all? {|k, v| self[k] == self.look_for_value(k, v)}
166
+ other.all? {|k, v| self[k] == self.map_field_value(k, v)}
146
167
  else
147
168
  super
148
169
  end
149
170
  end
150
171
 
151
- def look_for_value(bit_field_name, value)
152
- #FIXME add error handling
153
- if value.kind_of?(Integer)
154
- value
155
- elsif value.kind_of?(String)
156
- #FIXME this requires texts hash to have downcase key
157
- value = value.downcase
158
- if self.class.bit_layouts[bit_field_name].texts && self.class.bit_layouts[bit_field_name].texts[value]
159
- self.class.bit_layouts[bit_field_name].texts[value]
160
- else
161
- case value
162
- when /^\d+$/
163
- value.to_i
164
- when /^0x[\da-fA-F]+$/
165
- value.to_i(16)
166
- when /^0b[01]+$/
167
- value.to_i(2)
168
- end
169
- end
170
- else
171
- value
172
- end
172
+ # Return mapped field value by converting {value} to corresponding native form.
173
+ # The priority is
174
+ # 1. look for descriptors
175
+ # 2. simple conversion from string to integer if integer type
176
+ # 3. {value} itself
177
+ #
178
+ # @param [String, Symbol] field_name name of the field
179
+ # @param [String, Integer, Object] value value in descriptive form or native form
180
+ # @return [Object] value in native form
181
+ def map_field_value(field_name, value)
182
+ field_spec = self.class.field_specs[field_name]
183
+
184
+ descriptor_key = value.kind_of?(String) ? value.downcase : value
185
+ return field_spec.descriptors[descriptor_key] if field_spec.descriptors.has_key?(descriptor_key)
186
+
187
+ type = field_spec.type
188
+ return value.to_dec if (type.is_a?(Integer) || FFI::StructLayoutBuilder::NUMBER_TYPES.include?(type)) && value.is_a?(String)
189
+
190
+ value
173
191
  end
174
192
  end
175
193
  end
@@ -1,5 +1,5 @@
1
1
  module Ffi
2
2
  module StructEx
3
- VERSION = "0.0.2"
3
+ VERSION = "0.0.3"
4
4
  end
5
5
  end
@@ -36,12 +36,16 @@ class TestStructEx < Test::Unit::TestCase
36
36
  assert_equal(0b0110_1001, subject[:field_0].read)
37
37
  assert_equal(0b001, subject[:field_0][:bits_0_2])
38
38
 
39
- assert(subject[:field_0] == {bits_0_2: 0b001, bit_3: 0b1, bit_4: 0b0, bits_5_7: 0b011})
39
+ assert(subject[:field_0] == {bits_0_2: 0b001, bit_3: 0b1, bit_4: 0b0, bits_5_7: 0b011, bits_8_15: 0b0})
40
+ assert(subject[:field_0] == 0b0110_1001)
41
+ assert(subject[:field_0] == '0b0110_1001')
40
42
 
41
43
  subject[:field_1] = 1
42
44
  subject[:field_2] = 2
43
45
  subject[:field_3] = 3
44
- assert(subject == {field_0: {bits_0_2: 0b001, bit_3: 0b1, bit_4: 0b0, bits_5_7: 0b011}, field_1: 1, field_2: 2, field_3: 3})
46
+ assert(subject == {field_0: {bits_0_2: 0b001, bit_3: 0b1, bit_4: 0b0, bits_5_7: 0b011, bits_8_15: 0b0}, field_1: 1, field_2: 2, field_3: 3})
47
+ assert(subject == 0x0302010069)
48
+ assert(subject == '0x0302010069')
45
49
  end
46
50
 
47
51
  def test_pure_bit_fields
@@ -104,4 +108,70 @@ class TestStructEx < Test::Unit::TestCase
104
108
  subject[:bit_3] = 1
105
109
  assert_equal(0b1, subject[:bit_3])
106
110
  end
111
+
112
+ def test_equality
113
+ subject_class = Class.new(FFI::StructEx) do
114
+ layout :field_0, bit_fields(:bits_0_2, 3,
115
+ :bit_3, 1,
116
+ :bit_4, 1,
117
+ :bits_5_7, 3),
118
+ :field_1, :uint8
119
+ end
120
+
121
+ subject = subject_class.new({field_0: {bits_0_2: 0b001, bit_3: 0b1, bit_4: 0b0, bits_5_7: 0b011}, field_1: 0x1})
122
+
123
+ assert_equal(FFI::StructEx, subject[:field_0].class.superclass)
124
+ 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'))
127
+ end
128
+
129
+ def test_descriptors
130
+ subject_class = Class.new(FFI::StructEx) do
131
+ layout :field_0, :uint8, {'all_1' => 0xff, 'all_0' => 0x00},
132
+ :field_1, :uint8, {3 => 1}
133
+ end
134
+
135
+ assert_equal(2, subject_class.size)
136
+ assert_equal(1, subject_class.alignment)
137
+ assert_equal(1, subject_class.offset_of(:field_1))
138
+
139
+ subject = subject_class.new(field_0: 'all_1', field_1: 0x12)
140
+
141
+ assert_equal(0xff, subject[:field_0])
142
+ assert_equal(0x12, subject[:field_1])
143
+
144
+ subject[:field_0] = 'all_0'
145
+ assert_equal(0x00, subject[:field_0])
146
+
147
+ subject[:field_0] = 0x12
148
+ assert_equal(0x12, subject[:field_0])
149
+
150
+ subject[:field_1] = 3
151
+ assert_equal(0x1, subject[:field_1])
152
+ end
153
+
154
+ def test_initialized_memory_should_be_zero
155
+ subject_class = Class.new(FFI::StructEx) do
156
+ layout :field_0, bit_fields(:bits_0_2, 3,
157
+ :bit_3, 1,
158
+ :bit_4, 1,
159
+ :bits_5_7, 3),
160
+ :field_1, :uint8
161
+ end
162
+
163
+ subject = subject_class.new
164
+
165
+ assert_equal(0x00, subject[:field_0])
166
+ assert_equal(0x00, subject[:field_1])
167
+ end
168
+
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
107
177
  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.2
4
+ version: 0.0.3
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-19 00:00:00.000000000 Z
11
+ date: 2014-05-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi