bindata 0.9.1 → 0.9.2

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.

@@ -45,16 +45,11 @@ module BinData
45
45
  # parameter. A boolean return indicates success
46
46
  # or failure. Any other return is compared to
47
47
  # the value just read in.
48
- class Single < Base
48
+ class Single < BinData::Base
49
49
  # These are the parameters used by this class.
50
50
  optional_parameters :initial_value, :value, :check_value
51
51
  mutually_exclusive_parameters :initial_value, :value
52
52
 
53
- # Single objects don't contain fields so this returns an empty list.
54
- def self.all_possible_field_names(sanitized_params)
55
- []
56
- end
57
-
58
53
  def initialize(params = {}, env = nil)
59
54
  super(params, env)
60
55
  clear
@@ -76,16 +71,6 @@ module BinData
76
71
  true
77
72
  end
78
73
 
79
- # Single objects don't contain fields so this returns an empty list.
80
- def field_names
81
- []
82
- end
83
-
84
- # Returns a snapshot of this data object.
85
- def snapshot
86
- value
87
- end
88
-
89
74
  # To be called after calling #do_read.
90
75
  def done_read
91
76
  @in_read = false
@@ -122,7 +107,7 @@ module BinData
122
107
  current_value = self.value
123
108
  expected = eval_param(:check_value, :value => current_value)
124
109
  if not expected
125
- raise ValidityError, "value not as expected"
110
+ raise ValidityError, "value '#{current_value}' not as expected"
126
111
  elsif current_value != expected and expected != true
127
112
  raise ValidityError, "value is '#{current_value}' but " +
128
113
  "expected '#{expected}'"
@@ -141,6 +126,11 @@ module BinData
141
126
  val_to_str(_value).length
142
127
  end
143
128
 
129
+ # Returns a snapshot of this data object.
130
+ def _snapshot
131
+ value
132
+ end
133
+
144
134
  # The unmodified value of this data object. Note that #value calls this
145
135
  # method. This is so that #value can be overridden in subclasses to
146
136
  # modify the value.
@@ -98,7 +98,7 @@ module BinData
98
98
  @fields ||= []
99
99
 
100
100
  # check that type is known
101
- if lookup(type, endian).nil?
101
+ unless Sanitizer.type_exists?(type, endian)
102
102
  raise TypeError, "unknown type '#{type}' for #{self}", caller
103
103
  end
104
104
 
@@ -125,22 +125,16 @@ module BinData
125
125
 
126
126
  # Returns a sanitized +params+ that is of the form expected
127
127
  # by #initialize.
128
- def sanitize_parameters(params, endian = nil)
128
+ def sanitize_parameters(sanitizer, params)
129
129
  params = params.dup
130
130
 
131
- # possibly override endian
132
- endian = self.endian || endian
133
-
134
131
  hash = {}
135
132
  hash[:fields] = self.fields
136
-
137
- unless endian.nil?
138
- hash[:endian] = endian
139
- end
133
+ hash[:endian] = self.endian unless self.endian.nil?
140
134
 
141
135
  params[:struct_params] = hash
142
136
 
143
- super(params, endian)
137
+ super(sanitizer, params)
144
138
  end
145
139
  end
146
140
 
@@ -45,7 +45,7 @@ module BinData
45
45
  # return the value with all pad_chars trimmed
46
46
  # from the end of the string. The value will
47
47
  # not be trimmed when writing.
48
- class String < Single
48
+ class String < BinData::Single
49
49
 
50
50
  # Register this class
51
51
  register(self.name, self)
@@ -60,7 +60,7 @@ module BinData
60
60
 
61
61
  # Returns a sanitized +params+ that is of the form expected
62
62
  # by #initialize.
63
- def sanitize_parameters(params, *args)
63
+ def sanitize_parameters(sanitizer, params, *args)
64
64
  params = params.dup
65
65
 
66
66
  # warn about deprecated param - remove before releasing 1.0
@@ -79,7 +79,7 @@ module BinData
79
79
  params[:pad_char] = ch
80
80
  end
81
81
 
82
- super(params, *args)
82
+ super(sanitizer, params, *args)
83
83
  end
84
84
  end
85
85
 
@@ -25,7 +25,7 @@ module BinData
25
25
  #
