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 +4 -4
- data/README.md +3 -0
- data/lib/ffi/struct_ex/struct_ex.rb +93 -75
- data/lib/ffi/struct_ex/version.rb +1 -1
- data/test/test_struct_ex.rb +72 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 640a03806e45782319f75058970cf5c999927d03
|
4
|
+
data.tar.gz: f5f3247405c6243e66a4d958d356787cb231c7d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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(*
|
30
|
+
def bit_fields(*field_specs)
|
20
31
|
struct_class = Class.new(StructEx) do
|
21
|
-
layout(*
|
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, :
|
65
|
+
attr_reader :bits_size, :field_specs, :has_bit_field
|
52
66
|
|
53
|
-
def layout(*
|
54
|
-
if
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
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
|
-
|
76
|
+
while i < field_specs.size
|
77
|
+
field_name, type = field_specs[i, 2]
|
78
|
+
i += 2
|
61
79
|
|
62
|
-
|
63
|
-
|
80
|
+
unless type.is_a?(Integer)
|
81
|
+
type = find_field_type(type)
|
82
|
+
bits_size = type.size * 8
|
64
83
|
|
65
|
-
if
|
66
|
-
|
67
|
-
|
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
|
-
|
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
|
-
|
77
|
-
|
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
|
-
|
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
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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 [](
|
103
|
-
return super unless self.class.
|
124
|
+
def [](field_name)
|
125
|
+
return super unless self.class.has_bit_field
|
104
126
|
|
105
|
-
|
106
|
-
mask = ((1 <<
|
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) >>
|
130
|
+
(self.read & mask) >> field_spec.bits_offset
|
109
131
|
end
|
110
132
|
|
111
|
-
|
112
|
-
|
133
|
+
# Set field value
|
134
|
+
def []=(field_name, value)
|
135
|
+
value = map_field_value(field_name, value)
|
113
136
|
|
114
|
-
|
137
|
+
return super(field_name, value) unless self.class.has_bit_field
|
115
138
|
|
116
|
-
|
117
|
-
mask = ((1 <<
|
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 <<
|
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.
|
147
|
+
to_ptr.write_array_of_uint8(value.to_bytes(self.class.size))
|
125
148
|
elsif value.is_a?(Hash)
|
126
|
-
value.each do |
|
127
|
-
self[
|
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.
|
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.
|
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
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
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
|
data/test/test_struct_ex.rb
CHANGED
@@ -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.
|
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-
|
11
|
+
date: 2014-05-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi
|