bindata 2.4.15 → 2.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog.rdoc +16 -0
  3. data/README.md +7 -10
  4. data/bindata.gemspec +5 -4
  5. data/examples/list.rb +1 -1
  6. data/lib/bindata/alignment.rb +15 -7
  7. data/lib/bindata/array.rb +54 -54
  8. data/lib/bindata/base.rb +14 -25
  9. data/lib/bindata/base_primitive.rb +24 -20
  10. data/lib/bindata/bits.rb +5 -5
  11. data/lib/bindata/buffer.rb +89 -11
  12. data/lib/bindata/choice.rb +9 -6
  13. data/lib/bindata/count_bytes_remaining.rb +1 -1
  14. data/lib/bindata/delayed_io.rb +10 -10
  15. data/lib/bindata/dsl.rb +34 -32
  16. data/lib/bindata/float.rb +3 -3
  17. data/lib/bindata/framework.rb +8 -10
  18. data/lib/bindata/int.rb +9 -9
  19. data/lib/bindata/io.rb +276 -253
  20. data/lib/bindata/name.rb +1 -1
  21. data/lib/bindata/params.rb +9 -7
  22. data/lib/bindata/primitive.rb +3 -3
  23. data/lib/bindata/registry.rb +46 -51
  24. data/lib/bindata/rest.rb +1 -1
  25. data/lib/bindata/sanitize.rb +9 -16
  26. data/lib/bindata/section.rb +97 -0
  27. data/lib/bindata/skip.rb +140 -51
  28. data/lib/bindata/string.rb +9 -9
  29. data/lib/bindata/stringz.rb +12 -10
  30. data/lib/bindata/struct.rb +83 -66
  31. data/lib/bindata/trace.rb +35 -42
  32. data/lib/bindata/transform/brotli.rb +35 -0
  33. data/lib/bindata/transform/lz4.rb +35 -0
  34. data/lib/bindata/transform/lzma.rb +35 -0
  35. data/lib/bindata/transform/xor.rb +19 -0
  36. data/lib/bindata/transform/xz.rb +35 -0
  37. data/lib/bindata/transform/zlib.rb +33 -0
  38. data/lib/bindata/transform/zstd.rb +35 -0
  39. data/lib/bindata/uint8_array.rb +2 -2
  40. data/lib/bindata/version.rb +1 -1
  41. data/lib/bindata/virtual.rb +4 -7
  42. data/lib/bindata/warnings.rb +1 -1
  43. data/lib/bindata.rb +3 -2
  44. data/test/array_test.rb +10 -8
  45. data/test/buffer_test.rb +9 -0
  46. data/test/choice_test.rb +1 -1
  47. data/test/delayed_io_test.rb +16 -0
  48. data/test/io_test.rb +54 -246
  49. data/test/registry_test.rb +1 -1
  50. data/test/section_test.rb +111 -0
  51. data/test/skip_test.rb +55 -10
  52. data/test/string_test.rb +4 -4
  53. data/test/stringz_test.rb +8 -0
  54. data/test/struct_test.rb +87 -12
  55. data/test/system_test.rb +119 -1
  56. data/test/test_helper.rb +30 -15
  57. data/test/warnings_test.rb +12 -0
  58. metadata +20 -18
  59. data/lib/bindata/offset.rb +0 -94
  60. data/test/offset_test.rb +0 -100
data/lib/bindata.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # BinData -- Binary data manipulator.
2
- # Copyright (c) 2007 - 2018 Dion Mendel.
2
+ # Copyright (c) 2007 - 2025 Dion Mendel.
3
3
 
4
4
  require 'bindata/version'
5
5
  require 'bindata/array'
@@ -13,6 +13,7 @@ require 'bindata/int'
13
13
  require 'bindata/primitive'
14
14
  require 'bindata/record'
15
15
  require 'bindata/rest'
16
+ require 'bindata/section'
16
17
  require 'bindata/skip'