26
26
  # <tt>:max_length</tt>:: The maximum length of the string including the zero
27
27
  # byte.
28
- class Stringz < Single
28
+ class Stringz < BinData::Single
29
29
 
30
30
  # Register this class
31
31
  register(self.name, self)
@@ -15,8 +15,8 @@ module BinData
15
15
  # obj = BinData::Struct.new(:hide => :a,
16
16
  # :fields => [ [:int32le, :a],
17
17
  # [:int16le, :b],
18
- # [:tuple, :nil] ])
19
- # obj.field_names =># ["b", "x", "y", "z"]
18
+ # [:tuple, :s] ])
19
+ # obj.field_names =># ["b", "s"]
20
20
  #
21
21
  #
22
22
  # == Parameters
@@ -27,10 +27,9 @@ module BinData
27
27
  # <tt>:fields</tt>:: An array specifying the fields for this struct.
28
28
  # Each element of the array is of the form [type, name,
29
29
  # params]. Type is a symbol representing a registered
30
- # type. Name is the name of this field. Name may be
31
- # nil as in the example above. Params is an optional
32
- # hash of parameters to pass to this field when
33
- # instantiating it.
30
+ # type. Name is the name of this field. Params is an
31
+ # optional hash of parameters to pass to this field
32
+ # when instantiating it.
34
33
  # <tt>:hide</tt>:: A list of the names of fields that are to be hidden
35
34
  # from the outside world. Hidden fields don't appear
36
35
  # in #snapshot or #field_names but are still accessible
@@ -40,6 +39,13 @@ module BinData
40
39
  # nested data objects.
41
40
  class Struct < BinData::Base
42
41
 
42
+ # These reserved words may not be used as field names
43
+ RESERVED = (::Hash.instance_methods +
44
+ %w{alias and begin break case class def defined do else elsif
45
+ end ensure false for if in module next nil not or redo
46
+ rescue retry return self super then true undef unless until
47
+ when while yield }).uniq
48
+
43
49
  # Register this class
44
50
  register(self.name, self)
45
51
 
@@ -74,7 +80,6 @@ module BinData
74
80
  # note that fields are stored in an instance variable not a class var
75
81
  @hide ||= []
76
82
  args.each do |name|
77
- next if name.nil?
78
83
  @hide << name.to_s
79
84
  end
80
85
  @hide
@@ -86,56 +91,51 @@ module BinData
86
91
  name, params = args
87
92
 
88
93
  type = symbol
89
- name = (name.nil? or name == "") ? nil : name.to_s
94
+ name = name.to_s
90
95
  params ||= {}
91
96
 
92
97
  # note that fields are stored in an instance variable not a class var
93
98
  @fields ||= []
94
99
 
95
100
  # check that type is known
96
- if lookup(type, endian).nil?
101
+ unless Sanitizer.type_exists?(type, endian)
97
102
  raise TypeError, "unknown type '#{type}' for #{self}", caller
98
103
  end
99
104
 
100
- # check that name is okay
101
- if name != nil
102
- # check for duplicate names
103
- @fields.each do |t, n, p|
104
- if n == name
105
- raise SyntaxError, "duplicate field '#{name}' in #{self}", caller
106
- end
105
+ # check for duplicate names
106
+ @fields.each do |t, n, p|
107
+ if n == name
108
+ raise SyntaxError, "duplicate field '#{name}' in #{self}", caller
107
109
  end
110
+ end
108
111
 
109
- # check that name doesn't shadow an existing method
110
- if self.instance_methods.include?(name)
111
- raise NameError.new("", name),
112
- "field '#{name}' shadows an existing method", caller
113
- end
112
+ # check that name doesn't shadow an existing method
113
+ if self.instance_methods.include?(name)
114
+ raise NameError.new("", name),
115
+ "field '#{name}' shadows an existing method", caller
116
+ end
114
117
 
115
- # check that name isn't reserved
116
- if ::Hash.instance_methods.include?(name)
117
- raise NameError.new("", name),
118
- "field '#{name}' is a reserved name", caller
119
- end
118
+ # check that name isn't reserved
119
+ if self::RESERVED.include?(name)
120
+ raise NameError.new("", name),
121
+ "field '#{name}' is a reserved name", caller
120
122
  end
121
123
 
