bindata 2.0.0 → 2.1.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.

@@ -49,55 +49,27 @@ module BinData
49
49
  # from the end of the string. The value will
50
50
  # not be trimmed when writing.
51
51
  class String < BinData::BasePrimitive
52
+ arg_processor :string
52
53
 
53
54
  optional_parameters :read_length, :length, :trim_padding, :pad_front, :pad_left
54
55
  default_parameters :pad_byte => "\0"
55
56
  mutually_exclusive_parameters :read_length, :length
56
57
  mutually_exclusive_parameters :length, :value
57
58
 
58
- class << self
59
-
60
- def sanitize_parameters!(params) #:nodoc:
61
- params.warn_replacement_parameter(:initial_length, :read_length)
62
-
63
- params.warn_renamed_parameter(:pad_char, :pad_byte) # Remove this line in the future
64
-
65
- if params.has_parameter?(:pad_left)
66
- params[:pad_front] = params.delete(:pad_left)
67
- end
68
-
69
- if params.has_parameter?(:pad_byte)
70
- byte = params[:pad_byte]
71
- params[:pad_byte] = sanitized_pad_byte(byte)
72
- end
73
- end
74
-
75
- #-------------
76
- private
77
-
78
- def sanitized_pad_byte(byte)
79
- result = byte.is_a?(Integer) ? byte.chr : byte.to_s
80
- len = result.respond_to?(:bytesize) ? result.bytesize : result.length
81
- if len > 1
82
- raise ArgumentError, ":pad_byte must not contain more than 1 byte"
83
- end
84
- result
85
- end
86
- end
87
-
88
59
  def assign(val)
89
60
  super(binary_string(val))
90
61
  end
91
62
 
92
63
  def snapshot
93
64
  # override to trim padding
94
- result = super
95
- result = clamp_to_length(result)
65
+ snap = super
66
+ snap = clamp_to_length(snap)
96
67
 
97
68
  if get_parameter(:trim_padding)
98
- result = trim_padding(result)
69
+ trim_padding(snap)
70
+ else
71
+ snap
99
72
  end
100
- result
101
73
  end
102
74
 
103
75
  #---------------
@@ -142,4 +114,31 @@ module BinData
142
114
  ""
143
115
  end
144
116
  end
117
+
118
+ class StringArgProcessor < BaseArgProcessor
119
+ def sanitize_parameters!(obj_class, params)
120
+ params.warn_replacement_parameter(:initial_length, :read_length)
121
+ params.must_be_integer(:read_length, :length)
122
+
123
+ if params.has_parameter?(:pad_left)
124
+ params[:pad_front] = params.delete(:pad_left)
125
+ end
126
+
127
+ if params.has_parameter?(:pad_byte)
128
+ byte = params[:pad_byte]
129
+ params[:pad_byte] = sanitized_pad_byte(byte)
130
+ end
131
+ end
132
+
133
+ #-------------
134
+ private
135
+
136
+ def sanitized_pad_byte(byte)
137
+ pad_byte = byte.is_a?(Integer) ? byte.chr : byte.to_s
138
+ if pad_byte.bytesize > 1
139
+ raise ArgumentError, ":pad_byte must not contain more than 1 byte"
140
+ end
141
+ pad_byte
142
+ end
143
+ end
145
144
  end
@@ -3,7 +3,7 @@ require 'bindata/base'
3
3
  module BinData
4
4
 
5
5
  class Base
6
- optional_parameter :onlyif # Used by Struct
6
+ optional_parameter :onlyif, :byte_align # Used by Struct
7
7
  end
8
8
 
9
9
  # A Struct is an ordered collection of named data objects.
@@ -47,10 +47,13 @@ module BinData
47
47
  #
48
48
  # Fields may have have extra parameters as listed below:
49
49
  #
50
- # [<tt>:onlyif</tt>] Used to indicate a data object is optional.
51
- # if +false+, this object will not be included in any
52
- # calls to #read, #write, #num_bytes or #snapshot.
50
+ # [<tt>:onlyif</tt>] Used to indicate a data object is optional.
51
+ # if +false+, this object will not be included in any
52
+ # calls to #read, #write, #num_bytes or #snapshot.
53
+ # [<tt>:byte_align</tt>] This field's rel_offset must be a multiple of
54
+ # <tt>:byte_align</tt>.
53
55
  class Struct < BinData::Base