17
18
  require 'bindata/string'
18
19
  require 'bindata/stringz'
@@ -34,4 +35,4 @@ require 'bindata/warnings'
34
35
  #
35
36
  # BinData is released under the same license as Ruby.
36
37
  #
37
- # Copyright (c) 2007 - 2018 Dion Mendel.
38
+ # Copyright (c) 2007 - 2025 Dion Mendel.
data/test/array_test.rb CHANGED
@@ -18,17 +18,15 @@ describe BinData::Array, "when instantiating" do
18
18
  end
19
19
 
20
20
  it "warns about :length" do
21
- Kernel.must_warn ":length is not used with BinData::Array. You probably want to change this to :initial_length" do
22
- obj = BinData::Array.new(type: :uint8, length: 3)
23
- obj.read "123"
24
- end
21
+ _ {
22
+ BinData::Array.new(type: :uint8, length: 3)
23
+ }.must_warn ":length is not used with BinData::Array. You probably want to change this to :initial_length"
25
24
  end
26
25
 
27
26
  it "warns about :read_length" do
28
- Kernel.must_warn ":read_length is not used with BinData::Array. You probably want to change this to :initial_length" do
29
- obj = BinData::Array.new(type: :uint8, read_length: 3)
30
- obj.read "123"
31
- end
27
+ _ {
28
+ BinData::Array.new(type: :uint8, read_length: 3)
29
+ }.must_warn ":read_length is not used with BinData::Array. You probably want to change this to :initial_length"
32
30
  end
33
31
 
34
32
  it "fails if a given type is unknown" do
@@ -71,6 +69,10 @@ describe BinData::Array, "with no elements" do
71
69
  it "returns [] for the last n elements" do
72
70
  _(obj.last(3)).must_equal []
73
71
  end
72
+
73
+ it "won't assign nil" do
74
+ _ { obj.assign(nil) }.must_raise ArgumentError
75
+ end
74
76
  end
75
77
 
76
78
  describe BinData::Array, "with several elements" do
data/test/buffer_test.rb CHANGED
@@ -144,6 +144,15 @@ describe BinData::Buffer, "nested buffers" do
144
144
  _(obj.b).must_equal "klmno"
145
145
  end
146
146
 
147
+ it "pads undersize writes" do
148
+ obj = NestedBufferRecord.new
149
+ obj.a.aa = "abc"
150
+ obj.a.bb = "ABC"
151
+ obj.b = "123"
152
+
153
+ _(obj.to_binary_s).must_equal_binary "abc\000\000ABC\000\000123"
154
+ end
155
+
147
156
  it "restricts oversize writes" do
148
157
  obj = NestedBufferRecord.new
149
158
  obj.a.aa = "abcdefghij"
data/test/choice_test.rb CHANGED
@@ -243,7 +243,7 @@ describe BinData::Choice, "subclassed with default parameters" do
243
243
  _(obj.num_bytes).must_equal 2
244
244
  end
245
245
 
246
- it "overides default parameter" do
246
+ it "overrides default parameter" do
247
247
  obj = DerivedChoice.new(selection: 'b')
248
248
  _(obj.num_bytes).must_equal 4
249
249
  end
@@ -266,3 +266,19 @@ describe BinData::DelayedIO, "with auto_call" do
266
266
  _(obj.num_bytes).must_equal 2
267
267
  end
268
268
  end
269
+
270
+ describe BinData::DelayedIO, "with multiple auto_call" do
271
+ class MultipleAutoCallDelayedIORecord < BinData::Record
272
+ auto_call_delayed_io
273
+ auto_call_delayed_io
274
+ uint8 :a
275
+ delayed_io :b, read_abs_offset: 1 do
276
+ uint8
277
+ end
278
+ end
279
+
280
+ it "class reads" do
281
+ obj = MultipleAutoCallDelayedIORecord.read "\x01\x02"
282
+ _(obj.snapshot).must_equal({a: 1, b: 2})
283
+ end
284
+ end
data/test/io_test.rb CHANGED
@@ -15,16 +15,24 @@ describe BinData::IO::Read, "reading from non seekable stream" do
15
15
  @rd.close
