bindata 1.8.2 → 1.8.3

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.

@@ -1,5 +1,13 @@
1
1
  = BinData Changelog
2
2
 
3
+ == Version 1.8.3 (2014-04-16)
4
+
5
+ * #eval_parameters can now call private methods. Requested by Ole Rasmussen.
6
+ * Can now determine state of :onlyif fields. Requested by Ole Rasmussen.
7
+ * Renamed #offset to #abs_offset for clarity. #offset is now deprecated.
8
+ * Support :byte_align for fields in structs. Requested by Igor Yamolov.
9
+ * Added bit fields with dynamic length. Requested by Jacob Dam.
10
+
3
11
  == Version 1.8.2 (2014-02-02)
4
12
 
5
13
  * Virtual fields can now have values.
@@ -59,7 +59,7 @@ class Gzip
59
59
 
60
60
  attr_accessor :compressed
61
61
  def_delegators :@header, :file_name=, :file_name
62
- def_delegators :@header, :comment=, :comment
62
+ def_delegators :@header, :comment=, :comment, :comment?
63
63
  def_delegators :@header, :compression_method
64
64
  def_delegators :@footer, :crc32, :uncompressed_size
65
65
 
@@ -153,7 +153,7 @@ if __FILE__ == $0
153
153
  g.uncompressed_size,
154
154
  ratio,
155
155
  g.file_name]
156
- puts "Comment: #{g.comment}" if g.comment != ""
156
+ puts "Comment: #{g.comment}" if g.comment?
157
157
  puts
158
158
 
159
159
  puts "Executing gzip -l -v"
@@ -233,16 +233,17 @@ module BinData
233
233
  end
234
234
  end
235
235
 
236
- # Returns the offset of this object wrt to its most distant ancestor.
237
- def offset
236
+ # Returns the offset (in bytes) of this object with respect to its most
237
+ # distant ancestor.
238
+ def abs_offset
238
239
  if @parent
239
- @parent.offset + @parent.offset_of(self)
240
+ @parent.abs_offset + @parent.offset_of(self)
240
241
  else
241
242
  0
242
243
  end
243
244
  end
244
245
 
245
- # Returns the offset of this object wrt to its parent.
246
+ # Returns the offset (in bytes) of this object with respect to its parent.
246
247
  def rel_offset
247
248
  if @parent
248
249
  @parent.offset_of(self)
@@ -6,13 +6,11 @@ module BinData
6
6
 
7
7
  module BitField #:nodoc: all
8
8
  class << self
9
- def define_class(nbits, endian, signed = :unsigned)
10
- name = ((signed == :signed ) ? "Sbit" : "Bit") + nbits.to_s
11
- name << "le" if endian == :little
9
+ def define_class(name, nbits, endian, signed = :unsigned)
12
10
  unless BinData.const_defined?(name)
13
11
  BinData.module_eval <<-END
14
12
  class #{name} < BinData::BasePrimitive
