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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a1591b7284eb5311a1577007b50506a3169849cc
4
- data.tar.gz: a60a5656dcb5586f12bc7fe88e98af7d12652d7a
3
+ metadata.gz: fba829df2a0f618021920683747584daef8f572a
4
+ data.tar.gz: efe2ac6d0ab61895a09f271fb9d7712e289f1897
5
5
  SHA512:
6
- metadata.gz: 57b52b55ca1ab97820293d9c73b3e085542a955052b64e3873a87e7b8f0fdd84635a33843ad4aeb92dd7b089a7b988e2b9fe236c265189dbe310766811d7c5cc
7
- data.tar.gz: 01a0327084e334cd02459daf396253b11f266a3c443dcf3f57e144dea6f653e98e5aa4d03249a8f5794bd867e727df609137b5e7519b400aa00cdec9c2cfc64b
6
+ metadata.gz: eef3a6fd17fcb5fe9afcffa35c566cd4e0752063f20bc4d9d135aa3300184454b8974d3161d90e37390dfa3b6606b5f1bfe1217705075e28bd2e5641b11f878d
7
+ data.tar.gz: 55bc265de56013c33f1e9abf7d38a796deff58cedf7faa1c20e77ee6dbad5e42a080c3ab46c8f06503e7268888cedd38b51667e1d091fa23cb89afc39b5b124e
@@ -2,4 +2,6 @@ language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
4
  - 2.0.0
5
+ - 2.1.1
5
6
  - jruby
7
+ - ruby-head
@@ -1,5 +1,18 @@
1
1
  = BinData Changelog
2
2
 
3
+ == Version 2.1.0 (2014-04-16)
4
+
5
+ * Performance improvements.
6
+ * Removed deprecated parameters.
7
+ * Code refactored to use Ruby 1.9 features.
8
+ * #eval_parameters can now call private methods. Requested by Ole Rasmussen.
9
+ * Can now determine state of :onlyif fields. Requested by Ole Rasmussen.
10
+ * Renamed #offset to #abs_offset for clarity. #offset is now deprecated.
11
+ * Support :byte_align for fields in structs. Requested by Igor Yamolov.
12
+ * Added bit fields with dynamic length. Requested by Jacob Dam.
13
+ * Added "endian :big_and_little" option which creates :big and :little
14
+ versions of the class. Thanks to Jacob Lukas for the prototype.
15
+
3
16
  == Version 2.0.0 (2014-02-02)
4
17
 
5
18
  * Ruby 1.8 now has its own separate branch.
data/NEWS.rdoc CHANGED
@@ -1,3 +1,19 @@
1
+ = 2.1.0
2
+
3
+ Several new features in this release.
4
+
5
+ * Fields can be aligned on n-bytes boundaries. This make it easier to write
6
+ parsers for aligned structures.
7
+ * The endian value :big_and_little can be used to automatically create :big and
8
+ :little endian versions of the class.
9
+ * Bit fields can be defined to have their length determined at runtime.
10
+ * The state of :onlyif fields can be determined by #field?
11
+
12
+ == Deprecations
13
+
14
+ The #offset method has been renamed to #abs_offset. This make an obvious
15
+ distinction to #rel_offset.
16
+
1
17
  = 2.0.0
2
18
 
3
19
  BinData 2.0.0 requires ruby 1.9 or greater. Support for ruby 1.8 is found in
data/README.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # What is BinData?
2
2
 
