ffi-struct_ex 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|