16
16
  end
17
17
 
18
- it "has correct offset" do
19
- @io.readbytes(10)
20
- _(@io.offset).must_equal 10
18
+ it "seeks correctly" do
19
+ @io.skipbytes(1999)
20
+ _(@io.readbytes(5)).must_equal "abbbb"
21
21
  end
22
22
 
23
- it "seeks correctly" do
24
- @io.seekbytes(1999)
23
+ it "seeks to abs_offset" do
24
+ @io.skipbytes(1000)
25
+ @io.seek_to_abs_offset(1999)
25
26
  _(@io.readbytes(5)).must_equal "abbbb"
26
27
  end
27
28
 
29
+ it "wont seek backwards" do
30
+ @io.skipbytes(5)
31
+ _ {
32
+ @io.skipbytes(-1)
33
+ }.must_raise IOError
34
+ end
35
+
28
36
  it "#num_bytes_remaining raises IOError" do
29
37
  _ {
30
38
  @io.num_bytes_remaining
@@ -32,6 +40,35 @@ describe BinData::IO::Read, "reading from non seekable stream" do
32
40
  end
33
41
  end
34
42
 
43
+ describe BinData::IO::Write, "writing to non seekable stream" do
44
+ before do
45
+ @rd, @wr = IO::pipe
46
+ @io = BinData::IO::Write.new(@wr)
47
+ end
48
+
49
+ after do
50
+ @rd.close
51
+ @wr.close
52
+ end
53
+
54
+ def written_data
55
+ @io.flush
56
+ @wr.close
57
+ @rd.read
58
+ end
59
+
60
+ it "writes correctly" do
61
+ @io.writebytes("hello")
62
+ _(written_data).must_equal "hello"
63
+ end
64
+
65
+ it "must not attempt to seek" do
66
+ _ {
67
+ @io.seek_to_abs_offset(5)
68
+ }.must_raise IOError
69
+ end
70
+ end
71
+
35
72
  describe BinData::IO::Read, "when reading" do
36
73
  let(:stream) { StringIO.new "abcdefghij" }
37
74
  let(:io) { BinData::IO::Read.new(stream) }
@@ -42,19 +79,19 @@ describe BinData::IO::Read, "when reading" do
42
79
  }.must_raise ArgumentError
43
80
  end
44
81
 
45
- it "returns correct offset" do
46
- stream.seek(3, IO::SEEK_CUR)
47
-
48
- _(io.offset).must_equal 0
49
- _(io.readbytes(4)).must_equal "defg"
50
- _(io.offset).must_equal 4
51
- end
52
-
53
82
  it "seeks correctly" do
54
- io.seekbytes(2)
83
+ io.skipbytes(2)
55
84
  _(io.readbytes(4)).must_equal "cdef"
56
85
  end
57
86
 
87
+ it "wont seek backwards" do
88
+ io.skipbytes(5)
89
+ _ {
90
+ io.skipbytes(-1)
91
+ }.must_raise IOError
92
+ end
93
+
94
+
58
95
  it "reads all bytes" do
59
96
  _(io.read_all_bytes).must_equal "abcdefghij"
60
97
  end
@@ -67,7 +104,7 @@ describe BinData::IO::Read, "when reading" do
67
104
  end
68
105
 
69
106
  it "raises error when reading at eof" do
70
- io.seekbytes(10)
107
+ io.skipbytes(10)
71
108
  _ {
72
109
  io.readbytes(3)
73
110
  }.must_raise EOFError
@@ -80,149 +117,6 @@ describe BinData::IO::Read, "when reading" do
80
117
  end
81
118
  end
