bit-struct 0.15.0 → 0.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.txt +5 -1
- data/LICENSE +56 -0
- data/README.md +2 -2
- data/Rakefile +2 -2
- data/examples/field-ripper.rb +1 -1
- data/examples/ip.rb +6 -6
- data/examples/longlong.rb +2 -2
- data/examples/md.rb +1 -1
- data/examples/modular-def.rb +2 -2
- data/examples/native.rb +3 -3
- data/examples/ping.rb +1 -1
- data/examples/raw.rb +1 -1
- data/examples/switch-endian.rb +2 -2
- data/examples/vector.rb +2 -2
- data/lib/bit-struct.rb +0 -11
- data/lib/bit-struct/bit-struct.rb +54 -144
- data/lib/bit-struct/char-field.rb +3 -3
- data/lib/bit-struct/field.rb +94 -0
- data/lib/bit-struct/fields.rb +13 -13
- data/lib/bit-struct/float-field.rb +4 -4
- data/lib/bit-struct/hex-octet-field.rb +3 -3
- data/lib/bit-struct/nested-field.rb +8 -8
- data/lib/bit-struct/octet-field.rb +4 -4
- data/lib/bit-struct/signed-field.rb +23 -28
- data/lib/bit-struct/text-field.rb +2 -2
- data/lib/bit-struct/unsigned-field.rb +24 -27
- data/lib/bit-struct/vector-field.rb +7 -7
- data/lib/bit-struct/vector.rb +21 -21
- data/lib/bit-struct/yaml.rb +2 -2
- data/test/test-endian.rb +3 -3
- data/test/test-vector.rb +4 -4
- data/test/test.rb +49 -43
- metadata +47 -45
@@ -5,7 +5,7 @@ class BitStruct
|
|
5
5
|
#def self.default
|
6
6
|
# don't define this, since it must specify N nulls and we don't know N
|
7
7
|
#end
|
8
|
-
|
8
|
+
|
9
9
|
# Used in describe.
|
10
10
|
def self.class_name
|
11
11
|
@class_name ||= "char"
|
@@ -17,13 +17,13 @@ class BitStruct
|
|
17
17
|
"Bad offset, #{offset}, for #{self.class} #{name}." +
|
18
18
|
" Must be multiple of 8."
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
unless length % 8 == 0
|
22
22
|
raise ArgumentError,
|
23
23
|
"Bad length, #{length}, for #{self.class} #{name}." +
|
24
24
|
" Must be multiple of 8."
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
offset_byte = offset / 8
|
28
28
|
length_byte = length / 8
|
29
29
|
last_byte = offset_byte + length_byte - 1
|
@@ -0,0 +1,94 @@
|
|
1
|
+
|
2
|
+
class BitStruct
|
3
|
+
class Field
|
4
|
+
# Offset of field in bits.
|
5
|
+
attr_reader :offset
|
6
|
+
|
7
|
+
# Length of field in bits.
|
8
|
+
attr_reader :length
|
9
|
+
alias size length
|
10
|
+
|
11
|
+
# Name of field (used for its accessors).
|
12
|
+
attr_reader :name
|
13
|
+
|
14
|
+
# Options, such as :default (varies for each field subclass).
|
15
|
+
# In general, options can be provided as strings or as symbols.
|
16
|
+
attr_reader :options
|
17
|
+
|
18
|
+
# Display name of field (used for printing).
|
19
|
+
attr_reader :display_name
|
20
|
+
|
21
|
+
# Default value.
|
22
|
+
attr_reader :default
|
23
|
+
|
24
|
+
# Format for printed value of field.
|
25
|
+
attr_reader :format
|
26
|
+
|
27
|
+
# Subclasses can override this to define a default for all fields of this
|
28
|
+
# class, not just the one currently being added to a BitStruct class, a
|
29
|
+
# "default default" if you will. The global default, if #default returns
|
30
|
+
# nil, is to fill the field with zero. Most field classes just let this
|
31
|
+
# default stand. The default can be overridden per-field when a BitStruct
|
32
|
+
# class is defined.
|
33
|
+
def self.default; nil; end
|
34
|
+
|
35
|
+
# Used in describe.
|
36
|
+
def self.class_name
|
37
|
+
@class_name ||= name[/\w+$/]
|
38
|
+
end
|
39
|
+
|
40
|
+
# Used in describe. Can be overridden per-subclass, as in NestedField.
|
41
|
+
def class_name
|
42
|
+
self.class.class_name
|
43
|
+
end
|
44
|
+
|
45
|
+
# Yield the description of this field, as an array of 5 strings: byte
|
46
|
+
# offset, type, name, size, and description. The opts hash may have:
|
47
|
+
#
|
48
|
+
# :expand :: if the value is true, expand complex fields
|
49
|
+
#
|
50
|
+
# (Subclass implementations may yield more than once for complex fields.)
|
51
|
+
#
|
52
|
+
def describe opts
|
53
|
+
bits = size
|
54
|
+
if bits > 32 and bits % 8 == 0
|
55
|
+
len_str = "%dB" % (bits/8)
|
56
|
+
else
|
57
|
+
len_str = "%db" % bits
|
58
|
+
end
|
59
|
+
|
60
|
+
byte_offset = offset / 8 + (opts[:byte_offset] || 0)
|
61
|
+
|
62
|
+
yield ["@%d" % byte_offset, class_name, name, len_str, display_name]
|
63
|
+
end
|
64
|
+
|
65
|
+
# Options are _display_name_, _default_, and _format_ (subclasses of Field
|
66
|
+
# may add other options).
|
67
|
+
def initialize(offset, length, name, opts = {})
|
68
|
+
@offset, @length, @name, @options =
|
69
|
+
offset, length, name, opts
|
70
|
+
|
71
|
+
@display_name = opts[:display_name] || opts["display_name"]
|
72
|
+
@default = opts[:default] || opts["default"] || self.class.default
|
73
|
+
@format = opts[:format] || opts["format"]
|
74
|
+
end
|
75
|
+
|
76
|
+
# Inspect the value of this field in the specified _obj_.
|
77
|
+
def inspect_in_object(obj, opts)
|
78
|
+
val = obj.send(name)
|
79
|
+
str =
|
80
|
+
begin
|
81
|
+
val.inspect_with_options(opts)
|
82
|
+
rescue NoMethodError
|
83
|
+
val.inspect
|
84
|
+
end
|
85
|
+
(f=@format) ? (f % str) : str
|
86
|
+
end
|
87
|
+
|
88
|
+
# Normally, all fields show up in inspect, but some, such as padding,
|
89
|
+
# should not.
|
90
|
+
def inspectable?; true; end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
|
data/lib/bit-struct/fields.rb
CHANGED
@@ -94,24 +94,24 @@ class BitStruct
|
|
94
94
|
# n.x = 3
|
95
95
|
# a.n = n
|
96
96
|
# p a # ==> #<A n=#<Sub x=3>>
|
97
|
-
#
|
97
|
+
#
|
98
98
|
def nest(name, *rest, &block)
|
99
99
|
nested_class = rest.grep(Class).find {|cl| cl <= BitStruct}
|
100
100
|
rest.delete nested_class
|
101
101
|
opts = parse_options(rest, name, NestedField)
|
102
102
|
nested_class = opts[:nested_class] ||= nested_class
|
103
|
-
|
103
|
+
|
104
104
|
unless (block and not nested_class) or (nested_class and not block)
|
105
105
|
raise ArgumentError,
|
106
106
|
"nested field must have either a nested_class option or a block," +
|
107
107
|
" but not both"
|
108
108
|
end
|
109
|
-
|
109
|
+
|
110
110
|
unless nested_class
|
111
111
|
nested_class = Class.new(BitStruct)
|
112
112
|
nested_class.class_eval(&block)
|
113
113
|
end
|
114
|
-
|
114
|
+
|
115
115
|
opts[:default] ||= nested_class.initial_value.dup
|
116
116
|
opts[:nested_class] = nested_class
|
117
117
|
field = add_field(name, nested_class.bit_length, opts)
|
@@ -250,17 +250,17 @@ class BitStruct
|
|
250
250
|
# entry.y = -456
|
251
251
|
# vec[2] = entry
|
252
252
|
# pkt.v = vec
|
253
|
-
#
|
253
|
+
#
|
254
254
|
def vector(name, *rest, &block)
|
255
255
|
opts = parse_options(rest, name, nil)
|
256
256
|
cl = opts[:field_class]
|
257
257
|
opts[:field_class] = VectorField
|
258
|
-
|
258
|
+
|
259
259
|
unless (block and not cl) or (cl and not block)
|
260
260
|
raise ArgumentError,
|
261
261
|
"vector must have either a class or a block, but not both"
|
262
262
|
end
|
263
|
-
|
263
|
+
|
264
264
|
case
|
265
265
|
when cl == nil
|
266
266
|
vector_class = Class.new(BitStruct::Vector)
|
@@ -272,12 +272,12 @@ class BitStruct
|
|
272
272
|
|
273
273
|
when cl < BitStruct::Vector
|
274
274
|
vector_class = cl
|
275
|
-
|
275
|
+
|
276
276
|
else raise ArgumentError, "Bad vector class: #{cl.inspect}"
|
277
277
|
end
|
278
|
-
|
278
|
+
|
279
279
|
vector_class.default_options default_options
|
280
|
-
|
280
|
+
|
281
281
|
length = opts[:length] || rest.grep(Integer).first
|
282
282
|
## what about :length => :lenfield
|
283
283
|
unless length
|
@@ -287,14 +287,14 @@ class BitStruct
|
|
287
287
|
|
288
288
|
opts[:default] ||= vector_class.new(length) ## nil if variable length
|
289
289
|
opts[:vector_class] = vector_class
|
290
|
-
|
290
|
+
|
291
291
|
bit_length = vector_class.struct_class.round_byte_length * 8 * length
|
292
|
-
|
292
|
+
|
293
293
|
field = add_field(name, bit_length, opts)
|
294
294
|
field
|
295
295
|
end
|
296
296
|
BitStruct.autoload :VectorField, "bit-struct/vector-field"
|
297
297
|
end
|
298
|
-
|
298
|
+
|
299
299
|
autoload :Vector, "bit-struct/vector"
|
300
300
|
end
|
@@ -6,20 +6,20 @@ class BitStruct
|
|
6
6
|
def self.class_name
|
7
7
|
@class_name ||= "float"
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
def add_accessors_to(cl, attr = name) # :nodoc:
|
11
11
|
unless offset % 8 == 0
|
12
12
|
raise ArgumentError,
|
13
13
|
"Bad offset, #{offset}, for #{self.class} #{name}." +
|
14
14
|
" Must be multiple of 8."
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
unless length == 32 or length == 64
|
18
18
|
raise ArgumentError,
|
19
19
|
"Bad length, #{length}, for #{self.class} #{name}." +
|
20
20
|
" Must be 32 or 64."
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
offset_byte = offset / 8
|
24
24
|
length_byte = length / 8
|
25
25
|
last_byte = offset_byte + length_byte - 1
|
@@ -46,7 +46,7 @@ class BitStruct
|
|
46
46
|
raise ArgumentError,
|
47
47
|
"Unrecognized endian option: #{endian.inspect}"
|
48
48
|
end
|
49
|
-
|
49
|
+
|
50
50
|
cl.class_eval do
|
51
51
|
define_method attr do ||
|
52
52
|
self[byte_range].unpack(ctl).first
|
@@ -1,18 +1,18 @@
|
|
1
|
-
require 'bit-struct/
|
1
|
+
require 'bit-struct/octet-field'
|
2
2
|
|
3
3
|
class BitStruct
|
4
4
|
# Class for char fields that can be accessed with values like
|
5
5
|
# "xx:xx:xx:xx", where each xx is up to 2 hex digits representing a
|
6
6
|
# single octet. The original string-based accessors are still available with
|
7
7
|
# the <tt>_chars</tt> suffix.
|
8
|
-
#
|
8
|
+
#
|
9
9
|
# Declared with BitStruct.hex_octets.
|
10
10
|
class HexOctetField < BitStruct::OctetField
|
11
11
|
# Used in describe.
|
12
12
|
def self.class_name
|
13
13
|
@class_name ||= "hex_octets"
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
SEPARATOR = ":"
|
17
17
|
FORMAT = "%02x"
|
18
18
|
BASE = 16
|
@@ -7,16 +7,16 @@ class BitStruct
|
|
7
7
|
def initialize(*args)
|
8
8
|
super
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
# Used in describe.
|
12
12
|
def self.class_name
|
13
13
|
@class_name ||= "nest"
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
def class_name
|
17
17
|
@class_name ||= nested_class.name[/\w+$/]
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
def nested_class
|
21
21
|
@nested_class ||= options[:nested_class] || options["nested_class"]
|
22
22
|
end
|
@@ -38,20 +38,20 @@ class BitStruct
|
|
38
38
|
"Bad offset, #{offset}, for nested field #{name}." +
|
39
39
|
" Must be multiple of 8."
|
40
40
|
end
|
41
|
-
|
41
|
+
|
42
42
|
unless length % 8 == 0
|
43
43
|
raise ArgumentError,
|
44
44
|
"Bad length, #{length}, for nested field #{name}." +
|
45
45
|
" Must be multiple of 8."
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
offset_byte = offset / 8
|
49
49
|
length_byte = length / 8
|
50
50
|
last_byte = offset_byte + length_byte - 1
|
51
51
|
byte_range = offset_byte..last_byte
|
52
52
|
|
53
53
|
nc = nested_class
|
54
|
-
|
54
|
+
|
55
55
|
cl.class_eval do
|
56
56
|
define_method attr do ||
|
57
57
|
nc.new(self[byte_range])
|
@@ -62,12 +62,12 @@ class BitStruct
|
|
62
62
|
raise ArgumentError, "Size mismatch in nested struct assignment " +
|
63
63
|
"to #{attr} with value #{val.inspect}"
|
64
64
|
end
|
65
|
-
|
65
|
+
|
66
66
|
if val.class != nc
|
67
67
|
warn "Type mismatch in nested struct assignment " +
|
68
68
|
"to #{attr} with value #{val.inspect}"
|
69
69
|
end
|
70
|
-
|
70
|
+
|
71
71
|
self[byte_range] = val
|
72
72
|
end
|
73
73
|
end
|
@@ -5,14 +5,14 @@ class BitStruct
|
|
5
5
|
# "xxx.xxx.xxx.xxx", where each xxx is up to 3 decimal digits representing a
|
6
6
|
# single octet. The original string-based accessors are still available with
|
7
7
|
# the <tt>_chars</tt> suffix.
|
8
|
-
#
|
8
|
+
#
|
9
9
|
# Declared with BitStruct.octets.
|
10
10
|
class OctetField < BitStruct::CharField
|
11
11
|
# Used in describe.
|
12
12
|
def self.class_name
|
13
13
|
@class_name ||= "octets"
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
SEPARATOR = "."
|
17
17
|
FORMAT = "%d"
|
18
18
|
BASE = 10
|
@@ -23,7 +23,7 @@ class BitStruct
|
|
23
23
|
sep = self.class::SEPARATOR
|
24
24
|
base = self.class::BASE
|
25
25
|
fmt = self.class::FORMAT
|
26
|
-
|
26
|
+
|
27
27
|
cl.class_eval do
|
28
28
|
define_method attr do ||
|
29
29
|
ary = []
|
@@ -32,7 +32,7 @@ class BitStruct
|
|
32
32
|
end
|
33
33
|
ary.join(sep)
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
old_writer = "#{attr_chars}="
|
37
37
|
|
38
38
|
define_method "#{attr}=" do |val|
|
@@ -6,25 +6,20 @@ class BitStruct
|
|
6
6
|
def self.class_name
|
7
7
|
@class_name ||= "signed"
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
def add_accessors_to(cl, attr = name) # :nodoc:
|
11
11
|
offset_byte = offset / 8
|
12
12
|
offset_bit = offset % 8
|
13
|
-
|
13
|
+
|
14
14
|
length_bit = offset_bit + length
|
15
15
|
length_byte = (length_bit/8.0).ceil
|
16
16
|
last_byte = offset_byte + length_byte - 1
|
17
|
-
max = 2**length-1
|
18
17
|
mid = 2**(length-1)
|
19
18
|
max_unsigned = 2**length
|
20
19
|
to_signed = proc {|n| (n>=mid) ? n - max_unsigned : n}
|
21
|
-
|
22
|
-
|
20
|
+
|
23
21
|
divisor = options[:fixed] || options["fixed"]
|
24
22
|
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
23
|
|
29
24
|
endian = (options[:endian] || options["endian"]).to_s
|
30
25
|
case endian
|
@@ -42,15 +37,15 @@ class BitStruct
|
|
42
37
|
raise ArgumentError,
|
43
38
|
"Unrecognized endian option: #{endian.inspect}"
|
44
39
|
end
|
45
|
-
|
40
|
+
|
46
41
|
data_is_big_endian =
|
47
42
|
([1234].pack(ctl) == [1234].pack(length_byte <= 2 ? "n" : "N"))
|
48
|
-
|
43
|
+
|
49
44
|
if length_byte == 1
|
50
45
|
rest = 8 - length_bit
|
51
46
|
mask = ["0"*offset_bit + "1"*length + "0"*rest].pack("B8")[0].ord
|
52
47
|
mask2 = ["1"*offset_bit + "0"*length + "1"*rest].pack("B8")[0].ord
|
53
|
-
|
48
|
+
|
54
49
|
cl.class_eval do
|
55
50
|
if divisor
|
56
51
|
define_method attr do ||
|
@@ -74,11 +69,11 @@ class BitStruct
|
|
74
69
|
end
|
75
70
|
end
|
76
71
|
end
|
77
|
-
|
72
|
+
|
78
73
|
elsif offset_bit == 0 and length % 8 == 0
|
79
74
|
field_length = length
|
80
75
|
byte_range = offset_byte..last_byte
|
81
|
-
|
76
|
+
|
82
77
|
cl.class_eval do
|
83
78
|
case field_length
|
84
79
|
when 8
|
@@ -91,7 +86,7 @@ class BitStruct
|
|
91
86
|
val = (val * divisor).round
|
92
87
|
self[offset_byte] = val
|
93
88
|
end
|
94
|
-
|
89
|
+
|
95
90
|
else
|
96
91
|
define_method attr do ||
|
97
92
|
to_signed[self[offset_byte]]
|
@@ -101,7 +96,7 @@ class BitStruct
|
|
101
96
|
self[offset_byte] = val
|
102
97
|
end
|
103
98
|
end
|
104
|
-
|
99
|
+
|
105
100
|
when 16, 32
|
106
101
|
if divisor
|
107
102
|
define_method attr do ||
|
@@ -112,7 +107,7 @@ class BitStruct
|
|
112
107
|
val = (val * divisor).round
|
113
108
|
self[byte_range] = [val].pack(ctl)
|
114
109
|
end
|
115
|
-
|
110
|
+
|
116
111
|
else
|
117
112
|
define_method attr do ||
|
118
113
|
to_signed[self[byte_range].unpack(ctl).first]
|
@@ -122,7 +117,7 @@ class BitStruct
|
|
122
117
|
self[byte_range] = [val].pack(ctl)
|
123
118
|
end
|
124
119
|
end
|
125
|
-
|
120
|
+
|
126
121
|
else
|
127
122
|
reader_helper = proc do |substr|
|
128
123
|
bytes = substr.unpack("C*")
|
@@ -131,7 +126,7 @@ class BitStruct
|
|
131
126
|
(sum << 8) + byte
|
132
127
|
end
|
133
128
|
end
|
134
|
-
|
129
|
+
|
135
130
|
writer_helper = proc do |val|
|
136
131
|
bytes = []
|
137
132
|
val += max_unsigned if val < 0
|
@@ -146,21 +141,21 @@ class BitStruct
|
|
146
141
|
bytes.reverse! if data_is_big_endian
|
147
142
|
bytes.pack("C*")
|
148
143
|
end
|
149
|
-
|
144
|
+
|
150
145
|
if divisor
|
151
146
|
define_method attr do ||
|
152
147
|
to_signed[reader_helper[self[byte_range]] / divisor_f]
|
153
148
|
end
|
154
|
-
|
149
|
+
|
155
150
|
define_method "#{attr}=" do |val|
|
156
151
|
self[byte_range] = writer_helper[(val * divisor).round]
|
157
152
|
end
|
158
|
-
|
153
|
+
|
159
154
|
else
|
160
155
|
define_method attr do ||
|
161
156
|
to_signed[reader_helper[self[byte_range]]]
|
162
157
|
end
|
163
|
-
|
158
|
+
|
164
159
|
define_method "#{attr}=" do |val|
|
165
160
|
self[byte_range] = writer_helper[val]
|
166
161
|
end
|
@@ -171,10 +166,10 @@ class BitStruct
|
|
171
166
|
elsif length_byte == 2 # unaligned field that fits within two whole bytes
|
172
167
|
byte_range = offset_byte..last_byte
|
173
168
|
rest = 16 - length_bit
|
174
|
-
|
169
|
+
|
175
170
|
mask = ["0"*offset_bit + "1"*length + "0"*rest]
|
176
171
|
mask = mask.pack("B16").unpack(ctl).first
|
177
|
-
|
172
|
+
|
178
173
|
mask2 = ["1"*offset_bit + "0"*length + "1"*rest]
|
179
174
|
mask2 = mask2.pack("B16").unpack(ctl).first
|
180
175
|
|
@@ -204,14 +199,14 @@ class BitStruct
|
|
204
199
|
end
|
205
200
|
end
|
206
201
|
end
|
207
|
-
|
202
|
+
|
208
203
|
elsif length_byte == 3 # unaligned field that fits within 3 whole bytes
|
209
204
|
byte_range = offset_byte..last_byte
|
210
205
|
rest = 32 - length_bit
|
211
|
-
|
206
|
+
|
212
207
|
mask = ["0"*offset_bit + "1"*length + "0"*rest]
|
213
208
|
mask = mask.pack("B32").unpack(ctl).first
|
214
|
-
|
209
|
+
|
215
210
|
mask2 = ["1"*offset_bit + "0"*length + "1"*rest]
|
216
211
|
mask2 = mask2.pack("B32").unpack(ctl).first
|
217
212
|
|
@@ -249,7 +244,7 @@ class BitStruct
|
|
249
244
|
end
|
250
245
|
end
|
251
246
|
end
|
252
|
-
|
247
|
+
|
253
248
|
else
|
254
249
|
raise "unsupported: #{inspect}"
|
255
250
|
end
|