bindata 2.3.1 → 2.3.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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8c2c402445f12f30bacb95e174ec062916c0f899
4
- data.tar.gz: 05fd4d2d0d195a817fe0056d7db707073167826a
3
+ metadata.gz: a82037bfb06f2af645803e71fcaaf50ec475845a
4
+ data.tar.gz: 2b111c9173a7fb320fd5f98575e864da786dae0b
5
5
  SHA512:
6
- metadata.gz: e35b838106b7bf8d4317f5f12f82a5726a4b42cc4c9435c414b52e2ba6269ffe76732b2943879d7e5fd316f8857237e59c516403af667e231c5ff17767900877
7
- data.tar.gz: 048dec43da3620cbd4d950378af2f8ea9fdfeb3b38f8eb842faffa0b78a0d94344c656b90a649efabc638f07dfb2619f780b1088964171d08ae593073aec5c7c
6
+ metadata.gz: 44f161193a8242656fe81684dc419f1936abcb64232928abcd2ab32288c767ccdd2b4dfc7a7168a93af073b02c87c8a3e2fb6a638bc275dc121e67644c77f09a
7
+ data.tar.gz: 7258cad731fab07ddf5414733d950aefe7b0f78bdf08f886e8d4dcd18710f1c9eb716c22bdcb9128e18695c47334ff1b72dd734bea9997b17d3cb5da3cc7b4ab
@@ -1,12 +1,13 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.0.0
4
- - 2.1.8
5
- - 2.2.4
6
- - 2.3.0
3
+ - 2.0
4
+ - 2.1.10
5
+ - 2.2
6
+ - 2.3
7
7
  - jruby
8
8
  - ruby-head
9
9
 
10
10
  matrix:
11
11
  allow_failures:
12
+ - rvm: jruby
12
13
  - rvm: ruby-head
@@ -1,5 +1,10 @@
1
1
  = BinData Changelog
2
2
 
3
+ == Version 2.3.2 (2016-09-02)
4
+
5
+ * IO#num_bytes_remaining now works inside Buffers.
6
+ * Added ability to skip to arbitrary byte patterns. Requested by Stefan Kolb.
7
+
3
8
  == Version 2.3.1 (2016-06-17)
4
9
 
5
10
  * Improve list of reserved words. Thanks to Claudius Coenen.
@@ -122,7 +122,8 @@ module BinData
122
122
  end
123
123
 
124
124
  def dsl_params
125
- send(parser_abilities[@parser_type].at(0))
125
+ abilities = parser_abilities[@parser_type]
126
+ send(abilities.at(0), abilities.at(1))
126
127
  end
127
128
 
128
129
  def method_missing(*args, &block)
@@ -135,17 +136,18 @@ module BinData
135
136
 
136
137
  def parser_abilities
137
138
  @abilities ||= {
138
- :struct => [:to_struct_params, [:multiple_fields, :optional_fieldnames, :hidden_fields]],
139
- :array => [:to_array_params, [:multiple_fields, :optional_fieldnames]],
140
- :buffer => [:to_array_params, [:multiple_fields, :optional_fieldnames, :hidden_fields]],
141
- :choice => [:to_choice_params, [:multiple_fields, :all_or_none_fieldnames, :fieldnames_are_values]],
142
- :delayed_io => [:to_array_params, [:multiple_fields, :optional_fieldnames, :hidden_fields]],
143
- :primitive => [:to_struct_params, [:multiple_fields, :optional_fieldnames]]
139
+ :struct => [:to_struct_params, :struct, [:multiple_fields, :optional_fieldnames, :hidden_fields]],
140
+ :array => [:to_object_params, :type, [:multiple_fields, :optional_fieldnames]],
141
+ :buffer => [:to_object_params, :type, [:multiple_fields, :optional_fieldnames, :hidden_fields]],
142
+ :choice => [:to_choice_params, :choices, [:multiple_fields, :all_or_none_fieldnames, :fieldnames_are_values]],
143
+ :delayed_io => [:to_object_params, :type, [:multiple_fields, :optional_fieldnames, :hidden_fields]],
144
+ :primitive => [:to_struct_params, :struct, [:multiple_fields, :optional_fieldnames]],
145
+ :skip => [:to_object_params, :until_valid, [:multiple_fields, :optional_fieldnames]],
144
146
  }