82
119
 
83
- describe BinData::IO::Read, "#with_buffer" do
84
- let(:stream) { StringIO.new "abcdefghijklmnopqrst" }
85
- let(:io) { BinData::IO::Read.new(stream) }
86
-
87
- it "consumes entire buffer on short reads" do
88
- io.with_buffer(10) do
89
- _(io.readbytes(4)).must_equal "abcd"
90
- end
91
- _(io.offset).must_equal(10)
92
- end
93
-
94
- it "consumes entire buffer on read_all_bytes" do
95
- io.with_buffer(10) do
96
- _(io.read_all_bytes).must_equal "abcdefghij"
97
- end
98
- _(io.offset).must_equal(10)
99
- end
100
-
101
- it "restricts large reads" do
102
- io.with_buffer(10) do
103
- _ {
104
- io.readbytes(15)
105
- }.must_raise IOError
106
- end
107
- end
108
-
109
- it "is nestable" do
110
- io.with_buffer(10) do
111
- _(io.readbytes(2)).must_equal "ab"
112
- io.with_buffer(5) do
113
- _(io.read_all_bytes).must_equal "cdefg"
114
- end
115
- _(io.offset).must_equal(2 + 5)
116
- end
117
- _(io.offset).must_equal(10)
118
- end
119
-
120
- it "restricts large nested buffers" do
121
- io.with_buffer(10) do
122
- _(io.readbytes(2)).must_equal "ab"
123
- io.with_buffer(20) do
124
- _(io.read_all_bytes).must_equal "cdefghij"
125
- _(io.offset).must_equal(10)
126
- end
127
- end
128
- _(io.offset).must_equal(10)
129
- end
130
-
131
- it "restricts large seeks" do
132
- io.with_buffer(10) do
133
- io.seekbytes(15)
134
- end
135
- _(io.offset).must_equal(10)
136
- end
137
-
138
- it "restricts large -ve seeks" do
139
- io.readbytes(2)
140
- io.with_buffer(10) do
141
- io.seekbytes(-1)
142
- _(io.offset).must_equal(2)
143
- end
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
224
- end
225
-
226
120
  describe BinData::IO::Write, "writing to non seekable stream" do
227
121
  before do
228
122
  @rd, @wr = IO::pipe
@@ -238,30 +132,6 @@ describe BinData::IO::Write, "writing to non seekable stream" do
238
132
  @io.writebytes("1234567890")
239
133
  _(@rd.read(10)).must_equal "1234567890"
240
134
  end
241
-
242
- it "has correct offset" do
243
- @io.writebytes("1234567890")
244
- _(@io.offset).must_equal 10
245
- end
246
-
247
- it "does not seek backwards" do
248
- @io.writebytes("1234567890")
249
- _ {
250
- @io.seekbytes(-5)
251
- }.must_raise IOError
252
- end
253
-
254
- it "does not seek forwards" do
255
- _ {
256
- @io.seekbytes(5)
257
- }.must_raise IOError
258
- end
259
-
260
- it "#num_bytes_remaining raises IOError" do
261
- _ {
262
- @io.num_bytes_remaining
263
- }.must_raise IOError
264
- end
265
135
  end
266
136
 
267
137
  describe BinData::IO::Write, "when writing" do
@@ -280,21 +150,6 @@ describe BinData::IO::Write, "when writing" do
280
150
  _(stream.value).must_equal "abcd"
281
151
  end
282
152
 
283
- it "has #offset" do
284
- _(io.offset).must_equal 0
285
-
286
- io.writebytes("abcd")
287
- _(io.offset).must_equal 4
288
-
289
- io.writebytes("ABCD")
290
- _(io.offset).must_equal 8
291
- end
292
-
293
- it "rounds up #offset when writing bits" do
294
- io.writebits(123, 9, :little)
295
- _(io.offset).must_equal 2
296
- end
297
-
298
153
  it "flushes" do
