bzip2-ffi 1.0.0 → 1.1.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.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data/CHANGES.md +33 -2
- data/Gemfile +43 -2
- data/LICENSE +13 -13
- data/README.md +92 -62
- data/Rakefile +24 -0
- data/bzip2-ffi.gemspec +10 -1
- data/lib/bzip2/ffi/error.rb +5 -2
- data/lib/bzip2/ffi/io.rb +59 -47
- data/lib/bzip2/ffi/libbz2.rb +7 -3
- data/lib/bzip2/ffi/reader.rb +210 -104
- data/lib/bzip2/ffi/version.rb +4 -1
- data/lib/bzip2/ffi/writer.rb +81 -62
- data/lib/bzip2/ffi.rb +9 -6
- data/test/error_test.rb +19 -20
- data/test/fixtures/lorem-4096-bytes-compressed.txt.bz2 +0 -0
- data/test/fixtures/lorem-first-structure-4096-bytes.txt.bz2 +0 -0
- data/test/fixtures/two_structures.bz2 +0 -0
- data/test/io_test.rb +34 -32
- data/test/reader_test.rb +339 -111
- data/test/test_helper.rb +45 -8
- data/test/version_test.rb +4 -1
- data/test/writer_test.rb +99 -73
- data.tar.gz.sig +0 -0
- metadata +33 -26
- metadata.gz.sig +2 -1
- /data/test/fixtures/{bzipped → compressed.bz2} +0 -0
data/lib/bzip2/ffi/reader.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
1
4
|
require 'pathname'
|
2
5
|
require 'stringio'
|
3
6
|
|
4
7
|
module Bzip2
|
5
8
|
module FFI
|
6
|
-
#
|
7
|
-
# public instance methods of
|
9
|
+
# {Reader} reads and decompresses a bzip2 compressed stream or file. The
|
10
|
+
# public instance methods of {Reader} are intended to be equivalent to those
|
8
11
|
# of a standard `IO` object.
|
9
12
|
#
|
10
13
|
# Data can be read as a stream using {open} and {#read}, for example:
|
@@ -15,7 +18,7 @@ module Bzip2
|
|
15
18
|
# end
|
16
19
|
# end
|
17
20
|
#
|
18
|
-
# Alternatively, without passing a block to
|
21
|
+
# Alternatively, without passing a block to {open}:
|
19
22
|
#
|
20
23
|
# reader = Bzip2::FFI::Reader.open(io_or_path)
|
21
24
|
# begin
|
@@ -26,77 +29,93 @@ module Bzip2
|
|
26
29
|
# reader.close
|
27
30
|
# end
|
28
31
|
#
|
29
|
-
#
|
32
|
+
# All the available bzipped data can be read in a single step using {read}:
|
30
33
|
#
|
31
34
|
# uncompressed = Bzip2::FFI::Reader.read(io_or_path)
|
32
35
|
#
|
33
|
-
# The {open} and {read} methods accept either an
|
34
|
-
# path.
|
36
|
+
# The {open} and {read} methods accept either an IO-like object or a file
|
37
|
+
# path. IO-like objects must have a `#read` method. Paths can be given as
|
35
38
|
# either a `String` or `Pathname`.
|
36
39
|
#
|
37
40
|
# No character conversion is performed on decompressed bytes. The {read} and
|
38
41
|
# {#read} methods return instances of `String` that represent the raw
|
39
|
-
# decompressed bytes, with
|
42
|
+
# decompressed bytes, with `#encoding` set to `Encoding::ASCII_8BIT` (also
|
40
43
|
# known as `Encoding::BINARY`).
|
41
44
|
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
45
|
+
# {Reader} will normally read all consecutive bzip2 compressed structure
|
46
|
+
# from the given stream or file (unless the `:first_only` parameter is
|
47
|
+
# specified - see {open}). If the stream or file contains additional data
|
48
|
+
# beyond the end of the compressed bzip2 data, it may be read during
|
49
|
+
# decompression. If such an overread has occurred and the IO-like object
|
50
|
+
# being read from has a `#seek` method, {Reader} will use it to reposition
|
51
|
+
# the stream to the byte immediately following the end of the compressed
|
52
|
+
# bzip2 data. If `#seek` raises an `IOError`, it will be caught and the
|
53
|
+
# stream position will be left unchanged.
|
54
|
+
#
|
55
|
+
# {Reader} does not support seeking (it's not supported by the underlying
|
56
|
+
# libbz2 library). There are no `#seek` or `#pos=` methods. The only way to
|
57
|
+
# advance the position is to call {#read}. Discard the result if it's not
|
58
|
+
# needed.
|
50
59
|
class Reader < IO
|
51
60
|
# The number of bytes read from the compressed data stream at a time.
|
52
61
|
#
|
53
62
|
# @private
|
54
63
|
READ_BUFFER_SIZE = 4096 #:nodoc:
|
64
|
+
private_constant :READ_BUFFER_SIZE
|
55
65
|
|
56
66
|
# The number of uncompressed bytes to read at a time when using {#read}
|
57
67
|
# without a length.
|
58
68
|
#
|
59
69
|
# @private
|
60
70
|
DEFAULT_DECOMPRESS_COUNT = 4096 #:nodoc:
|
71
|
+
private_constant :DEFAULT_DECOMPRESS_COUNT
|
61
72
|
|
62
73
|
class << self
|
63
74
|
# Use send to keep this hidden from YARD (visibility tag does not work).
|
64
75
|
send(:public, :new)
|
65
76
|
|
66
|
-
# Opens a {Reader} to read and decompress data from either an
|
67
|
-
# object or a file.
|
77
|
+
# Opens a {Reader} to read and decompress data from either an IO-like
|
78
|
+
# object or a file. IO-like objects must have a `#read` method. Files
|
68
79
|
# can be specified using either a `String` containing the file path or a
|
69
80
|
# `Pathname`.
|
70
81
|
#
|
71
|
-
# If no block is given, the opened
|
82
|
+
# If no block is given, the opened {Reader} instance is returned. After
|
72
83
|
# use, the instance should be closed using the {#close} method.
|
73
84
|
#
|
74
|
-
# If a block is given, it will be passed the opened
|
75
|
-
# as an argument. After the block terminates, the
|
76
|
-
# automatically be closed.
|
85
|
+
# If a block is given, it will be passed the opened {Reader} instance
|
86
|
+
# as an argument. After the block terminates, the {Reader} instance will
|
87
|
+
# automatically be closed. {open} will then return the result of the
|
77
88
|
# block.
|
78
89
|
#
|
79
90
|
# The following options can be specified using the `options` `Hash`:
|
80
91
|
#
|
81
|
-
# * `:autoclose` - When passing an
|
82
|
-
# close
|
92
|
+
# * `:autoclose` - When passing an IO-like object, set to `true` to
|
93
|
+
# close it when the {Reader} instance is closed.
|
94
|
+
# * `:first_only` - Bzip2 files can contain multiple consecutive
|
95
|
+
# compressed strctures. Normally all the structures
|
96
|
+
# will be decompressed with the decompressed bytes
|
97
|
+
# concatenated. Set to `true` to only read the first
|
98
|
+
# structure.
|
83
99
|
# * `:small` - Set to `true` to use an alternative decompression
|
84
100
|
# algorithm that uses less memory, but at the cost of
|
85
101
|
# decompressing more slowly (roughly 2,300 kB less memory
|
86
102
|
# at about half the speed).
|
87
103
|
#
|
88
|
-
# If an
|
89
|
-
#
|
90
|
-
#
|
104
|
+
# If an IO-like object that has a `#binmode` method is passed to {open},
|
105
|
+
# `#binmode` will be called on `io_or_path` before yielding to the block
|
106
|
+
# or returning.
|
91
107
|
#
|
92
|
-
# @param io_or_path [Object] Either an
|
108
|
+
# @param io_or_path [Object] Either an IO-like object with a `#read`
|
93
109
|
# method or a file path as a `String` or
|
94
110
|
# `Pathname`.
|
95
111
|
# @param options [Hash] Optional parameters (`:autoclose` and `:small`).
|
96
|
-
# @
|
112
|
+
# @yield [reader] If a block is given, it is yielded to.
|
113
|
+
# @yieldparam reader [Reader] The new {Reader} instance.
|
114
|
+
# @yieldresult [Object] A result to be returned as the result of {open}.
|
115
|
+
# @return [Object] The opened {Reader} instance if no block is given, or
|
97
116
|
# the result of the block if a block is given.
|
98
117
|
# @raise [ArgumentError] If `io_or_path` is _not_ a `String`, `Pathname`
|
99
|
-
# or an
|
118
|
+
# or an IO-like object with a `#read` method.
|
100
119
|
# @raise [Errno::ENOENT] If the specified file does not exist.
|
101
120
|
# @raise [Error::Bzip2Error] If an error occurs when initializing
|
102
121
|
# libbz2.
|
@@ -113,37 +132,42 @@ module Bzip2
|
|
113
132
|
end
|
114
133
|
|
115
134
|
# Reads and decompresses and entire bzip2 compressed structure from
|
116
|
-
# either an
|
117
|
-
#
|
118
|
-
#
|
119
|
-
#
|
135
|
+
# either an IO-like object or a file and returns the decompressed bytes
|
136
|
+
# as a `String`. IO-like objects must have a `#read` method. Files can
|
137
|
+
# be specified using either a `String` containing the file path or a
|
138
|
+
# `Pathname`.
|
120
139
|
#
|
121
140
|
# The following options can be specified using the `options` `Hash`:
|
122
141
|
#
|
123
|
-
# * `:autoclose` - When passing an
|
124
|
-
# close
|
125
|
-
#
|
142
|
+
# * `:autoclose` - When passing an IO-like object, set to `true` to
|
143
|
+
# close it when the compressed data has been read.
|
144
|
+
# * `:first_only` - Bzip2 files can contain multiple consecutive
|
145
|
+
# compressed strctures. Normally all the structures
|
146
|
+
# will be decompressed with the decompressed bytes
|
147
|
+
# concatenated. Set to `true` to only read the first
|
148
|
+
# structure.
|
126
149
|
# * `:small` - Set to `true` to use an alternative decompression
|
127
150
|
# algorithm that uses less memory, but at the cost of
|
128
151
|
# decompressing more slowly (roughly 2,300 kB less memory
|
129
152
|
# at about half the speed).
|
130
153
|
#
|
131
|
-
# No character conversion is performed on decompressed bytes.
|
154
|
+
# No character conversion is performed on decompressed bytes. {read}
|
132
155
|
# returns a `String` that represents the raw decompressed bytes, with
|
133
156
|
# `encoding` set to `Encoding::ASCII_8BIT` (also known as
|
134
157
|
# `Encoding::BINARY`).
|
135
158
|
#
|
136
|
-
# If an
|
137
|
-
#
|
138
|
-
#
|
159
|
+
# If an IO-like object that has a `#inmode` method is passed to {read},
|
160
|
+
# `#binmode` will be called on `io_or_path` before any compressed data
|
161
|
+
# is read.
|
139
162
|
#
|
140
|
-
# @param io_or_path [Object] Either an
|
163
|
+
# @param io_or_path [Object] Either an IO-like object with a `#read`
|
141
164
|
# method or a file path as a `String` or
|
142
165
|
# `Pathname`.
|
143
|
-
# @param options [Hash] Optional parameters (`:autoclose
|
166
|
+
# @param options [Hash] Optional parameters (`:autoclose`, `:first_only`
|
167
|
+
# and `:small`).
|
144
168
|
# @return [String] The decompressed data.
|
145
169
|
# @raise [ArgumentError] If `io_or_path` is _not_ a `String`, `Pathname`
|
146
|
-
# or an
|
170
|
+
# or an IO-like object with a `#read` method.
|
147
171
|
# @raise [Errno::ENOENT] If the specified file does not exist.
|
148
172
|
# @raise [Error::Bzip2Error] If an error occurs when initializing
|
149
173
|
# libbz2 or decompressing data.
|
@@ -155,11 +179,11 @@ module Bzip2
|
|
155
179
|
|
156
180
|
private
|
157
181
|
|
158
|
-
# Returns a Proc that can be used as a finalizer to call
|
159
|
-
#
|
182
|
+
# Returns a `Proc` that can be used as a finalizer to call
|
183
|
+
# {Libbz2::BZ2_bzDecompressEnd} with the given `stream`.
|
160
184
|
#
|
161
185
|
# @param stream [Libbz2::BzStream] The stream that should be passed to
|
162
|
-
#
|
186
|
+
# {Libbz2::BZ2_bzDecompressEnd}.
|
163
187
|
def finalize(stream)
|
164
188
|
->(id) do
|
165
189
|
Libbz2::BZ2_bzDecompressEnd(stream)
|
@@ -167,65 +191,74 @@ module Bzip2
|
|
167
191
|
end
|
168
192
|
end
|
169
193
|
|
170
|
-
# Initializes a {Reader} to read compressed data from an
|
171
|
-
# (`io`). `io` must have a
|
194
|
+
# Initializes a {Reader} to read compressed data from an IO-like object
|
195
|
+
# (`io`). `io` must have a `#read` method.
|
172
196
|
#
|
173
197
|
# The following options can be specified using the `options` `Hash`:
|
174
198
|
#
|
175
|
-
# * `:autoclose` - Set to `true` to close `io` when the
|
199
|
+
# * `:autoclose` - Set to `true` to close `io` when the {Reader} instance
|
176
200
|
# is closed.
|
201
|
+
# * `:first_only` - Bzip2 files can contain multiple consecutive
|
202
|
+
# compressed strctures. Normally all the structures will
|
203
|
+
# be decompressed with the decompressed bytes
|
204
|
+
# concatenated. Set to `true` to only read the first
|
205
|
+
# structure.
|
177
206
|
# * `:small` - Set to `true` to use an alternative decompression
|
178
207
|
# algorithm that uses less memory, but at the cost of
|
179
208
|
# decompressing more slowly (roughly 2,300 kB less memory
|
180
209
|
# at about half the speed).
|
181
210
|
#
|
182
|
-
#
|
211
|
+
# `#binmode` is called on `io` if `io` responds to `#binmode`.
|
183
212
|
#
|
184
|
-
# After use, the
|
213
|
+
# After use, the {Reader} instance should be closed using the {#close}
|
185
214
|
# method.
|
186
215
|
#
|
187
|
-
# @param io [Object] An
|
188
|
-
# @param options [Hash] Optional parameters (`:autoclose
|
189
|
-
#
|
216
|
+
# @param io [Object] An IO-like object with a `#read` method.
|
217
|
+
# @param options [Hash] Optional parameters (`:autoclose`, `:first_only`
|
218
|
+
# and `:small`).
|
219
|
+
# @raise [ArgumentError] If `io` is `nil` or does not respond to `#read`.
|
190
220
|
# @raise [Error::Bzip2Error] If an error occurs when initializing libbz2.
|
191
221
|
def initialize(io, options = {})
|
192
222
|
super
|
193
223
|
raise ArgumentError, 'io must respond to read' unless io.respond_to?(:read)
|
194
224
|
|
195
|
-
|
225
|
+
@first_only = options[:first_only]
|
226
|
+
@small = options[:small] ? 1 : 0
|
196
227
|
|
197
228
|
@in_eof = false
|
198
229
|
@out_eof = false
|
199
230
|
@in_buffer = nil
|
231
|
+
@structure_number = 1
|
232
|
+
@structure_start_pos = 0
|
233
|
+
@in_pos = 0
|
234
|
+
@out_pos = 0
|
200
235
|
|
201
|
-
|
202
|
-
|
203
|
-
ObjectSpace.define_finalizer(self, self.class.send(:finalize, stream))
|
236
|
+
decompress_init(stream)
|
204
237
|
end
|
205
238
|
|
206
239
|
# Ends decompression and closes the {Reader}.
|
207
240
|
#
|
208
241
|
# If the {open} method is used with a block, it is not necessary to call
|
209
|
-
#
|
242
|
+
# {#close}. Otherwise, {#close} should be called once the {Reader} is no
|
210
243
|
# longer needed.
|
211
244
|
#
|
212
245
|
# @return [NilType] `nil`.
|
213
|
-
# @raise [IOError] If the
|
246
|
+
# @raise [IOError] If the {Reader} has already been closed.
|
214
247
|
def close
|
215
248
|
s = stream
|
216
249
|
|
217
250
|
unless @out_eof
|
218
251
|
decompress_end(s)
|
219
252
|
end
|
220
|
-
|
253
|
+
|
221
254
|
s[:next_in] = nil
|
222
255
|
s[:next_out] = nil
|
223
|
-
|
256
|
+
|
224
257
|
if @in_buffer
|
225
258
|
@in_buffer.free
|
226
259
|
@in_buffer = nil
|
227
260
|
end
|
228
|
-
|
261
|
+
|
229
262
|
super
|
230
263
|
end
|
231
264
|
|
@@ -240,17 +273,17 @@ module Bzip2
|
|
240
273
|
# A result of `nil` or a `String` with a length less than `length` bytes
|
241
274
|
# indicates that the end of the decompressed data has been reached.
|
242
275
|
#
|
243
|
-
# If `length` is `nil`,
|
276
|
+
# If `length` is `nil`, {#read} reads until the end of the decompressed
|
244
277
|
# data, returning the uncompressed bytes as a `String`.
|
245
278
|
#
|
246
|
-
# If `length` is 0,
|
279
|
+
# If `length` is 0, {#read} returns an empty `String`.
|
247
280
|
#
|
248
281
|
# If the optional `buffer` argument is present, it must reference a
|
249
282
|
# `String` that will receive the decompressed data. `buffer` will
|
250
|
-
# contain only the decompressed data after the call to
|
283
|
+
# contain only the decompressed data after the call to {#read}, even if it
|
251
284
|
# is not empty beforehand.
|
252
285
|
#
|
253
|
-
# No character conversion is performed on decompressed bytes.
|
286
|
+
# No character conversion is performed on decompressed bytes. {#read}
|
254
287
|
# returns a `String` that represents the raw decompressed bytes, with
|
255
288
|
# `encoding` set to `Encoding::ASCII_8BIT` (also known as
|
256
289
|
# `Encoding::BINARY`).
|
@@ -267,7 +300,7 @@ module Bzip2
|
|
267
300
|
# the end of the decompressed data has been reached.
|
268
301
|
# @raise [ArgumentError] If `length` is negative.
|
269
302
|
# @raise [Error::Bzip2Error] If an error occurs during decompression.
|
270
|
-
# @raise [IOError] If the
|
303
|
+
# @raise [IOError] If the {Reader} has been closed.
|
271
304
|
def read(length = nil, buffer = nil)
|
272
305
|
if buffer
|
273
306
|
buffer.clear
|
@@ -279,11 +312,11 @@ module Bzip2
|
|
279
312
|
|
280
313
|
if length == 0
|
281
314
|
check_closed
|
282
|
-
return buffer ||
|
315
|
+
return buffer || String.new
|
283
316
|
end
|
284
317
|
|
285
318
|
decompressed = decompress(length)
|
286
|
-
|
319
|
+
|
287
320
|
return nil unless decompressed
|
288
321
|
buffer ? buffer << decompressed : decompressed
|
289
322
|
else
|
@@ -292,11 +325,11 @@ module Bzip2
|
|
292
325
|
# StringIO#binmode is a no-op, but call in case it is implemented in
|
293
326
|
# future versions.
|
294
327
|
result.binmode
|
295
|
-
|
328
|
+
|
296
329
|
result.set_encoding(Encoding::ASCII_8BIT)
|
297
330
|
|
298
331
|
loop do
|
299
|
-
decompressed = decompress(DEFAULT_DECOMPRESS_COUNT)
|
332
|
+
decompressed = decompress(DEFAULT_DECOMPRESS_COUNT)
|
300
333
|
break unless decompressed
|
301
334
|
result.write(decompressed)
|
302
335
|
break if decompressed.bytesize < DEFAULT_DECOMPRESS_COUNT
|
@@ -306,6 +339,30 @@ module Bzip2
|
|
306
339
|
end
|
307
340
|
end
|
308
341
|
|
342
|
+
# Returns `true` if decompression has completed, otherwise `false`.
|
343
|
+
#
|
344
|
+
# Note that it is possible for `false` to be returned after all the
|
345
|
+
# decompressed data has been read. In such cases, the next call to {#read}
|
346
|
+
# will detect the end of the bzip2 structure and set {#eof?} to `true`.
|
347
|
+
#
|
348
|
+
# @return [Boolean] If decompression has completed, otherwise `false`.
|
349
|
+
# @raise [IOError] If the {Reader} has been closed.
|
350
|
+
def eof?
|
351
|
+
check_closed
|
352
|
+
@out_eof
|
353
|
+
end
|
354
|
+
alias eof eof?
|
355
|
+
|
356
|
+
# Returns the number of decompressed bytes that have been read.
|
357
|
+
#
|
358
|
+
# @return [Integer] The number of decompressed bytes that have been read.
|
359
|
+
# @raise [IOError] If the {Reader} has been closed.
|
360
|
+
def tell
|
361
|
+
check_closed
|
362
|
+
@out_pos
|
363
|
+
end
|
364
|
+
alias pos tell
|
365
|
+
|
309
366
|
private
|
310
367
|
|
311
368
|
# Attempts to decompress and return `count` bytes.
|
@@ -315,9 +372,9 @@ module Bzip2
|
|
315
372
|
# @return [String] The decompressed data as a `String` with ASCII-8BIT
|
316
373
|
# encoding, or `nil` if length was a positive integer and
|
317
374
|
# the end of the decompressed data has been reached.
|
318
|
-
# @raise [ArgumentError]
|
375
|
+
# @raise [ArgumentError] If `count` is not greater than or equal to 1.
|
319
376
|
# @raise [Error::Bzip2Error] If an error occurs during decompression.
|
320
|
-
# @raise [IOError] If the
|
377
|
+
# @raise [IOError] If the {Reader} has been closed.
|
321
378
|
def decompress(count)
|
322
379
|
raise ArgumentError, "count must be a positive integer" unless count >= 1
|
323
380
|
s = stream
|
@@ -335,6 +392,7 @@ module Bzip2
|
|
335
392
|
bytes = io.read(READ_BUFFER_SIZE)
|
336
393
|
|
337
394
|
if bytes && bytes.bytesize > 0
|
395
|
+
@in_pos += bytes.bytesize
|
338
396
|
@in_eof = bytes.bytesize < READ_BUFFER_SIZE
|
339
397
|
@in_buffer = ::FFI::MemoryPointer.new(1, bytes.bytesize)
|
340
398
|
@in_buffer.write_bytes(bytes)
|
@@ -345,8 +403,15 @@ module Bzip2
|
|
345
403
|
end
|
346
404
|
end
|
347
405
|
|
406
|
+
# Reached the end of input without reading anything in the current
|
407
|
+
# bzip2 structure. No more data to process.
|
408
|
+
if @in_pos == @structure_start_pos
|
409
|
+
@out_eof = true
|
410
|
+
break
|
411
|
+
end
|
412
|
+
|
348
413
|
prev_avail_out = s[:avail_out]
|
349
|
-
|
414
|
+
|
350
415
|
res = Libbz2::BZ2_bzDecompress(s)
|
351
416
|
|
352
417
|
if s[:avail_in] == 0 && @in_buffer
|
@@ -355,43 +420,51 @@ module Bzip2
|
|
355
420
|
@in_buffer = nil
|
356
421
|
end
|
357
422
|
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
# The input could contain data after the end of the bzip2 stream.
|
362
|
-
#
|
363
|
-
# s[:avail_in] will contain the number of bytes that have been
|
364
|
-
# read from io, but not been consumed by BZ2_bzDecompress.
|
423
|
+
if @structure_number > 1 && res == Libbz2::BZ_DATA_ERROR_MAGIC
|
424
|
+
# Found something other than the bzip2 magic bytes after the end
|
425
|
+
# of a bzip2 structure.
|
365
426
|
#
|
366
427
|
# Attempt to move the input stream back by the amount that has
|
367
428
|
# been over-read.
|
368
|
-
|
369
|
-
io.seek(-s[:avail_in], ::IO::SEEK_CUR) rescue IOError
|
370
|
-
end
|
371
|
-
|
372
|
-
if @in_buffer
|
373
|
-
s[:next_in] = nil
|
374
|
-
@in_buffer.free
|
375
|
-
@in_buffer = nil
|
376
|
-
end
|
377
|
-
|
429
|
+
attempt_seek_to_structure_start
|
378
430
|
decompress_end(s)
|
379
|
-
|
380
431
|
@out_eof = true
|
381
432
|
break
|
382
433
|
end
|
383
434
|
|
384
|
-
|
435
|
+
check_error(res)
|
385
436
|
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
437
|
+
if res == Libbz2::BZ_STREAM_END
|
438
|
+
decompress_end(s)
|
439
|
+
|
440
|
+
if (s[:avail_in] > 0 || !@in_eof) && !@first_only
|
441
|
+
# Re-initialize to read a second bzip2 structure if there is
|
442
|
+
# still input available and not restricting to the first stream.
|
443
|
+
@structure_number += 1
|
444
|
+
@structure_start_pos = @in_pos - s[:avail_in]
|
445
|
+
decompress_init(s)
|
446
|
+
else
|
447
|
+
# May have already read data after the end of the first bzip2
|
448
|
+
# structure.
|
449
|
+
attempt_seek_to_structure_start if @first_only
|
450
|
+
@out_eof = true
|
451
|
+
break
|
452
|
+
end
|
453
|
+
else
|
454
|
+
# No more input available and calling BZ2_bzDecompress didn't
|
455
|
+
# advance the output. Raise an error.
|
456
|
+
if @in_eof && s[:avail_in] == 0 && prev_avail_out == s[:avail_out]
|
457
|
+
decompress_end(s)
|
458
|
+
@out_eof = true
|
459
|
+
raise Error::UnexpectedEofError.new
|
460
|
+
end
|
390
461
|
end
|
462
|
+
|
463
|
+
break if s[:avail_out] == 0
|
391
464
|
end
|
392
465
|
|
393
466
|
result = out_buffer.read_bytes(out_buffer.size - s[:avail_out])
|
394
|
-
ensure
|
467
|
+
ensure
|
395
468
|
out_buffer.free
|
396
469
|
s[:next_out] = nil
|
397
470
|
s[:avail_out] = 0
|
@@ -400,18 +473,51 @@ module Bzip2
|
|
400
473
|
if @out_eof && result.bytesize == 0
|
401
474
|
nil
|
402
475
|
else
|
476
|
+
@out_pos += result.bytesize
|
403
477
|
result
|
404
|
-
end
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
# Attempts to reposition the compressed stream to the start of the current
|
482
|
+
# structure. Used when {Libbz2::BZ2_bzDecompress} has read beyond the end
|
483
|
+
# of a bzip2 structure.
|
484
|
+
def attempt_seek_to_structure_start
|
485
|
+
if io.respond_to?(:seek)
|
486
|
+
diff = @structure_start_pos - @in_pos
|
487
|
+
if diff < 0
|
488
|
+
begin
|
489
|
+
io.seek(diff, ::IO::SEEK_CUR)
|
490
|
+
@in_pos += diff
|
491
|
+
rescue IOError
|
492
|
+
end
|
493
|
+
end
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
# Calls {Libbz2::BZ2_bzDecompressInit} to initialize the decompression
|
498
|
+
# stream `s`.
|
499
|
+
#
|
500
|
+
# Defines a finalizer to ensure that the memory associated with the stream
|
501
|
+
# is deallocated.
|
502
|
+
#
|
503
|
+
# @param s [Libbz2::BzStream] The stream to initialize decompression for.
|
504
|
+
# @raise [Error::Bzip2Error] If {Libbz2::BZ2_bzDecompressInit} reports an
|
505
|
+
# error.
|
506
|
+
def decompress_init(s)
|
507
|
+
check_error(Libbz2::BZ2_bzDecompressInit(s, 0, @small))
|
508
|
+
|
509
|
+
ObjectSpace.define_finalizer(self, self.class.send(:finalize, s))
|
405
510
|
end
|
406
511
|
|
407
|
-
# Calls BZ2_bzDecompressEnd to release
|
408
|
-
# decompression stream `s`.
|
512
|
+
# Calls {Libbz2::BZ2_bzDecompressEnd} to release memory associated with
|
513
|
+
# the decompression stream `s`.
|
409
514
|
#
|
410
515
|
# Notifies `ObjectSpace` that it is no longer necessary to finalize the
|
411
|
-
#
|
516
|
+
# {Reader} instance.
|
412
517
|
#
|
413
518
|
# @param s [Libbz2::BzStream] The stream to end decompression for.
|
414
|
-
# @raise [Error::Bzip2Error] If
|
519
|
+
# @raise [Error::Bzip2Error] If {Libbz2::BZ2_bzDecompressEnd} reports an
|
520
|
+
# error.
|
415
521
|
def decompress_end(s)
|
416
522
|
res = Libbz2::BZ2_bzDecompressEnd(s)
|
417
523
|
ObjectSpace.undefine_finalizer(self)
|