56
+ arg_processor :struct
54
57
 
55
58
  mandatory_parameter :fields
56
59
  optional_parameters :endian, :hide
@@ -66,78 +69,11 @@ module BinData
66
69
  uniq.collect { |key| [key, true] }.flatten
67
70
  ]
68
71
 
69
- class << self
70
-
71
- def sanitize_parameters!(params) #:nodoc:
72
- sanitize_endian(params)
73
- sanitize_fields(params)
74
- sanitize_hide(params)
75
- end
76
-
77
- #-------------
78
- private
79
-
80
- def sanitize_endian(params)
81
- if params.needs_sanitizing?(:endian)
82
- endian = params.create_sanitized_endian(params[:endian])
83
- params[:endian] = endian
84
- params.endian = endian # sync params[:endian] and params.endian
85
- end
86
- end
87
-
88
- def sanitize_fields(params)
89
- if params.needs_sanitizing?(:fields)
90
- fields = params[:fields]
91
-
92
- params[:fields] = params.create_sanitized_fields
93
- fields.each do |ftype, fname, fparams|
94
- params[:fields].add_field(ftype, fname, fparams)
95
- end
96
-
97
- field_names = sanitized_field_names(params[:fields])
98
- ensure_field_names_are_valid(field_names)
99
- end
100
- end
101
-
102
- def sanitize_hide(params)
103
- if params.needs_sanitizing?(:hide) and params.has_parameter?(:fields)
104
- field_names = sanitized_field_names(params[:fields])
105
- hfield_names = hidden_field_names(params[:hide])
106
-
107
- params[:hide] = (hfield_names & field_names)
108
- end
109
- end
110
-
111
- def sanitized_field_names(sanitized_fields)
112
- sanitized_fields.field_names.compact
113
- end
114
-
115
- def hidden_field_names(hidden)
116
- (hidden || []).collect { |h| h.to_sym }
117
- end
118
-
119
- def ensure_field_names_are_valid(field_names)
120
- reserved_names = RESERVED
121
-
122
- field_names.each do |name|
123
- if self.class.method_defined?(name)
124
- raise NameError.new("Rename field '#{name}' in #{self}, " +
125
- "as it shadows an existing method.", name)
126
- end
127
- if reserved_names.include?(name)
128
- raise NameError.new("Rename field '#{name}' in #{self}, " +
129
- "as it is a reserved name.", name)
130
- end
131
- if field_names.count(name) != 1
132
- raise NameError.new("field '#{name}' in #{self}, " +
133
- "is defined multiple times.", name)
134
- end
135
- end
136
- end
137
- end
138
-
139
72
  def initialize_shared_instance
140
- @field_names = get_parameter(:fields).field_names.freeze
73
+ fields = get_parameter(:fields)
74
+ @field_names = fields.field_names.freeze
75
+ extend ByteAlignPlugin if fields.any_field_has_parameter?(:byte_align)
76
+ define_field_accessors
141
77
  super
142
78
  end
143
79
 
@@ -162,7 +98,7 @@ module BinData
162
98
  snapshot = Snapshot.new
163
99
  field_names.each do |name|
164
100
  obj = find_obj_for_name(name)
165
- snapshot[name] = obj.snapshot if include_obj(obj)
101
+ snapshot[name] = obj.snapshot if include_obj?(obj)
166
102
  end
167
103
  snapshot
168
104
  end
@@ -179,19 +115,6 @@ module BinData
179
115
  end
180
116
  end
181
117
 
182
- def respond_to?(symbol, include_private = false) #:nodoc:
183
- @field_names.include?(base_field_name(symbol)) || super
184
- end
185
-
186
- def method_missing(symbol, *args, &block) #:nodoc:
187
- obj = find_obj_for_name(symbol)
188
- if obj
189
- invoke_field(obj, symbol, args)
190
- else
191
- super
192
- end
193
- end
194
-
195
118
  def debug_name_of(child) #:nodoc:
