ffi-struct_ex 0.0.2 → 0.0.3

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.
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