15
- BitField.define_methods(self, #{nbits}, :#{endian}, :#{signed})
13
+ BitField.define_methods(self, #{nbits.inspect}, #{endian.inspect}, #{signed.inspect})
16
14
  end
17
15
  END
18
16
  end
@@ -22,27 +20,34 @@ module BinData
22
20
 
23
21
  def define_methods(bit_class, nbits, endian, signed)
24
22
  bit_class.module_eval <<-END
23
+ #{create_params_code(nbits)}
24
+
25
25
  def assign(val)
26
+ #{create_nbits_code(nbits)}
26
27
  #{create_clamp_code(nbits, signed)}
27
28
  super(val)
28
29
  end
29
30
 
30
31
  def do_write(io)
32
+ #{create_nbits_code(nbits)}
31
33
  val = _value
32
- #{create_int2uint_code(nbits) if signed == :signed}
34
+ #{create_int2uint_code(nbits, signed)}
33
35
  io.writebits(val, #{nbits}, :#{endian})
34
36
  end
35
37
 
36
38
  def do_num_bytes
37
- #{nbits / 8.0}
39
+ #{create_nbits_code(nbits)}
40
+ #{create_do_num_bytes_code(nbits)}
38
41
  end
39
42
 
40
43
  #---------------
41
44
  private
42
45
 
46
+
43
47
  def read_and_return_value(io)
48
+ #{create_nbits_code(nbits)}
44
49
  val = io.readbits(#{nbits}, :#{endian})
45
- #{create_uint2int_code(nbits) if signed == :signed}
50
+ #{create_uint2int_code(nbits, signed)}
46
51
  val
47
52
  end
48
53
 
@@ -52,7 +57,51 @@ module BinData
52
57
  END
53
58
  end
54
59
 
60
+ def create_params_code(nbits)
61
+ if nbits == :nbits
62
+ "mandatory_parameter :nbits"
63
+ else
64
+ ""
65
+ end
66
+ end
67
+
68
+ def create_nbits_code(nbits)
69
+ if nbits == :nbits
70
+ "nbits = eval_parameter(:nbits)"
71
+ else
72
+ ""
73
+ end
74
+ end
75
+
76
+ def create_do_num_bytes_code(nbits)
77
+ if nbits == :nbits
78
+ "nbits / 8.0"
79
+ else
80
+ nbits / 8.0
81
+ end
82
+ end
83
+
55
84
  def create_clamp_code(nbits, signed)
85
+ if nbits == :nbits
86
+ create_dynamic_clamp_code(nbits, signed)
87
+ else
88
+ create_fixed_clamp_code(nbits, signed)
89
+ end
90
+ end
91
+
92
+ def create_dynamic_clamp_code(nbits, signed)
93
+ if signed == :signed
94
+ max = "max = (1 << (nbits - 1)) - 1"
95
+ min = "min = -(max + 1)"
96
+ else
97
+ max = "max = (1 << nbits) - 1"
98
+ min = "min = 0"
99
+ end
100
+
101
+ "#{max}; #{min}; val = (val < min) ? min : (val > max) ? max : val"
102
+ end
103
+
104
+ def create_fixed_clamp_code(nbits, signed)
56
105
  if nbits == 1 and signed == :signed
57
106
  raise "signed bitfield must have more than one bit"
58
107
  end
@@ -75,30 +124,50 @@ module BinData
75
124
  "val = #{clamp}"
76
125
  end
77
126
 
78
- def create_int2uint_code(nbits)
79
- "val = val & #{(1 << nbits) - 1}"
127
+ def create_int2uint_code(nbits, signed)
128
+ if signed != :signed
129
+ ""
130
+ elsif nbits == :nbits
131
+ "val &= (1 << nbits) - 1"
132
+ else
133
+ "val &= #{(1 << nbits) - 1}"
134
+ end
80
135
  end
81
136
 
82
- def create_uint2int_code(nbits)
83
- "val = val - #{1 << nbits} if (val >= #{1 << (nbits - 1)})"
137
+ def create_uint2int_code(nbits, signed)
138
+ if signed != :signed
139
+ ""
140
+ elsif nbits == :nbits
141
+ "val -= (1 << nbits) if (val >= (1 << (nbits - 1)))"
142
+ else
143
+ "val -= #{1 << nbits} if (val >= #{1 << (nbits - 1)})"
144
+ end
84
145
  end
85
146
  end
86
147
  end
87
148
 
149
+ # Create classes for dynamic bitfields
150
+ {
151
+ "Bit" => :big,
152
+ "BitLe" => :little,
153
+ "Sbit" => [:big, :signed],
154
+ "SbitLe" => [:little, :signed],
155
+ }.each_pair { |name, args| BitField.define_class(name, :nbits, *args) }
156
+
88
157
  # Create classes on demand
89
158
  module BitFieldFactory
90
159
  def const_missing(name)
91
160
  mappings = {
92
- /^Bit(\d+)$/ => :big,
93
- /^Bit(\d+)le$/ => :little,
94
- /^Sbit(\d+)$/ => [:big, :signed],
161
+ /^Bit(\d+)$/ => :big,
162
+ /^Bit(\d+)le$/ => :little,
163
+ /^Sbit(\d+)$/ => [:big, :signed],
95
164
  /^Sbit(\d+)le$/ => [:little, :signed]
96
165
  }
97
166
 
98
167
  mappings.each_pair do |regex, args|
99
168
  if regex =~ name.to_s
100
169
  nbits = $1.to_i
101
- return BitField.define_class(nbits, *args)
170
+ return BitField.define_class(name, nbits, *args)
102
171
  end
103
172
  end
104
173
 
@@ -76,7 +76,7 @@ module BinData
76
76
  @type = get_parameter(:type).instantiate(nil, self)
77
77
  end
78
78
 
79
- def clear? #:nodoc:
79
+ def clear?
80
80
  @type.clear?
81
81
  end
82
82
 
@@ -125,40 +125,24 @@ module BinData
125
125
  selection
126
126
  end
127
127
 
128
- def clear? #:nodoc:
129
- current_choice.clear?
130
- end
131
-
132
- def assign(val)
133
- current_choice.assign(val)
134
- end
135
-
136
- def snapshot
137
- current_choice.snapshot
128
+ def safe_respond_to?(symbol, include_private = false) #:nodoc:
129
+ base_respond_to?(symbol, include_private)
138
130
  end
139
131
 
140
132
  def respond_to?(symbol, include_private = false) #:nodoc:
141
133
  current_choice.respond_to?(symbol, include_private) || super
142
134
  end
143
135
 
144
- def safe_respond_to?(symbol, include_private = false) #:nodoc:
145
- base_respond_to?(symbol, include_private)
146
- end
147
-
148
136
  def method_missing(symbol, *args, &block) #:nodoc:
149
137
  current_choice.__send__(symbol, *args, &block)
150
138
  end
151
139
 
152
- def do_read(io) #:nodoc:
153
- current_choice.do_read(io)
154
- end
155
-
156
- def do_write(io) #:nodoc:
157
- current_choice.do_write(io)
158
- end
159
-
160
- def do_num_bytes #:nodoc:
161
- current_choice.do_num_bytes
140
+ %w(clear? assign snapshot do_read do_write do_num_bytes).each do |m|
141
+ self.module_eval <<-END
142
+ def #{m}(*args)
143
+ current_choice.#{m}(*args)
144
+ end
145
+ END
162
146
  end
163
147
 
164
148
  #---------------
@@ -20,12 +20,12 @@ module BinData
20
20
 
21
21
  # Don't override initialize. If you are defining a new kind of datatype
22
22
  # (list, array, choice etc) then put your initialization code in
23
- # #initialize_instance. This is because BinData objects can be initialized
24
- # as prototypes and your initialization code may not be called.
23
+ # #initialize_instance. BinData objects might be initialized as prototypes
24
+ # and your initialization code may not be called.
25
25
  #
26
26
  # If you're subclassing BinData::Record, you are definitely doing the wrong
27
27
  # thing. Read the documentation on how to use BinData.
28
- # http://bindata.rubyforge.org/manual.html#records
28
+ # http://github.com/dmendel/bindata/wiki/Records
29
29
  alias_method :initialize_without_warning, :initialize
30
30
  def initialize_with_warning(*args)
31
31
  owner = method(:initialize).owner
@@ -46,5 +46,11 @@ module BinData
46
46
  end
47
47
  end
48
48
 
49
+ # #offset has been renamed to #abs_offset.
50
+ # Eventually #rel_offset will be renamed to #offset.
51
+ def offset
52
+ warn "#offset is deprecated in #{debug_name}. Use #abs_offset instead"
53
+ abs_offset
54
+ end
49
55
  end
50
56
  end
@@ -6,8 +6,7 @@ module BinData
6
6
 
7
7
  module Int #:nodoc: all
8
8
  class << self
9
- def define_class(nbits, endian, signed)
10
- name = class_name(nbits, endian, signed)
9
+ def define_class(name, nbits, endian, signed)
11
10
  unless BinData.const_defined?(name)
12
11
  BinData.module_eval <<-END
13
12
  class #{name} < BinData::BasePrimitive
@@ -19,13 +18,6 @@ module BinData
19
18
  BinData.const_get(name)
20
19
  end
21
20
 
22
- def class_name(nbits, endian, signed)
23
- endian_str = (endian == :big) ? "be" : "le"
24
- base = (signed == :signed) ? "Int" : "Uint"
25
-
26
- "#{base}#{nbits}#{endian_str}"
27
- end
28
-
29
21
  def define_methods(int_class, nbits, endian, signed)
30
22
  raise "nbits must be divisible by 8" unless (nbits % 8).zero?
31
23
 
@@ -170,7 +162,7 @@ module BinData
170
162
  if regex =~ name.to_s
171
163
  nbits = $1.to_i
172
164
  if (nbits % 8).zero?
173
- return Int.define_class(nbits, *args)
165
+ return Int.define_class(name, nbits, *args)
174
166
  end
175
167
  end
176
168
  end
@@ -190,8 +190,8 @@ module BinData
190
190
 
191
191
  # Use #seek and #pos on seekable streams
192
192
  module SeekableStream
193
- # Returns the current offset of the io stream. The exact value of
194
- # the offset when reading bitfields is not defined.
193
+ # Returns the current offset of the io stream. Offset will be rounded
194
+ # up when reading bitfields.
195
195
  def offset
196
196
  raw_io.pos - @initial_pos
197
197
  end
@@ -225,8 +225,8 @@ module BinData
225
225
 
226
226
  # Manually keep track of offset for unseekable streams.
227
227
  module UnSeekableStream
228
- # Returns the current offset of the io stream. The exact value of
229
- # the offset when reading bitfields is not defined.
228
+ # Returns the current offset of the io stream. Offset will be rounded
229
+ # up when reading bitfields.
230
230
  def offset
231
231
  @read_count ||= 0
232
232
  end
@@ -284,6 +284,8 @@ module BinData
284
284
  @wval = 0
285
285
  @wendian = nil
286
286
 
287
+ @write_count = 0
288
+
287
289
  @bytes_remaining = nil
288
290
  end
289
291
 
@@ -307,6 +309,12 @@ module BinData
307
309
  end
308
310
  end
309
311
 
312
+ # Returns the current offset of the io stream. Offset will be rounded
313
+ # up when writing bitfields.
314
+ def offset
315
+ @write_count + (@wnbits > 0 ? 1 : 0)
316
+ end
317
+
310
318
  # Writes the given string of bytes to the io stream.
311
319
  def writebytes(str)
312
320
  flushbits
@@ -394,6 +402,7 @@ module BinData
394
402
  @bytes_remaining -= data.size
395
403
  end
396
404
 
405
+ @write_count += data.size
397
406
  @raw_io.write(data)
398
407
  end
399
408
 
@@ -85,7 +85,7 @@ module BinData
85
85
 
86
86
  if obj_parent.has_parameter?(symbol)
87
87
  obj_parent.get_parameter(symbol)
88
- elsif obj_parent.safe_respond_to?(symbol)
88
+ elsif obj_parent.safe_respond_to?(symbol, true)
89
89
  obj_parent.__send__(symbol, *args)
90
90
  else
91
91
  symbol
@@ -1,28 +1,10 @@
1
1
  require 'bindata/dsl'
2
- require 'bindata/sanitize'
3
2
  require 'bindata/struct'
4
3
 
5
4
  module BinData
6
5
  # A Record is a declarative wrapper around Struct.
7
6
  #
8
- # require 'bindata'
9
- #
10
- # class SomeDataType < BinData::Record
11
- # hide :a
12
- #
13
- # int32le :a
14
- # int16le :b
15
- # struct :s do
16
- # int8 :x
17
- # int8 :y
18
- # int8 :z
19
- # end
20
- # end
21
- #
22
- # obj = SomeDataType.new
23
- # obj.field_names =># ["b", "s"]
24
- # obj.s.field_names =># ["x", "y", "z"]
25
- #
7
+ # See +Struct+ for more info.
26
8
  class Record < BinData::Struct
27
9
  include DSLMixin
28
10
 
@@ -24,6 +24,14 @@ module BinData
24
24
  end
25
25
  end
26
26
 
27
+ def has_parameter?(param)
28
+ if @factory
29
+ @factory.has_parameter?(param)
30
+ else
31
+ @obj_params.has_parameter?(param)
32
+ end
33
+ end
34
+
27
35
  def instantiate(value = nil, parent = nil)
28
36
  @factory ||= @obj_class.new(@obj_params)
29
37
 
@@ -48,6 +56,10 @@ module BinData
48
56
  @name
49
57
  end
50
58
 
59
+ def has_parameter?(param)
60
+ @prototype.has_parameter?(param)
61
+ end
62
+
51
63
  def instantiate(value = nil, parent = nil)
52
64
  @prototype.instantiate(value, parent)
53
65
  end
@@ -83,6 +95,10 @@ module BinData
83
95
  @fields.each(&block)
84
96
  end
85
97
 
98
+ def each_with_index(&block)
99
+ @fields.each_with_index(&block)
100
+ end
101
+
86
102
  def collect(&block)
87
103
  @fields.collect(&block)
88
104
  end
@@ -103,6 +119,10 @@ module BinData
103
119
  @fields.all? { |f| f.name != nil }
104
120
  end
105
121
 
122
+ def any_field_has_parameter?(parameter)
123
+ @fields.any? { |f| f.has_parameter?(parameter) }
124
+ end
125
+
106
126
  def copy_fields(other)
107
127
  @fields.concat(other.fields)
108
128
  end
@@ -91,13 +91,14 @@ module BinData
91
91
 
92
92
  def snapshot
93
93
  # override to trim padding
94
- result = super
95
- result = clamp_to_length(result)
94
+ snap = super
95
+ snap = clamp_to_length(snap)
96
96
 
97
97
  if get_parameter(:trim_padding)
98
- result = trim_padding(result)
98
+ trim_padding(snap)
99
+ else
100
+ snap
99
101
  end
100
- result
101
102
  end
102
103
 
103
104
  #---------------
@@ -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,9 +47,11 @@ 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
54
56
 
55
57
  mandatory_parameter :fields
@@ -137,7 +139,9 @@ module BinData
137
139
  end
138
140
 
139
141
  def initialize_shared_instance
140
- @field_names = get_parameter(:fields).field_names.freeze
142
+ fields = get_parameter(:fields)
143
+ @field_names = fields.field_names.freeze
144
+ extend ByteAlignPlugin if fields.any_field_has_parameter?(:byte_align)
141
145
  super
142
146
  end
143
147
 
@@ -162,7 +166,7 @@ module BinData
162
166
  snapshot = Snapshot.new
163
167
  field_names.each do |name|
164
168
  obj = find_obj_for_name(name)
165
- snapshot[name] = obj.snapshot if include_obj(obj)
169
+ snapshot[name] = obj.snapshot if include_obj?(obj)
166
170
  end
167
171
  snapshot
168
172
  end
@@ -182,7 +186,7 @@ module BinData
182
186
  def respond_to?(symbol, include_private = false) #:nodoc:
183
187
  @field_names.include?(base_field_name(symbol)) || super
184
188
  end
185
-
189
+
186
190
  def method_missing(symbol, *args, &block) #:nodoc:
187
191
  obj = find_obj_for_name(symbol)
188
192
  if obj
@@ -205,12 +209,12 @@ module BinData
205
209
 
206
210
  def do_read(io) #:nodoc:
207
211
  instantiate_all_objs
208
- @field_objs.each { |f| f.do_read(io) if include_obj(f) }
212
+ @field_objs.each { |f| f.do_read(io) if include_obj?(f) }
209
213
  end
210
214
 
211
215
  def do_write(io) #:nodoc
212
216
  instantiate_all_objs
213
- @field_objs.each { |f| f.do_write(io) if include_obj(f) }
217
+ @field_objs.each { |f| f.do_write(io) if include_obj?(f) }
214
218
  end
215
219
 
216
220
  def do_num_bytes #:nodoc:
@@ -242,16 +246,15 @@ module BinData
242
246
  #---------------
243
247
  private
244
248
 
245
- def base_field_name(name)
246
- name.to_s.chomp("=").to_sym
247
- end
248
-
249
249
  def invoke_field(obj, symbol, args)
250
250
  name = symbol.to_s
251
251
  is_writer = (name[-1, 1] == "=")
252
+ is_query = (name[-1, 1] == "?")
252
253
 
253
254
  if is_writer
254
255
  obj.assign(*args)
256
+ elsif is_query
257
+ include_obj?(obj)
255
258
  else
256
259
  obj
257
260
  end
@@ -271,6 +274,10 @@ module BinData
271
274
  end
272
275
  end
273
276
 
277
+ def base_field_name(name)
278
+ name.to_s.sub(/(=|\?)\z/, "").to_sym
279
+ end
280
+
274
281
  def instantiate_all_objs
275
282
  @field_names.each_index { |i| instantiate_obj_at(i) }
276
283
  end
@@ -310,19 +317,18 @@ module BinData
310
317
  end
311
318
 
312
319
  def sum_num_bytes_below_index(index)
313
- sum = 0
314
- (0...index).each do |i|
320
+ (0...index).inject(0) do |sum, i|
315
321
  obj = @field_objs[i]
316
- if include_obj(obj)
322
+ if include_obj?(obj)
317
323
  nbytes = obj.do_num_bytes
318
- sum = (nbytes.is_a?(Integer) ? sum.ceil : sum) + nbytes
324
+ (nbytes.is_a?(Integer) ? sum.ceil : sum) + nbytes
325
+ else
326
+ sum
319
327
  end
320
328
  end
321
-
322
- sum
323
329
  end
324
330
 
325
- def include_obj(obj)
331
+ def include_obj?(obj)
326
332
  not obj.has_parameter?(:onlyif) or obj.eval_parameter(:onlyif)
327
333
  end
328
334
 
@@ -381,4 +387,59 @@ module BinData
381
387
  end
382
388
  end
383
389
  end
390
+
391
+ # Align fields to a multiple of :byte_align
392
+ module ByteAlignPlugin
393
+ def do_read(io)
394
+ initial_offset = io.offset
395
+ instantiate_all_objs
396
+ @field_objs.each do |f|
397
+ if include_obj?(f)
398
+ if align_obj?(f)
399
+ io.seekbytes(bytes_to_align(f, io.offset - initial_offset))
400
+ end
401
+ f.do_read(io)
402
+ end
403
+ end
404
+ end
405
+
406
+ def do_write(io)
407
+ initial_offset = io.offset
408
+ instantiate_all_objs
409
+ @field_objs.each do |f|
410
+ if include_obj?(f)
411
+ if align_obj?(f)
412
+ io.writebytes("\x00" * bytes_to_align(f, io.offset - initial_offset))
413
+ end
414
+ f.do_write(io)
415
+ end
416
+ end
417
+ end
418
+
419
+ def sum_num_bytes_below_index(index)
420
+ sum = 0
421
+ (0...@field_objs.length).each do |i|
422
+ obj = @field_objs[i]
423
+ if include_obj?(obj)
424
+ sum = sum.ceil + bytes_to_align(obj, sum.ceil) if align_obj?(obj)
425
+
426
+ break if i >= index
427
+
428
+ nbytes = obj.do_num_bytes
429
+ sum = (nbytes.is_a?(Integer) ? sum.ceil : sum) + nbytes
430
+ end
431
+ end
432
+
433
+ sum
434
+ end
435
+
436
+ def bytes_to_align(obj, rel_offset)
437
+ align = obj.eval_parameter(:byte_align)
438
+ (align - (rel_offset % align)) % align
439
+ end
440
+
441
+ def align_obj?(obj)
442
+ obj.has_parameter?(:byte_align)
443
+ end
444
+ end
384
445
  end
@@ -1,3 +1,3 @@
1
1
  module BinData
2
- VERSION = "1.8.2"
2
+ VERSION = "1.8.3"
3
3
  end
@@ -168,7 +168,7 @@ describe BinData::Array, "with several elements" do
168
168
  end
169
169
 
170
170
  it "has correct offset" do
171
- obj[2].offset.must_equal ExampleSingle.new.num_bytes * 2
171
+ obj[2].rel_offset.must_equal ExampleSingle.new.num_bytes * 2
172
172
  end
173
173
 
174
174
  it "has correct num_bytes" do
@@ -364,8 +364,8 @@ describe BinData::Array, "of bits" do
364
364
  end
365
365
 
366
366
  it "has correct offset" do
367
- obj[7].offset.must_equal 0
368
- obj[8].offset.must_equal 1
367
+ obj[7].rel_offset.must_equal 0
368
+ obj[8].rel_offset.must_equal 1
369
369
  end
370
370
  end
371
371
 
@@ -5,66 +5,58 @@ require File.expand_path(File.join(File.dirname(__FILE__), "common"))
5
5
  module AllBitfields
6
6
 
7
7
  def test_has_a_sensible_value_of_zero
8
- all_classes do |bit_class|
9
- bit_class.new.must_equal 0
8
+ all_objects do |obj, nbits|
9
+ obj.must_equal 0
10
10
  end
11
11
  end
12
12
 
13
13
  def test_avoids_underflow
14
- all_classes do |bit_class|
15
- obj = bit_class.new
16
-
14
+ all_objects do |obj, nbits|
17
15
  obj.assign(min_value - 1)
18
16
  obj.must_equal min_value
19
17
  end
20
18
  end
21
19
 
22
20
  def test_avoids_overflow
23
- all_classes do |bit_class|
24
- obj = bit_class.new
25
-
21
+ all_objects do |obj, nbits|
26
22
  obj.assign(max_value + 1)
27
23
  obj.must_equal max_value
28
24
  end
29
25
  end
30
26
 
31
27
  def test_assign_values
32
- all_classes do |bit_class|
28
+ all_objects do |obj, nbits|
33
29
  some_values_within_range.each do |val|
34
- obj = bit_class.new
35
30
  obj.assign(val)
36
-
37
31
  obj.must_equal val
38
32
  end
39
33
  end
40
34
  end
41
35
 
42
36
  def test_assign_values_from_other_bit_objects
43
- all_classes do |bit_class|
37
+ all_objects do |obj, nbits|
44
38
  some_values_within_range.each do |val|
45
- obj = bit_class.new
46
- obj.assign(bit_class.new(val))
47
-
39
+ obj.assign(obj.new(val))
48
40
  obj.must_equal val
49
41
  end
50
42
  end
51
43
  end
52
44
 
53
45
  def test_symmetrically_read_and_write
54
- all_classes do |bit_class|
46
+ all_objects do |obj, nbits|
55
47
  some_values_within_range.each do |val|
56
- obj = bit_class.new
57
48
  obj.assign(val)
58
-
59
- obj.value_read_from_written.must_equal obj
49
+ other = obj.new
50
+ other.read(obj.to_binary_s)
51
+ other.must_equal obj
60
52
  end
61
53
  end
62
54
  end
63
55
 
64
- def all_classes(&block)
65
- @bits.each_pair do |bit_class, nbits|
56
+ def all_objects(&block)
57
+ @bits.each do |obj, nbits|
66
58
  @nbits = nbits
67
- yield bit_class
59
+ yield obj, nbits
68
60
  end
69
61
  end
70
62
 
@@ -98,7 +90,7 @@ module AllBitfields
98
90
  end
99
91
 
100
92
  def generate_bit_classes_to_test(endian, signed)
101
- bits = {}
93
+ bits = []
102
94
  if signed
103
95
  base = "Sbit"
104
96
  start = 2
@@ -107,12 +99,20 @@ def generate_bit_classes_to_test(endian, signed)
107
99
  start = 1
108
100
  end
109
101
 
110
- (start .. 50).each do |nbits|
102
+ (start .. 64).each do |nbits|
111
103
  name = "#{base}#{nbits}"
112
104
  name << "le" if endian == :little
113
- bit_class = BinData.const_get(name)
114
- bits[bit_class] = nbits
105
+ obj = BinData.const_get(name).new
106
+ bits << [obj, nbits]
107
+ end
108
+
109
+ (start .. 64).each do |nbits|
110
+ name = "#{base}"
111
+ name << "Le" if endian == :little
112
+ obj = BinData.const_get(name).new(:nbits => nbits)
113
+ bits << [obj, nbits]
115
114
  end
115
+
116
116
  bits
117
117
  end
118
118
 
@@ -125,11 +125,12 @@ describe "Unsigned big endian bitfields" do
125
125
  end
126
126
 
127
127
  it "read big endian values" do
128
- @bits.each_pair do |bit_class, nbits|
128
+ all_objects do |obj, nbits|
129
129
  nbytes = (nbits + 7) / 8
130
130
  str = [0b1000_0000].pack("C") + "\000" * (nbytes - 1)
131
131
 
132
- bit_class.read(str).must_equal 1 << (nbits - 1)
132
+ obj.read(str)
133
+ obj.must_equal 1 << (nbits - 1)
133
134
  end
134
135
  end
135
136
  end
@@ -143,11 +144,12 @@ describe "Signed big endian bitfields" do
143
144
  end
144
145
 
145
146
  it "read big endian values" do
146
- @bits.each_pair do |bit_class, nbits|
147
+ all_objects do |obj, nbits|
147
148
  nbytes = (nbits + 7) / 8
148
149
  str = [0b0100_0000].pack("C") + "\000" * (nbytes - 1)
149
150
 
150
- bit_class.read(str).must_equal 1 << (nbits - 2)
151
+ obj.read(str)
152
+ obj.must_equal 1 << (nbits - 2)
151
153
  end
152
154
  end
153
155
  end
@@ -161,11 +163,12 @@ describe "Unsigned little endian bitfields" do
161
163
  end
162
164
 
163
165
  it "read little endian values" do
164
- @bits.each_pair do |bit_class, nbits|
166
+ all_objects do |obj, nbits|
165
167
  nbytes = (nbits + 7) / 8
166
168
  str = [0b0000_0001].pack("C") + "\000" * (nbytes - 1)
167
169
 
168
- bit_class.read(str).must_equal 1
170
+ obj.read(str)
171
+ obj.must_equal 1
169
172
  end
170
173
  end
171
174
  end
@@ -179,11 +182,12 @@ describe "Signed little endian bitfields" do
179
182
  end
180
183
 
181
184
  it "read little endian values" do
182
- @bits.each_pair do |bit_class, nbits|
185
+ all_objects do |obj, nbits|
183
186
  nbytes = (nbits + 7) / 8
184
187
  str = [0b0000_0001].pack("C") + "\000" * (nbytes - 1)
185
188
 
186
- bit_class.read(str).must_equal 1
189
+ obj.read(str)
190
+ obj.must_equal 1
187
191
  end
188
192
  end
189
193
  end
@@ -152,6 +152,21 @@ describe BinData::IO::Write, "when writing" do
152
152
  stream.value.must_equal "abcd"
153
153
  end
154
154
 
155
+ it "has #offset" do
156
+ io.offset.must_equal 0
157
+
158
+ io.writebytes("abcd")
159
+ io.offset.must_equal 4
160
+
161
+ io.writebytes("ABCD")
162
+ io.offset.must_equal 8
163
+ end
164
+
165
+ it "rounds up #offset when writing bits" do
166
+ io.writebits(123, 9, :little)
167
+ io.offset.must_equal 2
168
+ end
169
+
155
170
  it "flushes" do
156
171
  io.writebytes("abcd")
157
172
  io.flush
@@ -73,12 +73,16 @@ end
73
73
 
74
74
  describe BinData::LazyEvaluator, "with one parent" do
75
75
  subject {
76
- parent_methods = {:m1 => 'Pm1', :com1 => 'PmC', :mm => 3}
76
+ parent_methods = {:m1 => 'Pm1', :m2 => 'Pm2', :com1 => 'PmC', :mm => 3}
77
77
  parent_params = {:p1 => 'Pp1', :com1 => 'PpC'}
78
78
  parent_obj = MockBinDataObject.new(parent_methods, parent_params)
79
79
 
80
- def parent_obj.echo(a1, a2)
81
- [a1, a2]
80
+ class << parent_obj
81
+ def echo(a1, a2)
82
+ [a1, a2]
83
+ end
84
+
85
+ private :m2
82
86
  end
83
87
 
84
88
  methods = {:m1 => 'm1', :com1 => 'mC'}
@@ -118,6 +122,10 @@ describe BinData::LazyEvaluator, "with one parent" do
118
122
  lazy_eval(lambda { echo(p1, m1) }).must_equal ['Pp1', 'Pm1']
119
123
  end
120
124
 
125
+ it "invokes private methods in the parent" do
126
+ lazy_eval(lambda { m2 }).must_equal 'Pm2'
127
+ end
128
+
121
129
  it "resolves parameters in preference to methods in the parent" do
122
130
  lazy_eval(lambda { com1 }).must_equal 'PpC'
123
131
  end
@@ -241,12 +241,12 @@ describe BinData::Record, "with nested structs" do
241
241
  obj.c.y.must_equal 3
242
242
  end
243
243
 
244
- it "returns correct offset" do
245
- obj.offset.must_equal 0
246
- obj.b.offset.must_equal 1
247
- obj.b.w.offset.must_equal 1
248
- obj.c.offset.must_equal 3
249
- obj.c.z.offset.must_equal 4
244
+ it "returns correct abs_offset" do
245
+ obj.abs_offset.must_equal 0
246
+ obj.b.abs_offset.must_equal 1
247
+ obj.b.w.abs_offset.must_equal 1
248
+ obj.c.abs_offset.must_equal 3
249
+ obj.c.z.abs_offset.must_equal 4
250
250
  end
251
251
 
252
252
  it "returns correct rel_offset" do
@@ -476,6 +476,12 @@ describe BinData::Record, "with :onlyif" do
476
476
  obj.to_binary_s.must_equal "\x03\x05"
477
477
  end
478
478
 
479
+ it "identifies if fields are included" do
480
+ obj.a?.must_equal true
481
+ obj.b?.must_equal true
482
+ obj.c?.must_equal false
483
+ end
484
+
479
485
  it "reads as lambdaed" do
480
486
  obj.read("\x01\x02")
481
487
  obj.snapshot.must_equal({"a" => 1, "c" => 2})
@@ -250,11 +250,11 @@ describe BinData::Struct, "with nested structs" do
250
250
  obj.c.z.must_equal 0
251
251
  end
252
252
 
253
- it "returns correct offset" do
254
- obj.b.offset.must_equal 1
255
- obj.b.w.offset.must_equal 1
256
- obj.c.offset.must_equal 3
257
- obj.c.z.offset.must_equal 4
253
+ it "returns correct abs_offset" do
254
+ obj.b.abs_offset.must_equal 1
255
+ obj.b.w.abs_offset.must_equal 1
256
+ obj.c.abs_offset.must_equal 3
257
+ obj.c.z.abs_offset.must_equal 4
258
258
  end
259
259
  end
260
260
 
@@ -313,10 +313,10 @@ describe BinData::Struct, "with bit fields" do
313
313
  end
314
314
 
315
315
  it "has correct offsets" do
316
- obj.a.offset.must_equal 0
317
- obj.b.offset.must_equal 0
318
- obj.c.offset.must_equal 1
319
- obj.d.offset.must_equal 2
316
+ obj.a.rel_offset.must_equal 0
317
+ obj.b.rel_offset.must_equal 0
318
+ obj.c.rel_offset.must_equal 1
319
+ obj.d.rel_offset.must_equal 2
320
320
  end
321
321
  end
322
322
 
@@ -338,6 +338,37 @@ describe BinData::Struct, "with nested endian" do
338
338
  end
339
339
  end
340
340
 
341
+ describe BinData::Struct, "with byte_align" do
342
+ let(:obj) {
343
+ params = { :fields => [[:int8, :a],
344
+ [:int8, :b, {:byte_align => 5}],
345
+ [:bit2, :c],
346
+ [:int8, :d, {:byte_align => 3}]] }
347
+ BinData::Struct.new(params)
348
+ }
349
+
350
+ it "has #num_bytes" do
351
+ obj.num_bytes.must_equal 10
352
+ end
353
+
354
+ it "reads" do
355
+ obj.read("\x01\x00\x00\x00\x00\x02\xc0\x00\x00\x04")
356
+ obj.snapshot.must_equal({ "a" => 1, "b" => 2, "c" => 3, "d" => 4 })
357
+ end
358
+
359
+ it "writes" do
360
+ obj.assign(:a => 1, :b => 2, :c => 3, :d => 4)
361
+ obj.to_binary_s.must_equal binary("\x01\x00\x00\x00\x00\x02\xc0\x00\x00\x04")
362
+ end
363
+
364
+ it "has correct offsets" do
365
+ obj.a.rel_offset.must_equal 0
366
+ obj.b.rel_offset.must_equal 5
367
+ obj.c.rel_offset.must_equal 6
368
+ obj.d.rel_offset.must_equal 9
369
+ end
370
+ end
371
+
341
372
  describe BinData::Struct, "with dynamically named types" do
342
373
  it "instantiates" do
343
374
  dyn = BinData::Struct.new(:name => :my_struct, :fields => [[:int8, :a, {:initial_value => 3}]])
@@ -93,7 +93,7 @@ describe BinData::Record, "with choice field" do
93
93
 
94
94
  it "has correct offset" do
95
95
  obj = RecordWithNestedChoiceField.new
96
- obj.x.b.offset.must_equal 2
96
+ obj.x.b.abs_offset.must_equal 2
97
97
  end
98
98
  end
99
99
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bindata
3
3
  version: !ruby/object:Gem::Version
4
- hash: 51
4
+ hash: 49
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 8
9
- - 2
10
- version: 1.8.2
9
+ - 3
10
+ version: 1.8.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Dion Mendel
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2014-02-02 00:00:00 +08:00
18
+ date: 2014-04-16 00:00:00 +08:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency