bindata 1.1.0 → 1.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.
- data/ChangeLog +7 -0
- data/README +32 -1167
- data/lib/bindata.rb +3 -3
- data/lib/bindata/array.rb +5 -6
- data/lib/bindata/base.rb +40 -58
- data/lib/bindata/base_primitive.rb +7 -11
- data/lib/bindata/bits.rb +47 -44
- data/lib/bindata/choice.rb +7 -11
- data/lib/bindata/deprecated.rb +17 -2
- data/lib/bindata/dsl.rb +332 -0
- data/lib/bindata/float.rb +48 -50
- data/lib/bindata/int.rb +66 -88
- data/lib/bindata/params.rb +112 -59
- data/lib/bindata/primitive.rb +8 -88
- data/lib/bindata/record.rb +11 -99
- data/lib/bindata/registry.rb +16 -3
- data/lib/bindata/rest.rb +1 -1
- data/lib/bindata/sanitize.rb +71 -53
- data/lib/bindata/skip.rb +2 -1
- data/lib/bindata/string.rb +3 -3
- data/lib/bindata/stringz.rb +1 -1
- data/lib/bindata/struct.rb +21 -20
- data/lib/bindata/trace.rb +8 -0
- data/lib/bindata/wrapper.rb +13 -69
- data/manual.haml +2 -2
- data/spec/array_spec.rb +1 -1
- data/spec/base_primitive_spec.rb +4 -4
- data/spec/base_spec.rb +19 -6
- data/spec/bits_spec.rb +5 -1
- data/spec/choice_spec.rb +13 -2
- data/spec/deprecated_spec.rb +31 -0
- data/spec/example.rb +5 -1
- data/spec/io_spec.rb +2 -4
- data/spec/lazy_spec.rb +10 -5
- data/spec/primitive_spec.rb +13 -5
- data/spec/record_spec.rb +149 -45
- data/spec/registry_spec.rb +18 -6
- data/spec/spec_common.rb +31 -6
- data/spec/string_spec.rb +0 -1
- data/spec/stringz_spec.rb +4 -4
- data/spec/struct_spec.rb +2 -2
- data/spec/system_spec.rb +26 -19
- data/spec/wrapper_spec.rb +52 -4
- data/tasks/manual.rake +1 -1
- data/tasks/pkg.rake +13 -0
- metadata +121 -46
- data/TODO +0 -3
data/lib/bindata/dsl.rb
ADDED
@@ -0,0 +1,332 @@
|
|
1
|
+
module BinData
|
2
|
+
module DSLMixin
|
3
|
+
def self.included(base) #:nodoc:
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
# Defines the DSL Parser for this BinData object. Allowed +args+ are:
|
9
|
+
#
|
10
|
+
# [<tt>:only_one_field</tt>] Only one field may be declared.
|
11
|
+
# [<tt>:multiple_fields</tt>] Multiple fields may be declared.
|
12
|
+
# [<tt>:hidden_fields</tt>] Hidden fields are allowed.
|
13
|
+
# [<tt>:sanitize_fields</tt>] Fields are to be sanitized.
|
14
|
+
# [<tt>:mandatory_fieldnames</tt>] Fieldnames are mandatory.
|
15
|
+
# [<tt>:optional_fieldnames</tt>] Fieldnames are optional.
|
16
|
+
# [<tt>:no_fieldnames</tt>] Fieldnames are prohibited.
|
17
|
+
# [<tt>:all_or_none_fieldnames</tt>] All fields must have names, or
|
18
|
+
# none may have names.
|
19
|
+
def dsl_parser(*args)
|
20
|
+
@dsl_parser ||= DSLParser.new(self, *args)
|
21
|
+
end
|
22
|
+
|
23
|
+
def method_missing(symbol, *args, &block) #:nodoc:
|
24
|
+
dsl_parser.__send__(symbol, *args, &block)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# An array containing a field definition of the form
|
29
|
+
# expected by BinData::Struct.
|
30
|
+
class UnSanitizedField < ::Array
|
31
|
+
def initialize(type, name, params)
|
32
|
+
super()
|
33
|
+
self << type << name << params
|
34
|
+
end
|
35
|
+
def type
|
36
|
+
self[0]
|
37
|
+
end
|
38
|
+
def name
|
39
|
+
self[1]
|
40
|
+
end
|
41
|
+
def params
|
42
|
+
self[2]
|
43
|
+
end
|
44
|
+
def to_type_params
|
45
|
+
[self.type, self.params]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class UnSanitizedFields < ::Array
|
50
|
+
def field_names
|
51
|
+
collect { |f| f.name }
|
52
|
+
end
|
53
|
+
|
54
|
+
def add_field(type, name, params, endian)
|
55
|
+
normalized_endian = endian.respond_to?(:endian) ? endian.endian : endian
|
56
|
+
normalized_type = RegisteredClasses.normalize_name(type, normalized_endian)
|
57
|
+
self << UnSanitizedField.new(normalized_type, name, params)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# A DSLParser parses and accumulates field definitions of the form
|
62
|
+
#
|
63
|
+
# type name, params
|
64
|
+
#
|
65
|
+
# where:
|
66
|
+
# * +type+ is the under_scored name of a registered type
|
67
|
+
# * +name+ is the (possible optional) name of the field
|
68
|
+
# * +params+ is a hash containing any parameters
|
69
|
+
#
|
70
|
+
class DSLParser
|
71
|
+
def initialize(the_class, *options)
|
72
|
+
@the_class = the_class
|
73
|
+
|
74
|
+
@options = parent_options_plus_these(options)
|
75
|
+
|
76
|
+
@endian = parent_attribute(:endian, nil)
|
77
|
+
|
78
|
+
if option?(:hidden_fields)
|
79
|
+
@hide = parent_attribute(:hide, []).dup
|
80
|
+
end
|
81
|
+
|
82
|
+
if option?(:sanitize_fields)
|
83
|
+
fields = parent_attribute(:fields, nil)
|
84
|
+
@fields = Sanitizer.new.create_sanitized_fields(fields)
|
85
|
+
else
|
86
|
+
fields = parent_attribute(:fields, UnSanitizedFields.new)
|
87
|
+
@fields = fields.dup
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
attr_reader :options
|
92
|
+
|
93
|
+
def endian(endian = nil)
|
94
|
+
if endian.nil?
|
95
|
+
@endian
|
96
|
+
elsif endian.respond_to? :endian
|
97
|
+
@endian = endian
|
98
|
+
elsif [:little, :big].include?(endian)
|
99
|
+
@endian = Sanitizer.new.create_sanitized_endian(endian)
|
100
|
+
else
|
101
|
+
dsl_raise ArgumentError, "unknown value for endian '#{endian}'"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def hide(*args)
|
106
|
+
if option?(:hidden_fields)
|
107
|
+
@hide.concat(args.collect { |name| name.to_s })
|
108
|
+
@hide
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def fields
|
113
|
+
@fields
|
114
|
+
end
|
115
|
+
|
116
|
+
def field
|
117
|
+
@fields[0]
|
118
|
+
end
|
119
|
+
|
120
|
+
def to_struct_params
|
121
|
+
result = {:fields => fields}
|
122
|
+
if not endian.nil?
|
123
|
+
result[:endian] = endian
|
124
|
+
end
|
125
|
+
if option?(:hidden_fields) and not hide.empty?
|
126
|
+
result[:hide] = hide
|
127
|
+
end
|
128
|
+
|
129
|
+
result
|
130
|
+
end
|
131
|
+
|
132
|
+
def method_missing(symbol, *args, &block) #:nodoc:
|
133
|
+
type = symbol
|
134
|
+
name = name_from_field_declaration(args)
|
135
|
+
params = params_from_field_declaration(type, args, &block)
|
136
|
+
|
137
|
+
append_field(type, name, params)
|
138
|
+
end
|
139
|
+
|
140
|
+
#-------------
|
141
|
+
private
|
142
|
+
|
143
|
+
def dsl_raise(exception, message)
|
144
|
+
backtrace = caller
|
145
|
+
backtrace.shift while %r{bindata/dsl.rb} =~ backtrace.first
|
146
|
+
|
147
|
+
raise exception, message + " in #{@the_class}", backtrace
|
148
|
+
end
|
149
|
+
|
150
|
+
def option?(opt)
|
151
|
+
@options.include?(opt)
|
152
|
+
end
|
153
|
+
|
154
|
+
def parent_attribute(attr, default = nil)
|
155
|
+
parent = @the_class.superclass.respond_to?(:dsl_parser) ? @the_class.superclass.dsl_parser : nil
|
156
|
+
if parent and parent.respond_to?(attr)
|
157
|
+
parent.send(attr)
|
158
|
+
else
|
159
|
+
default
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def parent_options_plus_these(options)
|
164
|
+
result = parent_attribute(:options, []).dup
|
165
|
+
|
166
|
+
mutexes = [
|
167
|
+
[:only_one_field, :multiple_fields],
|
168
|
+
[:mandatory_fieldnames, :optional_fieldnames, :no_fieldnames, :all_or_none_fieldnames]
|
169
|
+
]
|
170
|
+
|
171
|
+
options.each do |opt|
|
172
|
+
mutexes.each do |mutex|
|
173
|
+
if mutex.include?(opt)
|
174
|
+
result -= mutex
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
result << opt
|
179
|
+
end
|
180
|
+
|
181
|
+
result
|
182
|
+
end
|
183
|
+
|
184
|
+
def name_from_field_declaration(args)
|
185
|
+
name, params = args
|
186
|
+
name = nil if name.is_a?(Hash)
|
187
|
+
|
188
|
+
name.to_s
|
189
|
+
end
|
190
|
+
|
191
|
+
def params_from_field_declaration(type, args, &block)
|
192
|
+
params = params_from_args(args)
|
193
|
+
|
194
|
+
if block_given? and BlockParsers.has_key?(type)
|
195
|
+
params.merge(BlockParsers[type].extract_params(endian, &block))
|
196
|
+
else
|
197
|
+
params
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def params_from_args(args)
|
202
|
+
name, params = args
|
203
|
+
params = name if name.is_a?(Hash)
|
204
|
+
|
205
|
+
params || {}
|
206
|
+
end
|
207
|
+
|
208
|
+
def append_field(type, name, params)
|
209
|
+
if too_many_fields?
|
210
|
+
dsl_raise SyntaxError, "attempting to wrap more than one type"
|
211
|
+
end
|
212
|
+
|
213
|
+
ensure_valid_name(name)
|
214
|
+
|
215
|
+
fields.add_field(type, name, params, endian)
|
216
|
+
rescue UnRegisteredTypeError => err
|
217
|
+
dsl_raise TypeError, "unknown type '#{err.message}'"
|
218
|
+
end
|
219
|
+
|
220
|
+
def ensure_valid_name(name)
|
221
|
+
if must_not_have_a_name_failed?(name)
|
222
|
+
dsl_raise SyntaxError, "field must not have a name"
|
223
|
+
end
|
224
|
+
|
225
|
+
if all_or_none_names_failed?(name)
|
226
|
+
dsl_raise SyntaxError, "fields must either all have names, or none must have names"
|
227
|
+
end
|
228
|
+
|
229
|
+
if must_have_a_name_failed?(name)
|
230
|
+
dsl_raise SyntaxError, "field must have a name"
|
231
|
+
end
|
232
|
+
|
233
|
+
if duplicate_name?(name)
|
234
|
+
dsl_raise SyntaxError, "duplicate field '#{name}'"
|
235
|
+
end
|
236
|
+
|
237
|
+
if name_shadows_method?(name)
|
238
|
+
dsl_raise NameError.new("", name), "field '#{name}' shadows an existing method"
|
239
|
+
end
|
240
|
+
|
241
|
+
if name_is_reserved?(name)
|
242
|
+
dsl_raise NameError.new("", name), "field '#{name}' is a reserved name"
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def too_many_fields?
|
247
|
+
option?(:only_one_field) and not fields.empty?
|
248
|
+
end
|
249
|
+
|
250
|
+
def must_not_have_a_name_failed?(name)
|
251
|
+
option?(:no_fieldnames) and name != ""
|
252
|
+
end
|
253
|
+
|
254
|
+
def must_have_a_name_failed?(name)
|
255
|
+
option?(:mandatory_fieldnames) and name == ""
|
256
|
+
end
|
257
|
+
|
258
|
+
def all_or_none_names_failed?(name)
|
259
|
+
if option?(:all_or_none_fieldnames) and not fields.empty?
|
260
|
+
all_names_blank = fields.field_names.all? { |n| n == "" }
|
261
|
+
no_names_blank = fields.field_names.all? { |n| n != "" }
|
262
|
+
|
263
|
+
(name != "" and all_names_blank) or (name == "" and no_names_blank)
|
264
|
+
else
|
265
|
+
false
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
def duplicate_name?(name)
|
270
|
+
name != "" and fields.field_names.include?(name)
|
271
|
+
end
|
272
|
+
|
273
|
+
def name_shadows_method?(name)
|
274
|
+
name != "" and @the_class.method_defined?(name)
|
275
|
+
end
|
276
|
+
|
277
|
+
def name_is_reserved?(name)
|
278
|
+
name != "" and BinData::Struct::RESERVED.include?(name)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
class StructBlockParser
|
283
|
+
def self.extract_params(endian, &block)
|
284
|
+
parser = DSLParser.new(BinData::Struct, :multiple_fields, :optional_fieldnames, :hidden_fields)
|
285
|
+
parser.endian endian
|
286
|
+
parser.instance_eval(&block)
|
287
|
+
|
288
|
+
parser.to_struct_params
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
class ArrayBlockParser
|
293
|
+
def self.extract_params(endian, &block)
|
294
|
+
parser = DSLParser.new(BinData::Array, :multiple_fields, :optional_fieldnames)
|
295
|
+
parser.endian endian
|
296
|
+
parser.instance_eval(&block)
|
297
|
+
|
298
|
+
if parser.fields.length == 1
|
299
|
+
{:type => parser.field.to_type_params}
|
300
|
+
else
|
301
|
+
{:type => [:struct, parser.to_struct_params]}
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
class ChoiceBlockParser
|
307
|
+
def self.extract_params(endian, &block)
|
308
|
+
parser = DSLParser.new(BinData::Choice, :multiple_fields, :all_or_none_fieldnames)
|
309
|
+
parser.endian endian
|
310
|
+
parser.instance_eval(&block)
|
311
|
+
|
312
|
+
if all_blank?(parser.fields.field_names)
|
313
|
+
{:choices => parser.fields.collect { |f| f.to_type_params }}
|
314
|
+
else
|
315
|
+
choices = {}
|
316
|
+
parser.fields.each { |f| choices[f.name] = f.to_type_params }
|
317
|
+
{:choices => choices}
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
def self.all_blank?(array)
|
322
|
+
array.all? { |el| el == "" }
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
BlockParsers = {
|
327
|
+
:struct => StructBlockParser,
|
328
|
+
:array => ArrayBlockParser,
|
329
|
+
:choice => ChoiceBlockParser,
|
330
|
+
}
|
331
|
+
end
|
332
|
+
end
|
data/lib/bindata/float.rb
CHANGED
@@ -5,82 +5,80 @@ module BinData
|
|
5
5
|
# The float is defined by precision and endian.
|
6
6
|
|
7
7
|
module FloatingPoint #:nodoc: all
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
8
|
+
class << self
|
9
|
+
def define_methods(float_class, precision, endian)
|
10
|
+
float_class.module_eval <<-END
|
11
|
+
def _do_num_bytes
|
12
|
+
#{create_num_bytes_code(precision)}
|
13
|
+
end
|
14
|
+
|
15
|
+
#---------------
|
16
|
+
private
|
17
|
+
|
18
|
+
def sensible_default
|
19
|
+
0.0
|
20
|
+
end
|
21
|
+
|
22
|
+
def value_to_binary_string(val)
|
23
|
+
#{create_to_binary_s_code(precision, endian)}
|
24
|
+
end
|
25
|
+
|
26
|
+
def read_and_return_value(io)
|
27
|
+
#{create_read_code(precision, endian)}
|
28
|
+
end
|
29
|
+
END
|
23
30
|
end
|
24
31
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
def self.create_to_binary_s_code(precision, endian)
|
29
|
-
if precision == :single
|
30
|
-
pack = (endian == :little) ? 'e' : 'g'
|
31
|
-
else # double_precision
|
32
|
-
pack = (endian == :little) ? 'E' : 'G'
|
32
|
+
def create_num_bytes_code(precision)
|
33
|
+
(precision == :single) ? 4 : 8
|
33
34
|
end
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
36
|
+
def create_read_code(precision, endian)
|
37
|
+
if precision == :single
|
38
|
+
unpack = (endian == :little) ? 'e' : 'g'
|
39
|
+
nbytes = 4
|
40
|
+
else # double_precision
|
41
|
+
unpack = (endian == :little) ? 'E' : 'G'
|
42
|
+
nbytes = 8
|
42
43
|
end
|
43
44
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
def sensible_default
|
48
|
-
0.0
|
49
|
-
end
|
45
|
+
"io.readbytes(#{nbytes}).unpack('#{unpack}').at(0)"
|
46
|
+
end
|
50
47
|
|
51
|
-
|
52
|
-
|
48
|
+
def create_to_binary_s_code(precision, endian)
|
49
|
+
if precision == :single
|
50
|
+
pack = (endian == :little) ? 'e' : 'g'
|
51
|
+
else # double_precision
|
52
|
+
pack = (endian == :little) ? 'E' : 'G'
|
53
53
|
end
|
54
54
|
|
55
|
-
|
56
|
-
|
57
|
-
end
|
58
|
-
END
|
55
|
+
"[val].pack('#{pack}')"
|
56
|
+
end
|
59
57
|
end
|
60
58
|
end
|
61
59
|
|
62
60
|
|
63
61
|
# Single precision floating point number in little endian format
|
64
62
|
class FloatLe < BinData::BasePrimitive
|
65
|
-
|
66
|
-
FloatingPoint.
|
63
|
+
register_self
|
64
|
+
FloatingPoint.define_methods(self, :single, :little)
|
67
65
|
end
|
68
66
|
|
69
67
|
# Single precision floating point number in big endian format
|
70
68
|
class FloatBe < BinData::BasePrimitive
|
71
|
-
|
72
|
-
FloatingPoint.
|
69
|
+
register_self
|
70
|
+
FloatingPoint.define_methods(self, :single, :big)
|
73
71
|
end
|
74
72
|
|
75
73
|
# Double precision floating point number in little endian format
|
76
74
|
class DoubleLe < BinData::BasePrimitive
|
77
|
-
|
78
|
-
FloatingPoint.
|
75
|
+
register_self
|
76
|
+
FloatingPoint.define_methods(self, :double, :little)
|
79
77
|
end
|
80
78
|
|
81
79
|
# Double precision floating point number in big endian format
|
82
80
|
class DoubleBe < BinData::BasePrimitive
|
83
|
-
|
84
|
-
FloatingPoint.
|
81
|
+
register_self
|
82
|
+
FloatingPoint.define_methods(self, :double, :big)
|
85
83
|
end
|
86
84
|
end
|