3
+ [![Version ](http://img.shields.io/gem/v/bindata.svg) ](https://rubygems.org/gems/bindata)
4
+ [![Travis CI ](http://img.shields.io/travis/dmendel/bindata/master.svg) ](https://travis-ci.org/dmendel/bindata)
5
+ [![Quality ](http://img.shields.io/codeclimate/github/dmendel/bindata.svg)](https://codeclimate.com/github/dmendel/bindata)
6
+ [![Coverage ](http://img.shields.io/coveralls/dmendel/bindata.svg) ](https://coveralls.io/r/dmendel/bindata)
7
+
3
8
  Do you ever find yourself writing code like this?
4
9
 
5
10
  ```ruby
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
19
19
 
20
20
  s.add_development_dependency('rake')
21
21
  s.add_development_dependency('minitest', "> 5.0.0")
22
+ s.add_development_dependency('coveralls')
22
23
  s.description = <<-END.gsub(/^ +/, "")
23
24
  BinData is a declarative way to read and write binary file formats.
24
25
 
@@ -105,7 +105,7 @@ if __FILE__ == $0
105
105
  g.footer.uncompressed_size,
106
106
  ratio,
107
107
  g.file_name]
108
- puts "Comment: #{g.comment}" if g.comment != ""
108
+ puts "Comment: #{g.comment}" if g.comment?
109
109
  puts
110
110
 
111
111
  puts "Executing gzip -l -v"
@@ -23,7 +23,7 @@ require 'bindata/struct'
23
23
  require 'bindata/trace'
24
24
  require 'bindata/virtual'
25
25
  require 'bindata/alignment'
26
- require 'bindata/deprecated'
26
+ require 'bindata/warnings'
27
27
 
28
28
  # = BinData
29
29
  #
@@ -48,35 +48,16 @@ module BinData
48
48
  # Each data object in an array has the variable +index+ made available
49
49
  # to any lambda evaluated as a parameter of that data object.
50
50
  class Array < BinData::Base
51
- include DSLMixin
51
+ extend DSLMixin
52
52
  include Enumerable
53
53
 
54
- dsl_parser :array
54
+ dsl_parser :array
55
+ arg_processor :array
55
56
 
56
57
  mandatory_parameter :type
57
58
  optional_parameters :initial_length, :read_until
58
59
  mutually_exclusive_parameters :initial_length, :read_until
59
60
 
60
- class << self
61
-
62
- def sanitize_parameters!(params) #:nodoc:
63
- unless params.has_parameter?(:initial_length) or
64
- params.has_parameter?(:read_until)
65
- # ensure one of :initial_length and :read_until exists
66
- params[:initial_length] = 0
67
- end
68
-
69
- params.warn_replacement_parameter(:read_length, :initial_length)
70
-
71
- params.merge!(dsl_params)
72
-
73
- if params.needs_sanitizing?(:type)
74
- el_type, el_params = params[:type]
75
- params[:type] = params.create_sanitized_object_prototype(el_type, el_params)
76
- end
77
- end
78
- end
79
-
80
61
  def initialize_shared_instance
81
62
  @element_prototype = get_parameter(:type)
82
63
  if get_parameter(:read_until) == :eof
@@ -292,6 +273,26 @@ module BinData
292
273
  end
293
274
  end
294
275
 
276
+ class ArrayArgProcessor < BaseArgProcessor
277
+ def sanitize_parameters!(obj_class, params) #:nodoc:
278
+ unless params.has_parameter?(:initial_length) or
279
+ params.has_parameter?(:read_until)
280
+ # ensure one of :initial_length and :read_until exists
281
+ params[:initial_length] = 0
282
+ end
283
+
284
+ params.warn_replacement_parameter(:read_length, :initial_length)
285
+ params.must_be_integer(:initial_length, :read_length)
286
+
287
+ params.merge!(obj_class.dsl_params)
288
+
289
+ if params.needs_sanitizing?(:type)
290
+ el_type, el_params = params[:type]
291
+ params[:type] = params.create_sanitized_object_prototype(el_type, el_params)
292
+ end
293
+ end
294
+ end
295
+
295
296
  # Logic for the :read_until parameter
296
297
  module ReadUntilPlugin
297
298
  def do_read(io)
@@ -8,33 +8,6 @@ require 'bindata/registry'
8
8
  require 'bindata/sanitize'
9
9
 
10
10
  module BinData
11
- # ArgExtractors take the arguments passed to BinData::Base.new and
12
- # separate them into [value, parameters, parent].
13
- class BaseArgExtractor
14
- @@empty_hash = Hash.new.freeze
15
-
16
- def self.extract(the_class, the_args)
17
- args = the_args.dup
18
- value = parameters = parent = nil
19
-
20
- if args.length > 1 and args.last.is_a? BinData::Base
21
- parent = args.pop
22
- end
23
-
24
- if args.length > 0 and args.last.is_a? Hash
25
- parameters = args.pop
26
- end
27
-
28
- if args.length > 0
29
- value = args.pop
30
- end
31
-
32
- parameters ||= @@empty_hash
33
-
34
- return [value, parameters, parent]
35
- end
36
- end
37
-
38
11
  # This is the abstract base class for all data objects.
39
12
  class Base
40
13
  extend AcceptedParametersPlugin
@@ -43,18 +16,25 @@ module BinData
43
16
  include RegisterNamePlugin
44
17
 
45
18
  class << self
46
-
47
19
  # Instantiates this class and reads from +io+, returning the newly
48
- # created data object.
49
- def read(io)
50
- obj = self.new
20
+ # created data object. +args+ will be used when instantiating.
21
+ def read(io, *args)
22
+ obj = self.new(*args)
51
23
  obj.read(io)
52
24
  obj
53
25
  end
54
26
 
55
- # The arg extractor for this class.
56
- def arg_extractor
57
- BaseArgExtractor
27
+ # The arg processor for this class.
28
+ def arg_processor(name = nil)
29
+ if name
30
+ @arg_processor = "#{name}_arg_processor".gsub(/(?:^|_)(.)/) { $1.upcase }.to_sym
31
+ elsif @arg_processor.is_a? Symbol
32
+ @arg_processor = BinData::const_get(@arg_processor).new
33
+ elsif @arg_processor.nil?
34
+ @arg_processor = superclass.arg_processor
35
+ else
36
+ @arg_processor
37
+ end
58
38
  end
59
39
 
60
40
  # The name of this class as used by Records, Arrays etc.
@@ -69,11 +49,9 @@ module BinData
69
49
 
70
50
  # Registers all subclasses of this class for use
71
51
  def register_subclasses #:nodoc:
72
- class << self
73
- define_method(:inherited) do |subclass|
74
- RegisteredClasses.register(subclass.name, subclass)
75
- register_subclasses
76
- end
52
+ define_singleton_method(:inherited) do |subclass|
53
+ RegisteredClasses.register(subclass.name, subclass)
54
+ register_subclasses
77
55
  end
78
56
  end
79
57
 
@@ -83,6 +61,9 @@ module BinData
83
61
  # Register all subclasses of this class.
84
62
  register_subclasses
85
63
 
64
+ # Set the initial arg processor.
65
+ arg_processor :base
66
+
86
67
  # Creates a new data object.
87
68
  #
88
69
  # Args are optional, but if present, must be in the following order.
@@ -96,10 +77,7 @@ module BinData
96
77
  # object resides under.
97
78
  #
98
79
  def initialize(*args)
99
- value, parameters, parent = extract_args(args)
100
-
101
- @params = SanitizedParameters.sanitize(parameters, self.class)
102
- @parent = parent
80
+ value, @params, @parent = extract_args(args)
103
81
 
104
82
  initialize_shared_instance
105
83
  initialize_instance
@@ -233,16 +211,17 @@ module BinData
233
211
  end
234
212
  end
235
213
 
236
- # Returns the offset of this object wrt to its most distant ancestor.
237
- def offset
214
+ # Returns the offset (in bytes) of this object with respect to its most
215
+ # distant ancestor.
216
+ def abs_offset
238
217
  if @parent
239
- @parent.offset + @parent.offset_of(self)
218
+ @parent.abs_offset + @parent.offset_of(self)
240
219
  else
241
220
  0
242
221
  end
243
222
  end
244
223
 
245
- # Returns the offset of this object wrt to its parent.
224
+ # Returns the offset (in bytes) of this object with respect to its parent.
246
225
  def rel_offset
247
226
  if @parent
248
227
  @parent.offset_of(self)
@@ -266,8 +245,8 @@ module BinData
266
245
  #---------------
267
246
  private
268
247
 
269
- def extract_args(the_args)
270
- self.class.arg_extractor.extract(self.class, the_args)
248
+ def extract_args(args)
249
+ self.class.arg_processor.extract_args(self.class, args)
271
250
  end
272
251
 
273
252
  def furthest_ancestor
@@ -284,4 +263,51 @@ module BinData
284
263
  str.to_s.dup.force_encoding(Encoding::BINARY)
285
264
  end
286
265
  end
266
+
267
+ # ArgProcessors process the arguments passed to BinData::Base.new into
268
+ # the form required to initialise the BinData object.
269
+ #
270
+ # Any passed parameters are sanitized so the BinData object doesn't
271
+ # need to perform error checking on the parameters.
272
+ class BaseArgProcessor
273
+ @@empty_hash = Hash.new.freeze
274
+
275
+ # Takes the arguments passed to BinData::Base.new and
276
+ # extracts [value, sanitized_parameters, parent].
277
+ def extract_args(obj_class, obj_args)
278
+ value, params, parent = separate_args(obj_class, obj_args)
279
+ sanitized_params = SanitizedParameters.sanitize(params, obj_class)
280
+
281
+ [value, sanitized_params, parent]
282
+ end
283
+
284
+ # Separates the arguments passed to BinData::Base.new into
285
+ # [value, parameters, parent]. Called by #extract_args.
286
+ def separate_args(obj_class, obj_args)
287
+ args = obj_args.dup
288
+ value = parameters = parent = nil
289
+
290
+ if args.length > 1 and args.last.is_a? BinData::Base
291
+ parent = args.pop
292
+ end
293
+
294
+ if args.length > 0 and args.last.is_a? Hash
295
+ parameters = args.pop
296
+ end
297
+
298
+ if args.length > 0
299
+ value = args.pop
300
+ end
301
+
302
+ parameters ||= @@empty_hash
303
+
304
+ return [value, parameters, parent]
305
+ end
306
+
307
+ # Performs sanity checks on the given parameters.
308
+ # This method converts the parameters to the form expected
309
+ # by the data object.
310
+ def sanitize_parameters!(obj_class, obj_params)
311
+ end
312
+ end
287
313
  end
@@ -20,10 +20,10 @@ module BinData
20
20
  # obj.assign(5)
21
21
  # obj #=> 42
22
22
  #
23
- # obj = BinData::Uint8.new(:check_value => 3)
23
+ # obj = BinData::Uint8.new(:assert_value => 3)
24
24
  # obj.read("\005") #=> BinData::ValidityError: value is '5' but expected '3'
25
25
  #
26
- # obj = BinData::Uint8.new(:check_value => lambda { value < 5 })
26
+ # obj = BinData::Uint8.new(:assert_value => lambda { value < 5 })
27
27
  # obj.read("\007") #=> BinData::ValidityError: value not as expected
28
28
  #
29
29
  # == Parameters
@@ -49,22 +49,13 @@ module BinData
49
49
  class BasePrimitive < BinData::Base
50
50
  unregister_self
51
51
 
52
- optional_parameters :initial_value, :value, :check_value, :assert, :asserted_value
52
+ optional_parameters :initial_value, :value, :assert, :asserted_value
53
53
  mutually_exclusive_parameters :initial_value, :value
54
54
  mutually_exclusive_parameters :asserted_value, :value, :assert
55
- mutually_exclusive_parameters :check_value, :assert
56
- mutually_exclusive_parameters :check_value, :asserted_value
57
55
 
58
56
  def initialize_shared_instance
59
- if has_parameter?(:check_value) and has_parameter?(:value)
60
- warn ":check_value has been deprecated. Consider using :asserted_value instead of :check_value and :value in #{self.class}."
61
- elsif has_parameter?(:check_value)
62
- warn ":check_value has been deprecated. Use :assert instead in #{self.class}."
63
- end
64
-
65
57
  extend InitialValuePlugin if has_parameter?(:initial_value)
66
58
  extend ValuePlugin if has_parameter?(:value)
67
- extend CheckValuePlugin if has_parameter?(:check_value)
68
59
  extend AssertPlugin if has_parameter?(:assert)
69
60
  extend AssertedValuePlugin if has_parameter?(:asserted_value)
70
61
  super
@@ -169,26 +160,6 @@ module BinData
169
160
  end
170
161
  end
171
162
 
172
- # Logic for the :check_value parameter
173
- module CheckValuePlugin
174
- def do_read(io) #:nodoc:
175
- super(io)
176
- check_value(snapshot)
177
- end
178
-
179
- def check_value(current_value)
180
- expected = eval_parameter(:check_value, :value => current_value)
181
- if not expected
182
- raise ValidityError,
183
- "value '#{current_value}' not as expected for #{debug_name}"
184
- elsif current_value != expected and expected != true
185
- raise ValidityError,
186
- "value is '#{current_value}' but " +
187
- "expected '#{expected}' for #{debug_name}"
188
- end
189
- end
190
- end
191
-
192
163
  # Logic for the :assert parameter
193
164
  module AssertPlugin
194
165
  def assign(val)
@@ -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