bzip2-ffi 1.0.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 91dd417ec50336680acdedba3bc7eb9c80a268f6
4
+ data.tar.gz: 637b7684138b2e615ce3720dd8d9a927029481bb
5
+ SHA512:
6
+ metadata.gz: a74b7c8e55a23de078c58b0d1c10b9f1addba8867a3b91bac0fbd7c4f22a663e5dccbbc1523814c5e6fdd779728c2d6acb92ad08bce3c05102a42eb97dd743b4
7
+ data.tar.gz: 10a5f0950a4bb5e4005632679097bcfdfb838bfcebf120ea9f986b9d30c5d1042ebb01bb7e866cb1891a919cbc90746f2a30df5dbce58bc3aa0c570cd828c750
@@ -0,0 +1,2 @@
1
+ {-l�P["A���V�>����{�9�2� ���gc=CZ_
2
+ ��!���O �>g<��f�d���{s�L}V0>O�x�cp��8��L�91��ʯh��e���ˑ�
@@ -0,0 +1 @@
1
+ D �h��pX�曦���3�2��镀W��S��F0�'�����^3T{�0o�8�3��Ğ�|A��o!�j�K��� �����My�� v�)�8+X��G�f������}[2j�p��1�=i�Kņ��;��'o�@�IP!u׈�-���p��.�=�r1��� �/��iԇ�t��qy�+:[ʠ�7q��Q�l@,�F������62\�we��ep��������&�ѫ 8]� דzֶ��9t) �'iX'�
@@ -0,0 +1,7 @@
1
+ --markup=markdown
2
+ --no-private
3
+ lib/**/*.rb
4
+ -
5
+ CHANGES.md
6
+ LICENSE
7
+ README.md
@@ -0,0 +1,4 @@
1
+ Version 1.0.0 - 28-Feb-2015
2
+ ---------------------------
3
+
4
+ * First release.
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem 'rake', '~> 10.0'
7
+ gem 'git', '~> 1.2', require: false
8
+ end
9
+
10
+ group :test do
11
+ gem 'minitest', '~> 5.0'
12
+ gem 'simplecov', '~> 0.9', require: false
13
+ gem 'coveralls', '~> 0.7', require: false
14
+ end
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2015 Philip Ross
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,144 @@
1
+ # Bzip2::FFI #
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/bzip2-ffi.svg)](http://badge.fury.io/rb/bzip2-ffi) [![Build Status](https://travis-ci.org/philr/bzip2-ffi.svg?branch=master)](https://travis-ci.org/philr/bzip2-ffi) [![Coverage Status](https://coveralls.io/repos/philr/bzip2-ffi/badge.svg?branch=master)](https://coveralls.io/r/philr/bzip2-ffi?branch=master)
4
+
5
+ Bzip2::FFI is a Ruby wrapper for libbz2 using FFI bindings.
6
+
7
+ The Bzip2::FFI Reader and Writer classes support reading and writing bzip2
8
+ compressed data as an `IO`-like stream.
9
+
10
+
11
+ ## Installation ##
12
+
13
+ To install the Bzip2::FFI gem, run the following command:
14
+
15
+ gem install bzip2-ffi
16
+
17
+ To add Bzip2::FFI as a Bundler dependency, add the following line to your
18
+ `Gemfile`:
19
+
20
+ gem 'bzip2-ffi'
21
+
22
+
23
+ ## Compatibility ##
24
+
25
+ Bzip2::FFI is tested on Ruby MRI 1.9.3+, JRuby 1.7+ and Rubinius 2+.
26
+
27
+
28
+ ## Runtime Dependencies ##
29
+
30
+ Bzip2::FFI is a pure-Ruby library that uses
31
+ [Ruby-FFI](https://rubygems.org/gems/ffi) (Foreign Function Interface) to load
32
+ the libbz2 dynamic library at runtime.
33
+
34
+ libbz2 is available as a package on most UNIX-based systems (for example,
35
+ `libbz2-1.0` on Debian and Ubuntu, or `bzip2-libs` on Fedora, Red Hat, and
36
+ CentOS).
37
+
38
+
39
+ ### Windows ###
40
+
41
+ On Windows, you will need to have `libbz2.dll` or `bz2.dll` available on the
42
+ `PATH` or in the Ruby `bin` directory.
43
+
44
+ Suitable builds of `libbz2.dll` are available from the
45
+ [bzip2-windows project](https://github.com/philr/bzip2-windows/releases).
46
+ Download the DLL only package that matches your Ruby installation (x86 or x64)
47
+ and extract to your `ruby\bin` directory.
48
+
49
+ Builds from the bzip2-windows project depend on the Visual Studio 2013 C Runtime
50
+ Library (msvcr120.dll). This can be installed using the
51
+ [Visual C++ Redistributable Packages for Visual Studio 2013 installer](http://www.microsoft.com/en-gb/download/details.aspx?id=40784).
52
+
53
+
54
+ ## Usage ##
55
+
56
+ To use Bzip2::FFI, it must first be loaded with:
57
+
58
+ require 'bzip2/ffi'
59
+
60
+
61
+ ### Compressing ###
62
+
63
+ Data can be compressed using the `Bzip2::FFI::Writer` class. For example, the
64
+ following compresses lines read from standard input (`ARGF`):
65
+
66
+ Bzip2::FFI::Writer.open(io_or_path) do |writer|
67
+ ARGF.each_line do |line|
68
+ writer.write(line)
69
+ end
70
+ end
71
+
72
+ Alternatively, without passing a block to `open`:
73
+
74
+ writer = Bzip2::FFI::Writer.open(io_or_path)
75
+ begin
76
+ ARGF.each_line do |line|
77
+ writer.write(line)
78
+ end
79
+ ensure
80
+ writer.close
81
+ end
82
+
83
+ An entire bzip2 structure can also be written in a single step:
84
+
85
+ Bzip2::FFI::Writer.write(io_or_path, 'Hello, World!')
86
+
87
+ In each of the examples above, `io_or_path` can either be a path to a file to
88
+ write to or an `IO`-like object that has a `write` method.
89
+
90
+
91
+ ### Decompressing ###
92
+
93
+ Data can be decompressed using the `Bzip2::FFI::Reader` class. For example:
94
+
95
+ Bzip2::FFI::Reader.open(io_or_path) do |reader|
96
+ while buffer = reader.read(1024) do
97
+ # process uncompressed bytes in buffer
98
+ end
99
+ end
100
+
101
+ Alternatively, without passing a block to `open`:
102
+
103
+ reader = Bzip2::FFI::Reader.open(io_or_path)
104
+ begin
105
+ while buffer = reader.read(1024) do
106
+ # process uncompressed bytes in buffer
107
+ end
108
+ ensure
109
+ reader.close
110
+ end
111
+
112
+ An entire bzip2 structure can be read and decompressed in a single step:
113
+
114
+ uncompressed = Bzip2::FFI::Reader.read(io_or_path)
115
+
116
+ In each of the examples above, `io_or_path` can either be a path to a file to
117
+ read from or an `IO`-like object that has a `read` method.
118
+
119
+
120
+ ### Character Encoding ###
121
+
122
+ Bzip2::FFI does not perform any encoding conversion when reading or writing.
123
+ Data read using `Bzip2::FFI::Reader` is returned as `String` instances with
124
+ ASCII-8BIT (BINARY) encoding representing the raw decompressed bytes.
125
+ `Bzip2::FFI::Writer` compresses the raw bytes from the `Strings` passed to the
126
+ `write` method (using the encoding of the `String`).
127
+
128
+
129
+ ## Documentation ##
130
+
131
+ Documentation for Bzip2::FFI is available on
132
+ [RubyDoc.info](http://www.rubydoc.info/gems/bzip2-ffi).
133
+
134
+
135
+ ## License ##
136
+
137
+ Bzip2::FFI is distributed under the terms of the MIT license. A copy of this
138
+ license can be found in the included LICENSE file.
139
+
140
+
141
+ ## GitHub Project ##
142
+
143
+ Source code, release information and the issue tracker can be found on the
144
+ [Bzip2::FFI GitHub project page](https://github.com/philr/bzip2-ffi).
@@ -0,0 +1,62 @@
1
+ require 'rubygems'
2
+ require 'rubygems/package_task'
3
+ require 'rake/testtask'
4
+
5
+ BASE_DIR = File.expand_path(File.dirname(__FILE__))
6
+
7
+ task :default => :test
8
+
9
+ def spec
10
+ @spec ||= eval(File.read('bzip2-ffi.gemspec'))
11
+ end
12
+
13
+ # Attempt to find the private key and return a spec with added options for
14
+ # signing the gem if found.
15
+ def add_signing_key(spec)
16
+ private_key_path = File.expand_path(File.join(BASE_DIR, '..', 'key', 'gem-private_key.pem'))
17
+
18
+ if File.exist?(private_key_path)
19
+ spec = spec.clone
20
+ spec.signing_key = private_key_path
21
+ spec.cert_chain = [File.join(BASE_DIR, 'gem-public_cert.pem')]
22
+ else
23
+ puts 'WARNING: Private key not found. Not signing gem file.'
24
+ end
25
+
26
+ spec
27
+ end
28
+
29
+ package_task = Gem::PackageTask.new(add_signing_key(spec)) do
30
+ end
31
+
32
+ # Ensure files are world-readable before packaging.
33
+ Rake::Task[package_task.package_dir_path].enhance do
34
+ recurse_chmod(package_task.package_dir_path)
35
+ end
36
+
37
+ def recurse_chmod(dir)
38
+ File.chmod(0755, dir)
39
+
40
+ Dir.entries(dir).each do |entry|
41
+ if entry != '.' && entry != '..'
42
+ path = File.join(dir, entry)
43
+ if File.directory?(path)
44
+ recurse_chmod(path)
45
+ else
46
+ File.chmod(0644, path)
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ task :tag do
53
+ require 'git'
54
+ g = Git.init(BASE_DIR)
55
+ g.add_tag("v#{spec.version}", annotate: true, message: "Tagging v#{spec.version}")
56
+ end
57
+
58
+ Rake::TestTask.new do |t|
59
+ t.libs = [File.join(BASE_DIR, 'test')]
60
+ t.pattern = File.join(BASE_DIR, 'test', '**', '*_test.rb')
61
+ t.warning = true
62
+ end
@@ -0,0 +1,30 @@
1
+ require File.expand_path(File.join('..', 'lib', 'bzip2', 'ffi', 'version'), __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'bzip2-ffi'
5
+ s.version = Bzip2::FFI::VERSION
6
+ s.summary = 'Reads and writes bzip2 compressed data using FFI bindings for libbz2.'
7
+ s.description = <<-EOF
8
+ Bzip2::FFI is a Ruby wrapper for libbz2 using FFI bindings.
9
+
10
+ The Bzip2::FFI Reader and Writer classes support reading and writing bzip2
11
+ compressed data as an IO-like stream.
12
+ EOF
13
+ s.author = 'Philip Ross'
14
+ s.email = 'phil.ross@gmail.com'
15
+ s.homepage = 'https://github.com/philr/bzip2-ffi'
16
+ s.license = 'MIT'
17
+ s.files = %w(CHANGES.md Gemfile LICENSE README.md Rakefile bzip2-ffi.gemspec .yardopts) +
18
+ Dir['lib/**/*.rb'] +
19
+ Dir['test/**/*.rb'] +
20
+ Dir['test/fixtures/*']
21
+ s.platform = Gem::Platform::RUBY
22
+ s.require_path = 'lib'
23
+ s.rdoc_options << '--title' << 'Bzip2::FFI' <<
24
+ '--main' << 'README.md' <<
25
+ '--markup' << 'markdown'
26
+ s.extra_rdoc_files = ['CHANGES.md', 'LICENSE', 'README.md']
27
+ s.required_ruby_version = '>= 1.9.3'
28
+ s.add_runtime_dependency 'ffi', '~> 1.0'
29
+ s.requirements << 'libbz2.(so|dll|dylib) available on the library search path'
30
+ end
@@ -0,0 +1,14 @@
1
+ module Bzip2
2
+ # Bzip2::FFI is a wrapper for libbz2 using FFI bindings. Bzip2 compressed data
3
+ # can be read and written as a stream using the Reader and Writer classes.
4
+ module FFI
5
+ end
6
+ end
7
+
8
+ require 'bzip2/ffi/libbz2'
9
+ require 'bzip2/ffi/error'
10
+ require 'bzip2/ffi/io'
11
+ require 'bzip2/ffi/reader'
12
+ require 'bzip2/ffi/writer'
13
+ require 'bzip2/ffi/version'
14
+
@@ -0,0 +1,100 @@
1
+ module Bzip2
2
+ module FFI
3
+ # The Bzip2::FFI::Error namespace contains exception classes that are raised
4
+ # if an error occurs whilst compressing or decompressing data.
5
+ module Error
6
+ # Base class for Bzip2::FFI exceptions.
7
+ class Bzip2Error < IOError
8
+ end
9
+
10
+ # Raised if libbz2 functions were called out of sequence or with data
11
+ # structures in incorrect states.
12
+ class SequenceError < Bzip2Error
13
+ # Initializes a new instance of SequenceError.
14
+ #
15
+ # @private
16
+ def initialize #:nodoc:
17
+ super('libbz2 functions called out of sequence or with data structures in incorrect states (this is likely to be caused by a bug in Bzip2::FFI)')
18
+ end
19
+ end
20
+
21
+ # Raised if a parameter passed to libbz2 was out of range or incorrect.
22
+ class ParamError < Bzip2Error
23
+ # Initializes a new instance of ParamError.
24
+ #
25
+ # @private
26
+ def initialize #:nodoc:
27
+ super('A parameter passed to libbz2 is out of range or incorrect (this may indicate a bug in Bzip2::FFI)')
28
+ end
29
+ end
30
+
31
+ # Raised if a failure occurred allocating memory to complete a request.
32
+ class MemoryError < Bzip2Error
33
+ # Initializes a new instance of MemoryError.
34
+ #
35
+ # @private
36
+ def initialize #:nodoc:
37
+ super('Could not allocate enough memory to perform this request')
38
+ end
39
+ end
40
+
41
+ # Raised if a data integrity error is detected (a mismatch between
42
+ # stored and computed CRCs or another anomaly in the compressed data).
43
+ class DataError < Bzip2Error
44
+ # Initializes a new instance of DataError.
45
+ #
46
+ # @param message [String] Exception message (overrides the default).
47
+ # @private
48
+ def initialize(message = nil) #:nodoc:
49
+ super(message || 'Data integrity error detected (mismatch between stored and computed CRCs, or other anomaly in the compressed data)')
50
+ end
51
+ end
52
+
53
+ # Raised if the compressed data does not start with the correct magic
54
+ # bytes ('BZh').
55
+ class MagicDataError < DataError
56
+ # Initializes a new instance of MagicDataError.
57
+ #
58
+ # @private
59
+ def initialize #:nodoc:
60
+ super('Compressed data does not start with the correct magic bytes (\'BZh\')')
61
+ end
62
+ end
63
+
64
+ # Raised if libbz2 detects that it has been improperly compiled.
65
+ class ConfigError < Bzip2Error
66
+ # Initializes a new instance of ConfigError.
67
+ #
68
+ # @private
69
+ def initialize #:nodoc:
70
+ super('libbz2 has been improperly compiled on your platform')
71
+ end
72
+ end
73
+
74
+ # Raised if an end of file (EOF) condition was detected before the end
75
+ # of the logical bzip2 stream.
76
+ class UnexpectedEofError < Bzip2Error
77
+ # UnexpectedEofError is raised directly by Reader. It does not map to
78
+ # a libbz2 low-level interface error code.
79
+
80
+ # Initializes a new instance of UnexpectedEofError.
81
+ #
82
+ # @private
83
+ def initialize #:nodoc:
84
+ super('EOF was detected before the end of the logical stream')
85
+ end
86
+ end
87
+
88
+ # Raised if libbz2 reported an unexpected error code.
89
+ class UnexpectedError < Bzip2Error
90
+ # Initializes a new instance of UnexpectedError.
91
+ #
92
+ # @param error_code [Integer] The error_code reported by libbz2.
93
+ # @private
94
+ def initialize(error_code) #:nodoc:
95
+ super("An unexpected error was detected (error code: #{error_code})")
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,250 @@
1
+ module Bzip2
2
+ module FFI
3
+ # `IO` is a base class providing common functionality for the {Reader} and
4
+ # {Writer} subclasses.
5
+ #
6
+ # `Bzip2::FFI::IO` holds a reference to an underlying `IO`-like stream
7
+ # representing the bzip2-compressed data to be read from or written to.
8
+ class IO
9
+ class << self
10
+ protected :new
11
+
12
+ protected
13
+
14
+ # If no block is provided, returns a new `IO`. If a block is provided,
15
+ # a new `IO` is created and yielded to the block. After the block has
16
+ # executed, the `IO` is closed and the result of the block is returned.
17
+ #
18
+ # If `io_or_proc` is a `Proc`, it is called to obtain an IO-like
19
+ # instance to pass to `new`. Otherwise `io_or_proc` is passed directly
20
+ # to `new`.
21
+ #
22
+ # @param io_or_proc [Object] An IO-like object or a `Proc` that returns
23
+ # an IO-like object when called.
24
+ # @param options [Hash] Options to pass to `new`.
25
+ def open(io_or_proc, options = {})
26
+ if io_or_proc.kind_of?(Proc)
27
+ io = io_or_proc.call
28
+ begin
29
+ bz_io = new(io, options)
30
+ rescue
31
+ io.close if io.respond_to?(:close)
32
+ raise
33
+ end
34
+ else
35
+ bz_io = new(io_or_proc, options)
36
+ end
37
+
38
+ if block_given?
39
+ begin
40
+ yield bz_io
41
+ ensure
42
+ bz_io.close unless bz_io.closed?
43
+ end
44
+ else
45
+ bz_io
46
+ end
47
+ end
48
+
49
+ # Opens and returns a bzip `File` using the specified mode. The system
50
+ # is advised that the file will be accessed once sequentially.
51
+ #
52
+ # @param path [String] The path to open.
53
+ # @param mode [String] The file open mode to use.
54
+ # @return [File] An open `File` object for `path` opened using `mode`.
55
+ def open_bzip_file(path, mode)
56
+ io = File.open(path, mode)
57
+
58
+ begin
59
+ after_open_file(io)
60
+ rescue
61
+ io.close
62
+ raise
63
+ end
64
+
65
+ io
66
+ end
67
+
68
+ private
69
+
70
+ # Advises the system that an `IO` will be accessed once sequentially.
71
+ #
72
+ # @param io [IO] An `IO` instance to advise.
73
+ def after_open_file(io)
74
+ # JRuby 1.7.18 doesn't have a File#advise method (in any mode).
75
+ if io.respond_to?(:advise)
76
+ io.advise(:sequential)
77
+ io.advise(:noreuse)
78
+ end
79
+ end
80
+ end
81
+
82
+ # Returns `true` if the underlying compressed `IO` instance will be closed
83
+ # when {#close} is called, otherwise `false`.
84
+ #
85
+ # @return [Boolean] `true` if the underlying compressed IO instance will
86
+ # be closed when {#close} is closed, otherwise `false`.
87
+ # @raise [IOError] If the instance has been closed.
88
+ def autoclose?
89
+ check_closed
90
+ @autoclose
91
+ end
92
+
93
+ # Sets whether the underlying compressed `IO` instance should be closed
94
+ # when {#close} is called (`true`) or left open (`false`).
95
+ #
96
+ # @param autoclose [Boolean] `true` if the underlying compressed `IO`
97
+ # instance should be closed when {#close} is
98
+ # called, or `false` if it should be left open.
99
+ # @raise [IOError] If the instance has been closed.
100
+ def autoclose=(autoclose)
101
+ check_closed
102
+ @autoclose = !!autoclose
103
+ end
104
+
105
+ # Returns `true` to indicate that the `IO` is operating in binary mode
106
+ # (as is always the case).
107
+ #
108
+ # @return [Boolean] `true`.
109
+ # @raise [IOError] If the `IO` has been closed.
110
+ def binmode?
111
+ check_closed
112
+ true
113
+ end
114
+
115
+ # Puts the `IO` into binary mode.
116
+ #
117
+ # Note that `Bzip2::FFI::IO` and subclasses always operate in binary mode,
118
+ # so calling `binmode` has no effect.
119
+ #
120
+ # @return [IO] `self`.
121
+ # @raise [IOError] If the `IO` has been closed.
122
+ def binmode
123
+ check_closed
124
+ self
125
+ end
126
+
127
+ # Closes the `IO`.
128
+ #
129
+ # If {#autoclose?} is true and the underlying compressed `IO` responds to
130
+ # `close`, it will also be closed.
131
+ #
132
+ # @return [NilClass] `nil`.
133
+ # @raise [IOError] If the `IO` has already been closed.
134
+ def close
135
+ check_closed
136
+ @io.close if autoclose? && @io.respond_to?(:close)
137
+ @stream = nil
138
+ end
139
+
140
+ # Indicates whether the `IO` has been closed by calling {#close}.
141
+ #
142
+ # @return [Boolean] `true` if the `IO` has been closed, otherwise `false`.
143
+ def closed?
144
+ !@stream
145
+ end
146
+
147
+ # Returns the `Encoding` object that represents the encoding of data
148
+ # prior to being compressed or after being decompressed.
149
+ #
150
+ # No character conversion is performed, so `external_encoding` always
151
+ # returns `Encoding::ASCII_8BIT` (also known as `Encoding::BINARY`).
152
+ #
153
+ # @return [Encoding] `Encoding::ASCII_8BIT`.
154
+ # @raise [IOError] If the `IO` has been closed.
155
+ def external_encoding
156
+ check_closed
157
+ Encoding::ASCII_8BIT
158
+ end
159
+
160
+ # The internal encoding for character conversions.
161
+ #
162
+ # No character conversion is performed, so `internal_encoding` always
163
+ # returns `Encoding::ASCII_8BIT` (also known as `Encoding::BINARY`).
164
+ #
165
+ # @return [Encoding] `Encoding::ASCII_8BIT`.
166
+ # @raise [IOError] If the `IO` has been closed.
167
+ def internal_encoding
168
+ check_closed
169
+ Encoding::ASCII_8BIT
170
+ end
171
+
172
+ protected
173
+
174
+ # The underlying compressed `IO` instance.
175
+ attr_reader :io
176
+
177
+ # Initializes a new {Bzip2::FFI::IO} instance with an underlying
178
+ # compressed `IO` instance and `options` `Hash`.
179
+ #
180
+ # `binmode` is called on `io` if `io` responds to `binmode`.
181
+ #
182
+ # A single `:autoclose` option is supported. Set `:autoclose` to true
183
+ # to close the underlying compressed `IO` instance when {#close} is
184
+ # called.
185
+ #
186
+ # @param io [IO] An `IO`-like object that represents the compressed data.
187
+ # @param options [Hash] Optional parameters (:autoclose).
188
+ # @raise [ArgumentError] If `io` is nil.
189
+ def initialize(io, options = {})
190
+ raise ArgumentError, 'io is required' unless io
191
+
192
+ @io = io
193
+ @io.binmode if @io.respond_to?(:binmode)
194
+
195
+ @autoclose = !!options[:autoclose]
196
+
197
+ @stream = Libbz2::BzStream.new
198
+ end
199
+
200
+ # Returns the {Libbz2::BzStream} instance being used to interface with
201
+ # libbz2.
202
+ #
203
+ # @return [Libbz2::BzStream] The {Libbz2::BzStream} instance being used
204
+ # to interface with libbz2.
205
+ # @raise [IOError] If the `IO` has been closed.
206
+ def stream
207
+ check_closed
208
+ @stream
209
+ end
210
+
211
+ # Raises an `IOError` if {#close} has been called to close the {IO}.
212
+ #
213
+ # @raise [IOError] If the `IO` has been closed.
214
+ def check_closed
215
+ raise IOError, 'closed stream' if closed?
216
+ end
217
+
218
+ # Checks a return code from a libbz2 function. If it is greater than or
219
+ # equal to 0 (success), the return code is returned. If it is less than
220
+ # zero (an error), the appropriate {Bzip2::Bzip2Error} sub-class is
221
+ # raised.
222
+ #
223
+ # @param res [Integer] The result of a call to a libbz2 function.
224
+ # @return [Integer] `res` if `res` is greater than or equal to 0.
225
+ # @raise [Error::Bzip2Error] if `res` is less than 0.
226
+ def check_error(res)
227
+ return res if res >= 0
228
+
229
+ error_class = case res
230
+ when Libbz2::BZ_SEQUENCE_ERROR
231
+ Error::SequenceError
232
+ when Libbz2::BZ_PARAM_ERROR
233
+ Error::ParamError
234
+ when Libbz2::BZ_MEM_ERROR
235
+ Error::MemoryError
236
+ when Libbz2::BZ_DATA_ERROR
237
+ Error::DataError
238
+ when Libbz2::BZ_DATA_ERROR_MAGIC
239
+ Error::MagicDataError
240
+ when Libbz2::BZ_CONFIG_ERROR
241
+ Error::ConfigError
242
+ else
243
+ raise Error::UnexpectedError.new(res)
244
+ end
245
+
246
+ raise error_class.new
247
+ end
248
+ end
249
+ end
250
+ end