145
147
  end
146
148
 
147
149
  def option?(opt)
148
- parser_abilities[@parser_type].at(1).include?(opt)
150
+ parser_abilities[@parser_type].at(2).include?(opt)
149
151
  end
150
152
 
151
153
  def ensure_hints
@@ -219,30 +221,30 @@ module BinData
219
221
  raise exception, message + " in #{@the_class}", backtrace
220
222
  end
221
223
 
222
- def to_array_params
224
+ def to_object_params(key)
223
225
  case fields.length
224
226
  when 0
225
227
  {}
226
228
  when 1
227
- {:type => fields[0].prototype}
229
+ {key => fields[0].prototype}
228
230
  else
229
- {:type => [:struct, to_struct_params]}
231
+ {key=> [:struct, to_struct_params]}
230
232
  end
231
233
  end
232
234
 
233
- def to_choice_params
235
+ def to_choice_params(key)
234
236
  if fields.length == 0
235
237
  {}
236
238
  elsif fields.all_field_names_blank?
237
- {:choices => fields.collect { |f| f.prototype }}
239
+ {key => fields.collect { |f| f.prototype }}
238
240
  else
239
241
  choices = {}
240
242
  fields.each { |f| choices[f.name] = f.prototype }
241
- {:choices => choices}
243
+ {key => choices}
242
244
  end
243
245
  end
244
246
 
245
- def to_struct_params
247
+ def to_struct_params(*unused)
246
248
  result = {:fields => fields}
247
249
  if not endian.nil?
248
250
  result[:endian] = endian
@@ -386,7 +388,8 @@ module BinData
386
388
  :buffer => BinData::Buffer,
387
389
  :choice => BinData::Choice,
388
390
  :delayed_io => BinData::DelayedIO,
389
- :struct => BinData::Struct
391
+ :skip => BinData::Skip,
392
+ :struct => BinData::Struct,
390
393
  }
391
394
 
392
395
  if bindata_classes.include?(@type)
@@ -18,7 +18,7 @@ module BinData
18
18
  end
19
19
 
20
20
  @raw_io = io
21
- @buffer_end_pos = nil
21
+ @buffer_end_points = nil
22
22
 
23
23
  extend seekable? ? SeekableStream : UnSeekableStream
24
24
  stream_init
@@ -38,12 +38,12 @@ module BinData
38
38
  end
39
39
 
40
40
  def buffer_limited_n(n)
41
- if @buffer_end_pos
41
+ if @buffer_end_points
42
42
  if n.nil? or n > 0
43
- max = @buffer_end_pos[1] - offset
43
+ max = @buffer_end_points[1] - offset
44
44
  n = max if n.nil? or n > max
45
45
  else
46
- min = @buffer_end_pos[0] - offset
46
+ min = @buffer_end_points[0] - offset
47
47
  n = min if n < min
48
48
  end
49
49
  end
@@ -52,16 +52,16 @@ module BinData
52
52
  end
53
53
 
54
54
  def with_buffer_common(n, &block)
55
- prev = @buffer_end_pos
55
+ prev = @buffer_end_points
56
56
  if prev
57
57
  avail = prev[1] - offset
58
58
  n = avail if n > avail
59
59
  end
60
- @buffer_end_pos = [offset, offset + n]
60
+ @buffer_end_points = [offset, offset + n]
61
61
  begin
62
- block.call(*@buffer_end_pos)
62
+ block.call(*@buffer_end_points)
63
63
  ensure
64
- @buffer_end_pos = prev
64
+ @buffer_end_points = prev
65
65
  end
66
66
  end
67
67
 
@@ -69,14 +69,33 @@ module BinData
69
69
  module SeekableStream
70
70
  # The number of bytes remaining in the input stream.
71
71
  def num_bytes_remaining