299
154
  io.writebytes("abcd")
300
155
  io.flush
@@ -303,53 +158,6 @@ describe BinData::IO::Write, "when writing" do
303
158
  end
304
159
  end
305
160
 
306
- describe BinData::IO::Write, "#with_buffer" do
307
- let(:stream) { StringIO.new }
308
- let(:io) { BinData::IO::Write.new(stream) }
309
-
310
- it "pads entire buffer on short reads" do
311
- io.with_buffer(10) do
312
- io.writebytes "abcde"
313
- end
314
-
315
- _(stream.value).must_equal "abcde\0\0\0\0\0"
316
- end
317
-
318
- it "discards excess on large writes" do
319
- io.with_buffer(5) do
320
- io.writebytes "abcdefghij"
321
- end
322
-
323
- _(stream.value).must_equal "abcde"
324
- end
325
-
326
- it "is nestable" do
327
- io.with_buffer(10) do
328
- io.with_buffer(5) do
329
- io.writebytes "abc"
330
- end
331
- io.writebytes "de"
332
- end
333
-
334
- _(stream.value).must_equal "abc\0\0de\0\0\0"
335
- end
336
-
337
- it "restricts large seeks" do
338
- io.with_buffer(10) do
339
- io.seekbytes(15)
340
- end
341
- _(io.offset).must_equal(10)
342
- end
343
-
344
- it "restricts large -ve seeks" do
345
- io.writebytes("12")
346
- io.with_buffer(10) do
347
- io.seekbytes(-1)
348
- _(io.offset).must_equal(2)
349
- end
350
- end
351
- end
352
-
353
161
  describe BinData::IO::Read, "reading bits in big endian" do
354
162
  let(:b1) { 0b1111_1010 }
355
163
  let(:b2) { 0b1100_1110 }
@@ -554,7 +362,7 @@ describe BinData::IO::Write, "writing bits in little endian" do
554
362
  end
555
363
 
556
364
  describe BinData::IO::Read, "with changing endian" do
557
- it "does not mix different endianess when reading" do
365
+ it "does not mix different endianness when reading" do
558
366
  b1 = 0b0110_1010
559
367
  b2 = 0b1110_0010
560
368
  str = [b1, b2].pack("CC")
@@ -566,7 +374,7 @@ describe BinData::IO::Read, "with changing endian" do
566
374
  end
567
375
 
568
376
  describe BinData::IO::Write, "with changing endian" do
569
- it "does not mix different endianess when writing" do
377
+ it "does not mix different endianness when writing" do
570
378
  io = BitWriterHelper.new
571
379
  io.writebits(0b110, 3, :big)
572
380
  io.writebits(0b010, 3, :little)
@@ -36,7 +36,7 @@ describe BinData::Registry do
36
36
  end
37
37
 
38
38
  it "allows overriding of registered classes" do
39
- w, $-w = $-w, false
39
+ w, $-w = $-w, nil # disable warning
40
40
 
41
41
  begin