196
119
  field_name = @field_names[find_index_of(child)]
197
120
  "#{debug_name}.#{field_name}"
@@ -205,12 +128,12 @@ module BinData
205
128
 
206
129
  def do_read(io) #:nodoc:
207
130
  instantiate_all_objs
208
- @field_objs.each { |f| f.do_read(io) if include_obj(f) }
131
+ @field_objs.each { |f| f.do_read(io) if include_obj?(f) }
209
132
  end
210
133
 
211
134
  def do_write(io) #:nodoc
212
135
  instantiate_all_objs
213
- @field_objs.each { |f| f.do_write(io) if include_obj(f) }
136
+ @field_objs.each { |f| f.do_write(io) if include_obj?(f) }
214
137
  end
215
138
 
216
139
  def do_num_bytes #:nodoc:
@@ -242,18 +165,25 @@ module BinData
242
165
  #---------------
243
166
  private
244
167
 
245
- def base_field_name(name)
246
- name.to_s.chomp("=").to_sym
168
+ def define_field_accessors
169
+ get_parameter(:fields).each_with_index do |field, i|
170
+ name = field.name_as_sym
171
+ define_field_accessors_for(name, i) if name
172
+ end
247
173
  end
248
174
 
249
- def invoke_field(obj, symbol, args)
250
- name = symbol.to_s
251
- is_writer = (name[-1, 1] == "=")
252
-
253
- if is_writer
254
- obj.assign(*args)
255
- else
256
- obj
175
+ def define_field_accessors_for(name, index)
176
+ define_singleton_method(name) do
177
+ instantiate_obj_at(index) if @field_objs[index].nil?
178
+ @field_objs[index]
179
+ end
180
+ define_singleton_method("#{name}=") do |*vals|
181
+ instantiate_obj_at(index) if @field_objs[index].nil?
182
+ @field_objs[index].assign(*vals)
183
+ end
184
+ define_singleton_method("#{name}?") do
185
+ instantiate_obj_at(index) if @field_objs[index].nil?
186
+ include_obj?(@field_objs[index])
257
187
  end
258
188
  end
259
189
 
@@ -271,6 +201,10 @@ module BinData
271
201
  end
272
202
  end
273
203
 
204
+ def base_field_name(name)
205
+ name.to_s.sub(/(=|\?)\z/, "").to_sym
206
+ end
207
+
274
208
  def instantiate_all_objs
275
209
  @field_names.each_index { |i| instantiate_obj_at(i) }
276
210
  end
@@ -310,26 +244,25 @@ module BinData
310
244
  end
311
245
 
312
246
  def sum_num_bytes_below_index(index)
313
- sum = 0
314
- (0...index).each do |i|
247
+ (0...index).inject(0) do |sum, i|
315
248
  obj = @field_objs[i]
316
- if include_obj(obj)
249
+ if include_obj?(obj)
317
250
  nbytes = obj.do_num_bytes
318
- sum = (nbytes.is_a?(Integer) ? sum.ceil : sum) + nbytes
251
+ (nbytes.is_a?(Integer) ? sum.ceil : sum) + nbytes
252
+ else
253
+ sum
319
254
  end
320
255
  end
321
-
322
- sum
323
256
  end
324
257
 
325
- def include_obj(obj)
258
+ def include_obj?(obj)
326
259
  not obj.has_parameter?(:onlyif) or obj.eval_parameter(:onlyif)
327
260
  end
328
261
 
329
262
  # A hash that can be accessed via attributes.
330
263
  class Snapshot < ::Hash #:nodoc:
331
264
  def []=(key, value)
332
- super(key, value) unless value.nil?
265
+ super unless value.nil?
333
266
  end
334
267
 
335
268
  def respond_to?(symbol, include_private = false)
@@ -341,4 +274,128 @@ module BinData
341
274
  end
342
275
  end
343
276
  end
