bzip2-ffi 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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