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.

Files changed (47) hide show
  1. data/ChangeLog +7 -0
  2. data/README +32 -1167
  3. data/lib/bindata.rb +3 -3
  4. data/lib/bindata/array.rb +5 -6
  5. data/lib/bindata/base.rb +40 -58
  6. data/lib/bindata/base_primitive.rb +7 -11
  7. data/lib/bindata/bits.rb +47 -44
  8. data/lib/bindata/choice.rb +7 -11
  9. data/lib/bindata/deprecated.rb +17 -2
  10. data/lib/bindata/dsl.rb +332 -0
  11. data/lib/bindata/float.rb +48 -50
  12. data/lib/bindata/int.rb +66 -88
  13. data/lib/bindata/params.rb +112 -59
  14. data/lib/bindata/primitive.rb +8 -88
  15. data/lib/bindata/record.rb +11 -99
  16. data/lib/bindata/registry.rb +16 -3
  17. data/lib/bindata/rest.rb +1 -1
  18. data/lib/bindata/sanitize.rb +71 -53
  19. data/lib/bindata/skip.rb +2 -1
  20. data/lib/bindata/string.rb +3 -3
  21. data/lib/bindata/stringz.rb +1 -1
  22. data/lib/bindata/struct.rb +21 -20
  23. data/lib/bindata/trace.rb +8 -0
  24. data/lib/bindata/wrapper.rb +13 -69
  25. data/manual.haml +2 -2
  26. data/spec/array_spec.rb +1 -1
  27. data/spec/base_primitive_spec.rb +4 -4
  28. data/spec/base_spec.rb +19 -6
  29. data/spec/bits_spec.rb +5 -1
  30. data/spec/choice_spec.rb +13 -2
  31. data/spec/deprecated_spec.rb +31 -0
  32. data/spec/example.rb +5 -1
  33. data/spec/io_spec.rb +2 -4
  34. data/spec/lazy_spec.rb +10 -5
  35. data/spec/primitive_spec.rb +13 -5
  36. data/spec/record_spec.rb +149 -45
  37. data/spec/registry_spec.rb +18 -6
  38. data/spec/spec_common.rb +31 -6
  39. data/spec/string_spec.rb +0 -1
  40. data/spec/stringz_spec.rb +4 -4
  41. data/spec/struct_spec.rb +2 -2
  42. data/spec/system_spec.rb +26 -19
  43. data/spec/wrapper_spec.rb +52 -4
  44. data/tasks/manual.rake +1 -1
  45. data/tasks/pkg.rake +13 -0
  46. metadata +121 -46
  47. data/TODO +0 -3
@@ -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
@@ -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
- def self.create_float_methods(float_class, precision, endian)
9
- read = create_read_code(precision, endian)
10
- to_binary_s = create_to_binary_s_code(precision, endian)
11
- nbytes = (precision == :single) ? 4 : 8
12
-
13
- define_methods(float_class, nbytes, read, to_binary_s)
14
- end
15
-
16
- def self.create_read_code(precision, endian)
17
- if precision == :single
18
- unpack = (endian == :little) ? 'e' : 'g'
19
- nbytes = 4
20
- else # double_precision
21
- unpack = (endian == :little) ? 'E' : 'G'
22
- nbytes = 8
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
- "io.readbytes(#{nbytes}).unpack('#{unpack}').at(0)"
26
- end
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
- "[val].pack('#{pack}')"
36
- end
37
-
38
- def self.define_methods(float_class, nbytes, read, to_binary_s)
39
- float_class.module_eval <<-END
40
- def _do_num_bytes
41
- #{nbytes}
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
- private
46
-
47
- def sensible_default
48
- 0.0
49
- end
45
+ "io.readbytes(#{nbytes}).unpack('#{unpack}').at(0)"
46
+ end
50
47
 
51
- def value_to_binary_string(val)
52
- #{to_binary_s}
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
- def read_and_return_value(io)
56
- #{read}
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
- register(self.name, self)
66
- FloatingPoint.create_float_methods(self, :single, :little)
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
- register(self.name, self)
72
- FloatingPoint.create_float_methods(self, :single, :big)
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
- register(self.name, self)
78
- FloatingPoint.create_float_methods(self, :double, :little)
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
- register(self.name, self)
84
- FloatingPoint.create_float_methods(self, :double, :big)
81
+ register_self
82
+ FloatingPoint.define_methods(self, :double, :big)
85
83
  end
86
84
  end