122
124
  # remember this field. These fields will be recalled upon creating
123
125
  # an instance of this class
124
126
  @fields.push([type, name, params])
125
127
  end
126
- def deprecated_hack(params, endian = nil)
128
+ def deprecated_hack(params)
127
129
  params = params.dup
128
130
 
129
131
  # possibly override endian
130
- endian = params[:endian] || self.endian || endian
131
- unless endian.nil?
132
- params[:endian] = endian
133
- end
132
+ endian = params[:endian] || self.endian
133
+ params[:endian] = endian unless endian.nil?
134
134
 
135
135
  params[:fields] = params[:fields] || self.fields
136
136
  params[:hide] = params[:hide] || self.hide
137
137
 
138
- [params, endian]
138
+ params
139
139
  end
140
140
  #
141
141
  #### DEPRECATION HACK to allow inheriting from BinData::Struct
@@ -143,17 +143,17 @@ module BinData
143
143
 
144
144
  # Returns a sanitized +params+ that is of the form expected
145
145
  # by #initialize.
146
- def sanitize_parameters(params, endian = nil)
146
+ def sanitize_parameters(sanitizer, params)
147
147
  #### DEPRECATION HACK to allow inheriting from BinData::Struct
148
148
  #
149
- params, endian = deprecated_hack(params, endian)
149
+ params = deprecated_hack(params)
150
150
  #
151
151
  #### DEPRECATION HACK to allow inheriting from BinData::Struct
152
152
 
153
153
  params = params.dup
154
154
 
155
155
  # possibly override endian
156
- endian = params[:endian] || endian
156
+ endian = params[:endian]
157
157
  if endian != nil
158
158
  unless [:little, :big].include?(endian)
159
159
  raise ArgumentError, "unknown value for endian '#{endian}'"
@@ -163,87 +163,59 @@ module BinData
163
163
  end
164
164
 
165
165
  if params.has_key?(:fields)
166
- # ensure the names of fields are strings and that params is sanitized
167
- all_fields = params[:fields].collect do |ftype, fname, fparams|
168
- fname = fname.nil? ? "" : fname.to_s
169
- klass = lookup(ftype, endian)
170
- raise TypeError, "unknown type '#{ftype}' for #{self}" if klass.nil?
171
- [klass, fname, SanitizedParameters.new(klass, fparams, endian)]
166
+ sanitizer.with_endian(endian) do
167
+ # ensure names of fields are strings and that params is sanitized
168
+ all_fields = params[:fields].collect do |ftype, fname, fparams|
169
+ fname = fname.to_s
170
+ klass, new_params = sanitizer.sanitize(ftype, fparams)
171
+ [klass, fname, new_params]
172
+ end
173
+ params[:fields] = all_fields
172
174
  end
173
- params[:fields] = all_fields
174
175
 
175
176
  # collect all hidden names that correspond to a field name
176
177
  hide = []
177
178
  if params.has_key?(:hide)
178
- hidden = params[:hide] || []
179
- hidden.each do |h|
180
- next if h.nil? or h == ""
181
- h = h.to_s
182
- hide << h if all_fields.find { |k,n,p| n == h }
183
- end
179
+ hidden = (params[:hide] || []).collect { |h| h.to_s }
180
+ all_field_names = params[:fields].collect { |k,n,p| n }
181
+ hide = hidden & all_field_names
184
182
  end
185
183
  params[:hide] = hide
186
184
  end
187
185
 
188
186
  # obtain SanitizedParameters
189
- params = super(params, endian)
187
+ params = super(sanitizer, params)
190
188
 
191
189
  # now params are sanitized, check that parameter names are okay
192
190
 
193
191
  field_names = []
194
192
  instance_methods = self.instance_methods
195
- reserved_names = ::Hash.instance_methods
193
+ reserved_names = RESERVED
196
194
 
197
195
  params[:fields].each do |fklass, fname, fparams|
198
196
 
199
197
  # check that name doesn't shadow an existing method
200
198
  if instance_methods.include?(fname)
201
- raise NameError.new("field '#{fname}' shadows an existing method in #{self}. Rename it.", fname)
199
+ raise NameError.new("Rename field '#{fname}' in #{self}, " +
200
+ "as it shadows an existing method.", fname)
202
201
  end