72
- mark = @raw_io.pos
72
+ start_mark = @raw_io.pos
73
73
  @raw_io.seek(0, ::IO::SEEK_END)
74
- bytes_remaining = @raw_io.pos - mark
75
- @raw_io.seek(mark, ::IO::SEEK_SET)
74
+ end_mark = @raw_io.pos
75
+
76
+ if @buffer_end_points
77
+ if @buffer_end_points[1] < end_mark
78
+ end_mark = @buffer_end_points[1]
79
+ end
80
+ end
81
+
82
+ bytes_remaining = end_mark - start_mark
83
+ @raw_io.seek(start_mark, ::IO::SEEK_SET)
76
84
 
77
85
  bytes_remaining
78
86
  end
79
87
 
88
+ # All io calls in +block+ are rolled back after this
89
+ # method completes.
90
+ def with_readahead(&block)
91
+ mark = @raw_io.pos
92
+ begin
93
+ block.call
94
+ ensure
95
+ @raw_io.seek(mark, ::IO::SEEK_SET)
96
+ end
97
+ end
98
+
80
99
  #-----------
81
100
  private
82
101
 
@@ -112,6 +131,26 @@ module BinData
112
131
  raise IOError, "stream is unseekable"
113
132
  end
114
133
 
134
+ # All io calls in +block+ are rolled back after this
135
+ # method completes.
136
+ def with_readahead(&block)
137
+ mark = @offset
138
+ @read_data = ""
139
+ @in_readahead = true
140
+
141
+ class << self
142
+ alias_method :read_raw_without_readahead, :read_raw
143
+ alias_method :read_raw, :read_raw_with_readahead
144
+ end
145
+
146
+ begin
147
+ block.call
148
+ ensure
149
+ @offset = mark
150
+ @in_readahead = false
151
+ end
152
+ end
153
+
115
154
  #-----------
116
155
  private
117
156
 
@@ -125,6 +164,33 @@ module BinData
125
164
  data
126
165
  end
127
166
 
167
+ def read_raw_with_readahead(n)
168
+ data = ""
169
+
170
+ if @read_data.length > 0 and not @in_readahead
171
+ bytes_to_consume = [n, @read_data.length].min
172
+ data << @read_data.slice!(0, bytes_to_consume)
173
+ n -= bytes_to_consume
174
+
175
+ if @read_data.length == 0
176
+ class << self
177
+ alias_method :read_raw, :read_raw_without_readahead
178
+ end
179
+ end
180
+ end
181
+
182
+ raw_data = @raw_io.read(n)
183
+ data << raw_data if raw_data
184
+
185
+ if @in_readahead
186
+ @read_data << data
187
+ end
188
+
189
+ @offset += data.size
190
+
191
+ data
192
+ end
193
+
128
194
  def write_raw(data)
129
195
  @offset += data.size
130
196
  @raw_io.write(data)
@@ -16,6 +16,16 @@ module BinData
16
16
  # obj = A.read("abcdefghij")
17
17
  # obj.a #=> "fghij"
18
18
  #
19
+ #
20
+ # class B < BinData::Record
21
+ # skip :until_valid => [:string, {:read_length => 2, :assert => "ef"} ]
22
+ # string :b, :read_length => 5
23
+ # end
24
+ #
25
+ # obj = B.read("abcdefghij")
26
+ # obj.b #=> "efghi"
27
+ #
28
+ #
19
29
  # == Parameters
20
30
  #
21
31
  # Skip objects accept all the params that BinData::BasePrimitive
@@ -23,17 +33,25 @@ module BinData
23
33
  #
24
34
  # <tt>:length</tt>:: The number of bytes to skip.
25
35
  # <tt>:to_abs_offset</tt>:: Skips to the given absolute offset.
36
+ # <tt>:until_valid</tt>:: Skips untils a given byte pattern is matched.
37
+ # This parameter contains a type that will raise
38
+ # a BinData::ValidityError unless an acceptable byte
39
+ # sequence is found. The type is represented by a
40
+ # Symbol, or if the type is to have params #
41
+ # passed to it, then it should be provided as #
42
+ # <tt>[type_symbol, hash_params]</tt>.
26
43
  #
