bindata 2.2.0 → 2.3.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 +4 -4
- data/ChangeLog.rdoc +7 -0
- data/NEWS.rdoc +14 -0
- data/lib/bindata.rb +1 -0
- data/lib/bindata/alignment.rb +4 -3
- data/lib/bindata/array.rb +3 -2
- data/lib/bindata/base.rb +47 -31
- data/lib/bindata/base_primitive.rb +2 -7
- data/lib/bindata/bits.rb +4 -1
- data/lib/bindata/buffer.rb +0 -4
- data/lib/bindata/choice.rb +0 -4
- data/lib/bindata/delayed_io.rb +194 -0
- data/lib/bindata/dsl.rb +17 -23
- data/lib/bindata/framework.rb +5 -0
- data/lib/bindata/int.rb +1 -1
- data/lib/bindata/io.rb +174 -152
- data/lib/bindata/lazy.rb +1 -1
- data/lib/bindata/offset.rb +14 -0
- data/lib/bindata/primitive.rb +1 -0
- data/lib/bindata/sanitize.rb +11 -11
- data/lib/bindata/skip.rb +47 -6
- data/lib/bindata/struct.rb +1 -1
- data/lib/bindata/version.rb +1 -1
- data/lib/bindata/warnings.rb +0 -7
- data/test/alignment_test.rb +4 -0
- data/test/array_test.rb +19 -0
- data/test/base_primitive_test.rb +6 -0
- data/test/base_test.rb +2 -2
- data/test/bits_test.rb +10 -0
- data/test/buffer_test.rb +1 -0
- data/test/delayed_io_test.rb +223 -0
- data/test/int_test.rb +0 -1
- data/test/io_test.rb +64 -0
- data/test/offset_test.rb +1 -0
- data/test/record_test.rb +19 -1
- data/test/skip_test.rb +60 -1
- data/test/string_test.rb +0 -13
- data/test/struct_test.rb +10 -1
- data/test/test_helper.rb +11 -0
- metadata +4 -2
data/lib/bindata/dsl.rb
CHANGED
@@ -135,11 +135,12 @@ module BinData
|
|
135
135
|
|
136
136
|
def parser_abilities
|
137
137
|
@abilities ||= {
|
138
|
-
:struct
|
139
|
-
:array
|
140
|
-
:buffer
|
141
|
-
:choice
|
142
|
-
:
|
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]]
|
143
144
|
}
|
144
145
|
end
|
145
146
|
|
@@ -153,10 +154,7 @@ module BinData
|
|
153
154
|
end
|
154
155
|
|
155
156
|
def hints
|
156
|
-
{
|
157
|
-
:endian => endian,
|
158
|
-
:search_prefix => search_prefix,
|
159
|
-
}
|
157
|
+
{ :endian => endian, :search_prefix => search_prefix }
|
160
158
|
end
|
161
159
|
|
162
160
|
def set_endian(endian)
|
@@ -185,7 +183,7 @@ module BinData
|
|
185
183
|
end
|
186
184
|
|
187
185
|
def has_fields?
|
188
|
-
@fields
|
186
|
+
defined? @fields and @fields.length > 0
|
189
187
|
end
|
190
188
|
|
191
189
|
def parse_and_append_field(*args, &block)
|
@@ -289,7 +287,7 @@ module BinData
|
|
289
287
|
}
|
290
288
|
bnl_class.define_singleton_method(:new) do |*args|
|
291
289
|
if self == bnl_class
|
292
|
-
|
290
|
+
_, options, _ = arg_processor.separate_args(self, args)
|
293
291
|
delegate = endian_classes[options[:endian]]
|
294
292
|
return delegate.new(*args) if delegate
|
295
293
|
end
|
@@ -339,13 +337,8 @@ module BinData
|
|
339
337
|
RegisteredClasses.lookup(class_name, hints)
|
340
338
|
end
|
341
339
|
|
342
|
-
def obj_attribute(obj, attr
|
343
|
-
|
344
|
-
if parser and parser.respond_to?(attr)
|
345
|
-
parser.send(attr)
|
346
|
-
else
|
347
|
-
default
|
348
|
-
end
|
340
|
+
def obj_attribute(obj, attr)
|
341
|
+
obj.dsl_parser.send(attr)
|
349
342
|
end
|
350
343
|
end
|
351
344
|
end
|
@@ -362,7 +355,7 @@ module BinData
|
|
362
355
|
attr_reader :type, :name, :params
|
363
356
|
|
364
357
|
def name_from_field_declaration(args)
|
365
|
-
name,
|
358
|
+
name, _ = args
|
366
359
|
if name == "" or name.is_a?(Hash)
|
367
360
|
nil
|
368
361
|
else
|
@@ -389,10 +382,11 @@ module BinData
|
|
389
382
|
|
390
383
|
def params_from_block(&block)
|
391
384
|
bindata_classes = {
|
392
|
-
:array
|
393
|
-
:buffer
|
394
|
-
:choice
|
395
|
-
:
|
385
|
+
:array => BinData::Array,
|
386
|
+
:buffer => BinData::Buffer,
|
387
|
+
:choice => BinData::Choice,
|
388
|
+
:delayed_io => BinData::DelayedIO,
|
389
|
+
:struct => BinData::Struct
|
396
390
|
}
|
397
391
|
|
398
392
|
if bindata_classes.include?(@type)
|
data/lib/bindata/framework.rb
CHANGED
data/lib/bindata/int.rb
CHANGED
data/lib/bindata/io.rb
CHANGED
@@ -4,6 +4,147 @@ module BinData
|
|
4
4
|
# A wrapper around an IO object. The wrapper provides a consistent
|
5
5
|
# interface for BinData objects to use when accessing the IO.
|
6
6
|
module IO
|
7
|
+
|
8
|
+
# Common operations for both Read and Write.
|
9
|
+
module Common
|
10
|
+
def initialize(io)
|
11
|
+
if self.class === io
|
12
|
+
raise ArgumentError, "io must not be a #{self.class}"
|
13
|
+
end
|
14
|
+
|
15
|
+
# wrap strings in a StringIO
|
16
|
+
if io.respond_to?(:to_str)
|
17
|
+
io = BinData::IO.create_string_io(io.to_str)
|
18
|
+
end
|
19
|
+
|
20
|
+
@raw_io = io
|
21
|
+
@buffer_end_pos = nil
|
22
|
+
|
23
|
+
extend seekable? ? SeekableStream : UnSeekableStream
|
24
|
+
stream_init
|
25
|
+
end
|
26
|
+
|
27
|
+
#-------------
|
28
|
+
private
|
29
|
+
|
30
|
+
def seekable?
|
31
|
+
@raw_io.pos
|
32
|
+
rescue NoMethodError, Errno::ESPIPE, Errno::EPIPE
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def seek(n)
|
37
|
+
seek_raw(buffer_limited_n(n))
|
38
|
+
end
|
39
|
+
|
40
|
+
def buffer_limited_n(n)
|
41
|
+
if @buffer_end_pos
|
42
|
+
if n.nil? or n > 0
|
43
|
+
max = @buffer_end_pos[1] - offset
|
44
|
+
n = max if n.nil? or n > max
|
45
|
+
else
|
46
|
+
min = @buffer_end_pos[0] - offset
|
47
|
+
n = min if n < min
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
n
|
52
|
+
end
|
53
|
+
|
54
|
+
def with_buffer_common(n, &block)
|
55
|
+
prev = @buffer_end_pos
|
56
|
+
if prev
|
57
|
+
avail = prev[1] - offset
|
58
|
+
n = avail if n > avail
|
59
|
+
end
|
60
|
+
@buffer_end_pos = [offset, offset + n]
|
61
|
+
begin
|
62
|
+
block.call(*@buffer_end_pos)
|
63
|
+
ensure
|
64
|
+
@buffer_end_pos = prev
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Use #seek and #pos on seekable streams
|
69
|
+
module SeekableStream
|
70
|
+
# The number of bytes remaining in the input stream.
|
71
|
+
def num_bytes_remaining
|
72
|
+
mark = @raw_io.pos
|
73
|
+
@raw_io.seek(0, ::IO::SEEK_END)
|
74
|
+
bytes_remaining = @raw_io.pos - mark
|
75
|
+
@raw_io.seek(mark, ::IO::SEEK_SET)
|
76
|
+
|
77
|
+
bytes_remaining
|
78
|
+
end
|
79
|
+
|
80
|
+
#-----------
|
81
|
+
private
|
82
|
+
|
83
|
+
def stream_init
|
84
|
+
@initial_pos = @raw_io.pos
|
85
|
+
end
|
86
|
+
|
87
|
+
def offset_raw
|
88
|
+
@raw_io.pos - @initial_pos
|
89
|
+
end
|
90
|
+
|
91
|
+
def seek_raw(n)
|
92
|
+
@raw_io.seek(n, ::IO::SEEK_CUR)
|
93
|
+
end
|
94
|
+
|
95
|
+
def read_raw(n)
|
96
|
+
@raw_io.read(n)
|
97
|
+
end
|
98
|
+
|
99
|
+
def write_raw(data)
|
100
|
+
@raw_io.write(data)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Manually keep track of offset for unseekable streams.
|
105
|
+
module UnSeekableStream
|
106
|
+
def offset_raw
|
107
|
+
@offset
|
108
|
+
end
|
109
|
+
|
110
|
+
# The number of bytes remaining in the input stream.
|
111
|
+
def num_bytes_remaining
|
112
|
+
raise IOError, "stream is unseekable"
|
113
|
+
end
|
114
|
+
|
115
|
+
#-----------
|
116
|
+
private
|
117
|
+
|
118
|
+
def stream_init
|
119
|
+
@offset = 0
|
120
|
+
end
|
121
|
+
|
122
|
+
def read_raw(n)
|
123
|
+
data = @raw_io.read(n)
|
124
|
+
@offset += data.size if data
|
125
|
+
data
|
126
|
+
end
|
127
|
+
|
128
|
+
def write_raw(data)
|
129
|
+
@offset += data.size
|
130
|
+
@raw_io.write(data)
|
131
|
+
end
|
132
|
+
|
133
|
+
def seek_raw(n)
|
134
|
+
raise IOError, "stream is unseekable" if n < 0
|
135
|
+
|
136
|
+
# NOTE: how do we seek on a writable stream?
|
137
|
+
|
138
|
+
# skip over data in 8k blocks
|
139
|
+
while n > 0
|
140
|
+
bytes_to_read = [n, 8192].min
|
141
|
+
read_raw(bytes_to_read)
|
142
|
+
n -= bytes_to_read
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
7
148
|
# Creates a StringIO around +str+.
|
8
149
|
def self.create_string_io(str = "")
|
9
150
|
StringIO.new(str.dup.force_encoding(Encoding::BINARY))
|
@@ -27,43 +168,32 @@ module BinData
|
|
27
168
|
# readbits(6), readbits(5) #=> [543210, a9876]
|
28
169
|
#
|
29
170
|
class Read
|
30
|
-
|
31
|
-
raise ArgumentError, "io must not be a BinData::IO::Read" if BinData::IO::Read === io
|
32
|
-
|
33
|
-
# wrap strings in a StringIO
|
34
|
-
if io.respond_to?(:to_str)
|
35
|
-
io = BinData::IO.create_string_io(io.to_str)
|
36
|
-
end
|
171
|
+
include Common
|
37
172
|
|
38
|
-
|
173
|
+
def initialize(io)
|
174
|
+
super(io)
|
39
175
|
|
40
176
|
# bits when reading
|
41
177
|
@rnbits = 0
|
42
178
|
@rval = 0
|
43
179
|
@rendian = nil
|
44
|
-
|
45
|
-
@buffer_end_pos = nil
|
46
|
-
|
47
|
-
extend seekable? ? SeekableStream : UnSeekableStream
|
48
180
|
end
|
49
181
|
|
50
182
|
# Sets a buffer of +n+ bytes on the io stream. Any reading or seeking
|
51
183
|
# calls inside the +block+ will be contained within this buffer.
|
52
184
|
def with_buffer(n, &block)
|
53
|
-
|
54
|
-
if prev
|
55
|
-
avail = prev - offset
|
56
|
-
n = avail if n > avail
|
57
|
-
end
|
58
|
-
@buffer_end_pos = offset + n
|
59
|
-
begin
|
185
|
+
with_buffer_common(n) do
|
60
186
|
block.call
|
61
187
|
read
|
62
|
-
ensure
|
63
|
-
@buffer_end_pos = prev
|
64
188
|
end
|
65
189
|
end
|
66
190
|
|
191
|
+
# Returns the current offset of the io stream. Offset will be rounded
|
192
|
+
# up when reading bitfields.
|
193
|
+
def offset
|
194
|
+
offset_raw
|
195
|
+
end
|
196
|
+
|
67
197
|
# Seek +n+ bytes from the current position in the io stream.
|
68
198
|
def seekbytes(n)
|
69
199
|
reset_read_bits
|
@@ -116,29 +246,10 @@ module BinData
|
|
116
246
|
#---------------
|
117
247
|
private
|
118
248
|
|
119
|
-
def seekable?
|
120
|
-
@raw_io.pos
|
121
|
-
rescue NoMethodError, Errno::ESPIPE, Errno::EPIPE
|
122
|
-
nil
|
123
|
-
end
|
124
|
-
|
125
|
-
def seek(n)
|
126
|
-
seek_raw(buffer_limited_n(n))
|
127
|
-
end
|
128
|
-
|
129
249
|
def read(n = nil)
|
130
250
|
read_raw(buffer_limited_n(n))
|
131
251
|
end
|
132
252
|
|
133
|
-
def buffer_limited_n(n)
|
134
|
-
if @buffer_end_pos
|
135
|
-
max = @buffer_end_pos - offset
|
136
|
-
n = max if n.nil? or n > max
|
137
|
-
end
|
138
|
-
|
139
|
-
n
|
140
|
-
end
|
141
|
-
|
142
253
|
def read_big_endian_bits(nbits)
|
143
254
|
while @rnbits < nbits
|
144
255
|
accumulate_big_endian_bits
|
@@ -184,77 +295,6 @@ module BinData
|
|
184
295
|
def mask(nbits)
|
185
296
|
(1 << nbits) - 1
|
186
297
|
end
|
187
|
-
|
188
|
-
# Use #seek and #pos on seekable streams
|
189
|
-
module SeekableStream
|
190
|
-
# Returns the current offset of the io stream. Offset will be rounded
|
191
|
-
# up when reading bitfields.
|
192
|
-
def offset
|
193
|
-
raw_io.pos - @initial_pos
|
194
|
-
end
|
195
|
-
|
196
|
-
# The number of bytes remaining in the input stream.
|
197
|
-
def num_bytes_remaining
|
198
|
-
mark = raw_io.pos
|
199
|
-
raw_io.seek(0, ::IO::SEEK_END)
|
200
|
-
bytes_remaining = raw_io.pos - mark
|
201
|
-
raw_io.seek(mark, ::IO::SEEK_SET)
|
202
|
-
|
203
|
-
bytes_remaining
|
204
|
-
end
|
205
|
-
|
206
|
-
#-----------
|
207
|
-
private
|
208
|
-
|
209
|
-
def read_raw(n)
|
210
|
-
raw_io.read(n)
|
211
|
-
end
|
212
|
-
|
213
|
-
def seek_raw(n)
|
214
|
-
raw_io.seek(n, ::IO::SEEK_CUR)
|
215
|
-
end
|
216
|
-
|
217
|
-
def raw_io
|
218
|
-
@initial_pos ||= @raw_io.pos
|
219
|
-
@raw_io
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
|
-
# Manually keep track of offset for unseekable streams.
|
224
|
-
module UnSeekableStream
|
225
|
-
# Returns the current offset of the io stream. Offset will be rounded
|
226
|
-
# up when reading bitfields.
|
227
|
-
def offset
|
228
|
-
@read_count ||= 0
|
229
|
-
end
|
230
|
-
|
231
|
-
# The number of bytes remaining in the input stream.
|
232
|
-
def num_bytes_remaining
|
233
|
-
raise IOError, "stream is unseekable"
|
234
|
-
end
|
235
|
-
|
236
|
-
#-----------
|
237
|
-
private
|
238
|
-
|
239
|
-
def read_raw(n)
|
240
|
-
@read_count ||= 0
|
241
|
-
|
242
|
-
data = @raw_io.read(n)
|
243
|
-
@read_count += data.size if data
|
244
|
-
data
|
245
|
-
end
|
246
|
-
|
247
|
-
def seek_raw(n)
|
248
|
-
raise IOError, "stream is unseekable" if n < 0
|
249
|
-
|
250
|
-
# skip over data in 8k blocks
|
251
|
-
while n > 0
|
252
|
-
bytes_to_read = [n, 8192].min
|
253
|
-
read_raw(bytes_to_read)
|
254
|
-
n -= bytes_to_read
|
255
|
-
end
|
256
|
-
end
|
257
|
-
end
|
258
298
|
end
|
259
299
|
|
260
300
|
# Create a new IO Write wrapper around +io+. +io+ must provide #write.
|
@@ -265,25 +305,13 @@ module BinData
|
|
265
305
|
#
|
266
306
|
# See IO::Read for more information.
|
267
307
|
class Write
|
308
|
+
include Common
|
268
309
|
def initialize(io)
|
269
|
-
|
270
|
-
raise ArgumentError, "io must not be a BinData::IO::Write"
|
271
|
-
end
|
272
|
-
|
273
|
-
# wrap strings in a StringIO
|
274
|
-
if io.respond_to?(:to_str)
|
275
|
-
io = BinData::IO.create_string_io(io.to_str)
|
276
|
-
end
|
277
|
-
|
278
|
-
@raw_io = io
|
310
|
+
super(io)
|
279
311
|
|
280
312
|
@wnbits = 0
|
281
313
|
@wval = 0
|
282
314
|
@wendian = nil
|
283
|
-
|
284
|
-
@write_count = 0
|
285
|
-
|
286
|
-
@bytes_remaining = nil
|
287
315
|
end
|
288
316
|
|
289
317
|
# Sets a buffer of +n+ bytes on the io stream. Any writes inside the
|
@@ -291,31 +319,28 @@ module BinData
|
|
291
319
|
# are written inside the block, the remainder will be padded with '\0'
|
292
320
|
# bytes.
|
293
321
|
def with_buffer(n, &block)
|
294
|
-
|
295
|
-
if prev
|
296
|
-
n = prev if n > prev
|
297
|
-
prev -= n
|
298
|
-
end
|
299
|
-
|
300
|
-
@bytes_remaining = n
|
301
|
-
begin
|
322
|
+
with_buffer_common(n) do |buf_start, buf_end|
|
302
323
|
block.call
|
303
|
-
|
304
|
-
ensure
|
305
|
-
@bytes_remaining = prev
|
324
|
+
write("\0" * (buf_end - offset))
|
306
325
|
end
|
307
326
|
end
|
308
327
|
|
309
328
|
# Returns the current offset of the io stream. Offset will be rounded
|
310
329
|
# up when writing bitfields.
|
311
330
|
def offset
|
312
|
-
|
331
|
+
offset_raw + (@wnbits > 0 ? 1 : 0)
|
332
|
+
end
|
333
|
+
|
334
|
+
# Seek +n+ bytes from the current position in the io stream.
|
335
|
+
def seekbytes(n)
|
336
|
+
flushbits
|
337
|
+
seek(n)
|
313
338
|
end
|
314
339
|
|
315
340
|
# Writes the given string of bytes to the io stream.
|
316
341
|
def writebytes(str)
|
317
342
|
flushbits
|
318
|
-
|
343
|
+
write(str)
|
319
344
|
end
|
320
345
|
|
321
346
|
# Writes +nbits+ bits from +val+ to the stream. +endian+ specifies whether
|
@@ -349,6 +374,15 @@ module BinData
|
|
349
374
|
#---------------
|
350
375
|
private
|
351
376
|
|
377
|
+
def write(data)
|
378
|
+
n = buffer_limited_n(data.size)
|
379
|
+
if n < data.size
|
380
|
+
data = data[0, n]
|
381
|
+
end
|
382
|
+
|
383
|
+
write_raw(data)
|
384
|
+
end
|
385
|
+
|
352
386
|
def write_big_endian_bits(val, nbits)
|
353
387
|
while nbits > 0
|
354
388
|
bits_req = 8 - @wnbits
|
@@ -358,7 +392,7 @@ module BinData
|
|
358
392
|
val &= mask(nbits)
|
359
393
|
|
360
394
|
@wval = (@wval << bits_req) | msb_bits
|
361
|
-
|
395
|
+
write(@wval.chr)
|
362
396
|
|
363
397
|
@wval = 0
|
364
398
|
@wnbits = 0
|
@@ -379,7 +413,7 @@ module BinData
|
|
379
413
|
val >>= bits_req
|
380
414
|
|
381
415
|
@wval = @wval | (lsb_bits << @wnbits)
|
382
|
-
|
416
|
+
write(@wval.chr)
|
383
417
|
|
384
418
|
@wval = 0
|
385
419
|
@wnbits = 0
|
@@ -391,18 +425,6 @@ module BinData
|
|
391
425
|
end
|
392
426
|
end
|
393
427
|
|
394
|
-
def write_raw(data)
|
395
|
-
if @bytes_remaining
|
396
|
-
if data.size > @bytes_remaining
|
397
|
-
data = data[0, @bytes_remaining]
|
398
|
-
end
|
399
|
-
@bytes_remaining -= data.size
|
400
|
-
end
|
401
|
-
|
402
|
-
@write_count += data.size
|
403
|
-
@raw_io.write(data)
|
404
|
-
end
|
405
|
-
|
406
428
|
def mask(nbits)
|
407
429
|
(1 << nbits) - 1
|
408
430
|
end
|