203
202
 
204
203
  # check that name isn't reserved
205
204
  if reserved_names.include?(fname)
206
- raise NameError.new("field '#{fname}' is a reserved name in #{self}. Rename it.", fname)
205
+ raise NameError.new("Rename field '#{fname}' in #{self}, " +
206
+ "as it is a reserved name.", fname)
207
207
  end
208
208
 
209
- if fname == ""
210
- fklass.all_possible_field_names(fparams).each do |name|
211
- if field_names.include?(name)
212
- raise NameError.new("field '#{name}' is defined multiple times in #{self}.", name)
213
- end
214
- field_names << name
215
- end
216
- else
217
- if field_names.include?(fname)
218
- raise NameError.new("field '#{fname}' is defined multiple times in #{self}.", fname)
219
- end
220
- field_names << fname
209
+ # check for multiple definitions
210
+ if field_names.include?(fname)
211
+ raise NameError.new("field '#{fname}' in #{self}, " +
212
+ "is defined multiple times.", fname)
221
213
  end
222
- end
223
214
 
224
- params
225
- end
226
-
227
- # Returns a list of the names of all possible field names for a Struct
228
- # created with +sanitized_params+. Hidden names will not be included
229
- # in the returned list.
230
- def all_possible_field_names(sanitized_params)
231
- unless SanitizedParameters === sanitized_params
232
- raise ArgumentError, "parameters aren't sanitized"
215
+ field_names << fname
233
216
  end
234
217
 
235
- hidden_names = sanitized_params[:hide]
236
-
237
- names = []
238
- sanitized_params[:fields].each do |fklass, fname, fparams|
239
- if fname == ""
240
- names.concat(fklass.all_possible_field_names(fparams))
241
- else
242
- names << fname unless hidden_names.include?(fname)
243
- end
244
- end
245
-
246
- names
218
+ params
247
219
  end
248
220
  end
249
221
 
@@ -255,19 +227,19 @@ module BinData
255
227
  def initialize(params = {}, env = nil)
256
228
  super(params, env)
257
229
 
258
- # create instances of the fields
259
- @fields = param(:fields).collect do |fklass, fname, fparams|
260
- [fname, fklass.new(fparams, create_env)]
261
- end
230
+ # extract field names but don't instantiate the fields
231
+ @field_names = param(:fields).collect { |k, n, p| n }
232
+ @field_objs = []
262
233
  end
263
234
 
264
235
  # Clears the field represented by +name+. If no +name+
265
236
  # is given, clears all fields in the struct.
266
237
  def clear(name = nil)
267
238
  if name.nil?
268
- bindata_objects.each { |f| f.clear }
239
+ @field_objs.each { |f| f.clear unless f.nil? }
269
240
  else
270
- find_obj_for_name(name.to_s).clear
241
+ obj = find_obj_for_name(name.to_s)
242
+ obj.clear unless obj.nil?
271
243
  end
272
244
  end
273
245
 
@@ -275,10 +247,13 @@ module BinData
275
247
  # is given, returns whether all fields are clear.
276
248
  def clear?(name = nil)
277
249
  if name.nil?
278
- bindata_objects.each { |f| return false if not f.clear? }
250
+ @field_objs.each do |f|
251
+ return false unless f.nil? or f.clear?
252
+ end
279
253
  true
280
254
  else
281
- find_obj_for_name(name.to_s).clear?
255
+ obj = find_obj_for_name(name.to_s)
256
+ obj.nil? ? true : obj.clear?
282
257
  end
283
258
  end
284
259
 
@@ -295,65 +270,47 @@ module BinData
295
270
  # collect field names
296
271
  names = []
297
272
  hidden = param(:hide)
298
- @fields.each do |name, obj|
299
- if name != ""
300
- if include_hidden or not hidden.include?(name)
301
- names << name
302
- end
303
- else
304
- names.concat(obj.field_names)
273
+ @field_names.each do |name|
274
+ if include_hidden or not hidden.include?(name)
275
+ names << name
305
276
  end
306
277
  end
307
278
  names
308
279
  end
309
280
 
310
- # Returns a snapshot of this struct as a hash.
311
- def snapshot
312
- hash = Snapshot.new
313
- field_names.each do |name|
314
- hash[name] = find_obj_for_name(name).snapshot
315
- end
316
- hash
317
- end
318
-
319
281
  # To be called after calling #read.