27
44
  class Skip < BinData::BasePrimitive
28
45
 
29
46
  arg_processor :skip
30
47
 
31
- optional_parameters :length, :to_abs_offset
32
- mutually_exclusive_parameters :length, :to_abs_offset
48
+ optional_parameters :length, :to_abs_offset, :until_valid
49
+ mutually_exclusive_parameters :length, :to_abs_offset, :until_valid
33
50
 
34
51
  def initialize_shared_instance
35
52
  extend SkipLengthPlugin if has_parameter?(:length)
36
53
  extend SkipToAbsOffsetPlugin if has_parameter?(:to_abs_offset)
54
+ extend SkipUntilValidPlugin if has_parameter?(:until_valid)
37
55
  super
38
56
  end
39
57
 
@@ -66,10 +84,17 @@ module BinData
66
84
 
67
85
  class SkipArgProcessor < BaseArgProcessor
68
86
  def sanitize_parameters!(obj_class, params)
69
- unless (params.has_parameter?(:length) or params.has_parameter?(:to_abs_offset))
70
- raise ArgumentError, "#{obj_class} requires either :length or :to_abs_offset"
87
+ unless params.has_parameter?(:length) or
88
+ params.has_parameter?(:to_abs_offset) or
89
+ params.has_parameter?(:until_valid)
90
+ raise ArgumentError, "#{obj_class} requires either :length, :to_abs_offset or :until_valid"
71
91
  end
72
92
  params.must_be_integer(:to_abs_offset, :length)
93
+
94
+ if params.needs_sanitizing?(:until_valid)
95
+ el_type, el_params = params[:until_valid]
96
+ params[:until_valid] = params.create_sanitized_object_prototype(el_type, el_params)
97
+ end
73
98
  end
74
99
  end
75
100
 
@@ -86,4 +111,29 @@ module BinData
86
111
  eval_parameter(:to_abs_offset) - abs_offset
87
112
  end
88
113
  end
114
+
115
+ # Logic for the :until_valid parameter
116
+ module SkipUntilValidPlugin
117
+ def skip_length
118
+ # no skipping when writing
119
+ 0
120
+ end
121
+
122
+ def read_and_return_value(io)
123
+ prototype = get_parameter(:until_valid)
124
+ validator = prototype.instantiate(nil, self)
125
+
126
+ valid = false
127
+ until valid
128
+ begin
129
+ io.with_readahead do
130
+ validator.read(io)
131
+ valid = true
132
+ end
133
+ rescue ValidityError
134
+ io.readbytes(1)
135
+ end
136
+ end
137
+ end
138
+ end
89
139
  end
@@ -1,3 +1,3 @@
1
1
  module BinData
2
- VERSION = "2.3.1"
2
+ VERSION = "2.3.2"
3
3
  end
@@ -142,6 +142,85 @@ describe BinData::IO::Read, "#with_buffer" do
142
142
  io.offset.must_equal(2)
143
143
  end
144
144
  end
