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 +4 -4
- data/.travis.yml +5 -4
- data/ChangeLog.rdoc +5 -0
- data/lib/bindata/dsl.rb +19 -16
- data/lib/bindata/io.rb +77 -11
- data/lib/bindata/skip.rb +54 -4
- data/lib/bindata/version.rb +1 -1
- data/test/io_test.rb +79 -0
- data/test/skip_test.rb +50 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a82037bfb06f2af645803e71fcaaf50ec475845a
|
4
|
+
data.tar.gz: 2b111c9173a7fb320fd5f98575e864da786dae0b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 44f161193a8242656fe81684dc419f1936abcb64232928abcd2ab32288c767ccdd2b4dfc7a7168a93af073b02c87c8a3e2fb6a638bc275dc121e67644c77f09a
|
7
|
+
data.tar.gz: 7258cad731fab07ddf5414733d950aefe7b0f78bdf08f886e8d4dcd18710f1c9eb716c22bdcb9128e18695c47334ff1b72dd734bea9997b17d3cb5da3cc7b4ab
|
data/.travis.yml
CHANGED
data/ChangeLog.rdoc
CHANGED
@@ -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.
|
data/lib/bindata/dsl.rb
CHANGED
@@ -122,7 +122,8 @@ module BinData
|
|
122
122
|
end
|
123
123
|
|
124
124
|
def dsl_params
|
125
|
-
|
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 => [:
|
140
|
-
:buffer => [:
|
141
|
-
:choice => [:to_choice_params, [:multiple_fields, :all_or_none_fieldnames, :fieldnames_are_values]],
|
142
|
-
:delayed_io => [:
|
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(
|
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
|
224
|
+
def to_object_params(key)
|
223
225
|
case fields.length
|
224
226
|
when 0
|
225
227
|
{}
|
226
228
|
when 1
|
227
|
-
{
|
229
|
+
{key => fields[0].prototype}
|
228
230
|
else
|
229
|
-
{
|
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
|
-
{
|
239
|
+
{key => fields.collect { |f| f.prototype }}
|
238
240
|
else
|
239
241
|
choices = {}
|
240
242
|
fields.each { |f| choices[f.name] = f.prototype }
|
241
|
-
{
|
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
|
-
:
|
391
|
+
:skip => BinData::Skip,
|
392
|
+
:struct => BinData::Struct,
|
390
393
|
}
|
391
394
|
|
392
395
|
if bindata_classes.include?(@type)
|
data/lib/bindata/io.rb
CHANGED
@@ -18,7 +18,7 @@ module BinData
|
|
18
18
|
end
|
19
19
|
|
20
20
|
@raw_io = io
|
21
|
-
@
|
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 @
|
41
|
+
if @buffer_end_points
|
42
42
|
if n.nil? or n > 0
|
43
|
-
max = @
|
43
|
+
max = @buffer_end_points[1] - offset
|
44
44
|
n = max if n.nil? or n > max
|
45
45
|
else
|
46
|
-
min = @
|
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 = @
|
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
|
-
@
|
60
|
+
@buffer_end_points = [offset, offset + n]
|
61
61
|
begin
|
62
|
-
block.call(*@
|
62
|
+
block.call(*@buffer_end_points)
|
63
63
|
ensure
|
64
|
-
@
|
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
|
-
|
72
|
+
start_mark = @raw_io.pos
|
73
73
|
@raw_io.seek(0, ::IO::SEEK_END)
|
74
|
-
|
75
|
-
|
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)
|
data/lib/bindata/skip.rb
CHANGED
@@ -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
|
70
|
-
|
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
|
data/lib/bindata/version.rb
CHANGED
data/test/io_test.rb
CHANGED
@@ -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
|
data/test/skip_test.rb
CHANGED
@@ -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.
|
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-
|
11
|
+
date: 2016-09-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|