42
42
  r.register('A', A)
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
4
+
5
+ describe BinData::Section do
6
+ it "transforms data byte at a time" do
7
+ require 'bindata/transform/xor'
8
+
9
+ obj = BinData::Section.new(transform: -> { BinData::Transform::Xor.new(0xff) },
10
+ type: [:string, read_length: 5])
11
+
12
+ _(obj.read("\x97\x9A\x93\x93\x90")).must_equal "hello"
13
+ end
14
+
15
+ begin
16
+ require 'brotli'
17
+ it "transform brotli" do
18
+ require 'bindata/transform/brotli'
19
+
20
+ class BrotliRecord < BinData::Record
21
+ int32le :len, value: -> { s.num_bytes }
22
+ section :s, transform: -> { BinData::Transform::Brotli.new(len) } do
23
+ int32le :str_len, value: -> { str.length }
24
+ string :str, read_length: :str_len
25
+
26
+ end
27
+ end
28
+
29
+ obj = BrotliRecord.new
30
+ data = "highly compressible" * 100
31
+ obj.s.str = data
32
+ _(obj.len).must_be :<, (data.length / 10)
33
+
34
+ str = obj.to_binary_s
35
+ obj = BrotliRecord.read(str)
36
+ _(obj.s.str).must_equal data
37
+ end
38
+ rescue LoadError; end
39
+
40
+ begin
41
+ require 'extlz4'
42
+ it "transform lz4" do
43
+ require 'bindata/transform/lz4'
44
+
45
+ class LZ4Record < BinData::Record
46
+ int32le :len, value: -> { s.num_bytes }
47
+ section :s, transform: -> { BinData::Transform::LZ4.new(len) } do
48
+ int32le :str_len, value: -> { str.length }
49
+ string :str, read_length: :str_len
50
+
51
+ end
52
+ end
53
+
54
+ obj = LZ4Record.new
55
+ data = "highly compressible" * 100
56
+ obj.s.str = data
57
+ _(obj.len).must_be :<, (data.length / 10)
58
+
59
+ str = obj.to_binary_s
60
+ obj = LZ4Record.read(str)
61
+ _(obj.s.str).must_equal data
62
+ end
63
+ rescue LoadError; end
64
+
65
+ it "transform zlib" do
66
+ require 'bindata/transform/zlib'
67
+
68
+ class ZlibRecord < BinData::Record
69
+ int32le :len, value: -> { s.num_bytes }
70
+ section :s, transform: -> { BinData::Transform::Zlib.new(len) } do
71
+ int32le :str_len, value: -> { str.length }
72
+ string :str, read_length: :str_len
73
+
74
+ end
75
+ end
76
+
77
+ obj = ZlibRecord.new
78
+ data = "highly compressible" * 100
79
+ obj.s.str = data
80
+ _(obj.len).must_be :<, (data.length / 10)
81
+
82
+ str = obj.to_binary_s
83
+ obj = ZlibRecord.read(str)
84
+ _(obj.s.str).must_equal data
85
+ end
86
+
87
+ begin
88
+ require 'zstd-ruby'
89
+ it "transform zstd" do
90
+ require 'bindata/transform/zstd'
91
+
92
+ class ZstdRecord < BinData::Record
93
+ int32le :len, value: -> { s.num_bytes }
94
+ section :s, transform: -> { BinData::Transform::Zstd.new(len) } do
95
+ int32le :str_len, value: -> { str.length }
96
+ string :str, read_length: :str_len
97
+
98
+ end
99
+ end
100
+
101
+ obj = ZstdRecord.new
102
+ data = "highly compressible" * 100
103
+ obj.s.str = data
104
+ _(obj.len).must_be :<, (data.length / 10)
105
+
106
+ str = obj.to_binary_s
107
+ obj = ZstdRecord.read(str)
108
+ _(obj.s.str).must_equal data
109
+ end
110
+ rescue LoadError; end
111
+ end
data/test/skip_test.rb CHANGED
@@ -62,7 +62,7 @@ describe BinData::Skip, "with :to_abs_offset" do
62
62
 
63
63
  _ {
64
64
  obj.read(io)
65
- }.must_raise BinData::ValidityError
65
+ }.must_raise ArgumentError
66
66
  end
67
67
 
68
68
  it "writes skipping forward" do
@@ -82,13 +82,20 @@ describe BinData::Skip, "with :to_abs_offset" do
82
82
  obj = BinData::Struct.new(fields: fields)
83
83
  _ {
84
84
  obj.to_binary_s
85
- }.must_raise BinData::ValidityError
85
+ }.must_raise ArgumentError
86
86
  end
87
87
  end
88
88
 
89
89
  describe BinData::Skip, "with :until_valid" do
90
90
  let(:io) { StringIO.new("abcdefghij") }