145
+
146
+ it "greater than stream size consumes all bytes" do
147
+ io.with_buffer(30) do
148
+ io.readbytes(4).must_equal "abcd"
149
+ end
150
+ io.offset.must_equal(20)
151
+ end
152
+
153
+ it "restricts #num_bytes_remaining" do
154
+ io.with_buffer(10) do
155
+ io.readbytes(2)
156
+ io.num_bytes_remaining.must_equal 8
157
+ end
158
+ end
159
+
160
+ it "greater than stream size doesn't restrict #num_bytes_remaining" do
161
+ io.with_buffer(30) do
162
+ io.readbytes(2)
163
+ io.num_bytes_remaining.must_equal 18
164
+ end
165
+ end
166
+ end
167
+
168
+ module IOReadWithReadahead
169
+ def test_rolls_back_short_reads
170
+ io.readbytes(2).must_equal "ab"
171
+ io.with_readahead do
172
+ io.readbytes(4).must_equal "cdef"
173
+ end
174
+ io.offset.must_equal 2
175
+ end
176
+
177
+ def test_rolls_back_read_all_bytes
178
+ io.readbytes(3).must_equal "abc"
179
+ io.with_readahead do
180
+ io.read_all_bytes.must_equal "defghijklmnopqrst"
181
+ end
182
+ io.offset.must_equal 3
183
+ end
184
+
185
+ def test_inside_buffer_rolls_back_reads
186
+ io.with_buffer(10) do
187
+ io.with_readahead do
188
+ io.readbytes(4).must_equal "abcd"
189
+ end
190
+ io.offset.must_equal 0
191
+ end
192
+ io.offset.must_equal 10
193
+ end
194
+
195
+ def test_outside_buffer_rolls_back_reads
196
+ io.with_readahead do
197
+ io.with_buffer(10) do
198
+ io.readbytes(4).must_equal "abcd"
199
+ end
200
+ io.offset.must_equal 10
201
+ end
202
+ io.offset.must_equal 0
203
+ end
204
+ end
205
+
206
+ describe BinData::IO::Read, "#with_readahead" do
207
+ let(:stream) { StringIO.new "abcdefghijklmnopqrst" }
208
+ let(:io) { BinData::IO::Read.new(stream) }
209
+
210
+ include IOReadWithReadahead
211
+ end
212
+
213
+ describe BinData::IO::Read, "unseekable stream #with_readahead" do
214
+ let(:stream) {
215
+ io = StringIO.new "abcdefghijklmnopqrst"
216
+ def io.pos
217
+ raise Errno::EPIPE
218
+ end
219
+ io
220
+ }
221
+ let(:io) { BinData::IO::Read.new(stream) }
222
+
223
+ include IOReadWithReadahead
145
224
  end
146
225
 
147
226
  describe BinData::IO::Write, "writing to non seekable stream" do
@@ -85,3 +85,53 @@ describe BinData::Skip, "with :to_abs_offset" do
85
85
  }.must_raise BinData::ValidityError
86
86
  end
87
87
  end
88
+
89
+ describe BinData::Skip, "with :until_valid" do
90
+ let(:io) { StringIO.new("abcdefghij") }
91
+
92
+ it "skips to valid match" do
93
+ skip_obj = [:string, { :read_length => 1, :assert => "f" }]
94
+ fields = [ [:skip, :s, { :until_valid => skip_obj }] ]
95
+ obj = BinData::Struct.new(:fields => fields)
96
+ obj.read(io)
97
+ io.pos.must_equal 5
98
+ end
99
+
100
+ it "doesn't skip when validator doesn't assert" do
101
+ skip_obj = [:string, { :read_length => 1 }]
102
+ fields = [ [:skip, :s, { :until_valid => skip_obj }] ]
103
+ obj = BinData::Struct.new(:fields => fields)
104
+ obj.read(io)
105
+ io.pos.must_equal 0
106
+ end
107
+
108
+ it "raises EOFError when no match" do
109
+ skip_obj = [:string, { :read_length => 1, :assert => "X" }]
110
+ fields = [ [:skip, :s, { :until_valid => skip_obj }] ]
111
+ obj = BinData::Struct.new(:fields => fields)
112
+ lambda {
113
+ obj.read(io)
114
+ }.must_raise EOFError
115
+ end
116
+
117
+ it "raises IOError when validator reads beyond stream" do
118
+ skip_obj = [:string, { :read_length => 30 }]
119
+ fields = [ [:skip, :s, { :until_valid => skip_obj }] ]
120
+ obj = BinData::Struct.new(:fields => fields)
121
+ lambda {
122
+ obj.read(io)
123
+ }.must_raise IOError
124
+ end
125
+
126
+ class DSLSkip < BinData::Record
127
+ skip :s do
128
+ string :read_length => 1, :assert => "f"
129
+ end
130
+ string :a, :read_length => 1
131
+ end
132
+
133
+ it "uses block form" do
134
+ obj = DSLSkip.read(io)
135
+ obj.a.must_equal "f"
136
+ end
137
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bindata
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.1
4
+ version: 2.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dion Mendel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-06-17 00:00:00.000000000 Z
11
+ date: 2016-09-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake