bit-struct 0.13.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/History.txt +102 -0
- data/README.txt +189 -0
- data/Rakefile +34 -0
- data/TODO +23 -0
- data/TODO-ALSO +71 -0
- data/examples/ara-player-data.rb +82 -0
- data/examples/bignum.rb +18 -0
- data/examples/bits.rb +19 -0
- data/examples/byte-bdy.rb +30 -0
- data/examples/field-ripper.rb +22 -0
- data/examples/fixed-point.rb +17 -0
- data/examples/ip.rb +81 -0
- data/examples/longlong.rb +30 -0
- data/examples/md.rb +23 -0
- data/examples/modular-def.rb +38 -0
- data/examples/native.rb +31 -0
- data/examples/nested.rb +33 -0
- data/examples/pad.rb +14 -0
- data/examples/ping-recv.rb +25 -0
- data/examples/ping.rb +73 -0
- data/examples/player-data.rb +75 -0
- data/examples/raw.rb +62 -0
- data/examples/rest.rb +30 -0
- data/examples/switch-endian.rb +49 -0
- data/examples/vector.rb +98 -0
- data/lib/bit-struct.rb +6 -0
- data/lib/bit-struct/bit-struct.rb +549 -0
- data/lib/bit-struct/char-field.rb +48 -0
- data/lib/bit-struct/fields.rb +273 -0
- data/lib/bit-struct/float-field.rb +61 -0
- data/lib/bit-struct/hex-octet-field.rb +20 -0
- data/lib/bit-struct/nested-field.rb +76 -0
- data/lib/bit-struct/octet-field.rb +45 -0
- data/lib/bit-struct/pad-field.rb +15 -0
- data/lib/bit-struct/signed-field.rb +258 -0
- data/lib/bit-struct/text-field.rb +44 -0
- data/lib/bit-struct/unsigned-field.rb +248 -0
- data/lib/bit-struct/vector-field.rb +77 -0
- data/lib/bit-struct/vector.rb +173 -0
- data/lib/bit-struct/yaml.rb +69 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +34 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +292 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/tasks/zentest.rake +36 -0
- data/test/test-endian.rb +39 -0
- data/test/test-vector.rb +38 -0
- data/test/test.rb +433 -0
- metadata +126 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'bit-struct/char-field'
|
2
|
+
|
3
|
+
class BitStruct
|
4
|
+
# Class for char fields that can be accessed with values like
|
5
|
+
# "xxx.xxx.xxx.xxx", where each xxx is up to 3 decimal digits representing a
|
6
|
+
# single octet. The original string-based accessors are still available with
|
7
|
+
# the <tt>_chars</tt> suffix.
|
8
|
+
#
|
9
|
+
# Declared with BitStruct.octets.
|
10
|
+
class OctetField < BitStruct::CharField
|
11
|
+
# Used in describe.
|
12
|
+
def self.class_name
|
13
|
+
@class_name ||= "octets"
|
14
|
+
end
|
15
|
+
|
16
|
+
SEPARATOR = "."
|
17
|
+
FORMAT = "%d"
|
18
|
+
BASE = 10
|
19
|
+
|
20
|
+
def add_accessors_to(cl, attr = name) # :nodoc:
|
21
|
+
attr_chars = "#{attr}_chars"
|
22
|
+
super(cl, attr_chars)
|
23
|
+
sep = self.class::SEPARATOR
|
24
|
+
base = self.class::BASE
|
25
|
+
fmt = self.class::FORMAT
|
26
|
+
|
27
|
+
cl.class_eval do
|
28
|
+
define_method attr do ||
|
29
|
+
ary = []
|
30
|
+
send(attr_chars).each_byte do |c|
|
31
|
+
ary << fmt % c
|
32
|
+
end
|
33
|
+
ary.join(sep)
|
34
|
+
end
|
35
|
+
|
36
|
+
old_writer = "#{attr_chars}="
|
37
|
+
|
38
|
+
define_method "#{attr}=" do |val|
|
39
|
+
data = val.split(sep).map{|s|s.to_i(base)}.pack("c*")
|
40
|
+
send(old_writer, data)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class BitStruct
|
2
|
+
# Class for fixed length padding.
|
3
|
+
class PadField < Field
|
4
|
+
# Used in describe.
|
5
|
+
def self.class_name
|
6
|
+
@class_name ||= "padding"
|
7
|
+
end
|
8
|
+
|
9
|
+
def add_accessors_to(cl, attr = name) # :nodoc:
|
10
|
+
# No accessors for padding.
|
11
|
+
end
|
12
|
+
|
13
|
+
def inspectable?; false; end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,258 @@
|
|
1
|
+
class BitStruct
|
2
|
+
# Class for signed integers in network order, 1-16 bits, or 8n bits.
|
3
|
+
# Declared with BitStruct.signed.
|
4
|
+
class SignedField < Field
|
5
|
+
# Used in describe.
|
6
|
+
def self.class_name
|
7
|
+
@class_name ||= "signed"
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_accessors_to(cl, attr = name) # :nodoc:
|
11
|
+
offset_byte = offset / 8
|
12
|
+
offset_bit = offset % 8
|
13
|
+
|
14
|
+
length_bit = offset_bit + length
|
15
|
+
length_byte = (length_bit/8.0).ceil
|
16
|
+
last_byte = offset_byte + length_byte - 1
|
17
|
+
max = 2**length-1
|
18
|
+
mid = 2**(length-1)
|
19
|
+
max_unsigned = 2**length
|
20
|
+
to_signed = proc {|n| (n>=mid) ? n - max_unsigned : n}
|
21
|
+
# to_signed = proc {|n| (n>=mid) ? -((n ^ max) + 1) : n}
|
22
|
+
|
23
|
+
divisor = options[:fixed] || options["fixed"]
|
24
|
+
divisor_f = divisor && divisor.to_f
|
25
|
+
# if divisor and not divisor.is_a? Fixnum
|
26
|
+
# raise ArgumentError, "fixed-point divisor must be a fixnum"
|
27
|
+
# end
|
28
|
+
|
29
|
+
endian = (options[:endian] || options["endian"]).to_s
|
30
|
+
case endian
|
31
|
+
when "native"
|
32
|
+
ctl = length_byte <= 2 ? "s" : "l"
|
33
|
+
if length == 16 or length == 32
|
34
|
+
to_signed = proc {|n| n}
|
35
|
+
# with pack support, to_signed can be replaced with no-op
|
36
|
+
end
|
37
|
+
when "little"
|
38
|
+
ctl = length_byte <= 2 ? "v" : "V"
|
39
|
+
when "network", "big", ""
|
40
|
+
ctl = length_byte <= 2 ? "n" : "N"
|
41
|
+
else
|
42
|
+
raise ArgumentError,
|
43
|
+
"Unrecognized endian option: #{endian.inspect}"
|
44
|
+
end
|
45
|
+
|
46
|
+
data_is_big_endian =
|
47
|
+
([1234].pack(ctl) == [1234].pack(length_byte <= 2 ? "n" : "N"))
|
48
|
+
|
49
|
+
if length_byte == 1
|
50
|
+
rest = 8 - length_bit
|
51
|
+
mask = ["0"*offset_bit + "1"*length + "0"*rest].pack("B8")[0]
|
52
|
+
mask2 = ["1"*offset_bit + "0"*length + "1"*rest].pack("B8")[0]
|
53
|
+
|
54
|
+
cl.class_eval do
|
55
|
+
if divisor
|
56
|
+
define_method attr do ||
|
57
|
+
to_signed[(self[offset_byte] & mask) >> rest] / divisor_f
|
58
|
+
end
|
59
|
+
|
60
|
+
define_method "#{attr}=" do |val|
|
61
|
+
val = (val * divisor).round
|
62
|
+
self[offset_byte] =
|
63
|
+
(self[offset_byte] & mask2) | ((val<<rest) & mask)
|
64
|
+
end
|
65
|
+
|
66
|
+
else
|
67
|
+
define_method attr do ||
|
68
|
+
to_signed[(self[offset_byte] & mask) >> rest]
|
69
|
+
end
|
70
|
+
|
71
|
+
define_method "#{attr}=" do |val|
|
72
|
+
self[offset_byte] =
|
73
|
+
(self[offset_byte] & mask2) | ((val<<rest) & mask)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
elsif offset_bit == 0 and length % 8 == 0
|
79
|
+
field_length = length
|
80
|
+
byte_range = offset_byte..last_byte
|
81
|
+
|
82
|
+
cl.class_eval do
|
83
|
+
case field_length
|
84
|
+
when 8
|
85
|
+
if divisor
|
86
|
+
define_method attr do ||
|
87
|
+
to_signed[self[offset_byte]] / divisor_f
|
88
|
+
end
|
89
|
+
|
90
|
+
define_method "#{attr}=" do |val|
|
91
|
+
val = (val * divisor).round
|
92
|
+
self[offset_byte] = val
|
93
|
+
end
|
94
|
+
|
95
|
+
else
|
96
|
+
define_method attr do ||
|
97
|
+
to_signed[self[offset_byte]]
|
98
|
+
end
|
99
|
+
|
100
|
+
define_method "#{attr}=" do |val|
|
101
|
+
self[offset_byte] = val
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
when 16, 32
|
106
|
+
if divisor
|
107
|
+
define_method attr do ||
|
108
|
+
to_signed[self[byte_range].unpack(ctl).first] / divisor_f
|
109
|
+
end
|
110
|
+
|
111
|
+
define_method "#{attr}=" do |val|
|
112
|
+
val = (val * divisor).round
|
113
|
+
self[byte_range] = [val].pack(ctl)
|
114
|
+
end
|
115
|
+
|
116
|
+
else
|
117
|
+
define_method attr do ||
|
118
|
+
to_signed[self[byte_range].unpack(ctl).first]
|
119
|
+
end
|
120
|
+
|
121
|
+
define_method "#{attr}=" do |val|
|
122
|
+
self[byte_range] = [val].pack(ctl)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
else
|
127
|
+
reader_helper = proc do |substr|
|
128
|
+
bytes = substr.unpack("C*")
|
129
|
+
bytes.reverse! unless data_is_big_endian
|
130
|
+
bytes.inject do |sum, byte|
|
131
|
+
(sum << 8) + byte
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
writer_helper = proc do |val|
|
136
|
+
bytes = []
|
137
|
+
val += max_unsigned if val < 0
|
138
|
+
while val > 0
|
139
|
+
bytes.push val % 256
|
140
|
+
val = val >> 8
|
141
|
+
end
|
142
|
+
if bytes.length < length_byte
|
143
|
+
bytes.concat [0] * (length_byte - bytes.length)
|
144
|
+
end
|
145
|
+
|
146
|
+
bytes.reverse! if data_is_big_endian
|
147
|
+
bytes.pack("C*")
|
148
|
+
end
|
149
|
+
|
150
|
+
if divisor
|
151
|
+
define_method attr do ||
|
152
|
+
to_signed[reader_helper[self[byte_range]] / divisor_f]
|
153
|
+
end
|
154
|
+
|
155
|
+
define_method "#{attr}=" do |val|
|
156
|
+
self[byte_range] = writer_helper[(val * divisor).round]
|
157
|
+
end
|
158
|
+
|
159
|
+
else
|
160
|
+
define_method attr do ||
|
161
|
+
to_signed[reader_helper[self[byte_range]]]
|
162
|
+
end
|
163
|
+
|
164
|
+
define_method "#{attr}=" do |val|
|
165
|
+
self[byte_range] = writer_helper[val]
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
elsif length_byte == 2 # unaligned field that fits within two whole bytes
|
172
|
+
byte_range = offset_byte..last_byte
|
173
|
+
rest = 16 - length_bit
|
174
|
+
|
175
|
+
mask = ["0"*offset_bit + "1"*length + "0"*rest]
|
176
|
+
mask = mask.pack("B16").unpack(ctl).first
|
177
|
+
|
178
|
+
mask2 = ["1"*offset_bit + "0"*length + "1"*rest]
|
179
|
+
mask2 = mask2.pack("B16").unpack(ctl).first
|
180
|
+
|
181
|
+
cl.class_eval do
|
182
|
+
if divisor
|
183
|
+
define_method attr do ||
|
184
|
+
to_signed[(self[byte_range].unpack(ctl).first & mask) >> rest] /
|
185
|
+
divisor_f
|
186
|
+
end
|
187
|
+
|
188
|
+
define_method "#{attr}=" do |val|
|
189
|
+
val = (val * divisor).round
|
190
|
+
x = (self[byte_range].unpack(ctl).first & mask2) |
|
191
|
+
((val<<rest) & mask)
|
192
|
+
self[byte_range] = [x].pack(ctl)
|
193
|
+
end
|
194
|
+
|
195
|
+
else
|
196
|
+
define_method attr do ||
|
197
|
+
to_signed[(self[byte_range].unpack(ctl).first & mask) >> rest]
|
198
|
+
end
|
199
|
+
|
200
|
+
define_method "#{attr}=" do |val|
|
201
|
+
x = (self[byte_range].unpack(ctl).first & mask2) |
|
202
|
+
((val<<rest) & mask)
|
203
|
+
self[byte_range] = [x].pack(ctl)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
elsif length_byte == 3 # unaligned field that fits within 3 whole bytes
|
209
|
+
byte_range = offset_byte..last_byte
|
210
|
+
rest = 32 - length_bit
|
211
|
+
|
212
|
+
mask = ["0"*offset_bit + "1"*length + "0"*rest]
|
213
|
+
mask = mask.pack("B32").unpack(ctl).first
|
214
|
+
|
215
|
+
mask2 = ["1"*offset_bit + "0"*length + "1"*rest]
|
216
|
+
mask2 = mask2.pack("B32").unpack(ctl).first
|
217
|
+
|
218
|
+
cl.class_eval do
|
219
|
+
if divisor
|
220
|
+
define_method attr do ||
|
221
|
+
bytes = self[byte_range]
|
222
|
+
bytes << 0
|
223
|
+
to_signed[((bytes.unpack(ctl).first & mask) >> rest)] /
|
224
|
+
divisor_f
|
225
|
+
end
|
226
|
+
|
227
|
+
define_method "#{attr}=" do |val|
|
228
|
+
val = (val * divisor).round
|
229
|
+
bytes = self[byte_range]
|
230
|
+
bytes << 0
|
231
|
+
x = (bytes.unpack(ctl).first & mask2) |
|
232
|
+
((val<<rest) & mask)
|
233
|
+
self[byte_range] = [x].pack(ctl)[0..2]
|
234
|
+
end
|
235
|
+
|
236
|
+
else
|
237
|
+
define_method attr do ||
|
238
|
+
bytes = self[byte_range]
|
239
|
+
bytes << 0
|
240
|
+
to_signed[(bytes.unpack(ctl).first & mask) >> rest]
|
241
|
+
end
|
242
|
+
|
243
|
+
define_method "#{attr}=" do |val|
|
244
|
+
bytes = self[byte_range]
|
245
|
+
bytes << 0
|
246
|
+
x = (bytes.unpack(ctl).first & mask2) |
|
247
|
+
((val<<rest) & mask)
|
248
|
+
self[byte_range] = [x].pack(ctl)[0..2]
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
else
|
254
|
+
raise "unsupported: #{inspect}"
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class BitStruct
|
2
|
+
# Class for null-terminated printable text strings.
|
3
|
+
# Declared with BitStruct.text.
|
4
|
+
class TextField < Field
|
5
|
+
# Used in describe.
|
6
|
+
def self.class_name
|
7
|
+
@class_name ||= "text"
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_accessors_to(cl, attr = name) # :nodoc:
|
11
|
+
unless offset % 8 == 0
|
12
|
+
raise ArgumentError,
|
13
|
+
"Bad offset, #{offset}, for #{self.class} #{name}." +
|
14
|
+
" Must be multiple of 8."
|
15
|
+
end
|
16
|
+
|
17
|
+
unless length % 8 == 0
|
18
|
+
raise ArgumentError,
|
19
|
+
"Bad length, #{length}, for #{self.class} #{name}." +
|
20
|
+
" Must be multiple of 8."
|
21
|
+
end
|
22
|
+
|
23
|
+
offset_byte = offset / 8
|
24
|
+
length_byte = length / 8
|
25
|
+
last_byte = offset_byte + length_byte - 1
|
26
|
+
byte_range = offset_byte..last_byte
|
27
|
+
val_byte_range = 0..length_byte-1
|
28
|
+
|
29
|
+
cl.class_eval do
|
30
|
+
define_method attr do ||
|
31
|
+
self[byte_range].sub(/\0*$/, "").to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
define_method "#{attr}=" do |val|
|
35
|
+
val = val.to_s
|
36
|
+
if val.length < length_byte
|
37
|
+
val += "\0" * (length_byte - val.length)
|
38
|
+
end
|
39
|
+
self[byte_range] = val[val_byte_range]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,248 @@
|
|
1
|
+
class BitStruct
|
2
|
+
# Class for unsigned integers in network order, 1-16 bits, or 8n bits.
|
3
|
+
# Declared with BitStruct.unsigned.
|
4
|
+
class UnsignedField < Field
|
5
|
+
# Used in describe.
|
6
|
+
def self.class_name
|
7
|
+
@class_name ||= "unsigned"
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_accessors_to(cl, attr = name) # :nodoc:
|
11
|
+
offset_byte = offset / 8
|
12
|
+
offset_bit = offset % 8
|
13
|
+
|
14
|
+
length_bit = offset_bit + length
|
15
|
+
length_byte = (length_bit/8.0).ceil
|
16
|
+
last_byte = offset_byte + length_byte - 1
|
17
|
+
|
18
|
+
divisor = options[:fixed] || options["fixed"]
|
19
|
+
divisor_f = divisor && divisor.to_f
|
20
|
+
# if divisor and not divisor.is_a? Fixnum
|
21
|
+
# raise ArgumentError, "fixed-point divisor must be a fixnum"
|
22
|
+
# end
|
23
|
+
|
24
|
+
endian = (options[:endian] || options["endian"]).to_s
|
25
|
+
case endian
|
26
|
+
when "native"
|
27
|
+
ctl = length_byte <= 2 ? "S" : "L"
|
28
|
+
when "little"
|
29
|
+
ctl = length_byte <= 2 ? "v" : "V"
|
30
|
+
when "network", "big", ""
|
31
|
+
ctl = length_byte <= 2 ? "n" : "N"
|
32
|
+
else
|
33
|
+
raise ArgumentError,
|
34
|
+
"Unrecognized endian option: #{endian.inspect}"
|
35
|
+
end
|
36
|
+
|
37
|
+
data_is_big_endian =
|
38
|
+
([1234].pack(ctl) == [1234].pack(length_byte <= 2 ? "n" : "N"))
|
39
|
+
|
40
|
+
if length_byte == 1
|
41
|
+
rest = 8 - length_bit
|
42
|
+
mask = ["0"*offset_bit + "1"*length + "0"*rest].pack("B8")[0]
|
43
|
+
mask2 = ["1"*offset_bit + "0"*length + "1"*rest].pack("B8")[0]
|
44
|
+
|
45
|
+
cl.class_eval do
|
46
|
+
if divisor
|
47
|
+
define_method attr do ||
|
48
|
+
((self[offset_byte] & mask) >> rest) / divisor_f
|
49
|
+
end
|
50
|
+
|
51
|
+
define_method "#{attr}=" do |val|
|
52
|
+
val = (val * divisor).round
|
53
|
+
self[offset_byte] =
|
54
|
+
(self[offset_byte] & mask2) | ((val<<rest) & mask)
|
55
|
+
end
|
56
|
+
|
57
|
+
else
|
58
|
+
define_method attr do ||
|
59
|
+
(self[offset_byte] & mask) >> rest
|
60
|
+
end
|
61
|
+
|
62
|
+
define_method "#{attr}=" do |val|
|
63
|
+
self[offset_byte] =
|
64
|
+
(self[offset_byte] & mask2) | ((val<<rest) & mask)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
elsif offset_bit == 0 and length % 8 == 0
|
70
|
+
field_length = length
|
71
|
+
byte_range = offset_byte..last_byte
|
72
|
+
|
73
|
+
cl.class_eval do
|
74
|
+
case field_length
|
75
|
+
when 8
|
76
|
+
if divisor
|
77
|
+
define_method attr do ||
|
78
|
+
self[offset_byte] / divisor_f
|
79
|
+
end
|
80
|
+
|
81
|
+
define_method "#{attr}=" do |val|
|
82
|
+
val = (val * divisor).round
|
83
|
+
self[offset_byte] = val
|
84
|
+
end
|
85
|
+
|
86
|
+
else
|
87
|
+
define_method attr do ||
|
88
|
+
self[offset_byte]
|
89
|
+
end
|
90
|
+
|
91
|
+
define_method "#{attr}=" do |val|
|
92
|
+
self[offset_byte] = val
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
when 16, 32
|
97
|
+
if divisor
|
98
|
+
define_method attr do ||
|
99
|
+
self[byte_range].unpack(ctl).first / divisor_f
|
100
|
+
end
|
101
|
+
|
102
|
+
define_method "#{attr}=" do |val|
|
103
|
+
val = (val * divisor).round
|
104
|
+
self[byte_range] = [val].pack(ctl)
|
105
|
+
end
|
106
|
+
|
107
|
+
else
|
108
|
+
define_method attr do ||
|
109
|
+
self[byte_range].unpack(ctl).first
|
110
|
+
end
|
111
|
+
|
112
|
+
define_method "#{attr}=" do |val|
|
113
|
+
self[byte_range] = [val].pack(ctl)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
else
|
118
|
+
reader_helper = proc do |substr|
|
119
|
+
bytes = substr.unpack("C*")
|
120
|
+
bytes.reverse! unless data_is_big_endian
|
121
|
+
bytes.inject do |sum, byte|
|
122
|
+
(sum << 8) + byte
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
writer_helper = proc do |val|
|
127
|
+
bytes = []
|
128
|
+
while val > 0
|
129
|
+
bytes.push val % 256
|
130
|
+
val = val >> 8
|
131
|
+
end
|
132
|
+
if bytes.length < length_byte
|
133
|
+
bytes.concat [0] * (length_byte - bytes.length)
|
134
|
+
end
|
135
|
+
|
136
|
+
bytes.reverse! if data_is_big_endian
|
137
|
+
bytes.pack("C*")
|
138
|
+
end
|
139
|
+
|
140
|
+
if divisor
|
141
|
+
define_method attr do ||
|
142
|
+
reader_helper[self[byte_range]] / divisor_f
|
143
|
+
end
|
144
|
+
|
145
|
+
define_method "#{attr}=" do |val|
|
146
|
+
self[byte_range] = writer_helper[(val * divisor).round]
|
147
|
+
end
|
148
|
+
|
149
|
+
else
|
150
|
+
define_method attr do ||
|
151
|
+
reader_helper[self[byte_range]]
|
152
|
+
end
|
153
|
+
|
154
|
+
define_method "#{attr}=" do |val|
|
155
|
+
self[byte_range] = writer_helper[val]
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
elsif length_byte == 2 # unaligned field that fits within two whole bytes
|
162
|
+
byte_range = offset_byte..last_byte
|
163
|
+
rest = 16 - length_bit
|
164
|
+
|
165
|
+
mask = ["0"*offset_bit + "1"*length + "0"*rest]
|
166
|
+
mask = mask.pack("B16").unpack(ctl).first
|
167
|
+
|
168
|
+
mask2 = ["1"*offset_bit + "0"*length + "1"*rest]
|
169
|
+
mask2 = mask2.pack("B16").unpack(ctl).first
|
170
|
+
|
171
|
+
cl.class_eval do
|
172
|
+
if divisor
|
173
|
+
define_method attr do ||
|
174
|
+
((self[byte_range].unpack(ctl).first & mask) >> rest) /
|
175
|
+
divisor_f
|
176
|
+
end
|
177
|
+
|
178
|
+
define_method "#{attr}=" do |val|
|
179
|
+
val = (val * divisor).round
|
180
|
+
x = (self[byte_range].unpack(ctl).first & mask2) |
|
181
|
+
((val<<rest) & mask)
|
182
|
+
self[byte_range] = [x].pack(ctl)
|
183
|
+
end
|
184
|
+
|
185
|
+
else
|
186
|
+
define_method attr do ||
|
187
|
+
(self[byte_range].unpack(ctl).first & mask) >> rest
|
188
|
+
end
|
189
|
+
|
190
|
+
define_method "#{attr}=" do |val|
|
191
|
+
x = (self[byte_range].unpack(ctl).first & mask2) |
|
192
|
+
((val<<rest) & mask)
|
193
|
+
self[byte_range] = [x].pack(ctl)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
elsif length_byte == 3 # unaligned field that fits within 3 whole bytes
|
199
|
+
byte_range = offset_byte..last_byte
|
200
|
+
rest = 32 - length_bit
|
201
|
+
|
202
|
+
mask = ["0"*offset_bit + "1"*length + "0"*rest]
|
203
|
+
mask = mask.pack("B32").unpack(ctl).first
|
204
|
+
|
205
|
+
mask2 = ["1"*offset_bit + "0"*length + "1"*rest]
|
206
|
+
mask2 = mask2.pack("B32").unpack(ctl).first
|
207
|
+
|
208
|
+
cl.class_eval do
|
209
|
+
if divisor
|
210
|
+
define_method attr do ||
|
211
|
+
bytes = self[byte_range]
|
212
|
+
bytes << 0
|
213
|
+
((bytes.unpack(ctl).first & mask) >> rest) /
|
214
|
+
divisor_f
|
215
|
+
end
|
216
|
+
|
217
|
+
define_method "#{attr}=" do |val|
|
218
|
+
val = (val * divisor).round
|
219
|
+
bytes = self[byte_range]
|
220
|
+
bytes << 0
|
221
|
+
x = (bytes.unpack(ctl).first & mask2) |
|
222
|
+
((val<<rest) & mask)
|
223
|
+
self[byte_range] = [x].pack(ctl)[0..2]
|
224
|
+
end
|
225
|
+
|
226
|
+
else
|
227
|
+
define_method attr do ||
|
228
|
+
bytes = self[byte_range]
|
229
|
+
bytes << 0
|
230
|
+
(bytes.unpack(ctl).first & mask) >> rest
|
231
|
+
end
|
232
|
+
|
233
|
+
define_method "#{attr}=" do |val|
|
234
|
+
bytes = self[byte_range]
|
235
|
+
bytes << 0
|
236
|
+
x = (bytes.unpack(ctl).first & mask2) |
|
237
|
+
((val<<rest) & mask)
|
238
|
+
self[byte_range] = [x].pack(ctl)[0..2]
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
else
|
244
|
+
raise "unsupported: #{inspect}"
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|