91
91
 
92
+ it "doesn't skip when writing" do
93
+ skip_obj = [:string, { read_length: 1, assert: "f" }]
94
+ args = { until_valid: skip_obj }
95
+ obj = BinData::Skip.new(args)
96
+ _(obj.to_binary_s).must_equal ""
97
+ end
98
+
92
99
  it "skips to valid match" do
93
100
  skip_obj = [:string, { read_length: 1, assert: "f" }]
94
101
  fields = [ [:skip, :s, { until_valid: skip_obj }] ]
@@ -97,6 +104,19 @@ describe BinData::Skip, "with :until_valid" do
97
104
  _(io.pos).must_equal 5
98
105
  end
99
106
 
107
+ it "won't skip on unseekable stream" do
108
+ rd, wr = IO::pipe
109
+ unseekable_io = BinData::IO::Read.new(rd)
110
+ wr.write io
111
+ wr.close
112
+
113
+ skip_obj = [:string, { read_length: 1, assert: "f" }]
114
+ fields = [ [:skip, :s, { until_valid: skip_obj }] ]
115
+ obj = BinData::Struct.new(fields: fields)
116
+ _ {obj.read(unseekable_io)}.must_raise IOError
117
+ rd.close
118
+ end
119
+
100
120
  it "doesn't skip when validator doesn't assert" do
101
121
  skip_obj = [:string, { read_length: 1 }]
102
122
  fields = [ [:skip, :s, { until_valid: skip_obj }] ]
@@ -105,13 +125,13 @@ describe BinData::Skip, "with :until_valid" do
105
125
  _(io.pos).must_equal 0
106
126
  end
107
127
 
108
- it "raises EOFError when no match" do
128
+ it "raises IOError when no match" do
109
129
  skip_obj = [:string, { read_length: 1, assert: "X" }]
110
130
  fields = [ [:skip, :s, { until_valid: skip_obj }] ]
111
131
  obj = BinData::Struct.new(fields: fields)
112
132
  _ {
113
133
  obj.read(io)
114
- }.must_raise EOFError
134
+ }.must_raise IOError
115
135
  end
116
136
 
117
137
  it "raises IOError when validator reads beyond stream" do
@@ -123,15 +143,40 @@ describe BinData::Skip, "with :until_valid" do
123
143
  }.must_raise IOError
124
144
  end
125
145
 
126
- class DSLSkip < BinData::Record
127
- skip :s do
128
- string read_length: 1, assert: "f"
146
+ it "uses block form" do
147
+ class DSLSkip < BinData::Record
148
+ skip :s do
149
+ string read_length: 1, assert: "f"
150
+ end
151
+ string :a, read_length: 1
129
152
  end
130
- string :a, read_length: 1
131
- end
132
153
 
133
- it "uses block form" do
134
154
  obj = DSLSkip.read(io)
135
155
  _(obj.a).must_equal "f"
136
156
  end
137
157
  end
158
+
159
+ describe BinData::Skip, "with :until_valid" do
160
+ class SkipSearch < BinData::Record
161
+ skip :s do
162
+ uint8
163
+ uint8 asserted_value: 1
164
+ uint8 :a
165
+ uint8 :b
166
+ virtual assert: -> { a == b }
167
+ end
168
+ array :data, type: :uint8, initial_length: 4
169
+ end
170
+
171
+ let(:io) { BinData::IO.create_string_io("\x0f" * 10 + "\x00\x01\x02\x03\x00" + "\x02\x01\x03\x03" + "\x06") }
172
+
173
+ it "finds valid match" do
174
+ obj = SkipSearch.read(io)
175
+ _(obj.data).must_equal [2, 1, 3, 3]
176
+ end
177
+
178
+ it "match is at expected offset" do
179
+ obj = SkipSearch.read(io)
180
+ _(obj.data.rel_offset).must_equal 15
181
+ end
182
+ end