bindata 2.1.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bindata might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.travis.yml +7 -2
- data/COPYING +1 -1
- data/ChangeLog.rdoc +8 -0
- data/README.md +1 -1
- data/bindata.gemspec +1 -1
- data/lib/bindata.rb +6 -2
- data/lib/bindata/base.rb +5 -0
- data/lib/bindata/base_primitive.rb +2 -2
- data/lib/bindata/dsl.rb +142 -99
- data/lib/bindata/int.rb +50 -50
- data/lib/bindata/registry.rb +38 -14
- data/lib/bindata/sanitize.rb +33 -21
- data/lib/bindata/string.rb +16 -0
- data/lib/bindata/struct.rb +17 -2
- data/lib/bindata/version.rb +1 -1
- data/test/alignment_test.rb +3 -3
- data/test/array_test.rb +11 -11
- data/test/base_primitive_test.rb +21 -1
- data/test/base_test.rb +7 -1
- data/test/bits_test.rb +1 -1
- data/test/buffer_test.rb +6 -6
- data/test/choice_test.rb +14 -19
- data/test/count_bytes_remaining_test.rb +3 -3
- data/test/float_test.rb +5 -5
- data/test/int_test.rb +4 -4
- data/test/io_test.rb +1 -1
- data/test/lazy_test.rb +1 -1
- data/test/offset_test.rb +1 -1
- data/test/params_test.rb +1 -1
- data/test/primitive_test.rb +6 -6
- data/test/record_test.rb +86 -12
- data/test/registry_test.rb +43 -22
- data/test/rest_test.rb +2 -2
- data/test/skip_test.rb +4 -4
- data/test/string_test.rb +35 -7
- data/test/stringz_test.rb +6 -6
- data/test/struct_test.rb +56 -6
- data/test/system_test.rb +7 -7
- data/test/{common.rb → test_helper.rb} +11 -34
- data/test/virtual_test.rb +2 -2
- data/test/warnings_test.rb +1 -1
- metadata +16 -16
data/lib/bindata/int.rb
CHANGED
@@ -65,38 +65,44 @@ module BinData
|
|
65
65
|
end
|
66
66
|
|
67
67
|
def create_read_code(nbits, endian, signed)
|
68
|
-
|
69
|
-
assemble_str = create_read_assemble_code(nbits, endian, signed)
|
68
|
+
read_str = create_raw_read_code(nbits, endian, signed)
|
70
69
|
|
71
|
-
|
72
|
-
|
73
|
-
if need_conversion_code?(nbits, signed)
|
70
|
+
if need_signed_conversion_code?(nbits, signed)
|
74
71
|
"val = #{read_str} ; #{create_uint2int_code(nbits)}"
|
75
72
|
else
|
76
73
|
read_str
|
77
74
|
end
|
78
75
|
end
|
79
76
|
|
77
|
+
def create_raw_read_code(nbits, endian, signed)
|
78
|
+
# special case 8bit integers for speed
|
79
|
+
if nbits == 8
|
80
|
+
"io.readbytes(1).ord"
|
81
|
+
else
|
82
|
+
unpack_str = create_read_unpack_code(nbits, endian, signed)
|
83
|
+
assemble_str = create_read_assemble_code(nbits, endian, signed)
|
84
|
+
|
85
|
+
"(#{unpack_str} ; #{assemble_str})"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
80
89
|
def create_read_unpack_code(nbits, endian, signed)
|
81
|
-
nbytes
|
90
|
+
nbytes = nbits / 8
|
91
|
+
pack_directive = pack_directive(nbits, endian, signed)
|
82
92
|
|
83
|
-
"ints = io.readbytes(#{nbytes}).unpack('#{pack_directive
|
93
|
+
"ints = io.readbytes(#{nbytes}).unpack('#{pack_directive}')"
|
84
94
|
end
|
85
95
|
|
86
96
|
def create_read_assemble_code(nbits, endian, signed)
|
87
|
-
|
88
|
-
nwords = nbits / bits_per_word
|
97
|
+
nwords = nbits / bits_per_word(nbits)
|
89
98
|
|
90
99
|
idx = (0 ... nwords).to_a
|
91
100
|
idx.reverse! if (endian == :big)
|
92
101
|
|
93
102
|
parts = (0 ... nwords).collect do |i|
|
94
|
-
|
95
|
-
"ints.at(#{idx[i]})"
|
96
|
-
else
|
97
|
-
"(ints.at(#{idx[i]}) << #{bits_per_word * i})"
|
98
|
-
end
|
103
|
+
"(ints.at(#{idx[i]}) << #{bits_per_word(nbits) * i})"
|
99
104
|
end
|
105
|
+
parts[0].sub!(/ << 0\b/, "") # Remove " << 0" for optimisation
|
100
106
|
|
101
107
|
assemble_str = parts.join(" + ")
|
102
108
|
end
|
@@ -105,25 +111,29 @@ module BinData
|
|
105
111
|
# special case 8bit integers for speed
|
106
112
|
return "(val & 0xff).chr" if nbits == 8
|
107
113
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
vals = (0 ... nwords).collect do |i|
|
113
|
-
i.zero? ? "val" : "val >> #{bits_per_word * i}"
|
114
|
-
end
|
115
|
-
vals.reverse! if (endian == :big)
|
114
|
+
pack_directive = pack_directive(nbits, endian, signed)
|
115
|
+
words = val_as_packed_words(nbits, endian, signed)
|
116
|
+
pack_str = "[#{words}].pack('#{pack_directive}')"
|
116
117
|
|
117
|
-
|
118
|
-
pack_str = "#{array_str}.pack('#{pack_directive(nbits, endian, signed)}')"
|
119
|
-
|
120
|
-
if need_conversion_code?(nbits, signed)
|
118
|
+
if need_signed_conversion_code?(nbits, signed)
|
121
119
|
"#{create_int2uint_code(nbits)} ; #{pack_str}"
|
122
120
|
else
|
123
121
|
pack_str
|
124
122
|
end
|
125
123
|
end
|
126
124
|
|
125
|
+
def val_as_packed_words(nbits, endian, signed)
|
126
|
+
nwords = nbits / bits_per_word(nbits)
|
127
|
+
mask = (1 << bits_per_word(nbits)) - 1
|
128
|
+
|
129
|
+
vals = (0 ... nwords).collect { |i| "val >> #{bits_per_word(nbits) * i}" }
|
130
|
+
vals[0].sub!(/ >> 0\b/, "") # Remove " >> 0" for optimisation
|
131
|
+
vals.reverse! if (endian == :big)
|
132
|
+
|
133
|
+
vals = vals.collect { |val| "#{val} & #{mask}" } # TODO: "& mask" is needed to work around jruby bug. Remove this line when fixed.
|
134
|
+
vals.join(",")
|
135
|
+
end
|
136
|
+
|
127
137
|
def create_int2uint_code(nbits)
|
128
138
|
"val &= #{(1 << nbits) - 1}"
|
129
139
|
end
|
@@ -132,40 +142,30 @@ module BinData
|
|
132
142
|
"(val >= #{1 << (nbits - 1)}) ? val - #{1 << nbits} : val"
|
133
143
|
end
|
134
144
|
|
135
|
-
def
|
136
|
-
(nbits % 64).zero? ?
|
137
|
-
(nbits % 32).zero? ?
|
138
|
-
(nbits % 16).zero? ?
|
139
|
-
|
145
|
+
def bits_per_word(nbits)
|
146
|
+
(nbits % 64).zero? ? 64 :
|
147
|
+
(nbits % 32).zero? ? 32 :
|
148
|
+
(nbits % 16).zero? ? 16 :
|
149
|
+
8
|
140
150
|
end
|
141
151
|
|
142
152
|
def pack_directive(nbits, endian, signed)
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
d = (endian == :big) ? 'L>' : 'L<'
|
150
|
-
elsif (nbits % 16).zero?
|
151
|
-
d = (endian == :big) ? 'S>' : 'S<'
|
152
|
-
else
|
153
|
-
d = 'C'
|
154
|
-
end
|
153
|
+
nwords = nbits / bits_per_word(nbits)
|
154
|
+
|
155
|
+
directives = { 8 => "C", 16 => "S", 32 => "L", 64 => "Q" }
|
156
|
+
|
157
|
+
d = directives[bits_per_word(nbits)]
|
158
|
+
d << ((endian == :big) ? ">" : "<") unless d == "C"
|
155
159
|
|
156
|
-
if
|
160
|
+
if signed == :signed and directives.has_key?(nbits)
|
157
161
|
(d * nwords).downcase
|
158
162
|
else
|
159
163
|
d * nwords
|
160
164
|
end
|
161
165
|
end
|
162
166
|
|
163
|
-
def
|
164
|
-
signed == :signed and not
|
165
|
-
end
|
166
|
-
|
167
|
-
def pack_directive_signed?(nbits, signed)
|
168
|
-
signed == :signed and [64, 32, 16, 8].include?(nbits)
|
167
|
+
def need_signed_conversion_code?(nbits, signed)
|
168
|
+
signed == :signed and not [64, 32, 16].include?(nbits)
|
169
169
|
end
|
170
170
|
end
|
171
171
|
end
|
data/lib/bindata/registry.rb
CHANGED
@@ -5,8 +5,16 @@ module BinData
|
|
5
5
|
# This registry contains a register of name -> class mappings.
|
6
6
|
#
|
7
7
|
# Numerics (integers and floating point numbers) have an endian property as
|
8
|
-
# part of their name (e.g. int32be, float_le).
|
9
|
-
#
|
8
|
+
# part of their name (e.g. int32be, float_le).
|
9
|
+
#
|
10
|
+
# Classes can be looked up based on their full name or an abbreviated +name+
|
11
|
+
# with +hints+.
|
12
|
+
#
|
13
|
+
# There are two hints supported, :endian and :search_prefix.
|
14
|
+
#
|
15
|
+
# #lookup("int32", { endian: :big }) will return Int32Be.
|
16
|
+
#
|
17
|
+
# #lookup("my_type", { search_prefix: :ns }) will return NsMyType.
|
10
18
|
#
|
11
19
|
# Names are stored in under_score_style, not camelCase.
|
12
20
|
class Registry
|
@@ -28,21 +36,11 @@ module BinData
|
|
28
36
|
@registry.delete(underscore_name(name))
|
29
37
|
end
|
30
38
|
|
31
|
-
def lookup(name,
|
32
|
-
key = normalize_name(name,
|
39
|
+
def lookup(name, hints = {})
|
40
|
+
key = normalize_name(name, hints)
|
33
41
|
@registry[key] || raise(UnRegisteredTypeError, name.to_s)
|
34
42
|
end
|
35
43
|
|
36
|
-
def normalize_name(name, endian = nil)
|
37
|
-
name = underscore_name(name)
|
38
|
-
return name if is_registered?(name)
|
39
|
-
|
40
|
-
name = name_with_endian(name, endian)
|
41
|
-
return name if is_registered?(name)
|
42
|
-
|
43
|
-
name
|
44
|
-
end
|
45
|
-
|
46
44
|
# Convert CamelCase +name+ to underscore style.
|
47
45
|
def underscore_name(name)
|
48
46
|
name.to_s.sub(/.*::/, "").
|
@@ -55,6 +53,32 @@ module BinData
|
|
55
53
|
#---------------
|
56
54
|
private
|
57
55
|
|
56
|
+
def normalize_name(name, hints)
|
57
|
+
name = underscore_name(name)
|
58
|
+
|
59
|
+
if not is_registered?(name)
|
60
|
+
search_prefix = [""].concat(Array(hints[:search_prefix]))
|
61
|
+
search_prefix.each do |prefix|
|
62
|
+
n = name_with_endian(name_with_prefix(name, prefix), hints[:endian])
|
63
|
+
if is_registered?(n)
|
64
|
+
name = n
|
65
|
+
break
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
name
|
71
|
+
end
|
72
|
+
|
73
|
+
def name_with_prefix(name, prefix)
|
74
|
+
prefix = prefix.to_s.chomp("_")
|
75
|
+
if prefix == ""
|
76
|
+
name
|
77
|
+
else
|
78
|
+
"#{prefix}_#{name}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
58
82
|
def name_with_endian(name, endian)
|
59
83
|
return name if endian.nil?
|
60
84
|
|
data/lib/bindata/sanitize.rb
CHANGED
@@ -6,21 +6,24 @@ module BinData
|
|
6
6
|
class SanitizedParameter; end
|
7
7
|
|
8
8
|
class SanitizedPrototype < SanitizedParameter
|
9
|
-
def initialize(obj_type, obj_params,
|
10
|
-
|
9
|
+
def initialize(obj_type, obj_params, hints)
|
10
|
+
raw_hints = hints.dup
|
11
|
+
if raw_hints[:endian].respond_to?(:endian)
|
12
|
+
raw_hints[:endian] = raw_hints[:endian].endian
|
13
|
+
end
|
11
14
|
obj_params ||= {}
|
12
15
|
|
13
16
|
if BinData::Base === obj_type
|
14
17
|
obj_class = obj_type
|
15
18
|
else
|
16
|
-
obj_class = RegisteredClasses.lookup(obj_type,
|
19
|
+
obj_class = RegisteredClasses.lookup(obj_type, raw_hints)
|
17
20
|
end
|
18
21
|
|
19
22
|
if BinData::Base === obj_class
|
20
23
|
@factory = obj_class
|
21
24
|
else
|
22
25
|
@obj_class = obj_class
|
23
|
-
@obj_params = SanitizedParameters.new(obj_params, @obj_class,
|
26
|
+
@obj_params = SanitizedParameters.new(obj_params, @obj_class, hints)
|
24
27
|
end
|
25
28
|
end
|
26
29
|
|
@@ -41,9 +44,9 @@ module BinData
|
|
41
44
|
#----------------------------------------------------------------------------
|
42
45
|
|
43
46
|
class SanitizedField < SanitizedParameter
|
44
|
-
def initialize(name, field_type, field_params,
|
47
|
+
def initialize(name, field_type, field_params, hints)
|
45
48
|
@name = name
|
46
|
-
@prototype = SanitizedPrototype.new(field_type, field_params,
|
49
|
+
@prototype = SanitizedPrototype.new(field_type, field_params, hints)
|
47
50
|
end
|
48
51
|
|
49
52
|
attr_reader :prototype
|
@@ -69,16 +72,16 @@ module BinData
|
|
69
72
|
class SanitizedFields < SanitizedParameter
|
70
73
|
include Enumerable
|
71
74
|
|
72
|
-
def initialize(
|
75
|
+
def initialize(hints)
|
73
76
|
@fields = []
|
74
|
-
@
|
77
|
+
@hints = hints
|
75
78
|
end
|
76
79
|
attr_reader :fields
|
77
80
|
|
78
81
|
def add_field(type, name, params)
|
79
82
|
name = nil if name == ""
|
80
83
|
|
81
|
-
@fields << SanitizedField.new(name, type, params, @
|
84
|
+
@fields << SanitizedField.new(name, type, params, @hints)
|
82
85
|
end
|
83
86
|
|
84
87
|
def [](idx)
|
@@ -124,14 +127,14 @@ module BinData
|
|
124
127
|
#----------------------------------------------------------------------------
|
125
128
|
|
126
129
|
class SanitizedChoices < SanitizedParameter
|
127
|
-
def initialize(choices,
|
130
|
+
def initialize(choices, hints)
|
128
131
|
@choices = {}
|
129
132
|
choices.each_pair do |key, val|
|
130
133
|
if SanitizedParameter === val
|
131
134
|
prototype = val
|
132
135
|
else
|
133
136
|
type, param = val
|
134
|
-
prototype = SanitizedPrototype.new(type, param,
|
137
|
+
prototype = SanitizedPrototype.new(type, param, hints)
|
135
138
|
end
|
136
139
|
|
137
140
|
if key == :default
|
@@ -183,16 +186,23 @@ module BinData
|
|
183
186
|
if SanitizedParameters === parameters
|
184
187
|
parameters
|
185
188
|
else
|
186
|
-
SanitizedParameters.new(parameters, the_class,
|
189
|
+
SanitizedParameters.new(parameters, the_class, {})
|
187
190
|
end
|
188
191
|
end
|
189
192
|
end
|
190
193
|
|
191
|
-
def initialize(parameters, the_class,
|
194
|
+
def initialize(parameters, the_class, hints)
|
192
195
|
parameters.each_pair { |key, value| self[key.to_sym] = value }
|
193
196
|
|
194
197
|
@the_class = the_class
|
195
|
-
|
198
|
+
|
199
|
+
if hints[:endian]
|
200
|
+
self[:endian] ||= hints[:endian]
|
201
|
+
end
|
202
|
+
|
203
|
+
if hints[:search_prefix]
|
204
|
+
self[:search_prefix] = Array(self[:search_prefix]).concat(Array(hints[:search_prefix]))
|
205
|
+
end
|
196
206
|
|
197
207
|
sanitize!
|
198
208
|
end
|
@@ -235,10 +245,12 @@ module BinData
|
|
235
245
|
end
|
236
246
|
end
|
237
247
|
|
238
|
-
def
|
239
|
-
|
248
|
+
def hints
|
249
|
+
{
|
250
|
+
:endian => self[:endian],
|
251
|
+
:search_prefix => self[:search_prefix],
|
252
|
+
}
|
240
253
|
end
|
241
|
-
attr_writer :endian
|
242
254
|
|
243
255
|
def create_sanitized_endian(endian)
|
244
256
|
if endian == :big
|
@@ -253,19 +265,19 @@ module BinData
|
|
253
265
|
end
|
254
266
|
|
255
267
|
def create_sanitized_params(params, the_class)
|
256
|
-
SanitizedParameters.new(params, the_class,
|
268
|
+
SanitizedParameters.new(params, the_class, hints)
|
257
269
|
end
|
258
270
|
|
259
271
|
def create_sanitized_choices(choices)
|
260
|
-
SanitizedChoices.new(choices,
|
272
|
+
SanitizedChoices.new(choices, hints)
|
261
273
|
end
|
262
274
|
|
263
275
|
def create_sanitized_fields
|
264
|
-
SanitizedFields.new(
|
276
|
+
SanitizedFields.new(hints)
|
265
277
|
end
|
266
278
|
|
267
279
|
def create_sanitized_object_prototype(obj_type, obj_params)
|
268
|
-
SanitizedPrototype.new(obj_type, obj_params,
|
280
|
+
SanitizedPrototype.new(obj_type, obj_params, hints)
|
269
281
|
end
|
270
282
|
|
271
283
|
#---------------
|
data/lib/bindata/string.rb
CHANGED
@@ -56,6 +56,14 @@ module BinData
|
|
56
56
|
mutually_exclusive_parameters :read_length, :length
|
57
57
|
mutually_exclusive_parameters :length, :value
|
58
58
|
|
59
|
+
def initialize_shared_instance
|
60
|
+
if (has_parameter?(:value) || has_parameter?(:asserted_value)) &&
|
61
|
+
! has_parameter?(:read_length)
|
62
|
+
extend WarnNoReadLengthPlugin
|
63
|
+
end
|
64
|
+
super
|
65
|
+
end
|
66
|
+
|
59
67
|
def assign(val)
|
60
68
|
super(binary_string(val))
|
61
69
|
end
|
@@ -141,4 +149,12 @@ module BinData
|
|
141
149
|
pad_byte
|
142
150
|
end
|
143
151
|
end
|
152
|
+
|
153
|
+
# Warns when reading if :value && no :read_length
|
154
|
+
module WarnNoReadLengthPlugin
|
155
|
+
def read_and_return_value(io)
|
156
|
+
warn "#{debug_name} does not have a :read_length parameter - returning empty string"
|
157
|
+
""
|
158
|
+
end
|
159
|
+
end
|
144
160
|
end
|
data/lib/bindata/struct.rb
CHANGED
@@ -42,6 +42,9 @@ module BinData
|
|
42
42
|
# <tt>:endian</tt>:: Either :little or :big. This specifies the default
|
43
43
|
# endian of any numerics in this struct, or in any
|
44
44
|
# nested data objects.
|
45
|
+
# <tt>:search_prefix</tt>:: Allows abbreviated type names. If a type is
|
46
|
+
# unrecognised, then each prefix is applied until
|
47
|
+
# a match is found.
|
45
48
|
#
|
46
49
|
# == Field Parameters
|
47
50
|
#
|
@@ -56,7 +59,7 @@ module BinData
|
|
56
59
|
arg_processor :struct
|
57
60
|
|
58
61
|
mandatory_parameter :fields
|
59
|
-
optional_parameters :endian, :hide
|
62
|
+
optional_parameters :endian, :search_prefix, :hide
|
60
63
|
|
61
64
|
# These reserved words may not be used as field names
|
62
65
|
RESERVED = Hash[*
|
@@ -333,6 +336,7 @@ module BinData
|
|
333
336
|
class StructArgProcessor < BaseArgProcessor
|
334
337
|
def sanitize_parameters!(obj_class, params)
|
335
338
|
sanitize_endian(params)
|
339
|
+
sanitize_search_prefix(params)
|
336
340
|
sanitize_fields(obj_class, params)
|
337
341
|
sanitize_hide(params)
|
338
342
|
end
|
@@ -344,7 +348,18 @@ module BinData
|
|
344
348
|
if params.needs_sanitizing?(:endian)
|
345
349
|
endian = params.create_sanitized_endian(params[:endian])
|
346
350
|
params[:endian] = endian
|
347
|
-
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
def sanitize_search_prefix(params)
|
355
|
+
if params.needs_sanitizing?(:search_prefix)
|
356
|
+
search_prefix = []
|
357
|
+
Array(params[:search_prefix]).each do |prefix|
|
358
|
+
prefix = prefix.to_s.chomp("_")
|
359
|
+
search_prefix << prefix if prefix != ""
|
360
|
+
end
|
361
|
+
|
362
|
+
params[:search_prefix] = search_prefix
|
348
363
|
end
|
349
364
|
end
|
350
365
|
|
data/lib/bindata/version.rb
CHANGED