277
+
278
+ # Align fields to a multiple of :byte_align
279
+ module ByteAlignPlugin
280
+ def do_read(io)
281
+ initial_offset = io.offset
282
+ instantiate_all_objs
283
+ @field_objs.each do |f|
284
+ if include_obj?(f)
285
+ if align_obj?(f)
286
+ io.seekbytes(bytes_to_align(f, io.offset - initial_offset))
287
+ end
288
+ f.do_read(io)
289
+ end
290
+ end
291
+ end
292
+
293
+ def do_write(io)
294
+ initial_offset = io.offset
295
+ instantiate_all_objs
296
+ @field_objs.each do |f|
297
+ if include_obj?(f)
298
+ if align_obj?(f)
299
+ io.writebytes("\x00" * bytes_to_align(f, io.offset - initial_offset))
300
+ end
301
+ f.do_write(io)
302
+ end
303
+ end
304
+ end
305
+
306
+ def sum_num_bytes_below_index(index)
307
+ sum = 0
308
+ (0...@field_objs.length).each do |i|
309
+ obj = @field_objs[i]
310
+ if include_obj?(obj)
311
+ sum = sum.ceil + bytes_to_align(obj, sum.ceil) if align_obj?(obj)
312
+
313
+ break if i >= index
314
+
315
+ nbytes = obj.do_num_bytes
316
+ sum = (nbytes.is_a?(Integer) ? sum.ceil : sum) + nbytes
317
+ end
318
+ end
319
+
320
+ sum
321
+ end
322
+
323
+ def bytes_to_align(obj, rel_offset)
324
+ align = obj.eval_parameter(:byte_align)
325
+ (align - (rel_offset % align)) % align
326
+ end
327
+
328
+ def align_obj?(obj)
329
+ obj.has_parameter?(:byte_align)
330
+ end
331
+ end
332
+
333
+ class StructArgProcessor < BaseArgProcessor
334
+ def sanitize_parameters!(obj_class, params)
335
+ sanitize_endian(params)
336
+ sanitize_fields(obj_class, params)
337
+ sanitize_hide(params)
338
+ end
339
+
340
+ #-------------
341
+ private
342
+
343
+ def sanitize_endian(params)
344
+ if params.needs_sanitizing?(:endian)
345
+ endian = params.create_sanitized_endian(params[:endian])
346
+ params[:endian] = endian
347
+ params.endian = endian # sync params[:endian] and params.endian
348
+ end
349
+ end
350
+
351
+ def sanitize_fields(obj_class, params)
352
+ if params.needs_sanitizing?(:fields)
353
+ fields = params[:fields]
354
+
355
+ params[:fields] = params.create_sanitized_fields
356
+ fields.each do |ftype, fname, fparams|
357
+ params[:fields].add_field(ftype, fname, fparams)
358
+ end
359
+
360
+ field_names = sanitized_field_names(params[:fields])
361
+ ensure_field_names_are_valid(obj_class, field_names)
362
+ end
363
+ end
364
+
365
+ def sanitize_hide(params)
366
+ if params.needs_sanitizing?(:hide) and params.has_parameter?(:fields)
367
+ field_names = sanitized_field_names(params[:fields])
368
+ hfield_names = hidden_field_names(params[:hide])
369
+
370
+ params[:hide] = (hfield_names & field_names)
371
+ end
372
+ end
373
+
374
+ def sanitized_field_names(sanitized_fields)
375
+ sanitized_fields.field_names.compact
376
+ end
377
+
378
+ def hidden_field_names(hidden)
379
+ (hidden || []).collect { |h| h.to_sym }
380
+ end
381
+
382
+ def ensure_field_names_are_valid(obj_class, field_names)
383
+ reserved_names = BinData::Struct::RESERVED
384
+
385
+ field_names.each do |name|
386
+ if obj_class.method_defined?(name)
387
+ raise NameError.new("Rename field '#{name}' in #{obj_class}, " +
388
+ "as it shadows an existing method.", name)
389
+ end
390
+ if reserved_names.include?(name)
391
+ raise NameError.new("Rename field '#{name}' in #{obj_class}, " +
392
+ "as it is a reserved name.", name)
393
+ end
394
+ if field_names.count(name) != 1
395
+ raise NameError.new("field '#{name}' in #{obj_class}, " +
396
+ "is defined multiple times.", name)
397
+ end
398
+ end
399
+ end
400
+ end
344
401
  end