ffi-struct_ex 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.
- checksums.yaml +4 -4
- data/lib/ffi/struct_ex/struct_ex.rb +20 -47
- data/lib/ffi/struct_ex/version.rb +1 -1
- data/test/test_struct_ex.rb +26 -25
- 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: 4092e8230ac37c9885fe7fa0103252632d8aff4c
|
4
|
+
data.tar.gz: b7585d0ea88c05cec20c3cac0939fbecf54fd114
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b90c7ec9a5a6e5d2fdcb84346a19c9882de73a601d9cb35d6e18c05455648f6e700c92a61ea2ca2007659c3fe830b031a3a4408111fd1c7a26e79c8c75c1f1b4
|
7
|
+
data.tar.gz: b2077caf64d8b31265a2b30ec2c72690181ac5cb1f5be710a56888a7f75a546eaa853be20be7cce7c5c8b7fd2e2edc60eda30e6b78c867dd7f436f728fb6d905
|
@@ -1,15 +1,6 @@
|
|
1
1
|
require 'ffi'
|
2
2
|
|
3
3
|
class Integer
|
4
|
-
def to_bytes(size)
|
5
|
-
bytes = [0] * size
|
6
|
-
bytes.each_index do |i|
|
7
|
-
bytes[i] = (self >> (i * 8)) & 0xff
|
8
|
-
end
|
9
|
-
|
10
|
-
bytes
|
11
|
-
end
|
12
|
-
|
13
4
|
def to_signed(bits_size)
|
14
5
|
self & (1 << bits_size - 1) != 0 ? self - (1 << bits_size) : self
|
15
6
|
end
|
@@ -32,9 +23,10 @@ module FFI
|
|
32
23
|
class StructEx < FFI::Struct
|
33
24
|
SIGNED_NUMBER_TYPES = [FFI::Type::INT8, FFI::Type::INT16, FFI::Type::INT32, FFI::Type::INT64]
|
34
25
|
UNSIGNED_NUMBER_TYPES = [FFI::Type::UINT8, FFI::Type::UINT16, FFI::Type::UINT32, FFI::Type::UINT64]
|
26
|
+
NUMBER_TYPES = SIGNED_NUMBER_TYPES + UNSIGNED_NUMBER_TYPES
|
35
27
|
|
36
28
|
class << self
|
37
|
-
def
|
29
|
+
def struct_ex(*field_specs)
|
38
30
|
Class.new(FFI::StructLayout::Field) do
|
39
31
|
class << self
|
40
32
|
attr_accessor :struct_class
|
@@ -104,13 +96,13 @@ module FFI
|
|
104
96
|
|
105
97
|
private
|
106
98
|
def array_layout(builder, field_specs)
|
107
|
-
@field_specs = {}
|
108
|
-
|
109
99
|
field_spec_class = ::Struct.new(:name, :type, :descriptors)
|
110
100
|
|
111
|
-
|
101
|
+
@field_specs = {}
|
102
|
+
|
103
|
+
bits_unit = nil
|
112
104
|
|
113
|
-
|
105
|
+
i = 0
|
114
106
|
|
115
107
|
while i < field_specs.size
|
116
108
|
name, type = field_specs[i, 2]
|
@@ -128,7 +120,7 @@ module FFI
|
|
128
120
|
type = find_field_type(type)
|
129
121
|
builder.add name, type, offset
|
130
122
|
|
131
|
-
|
123
|
+
bits_unit = nil
|
132
124
|
else
|
133
125
|
if type.is_a?(Integer)
|
134
126
|
ffi_type, bits_size = UNSIGNED_NUMBER_TYPES.find {|ffi_type| type <= ffi_type.size * 8}, type
|
@@ -138,32 +130,22 @@ module FFI
|
|
138
130
|
raise "Unrecognized format #{type}" unless m
|
139
131
|
|
140
132
|
ffi_type, bits_size = find_field_type(m[:ffi_type].to_sym), m[:bits_size].to_i
|
141
|
-
raise "Unrecognized type #{type}" unless
|
133
|
+
raise "Unrecognized type #{type}" unless NUMBER_TYPES.include?(ffi_type)
|
142
134
|
end
|
143
135
|
|
144
136
|
raise "Illegal format #{type}" if bits_size > ffi_type.size * 8
|
145
137
|
|
146
|
-
|
147
|
-
|
148
|
-
|
138
|
+
# Adjacent bit fields are packed into the same 1-, 2-, or 4-byte allocation unit if the integral types are the same size
|
139
|
+
# and if the next bit field fits into the current allocation unit without crossing the boundary
|
140
|
+
# imposed by the common alignment requirements of the bit fields.
|
141
|
+
if bits_unit && bits_unit[:ffi_type].size == ffi_type.size && bits_unit[:bits_size] + bits_size <= bits_unit[:ffi_type].size * 8
|
142
|
+
bits_unit[:bits_size] += bits_size
|
149
143
|
else
|
150
|
-
|
151
|
-
|
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
|
144
|
+
offset = builder.send(:align, builder.size, [@min_alignment || 1, ffi_type.alignment].max)
|
145
|
+
bits_unit = {ffi_type: ffi_type, bits_size: bits_size}
|
164
146
|
end
|
165
147
|
|
166
|
-
builder.add name, find_field_type(bit_field(ffi_type, bits_size,
|
148
|
+
builder.add name, find_field_type(bit_field(ffi_type, bits_size, bits_unit[:bits_size] - bits_size)), offset
|
167
149
|
end
|
168
150
|
|
169
151
|
if field_specs[i].is_a?(Hash)
|
@@ -193,26 +175,17 @@ module FFI
|
|
193
175
|
end
|
194
176
|
|
195
177
|
def write(value)
|
196
|
-
if value.is_a?(
|
197
|
-
to_ptr.write_array_of_uint8(value.to_bytes(self.class.size))
|
198
|
-
elsif value.is_a?(Hash)
|
178
|
+
if value.is_a?(Hash)
|
199
179
|
value.each do |field_name, v|
|
200
180
|
self[field_name] = v
|
201
181
|
end
|
182
|
+
elsif value.is_a?(self.class)
|
183
|
+
self.pointer.__copy_from__(value.pointer, self.size)
|
202
184
|
end
|
203
185
|
end
|
204
186
|
|
205
|
-
def read
|
206
|
-
bytes = to_ptr.read_array_of_uint8(self.class.size)
|
207
|
-
bytes.reverse.inject(0) {|value, n| (value << 8) | n}
|
208
|
-
end
|
209
|
-
|
210
187
|
def ==(other)
|
211
|
-
if other.is_a?(
|
212
|
-
self.read == other
|
213
|
-
elsif other.is_a?(String)
|
214
|
-
self.==(other.to_dec)
|
215
|
-
elsif other.is_a?(Hash)
|
188
|
+
if other.is_a?(Hash)
|
216
189
|
other.all? {|k, v| self[k] == self.map_field_value(k, v)}
|
217
190
|
else
|
218
191
|
super
|
data/test/test_struct_ex.rb
CHANGED
@@ -3,53 +3,53 @@ require 'test/unit'
|
|
3
3
|
require 'ffi/struct_ex'
|
4
4
|
|
5
5
|
class TestStructEx < Test::Unit::TestCase
|
6
|
-
def
|
6
|
+
def test_struct_ex
|
7
7
|
subject_class = Class.new(FFI::StructEx) do
|
8
|
-
layout :field_0,
|
8
|
+
layout :field_0, struct_ex(:bits_0_2, 'uint16: 3',
|
9
9
|
:bit_3, 'uint16: 1',
|
10
10
|
:bit_4, 'uint16: 1',
|
11
11
|
:bits_5_7, 'uint16: 3',
|
12
|
-
:bits_8_15,
|
12
|
+
:bits_8_15, :uint8),
|
13
13
|
:field_1, :uint8,
|
14
14
|
:field_2, :uint8,
|
15
15
|
:field_3, :uint8
|
16
16
|
end
|
17
17
|
|
18
|
-
assert_equal(
|
18
|
+
assert_equal(8, subject_class.size)
|
19
19
|
assert_equal(2, subject_class.alignment)
|
20
|
-
assert_equal(
|
20
|
+
assert_equal(4, subject_class.offset_of(:field_1))
|
21
21
|
|
22
22
|
subject = subject_class.new
|
23
23
|
|
24
24
|
assert_equal(FFI::StructEx, subject[:field_0].class.superclass)
|
25
|
-
assert_equal(
|
25
|
+
assert_equal(4, subject[:field_0].size)
|
26
26
|
assert_equal(2, subject[:field_0].alignment)
|
27
27
|
|
28
|
-
subject[:field_0]
|
29
|
-
assert_equal(0b0110_1001, subject[:field_0].
|
28
|
+
subject[:field_0].to_ptr.write_uint16(0b0110_1001)
|
29
|
+
assert_equal(0b0110_1001, subject[:field_0].to_ptr.read_uint16)
|
30
30
|
assert_equal(0b001, subject[:field_0][:bits_0_2])
|
31
31
|
|
32
32
|
subject[:field_0][:bits_0_2] = 0b101
|
33
33
|
assert_equal(0b101, subject[:field_0][:bits_0_2])
|
34
|
-
assert_equal(0b0110_1101, subject[:field_0].
|
34
|
+
assert_equal(0b0110_1101, subject[:field_0].to_ptr.read_uint16)
|
35
35
|
|
36
36
|
subject[:field_0] = {bits_0_2: 0b001, bit_3: 0b1, bit_4: 0b0, bits_5_7: 0b011}
|
37
|
-
assert_equal(0b0110_1001, subject[:field_0].
|
37
|
+
assert_equal(0b0110_1001, subject[:field_0].to_ptr.read_uint16)
|
38
38
|
assert_equal(0b001, subject[:field_0][:bits_0_2])
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
assert(subject[:field_0] == '0b0110_1001')
|
40
|
+
assert_equal(subject[:field_0], {bits_0_2: 0b001, bit_3: 0b1, bit_4: 0b0, bits_5_7: 0b011, bits_8_15: 0b0})
|
41
|
+
assert_equal(subject[:field_0].to_ptr.read_uint16, 0b0110_1001)
|
43
42
|
|
44
43
|
subject[:field_1] = 1
|
45
44
|
subject[:field_2] = 2
|
46
45
|
subject[:field_3] = 3
|
47
|
-
|
48
|
-
|
49
|
-
|
46
|
+
assert_equal(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
|
+
|
48
|
+
subject[:field_0] = subject[:field_0].class.new(bits_0_2: 0b111, bit_3: 0b0, bit_4: 0b0, bits_5_7: 0b101, bits_8_15: 0b10011100)
|
49
|
+
assert_equal(subject[:field_0].to_ptr.read_uint32, 0b10011100_00000000_10100111)
|
50
50
|
end
|
51
51
|
|
52
|
-
def
|
52
|
+
def test_pure_struct_ex
|
53
53
|
subject_class = Class.new(FFI::StructEx) do
|
54
54
|
layout :bits_0_2, 3,
|
55
55
|
:bit_3, 1,
|
@@ -69,7 +69,8 @@ class TestStructEx < Test::Unit::TestCase
|
|
69
69
|
assert_equal(0b1, subject[:bit_3])
|
70
70
|
assert_equal(0b101, subject[:bits_0_2])
|
71
71
|
|
72
|
-
subject = subject_class.new
|
72
|
+
subject = subject_class.new
|
73
|
+
subject.to_ptr.write_uint8(0b0110_1001)
|
73
74
|
assert_equal(0b001, subject[:bits_0_2])
|
74
75
|
assert_equal(0b1, subject[:bit_3])
|
75
76
|
assert_equal(0b0, subject[:bit_4])
|
@@ -80,10 +81,10 @@ class TestStructEx < Test::Unit::TestCase
|
|
80
81
|
assert_equal(0b1, subject[:bit_3])
|
81
82
|
assert_equal(0b0, subject[:bit_4])
|
82
83
|
assert_equal(0b011, subject[:bits_5_7])
|
83
|
-
assert_equal(0b0110_1001, subject.
|
84
|
+
assert_equal(0b0110_1001, subject.to_ptr.read_uint8)
|
84
85
|
end
|
85
86
|
|
86
|
-
def
|
87
|
+
def test_interpreted_struct_ex
|
87
88
|
subject_class = Class.new(FFI::StructEx) do
|
88
89
|
layout :bits_0_2, 3, {'all_1' => 0b111, 'all_0' => 0b000},
|
89
90
|
:bit_3, 1, {'yes' => 0b1, 'no' => 0b0},
|
@@ -112,18 +113,18 @@ class TestStructEx < Test::Unit::TestCase
|
|
112
113
|
|
113
114
|
def test_equality
|
114
115
|
subject_class = Class.new(FFI::StructEx) do
|
115
|
-
layout :field_0,
|
116
|
+
layout :field_0, struct_ex(:bits_0_2, 3,
|
116
117
|
:bit_3, 1,
|
117
118
|
:bit_4, 1,
|
118
119
|
:bits_5_7, 3),
|
119
120
|
:field_1, :uint8
|
120
121
|
end
|
121
122
|
|
122
|
-
subject = subject_class.new(
|
123
|
+
subject = subject_class.new(field_0: {bits_0_2: 0b001, bit_3: 0b1, bit_4: 0b0, bits_5_7: 0b011}, field_1: 0x1)
|
123
124
|
|
124
125
|
assert_equal(FFI::StructEx, subject[:field_0].class.superclass)
|
125
126
|
assert_equal(1, subject[:field_0].size)
|
126
|
-
assert_equal(0b0110_1001, subject[:field_0])
|
127
|
+
assert_equal(0b0110_1001, subject[:field_0].to_ptr.read_uint8)
|
127
128
|
assert_equal(subject[:field_1], subject.map_field_value(:field_1, '0x1'))
|
128
129
|
end
|
129
130
|
|
@@ -154,7 +155,7 @@ class TestStructEx < Test::Unit::TestCase
|
|
154
155
|
|
155
156
|
def test_initialized_memory_should_be_zero
|
156
157
|
subject_class = Class.new(FFI::StructEx) do
|
157
|
-
layout :field_0,
|
158
|
+
layout :field_0, struct_ex(:bits_0_2, 3,
|
158
159
|
:bit_3, 1,
|
159
160
|
:bit_4, 1,
|
160
161
|
:bits_5_7, 3),
|
@@ -163,7 +164,7 @@ class TestStructEx < Test::Unit::TestCase
|
|
163
164
|
|
164
165
|
subject = subject_class.new
|
165
166
|
|
166
|
-
assert_equal(0x00, subject[:field_0])
|
167
|
+
assert_equal(0x00, subject[:field_0].to_ptr.read_uint8)
|
167
168
|
assert_equal(0x00, subject[:field_1])
|
168
169
|
end
|
169
170
|
|
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.1.
|
4
|
+
version: 0.1.1
|
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-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi
|