320
282
  def done_read
321
- bindata_objects.each { |f| f.done_read }
283
+ @field_objs.each { |f| f.done_read unless f.nil? }
322
284
  end
323
285
 
324
286
  # Returns the data object that stores values for +name+.
325
287
  def find_obj_for_name(name)
326
- @fields.each do |n, o|
327
- if n == name
328
- return o
329
- elsif n == "" and o.field_names.include?(name)
330
- return o.find_obj_for_name(name)
331
- end
288
+ idx = @field_names.index(name)
289
+ if idx
290
+ instantiate_obj(idx)
291
+ @field_objs[idx]
292
+ else
293
+ nil
332
294
  end
333
- nil
334
295
  end
335
296
 
336
297
  def offset_of(field)
337
- field_name = field.to_s
338
- offset = 0
339
- @fields.each do |name, obj|
340
- if name != ""
341
- break if name == field_name
342
- this_offset = obj.do_num_bytes
343
- if ::Float === offset and ::Integer === this_offset
344
- offset = offset.ceil
345
- end
346
- offset += this_offset
347
- elsif obj.field_names.include?(field_name)
348
- this_offset = obj.offset_of(field)
298
+ idx = @field_names.index(field.to_s)
299
+ if idx
300
+ instantiate_all
301
+
302
+ offset = 0
303
+ (0...idx).each do |i|
304
+ this_offset = @field_objs[i].do_num_bytes
349
305
  if ::Float === offset and ::Integer === this_offset
350
306
  offset = offset.ceil
351
307
  end
352
308
  offset += this_offset
353
- break
354
309
  end
310
+ offset
311
+ else
312
+ nil
355
313
  end
356
- offset
357
314
  end
358
315
 
359
316
  # Override to include field names
@@ -387,14 +344,29 @@ module BinData
387
344
  #---------------
388
345
  private
389
346
 
347
+ # Instantiates all fields.
348
+ def instantiate_all
349
+ (0...@field_names.length).each { |idx| instantiate_obj(idx) }
350
+ end
351
+
352
+ # Instantiates the field object at position +idx+.
353
+ def instantiate_obj(idx)
354
+ if @field_objs[idx].nil?
355
+ fklass, fname, fparams = param(:fields)[idx]
356
+ @field_objs[idx] = fklass.new(fparams, create_env)
357
+ end
358
+ end
359
+
390
360
  # Reads the values for all fields in this object from +io+.
391
361
  def _do_read(io)
392
- bindata_objects.each { |f| f.do_read(io) }
362
+ instantiate_all
363
+ @field_objs.each { |f| f.do_read(io) }
393
364
  end
394
365
 
395
366
  # Writes the values for all fields in this object to +io+.
396
367
  def _do_write(io)
397
- bindata_objects.each { |f| f.do_write(io) }
368
+ instantiate_all
369
+ @field_objs.each { |f| f.do_write(io) }
398
370
  end
399
371
 
400
372
  # Returns the number of bytes it will take to write the field represented
@@ -402,15 +374,22 @@ module BinData
402
374
  # to write all fields.
403
375
  def _do_num_bytes(name)
404
376
  if name.nil?
405
- (bindata_objects.inject(0) { |sum, f| sum + f.do_num_bytes }).ceil
377
+ instantiate_all
378
+ (@field_objs.inject(0) { |sum, f| sum + f.do_num_bytes }).ceil
406
379
  else
407
- find_obj_for_name(name.to_s).do_num_bytes
380
+ obj = find_obj_for_name(name.to_s)
381
+ obj.nil? ? 0 : obj.do_num_bytes
408
382
  end
409
383
  end
410
384
 
411
- # Returns a list of all the bindata objects for this struct.
412
- def bindata_objects
413
- @fields.collect { |f| f[1] }
385
+ # Returns a snapshot of this struct as a hash.
386
+ def _snapshot
387
+ hash = Snapshot.new
388
+ field_names.each do |name|
389
+ ss = find_obj_for_name(name).snapshot
390
+ hash[name] = ss unless ss.nil?
391
+ end
392
+ hash
414
393
  end
415
394
  end
416
395
  end