adsp 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.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/AUTHORS +1 -0
  3. data/LICENSE +21 -0
  4. data/README.md +300 -0
  5. data/lib/adsp/error.rb +14 -0
  6. data/lib/adsp/file.rb +76 -0
  7. data/lib/adsp/option.rb +51 -0
  8. data/lib/adsp/stream/abstract.rb +206 -0
  9. data/lib/adsp/stream/delegates.rb +39 -0
  10. data/lib/adsp/stream/raw/abstract.rb +69 -0
  11. data/lib/adsp/stream/raw/compressor.rb +110 -0
  12. data/lib/adsp/stream/raw/decompressor.rb +80 -0
  13. data/lib/adsp/stream/raw/native_compressor.rb +58 -0
  14. data/lib/adsp/stream/raw/native_decompressor.rb +44 -0
  15. data/lib/adsp/stream/reader.rb +234 -0
  16. data/lib/adsp/stream/reader_helpers.rb +219 -0
  17. data/lib/adsp/stream/stat.rb +80 -0
  18. data/lib/adsp/stream/writer.rb +206 -0
  19. data/lib/adsp/stream/writer_helpers.rb +102 -0
  20. data/lib/adsp/string.rb +58 -0
  21. data/lib/adsp/validation.rb +46 -0
  22. data/lib/adsp/version.rb +7 -0
  23. data/lib/adsp.rb +8 -0
  24. data/test/common.rb +108 -0
  25. data/test/coverage_helper.rb +18 -0
  26. data/test/file.test.rb +120 -0
  27. data/test/minitest.rb +20 -0
  28. data/test/mock/common.rb +57 -0
  29. data/test/mock/file.rb +60 -0
  30. data/test/mock/stream/raw/compressor.rb +20 -0
  31. data/test/mock/stream/raw/decompressor.rb +20 -0
  32. data/test/mock/stream/raw/native_compressor.rb +82 -0
  33. data/test/mock/stream/raw/native_decompressor.rb +70 -0
  34. data/test/mock/stream/reader.rb +18 -0
  35. data/test/mock/stream/writer.rb +18 -0
  36. data/test/mock/string.rb +44 -0
  37. data/test/option.rb +66 -0
  38. data/test/stream/abstract.rb +125 -0
  39. data/test/stream/minitar.test.rb +50 -0
  40. data/test/stream/raw/abstract.rb +45 -0
  41. data/test/stream/raw/compressor.test.rb +166 -0
  42. data/test/stream/raw/decompressor.test.rb +166 -0
  43. data/test/stream/reader.test.rb +643 -0
  44. data/test/stream/reader_helpers.test.rb +421 -0
  45. data/test/stream/writer.test.rb +610 -0
  46. data/test/stream/writer_helpers.test.rb +267 -0
  47. data/test/string.test.rb +95 -0
  48. data/test/validation.rb +71 -0
  49. data/test/version.test.rb +18 -0
  50. metadata +274 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7bd22e8463be2343be9f61ce80d54261f32aefed31b3f6b6550f38e41a5c0401
4
+ data.tar.gz: 690e5d1c232c781f2bd48b099b01f8ee2d60a7b79aadec7255775110acfedf1f
5
+ SHA512:
6
+ metadata.gz: 03e5c419cebc46df3e64aa5f71905358c0f7dc5d16a417b2239d22c9e93eca734258da71a819037d6c754b4c4a077a9d177e13f9ec8310ee87187d875606f120
7
+ data.tar.gz: 5759268ce78057265a84f667966e0869973da161035498438ef88e9c41fc06bdc4984ac8fd73f3b58a0010b2971eabc932ff874d152487c5f8ab6d60d1c92e3d
data/AUTHORS ADDED
@@ -0,0 +1 @@
1
+ Andrew Aladjev
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 AUTHORS
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,300 @@
1
+ # Abstract data stream processor
2
+
3
+ | AppVeyor | Jenkins | Github actions | Codecov | Gem |
4
+ | :------: | :-----: | :------------: | :-----: | :--: |
5
+ | [![AppVeyor test status](https://ci.appveyor.com/api/projects/status/github/andrew-aladev/adsp?branch=master&svg=true)](https://ci.appveyor.com/project/andrew-aladev/adsp/branch/master) | [![Jenkins test status](http://37.187.122.190:58182/buildStatus/icon?job=adsp)](http://37.187.122.190:58182/job/adsp) | [![Github Actions test status](https://github.com/andrew-aladev/adsp/workflows/test/badge.svg?branch=master)](https://github.com/andrew-aladev/adsp/actions) | [![Codecov](https://codecov.io/gh/andrew-aladev/adsp/branch/master/graph/badge.svg)](https://codecov.io/gh/andrew-aladev/adsp) | [![Gem](https://img.shields.io/gem/v/adsp.svg)](https://rubygems.org/gems/adsp) |
6
+
7
+ ## Installation
8
+
9
+ Operating systems: GNU/Linux, FreeBSD, OSX.
10
+
11
+ ## Usage
12
+
13
+ There are simple APIs: `String` and `File`. Also you can use generic streaming API: `Stream::Writer` and `Stream::Reader`.
14
+
15
+ ```ruby
16
+ require "adsp"
17
+
18
+ data = ADSP::String.compress "sample string"
19
+ puts ADSP::String.decompress(data)
20
+
21
+ ADSP::File.compress "file.txt", "file.txt.archive"
22
+ ADSP::File.decompress "file.txt.archive", "file.txt"
23
+
24
+ ADSP::Stream::Writer.open("file.txt.archive") { |writer| writer << "sample string" }
25
+ puts ADSP::Stream::Reader.open("file.txt.archive") { |reader| reader.read }
26
+
27
+ writer = ADSP::Stream::Writer.new output_socket
28
+ begin
29
+ bytes_written = writer.write_nonblock "sample string"
30
+ # handle "bytes_written"
31
+ rescue IO::WaitWritable
32
+ # handle wait
33
+ ensure
34
+ writer.close
35
+ end
36
+
37
+ reader = ADSP::Stream::Reader.new input_socket
38
+ begin
39
+ puts reader.read_nonblock(512)
40
+ rescue IO::WaitReadable
41
+ # handle wait
42
+ rescue ::EOFError
43
+ # handle eof
44
+ ensure
45
+ reader.close
46
+ end
47
+ ```
48
+
49
+ You can create and read `tar.archive` archives with [minitar](https://github.com/halostatue/minitar).
50
+
51
+ ```ruby
52
+ require "adsp"
53
+ require "minitar"
54
+
55
+ ADSP::Stream::Writer.open "file.tar.archive" do |writer|
56
+ Minitar::Writer.open writer do |tar|
57
+ tar.add_file_simple "file", :data => "sample string"
58
+ end
59
+ end
60
+
61
+ ADSP::Stream::Reader.open "file.tar.archive" do |reader|
62
+ Minitar::Reader.open reader do |tar|
63
+ tar.each_entry do |entry|
64
+ puts entry.name
65
+ puts entry.read
66
+ end
67
+ end
68
+ end
69
+ ```
70
+
71
+ All functionality (including streaming) can be used inside multiple threads with [parallel](https://github.com/grosser/parallel).
72
+ This code will provide heavy load for your CPU.
73
+
74
+ ```ruby
75
+ require "adsp"
76
+ require "parallel"
77
+
78
+ Parallel.each large_datas do |large_data|
79
+ ADSP::String.compress large_data
80
+ end
81
+ ```
82
+
83
+ ## Options
84
+
85
+ | Option | Values | Default | Description |
86
+ |---------------------------------|----------------|------------|-------------|
87
+ | `source_buffer_length` | 0 - inf | 0 (auto) | internal buffer length for source data |
88
+ | `destination_buffer_length` | 0 - inf | 0 (auto) | internal buffer length for description data |
89
+
90
+ There are internal buffers for compressed and decompressed data.
91
+ For example you want to use 1 KB as `source_buffer_length` for compressor - please use 256 B as `destination_buffer_length`.
92
+ You want to use 256 B as `source_buffer_length` for decompressor - please use 1 KB as `destination_buffer_length`.
93
+
94
+ Possible compressor options:
95
+ ```
96
+ :source_buffer_length
97
+ :destination_buffer_length
98
+ ```
99
+
100
+ Possible decompressor options:
101
+ ```
102
+ :source_buffer_length
103
+ :destination_buffer_length
104
+ ```
105
+
106
+ Example:
107
+
108
+ ```ruby
109
+ require "adsp"
110
+
111
+ data = ADSP::String.compress "sample string", :source_buffer_length => 512
112
+ puts ADSP::String.decompress(data, :source_buffer_length => 512)
113
+ ```
114
+
115
+ ## String
116
+
117
+ String maintains destination buffer only, so it accepts `destination_buffer_length` option only.
118
+
119
+ ```
120
+ ::compress(source, options = {})
121
+ ::decompress(source, options = {})
122
+ ```
123
+
124
+ `source` is a source string.
125
+
126
+ ## File
127
+
128
+ File maintains both source and destination buffers, it accepts both `source_buffer_length` and `destination_buffer_length` options.
129
+
130
+ ```
131
+ ::compress(source, destination, options = {})
132
+ ::decompress(source, destination, options = {})
133
+ ```
134
+
135
+ `source` and `destination` are file pathes.
136
+
137
+ ## Stream::Writer
138
+
139
+ Its behaviour is similar to builtin [`Zlib::GzipWriter`](https://ruby-doc.org/stdlib/libdoc/zlib/rdoc/Zlib/GzipWriter.html).
140
+
141
+ Writer maintains destination buffer only, so it accepts `destination_buffer_length` option only.
142
+
143
+ ```
144
+ ::open(file_path, options = {}, :external_encoding => nil, :transcode_options => {}, &block)
145
+ ```
146
+
147
+ Open file path and create stream writer associated with opened file.
148
+ Data will be transcoded to `:external_encoding` using `:transcode_options` before compressing.
149
+
150
+ It may be tricky to use both `:pledged_size` and `:transcode_options`. You have to provide size of transcoded input.
151
+
152
+ ```
153
+ ::new(destination_io, options = {}, :external_encoding => nil, :transcode_options => {})
154
+ ```
155
+
156
+ Create stream writer associated with destination io.
157
+ Data will be transcoded to `:external_encoding` using `:transcode_options` before compressing.
158
+
159
+ It may be tricky to use both `:pledged_size` and `:transcode_options`. You have to provide size of transcoded input.
160
+
161
+ ```
162
+ #set_encoding(external_encoding, nil, transcode_options)
163
+ ```
164
+
165
+ Set another encodings, `nil` is just for compatibility with `IO`.
166
+
167
+ ```
168
+ #io
169
+ #to_io
170
+ #stat
171
+ #external_encoding
172
+ #transcode_options
173
+ #pos
174
+ #tell
175
+ ```
176
+
177
+ See [`IO`](https://ruby-doc.org/core/IO.html) docs.
178
+
179
+ ```
180
+ #write(*objects)
181
+ #flush
182
+ #rewind
183
+ #close
184
+ #closed?
185
+ ```
186
+
187
+ See [`Zlib::GzipWriter`](https://ruby-doc.org/stdlib/libdoc/zlib/rdoc/Zlib/GzipWriter.html) docs.
188
+
189
+ ```
190
+ #write_nonblock(object, *options)
191
+ #flush_nonblock(*options)
192
+ #rewind_nonblock(*options)
193
+ #close_nonblock(*options)
194
+ ```
195
+
196
+ Special asynchronous methods missing in `Zlib::GzipWriter`.
197
+ `rewind` wants to `close`, `close` wants to `write` something and `flush`, `flush` want to `write` something.
198
+ So it is possible to have asynchronous variants for these synchronous methods.
199
+ Behaviour is the same as `IO#write_nonblock` method.
200
+
201
+ All nonblock operations for file will raise `EBADF` error on Windows.
202
+ Setting file into nonblocking mode is [not available on Windows](https://github.com/ruby/ruby/blob/master/win32/win32.c#L4388).
203
+
204
+ ```
205
+ #<<(object)
206
+ #print(*objects)
207
+ #printf(*args)
208
+ #putc(object, encoding: ::Encoding::BINARY)
209
+ #puts(*objects)
210
+ ```
211
+
212
+ Typical helpers, see [`Zlib::GzipWriter`](https://ruby-doc.org/stdlib/libdoc/zlib/rdoc/Zlib/GzipWriter.html) docs.
213
+
214
+ ## Stream::Reader
215
+
216
+ Its behaviour is similar to builtin [`Zlib::GzipReader`](https://ruby-doc.org/stdlib/libdoc/zlib/rdoc/Zlib/GzipReader.html).
217
+
218
+ Reader maintains both source and destination buffers, it accepts both `source_buffer_length` and `destination_buffer_length` options.
219
+
220
+ ```
221
+ ::open(file_path, options = {}, :external_encoding => nil, :internal_encoding => nil, :transcode_options => {}, &block)
222
+ ```
223
+
224
+ Open file path and create stream reader associated with opened file.
225
+ Data will be force encoded to `:external_encoding` and transcoded to `:internal_encoding` using `:transcode_options` after decompressing.
226
+
227
+ ```
228
+ ::new(source_io, options = {}, :external_encoding => nil, :internal_encoding => nil, :transcode_options => {})
229
+ ```
230
+
231
+ Create stream reader associated with source io.
232
+ Data will be force encoded to `:external_encoding` and transcoded to `:internal_encoding` using `:transcode_options` after decompressing.
233
+
234
+ ```
235
+ #set_encoding(external_encoding, internal_encoding, transcode_options)
236
+ ```
237
+
238
+ Set another encodings.
239
+
240
+ ```
241
+ #io
242
+ #to_io
243
+ #stat
244
+ #external_encoding
245
+ #internal_encoding
246
+ #transcode_options
247
+ #pos
248
+ #tell
249
+ ```
250
+
251
+ See [`IO`](https://ruby-doc.org/core/IO.html) docs.
252
+
253
+ ```
254
+ #read(bytes_to_read = nil, out_buffer = nil)
255
+ #eof?
256
+ #rewind
257
+ #close
258
+ #closed?
259
+ ```
260
+
261
+ See [`Zlib::GzipReader`](https://ruby-doc.org/stdlib/libdoc/zlib/rdoc/Zlib/GzipReader.html) docs.
262
+
263
+ ```
264
+ #readpartial(bytes_to_read = nil, out_buffer = nil)
265
+ #read_nonblock(bytes_to_read, out_buffer = nil, *options)
266
+ ```
267
+
268
+ See [`IO`](https://ruby-doc.org/core/IO.html) docs.
269
+
270
+ ```
271
+ #getbyte
272
+ #each_byte(&block)
273
+ #readbyte
274
+ #ungetbyte(byte)
275
+
276
+ #getc
277
+ #readchar
278
+ #each_char(&block)
279
+ #ungetc(char)
280
+
281
+ #lineno
282
+ #lineno=
283
+ #gets(separator = $OUTPUT_RECORD_SEPARATOR, limit = nil)
284
+ #readline
285
+ #readlines
286
+ #each(&block)
287
+ #each_line(&block)
288
+ #ungetline(line)
289
+ ```
290
+
291
+ Typical helpers, see [`Zlib::GzipReader`](https://ruby-doc.org/stdlib/libdoc/zlib/rdoc/Zlib/GzipReader.html) docs.
292
+
293
+ ## CI
294
+
295
+ Please visit [scripts/test-images](scripts/test-images).
296
+ See universal test script [scripts/ci_test.sh](scripts/ci_test.sh) for CI.
297
+
298
+ ## License
299
+
300
+ MIT license, see [LICENSE](LICENSE) and [AUTHORS](AUTHORS).
data/lib/adsp/error.rb ADDED
@@ -0,0 +1,14 @@
1
+ # Abstract data stream processor.
2
+ # Copyright (c) 2021 AUTHORS, MIT License.
3
+
4
+ module ADSP
5
+ class BaseError < ::StandardError; end
6
+
7
+ class ValidateError < BaseError; end
8
+
9
+ class NotEnoughDestinationError < BaseError; end
10
+ class UsedAfterCloseError < BaseError; end
11
+
12
+ class NotImplementedError < BaseError; end
13
+ class UnexpectedError < BaseError; end
14
+ end
data/lib/adsp/file.rb ADDED
@@ -0,0 +1,76 @@
1
+ # Abstract data stream processor.
2
+ # Copyright (c) 2021 AUTHORS, MIT License.
3
+
4
+ require_relative "error"
5
+ require_relative "option"
6
+ require_relative "validation"
7
+
8
+ module ADSP
9
+ # ADSP::File class.
10
+ class File
11
+ # Current option class.
12
+ Option = ADSP::Option
13
+
14
+ # Current buffer length names.
15
+ # It is a part of compressor and decompressor options.
16
+ BUFFER_LENGTH_NAMES = %i[source_buffer_length destination_buffer_length].freeze
17
+
18
+ # Compresses data from +source+ file path to +destination+ file path.
19
+ # Option: +:source_buffer_length+ source buffer length.
20
+ # Option: +:destination_buffer_length+ destination buffer length.
21
+ def self.compress(source, destination, options = {})
22
+ Validation.validate_string source
23
+ Validation.validate_string destination
24
+
25
+ options = self::Option.get_compressor_options options, BUFFER_LENGTH_NAMES
26
+
27
+ open_files source, destination do |source_io, destination_io|
28
+ native_compress_io source_io, destination_io, options
29
+ end
30
+
31
+ nil
32
+ end
33
+
34
+ # :nocov:
35
+
36
+ # Internal method for compressing data from +source_io+ file to +destination_io+ file.
37
+ def self.native_compress_io(source_io, destination_io, options)
38
+ raise NotImplementedError
39
+ end
40
+
41
+ # :nocov:
42
+
43
+ # Decompresses data from +source+ file path to +destination+ file path.
44
+ # Option: +:source_buffer_length+ source buffer length.
45
+ # Option: +:destination_buffer_length+ destination buffer length.
46
+ def self.decompress(source, destination, options = {})
47
+ Validation.validate_string source
48
+ Validation.validate_string destination
49
+
50
+ options = self::Option.get_decompressor_options options, BUFFER_LENGTH_NAMES
51
+
52
+ open_files source, destination do |source_io, destination_io|
53
+ native_decompress_io source_io, destination_io, options
54
+ end
55
+
56
+ nil
57
+ end
58
+
59
+ # :nocov:
60
+
61
+ # Internal method for decompressing data from +source_io+ file to +destination_io+ file.
62
+ def self.native_decompress_io(source_io, destination_io, options)
63
+ raise NotImplementedError
64
+ end
65
+
66
+ # :nocov:
67
+
68
+ private_class_method def self.open_files(source, destination, &_block)
69
+ ::File.open source, "rb" do |source_io|
70
+ ::File.open destination, "wb" do |destination_io|
71
+ yield source_io, destination_io
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,51 @@
1
+ # Abstract data stream processor.
2
+ # Copyright (c) 2021 AUTHORS, MIT License.
3
+
4
+ require_relative "validation"
5
+
6
+ module ADSP
7
+ # ADSP::Option class.
8
+ class Option
9
+ # Current default buffer length.
10
+ # It will be used when buffer length option is not defined.
11
+ DEFAULT_BUFFER_LENGTH = 0
12
+
13
+ # Validates and processes default values for compressor +options+.
14
+ # +buffer_length_names+ is an array of buffer length names (option names).
15
+ def self.get_compressor_options(options, buffer_length_names = [])
16
+ Validation.validate_hash options
17
+ Validation.validate_array buffer_length_names
18
+
19
+ buffer_length_names.each { |name| Validation.validate_symbol name }
20
+
21
+ buffer_length_defaults = buffer_length_names.each_with_object({}) do |name, defaults|
22
+ defaults[name] = DEFAULT_BUFFER_LENGTH
23
+ end
24
+
25
+ options = buffer_length_defaults.merge options
26
+
27
+ buffer_length_names.each { |name| Validation.validate_not_negative_integer options[name] }
28
+
29
+ options
30
+ end
31
+
32
+ # Validates and processes default values for decompressor +options+.
33
+ # +buffer_length_names+ is an array of buffer length names (option names).
34
+ def self.get_decompressor_options(options, buffer_length_names = [])
35
+ Validation.validate_hash options
36
+ Validation.validate_array buffer_length_names
37
+
38
+ buffer_length_names.each { |name| Validation.validate_symbol name }
39
+
40
+ buffer_length_defaults = buffer_length_names.each_with_object({}) do |name, defaults|
41
+ defaults[name] = DEFAULT_BUFFER_LENGTH
42
+ end
43
+
44
+ options = buffer_length_defaults.merge options
45
+
46
+ buffer_length_names.each { |name| Validation.validate_not_negative_integer options[name] }
47
+
48
+ options
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,206 @@
1
+ # Abstract data stream processor.
2
+ # Copyright (c) 2021 AUTHORS, MIT License.
3
+
4
+ require_relative "delegates"
5
+ require_relative "stat"
6
+ require_relative "../error"
7
+ require_relative "../validation"
8
+
9
+ module ADSP
10
+ module Stream
11
+ # ADSP::Stream::Abstract class.
12
+ class Abstract
13
+ # Native stream is typically not seekable.
14
+ # We don't need to implement methods like "seek" and "pos=".
15
+
16
+ # Typically we may not maintain correspondance between bytes
17
+ # consumed from source and bytes written to destination.
18
+ # So we are going to consume all source bytes and
19
+ # maintain buffer with remaining destination data.
20
+
21
+ include Delegates
22
+
23
+ # Native stream.
24
+ attr_reader :io
25
+
26
+ # Native stream status info.
27
+ attr_reader :stat
28
+
29
+ # Encoding name for destination data.
30
+ attr_reader :external_encoding
31
+
32
+ # Encoding name for source data.
33
+ attr_reader :internal_encoding
34
+
35
+ # Transcode options for native stream.
36
+ attr_reader :transcode_options
37
+
38
+ # Current offset for source data.
39
+ attr_reader :pos
40
+ alias tell pos
41
+
42
+ # Initializes stream using +io+ native stream and +options+.
43
+ # Option: +:external_encoding+ encoding name for destination data.
44
+ # Option: +:internal_encoding+ encoding name for source data.
45
+ # Option: +:transcode_options+ transcode options for data.
46
+ def initialize(io, options = {})
47
+ @raw_stream = create_raw_stream
48
+ @io = io
49
+
50
+ @stat = Stat.new @io.stat if @io.respond_to? :stat
51
+
52
+ set_encoding options[:external_encoding], options[:internal_encoding], options[:transcode_options]
53
+ reset_buffer
54
+ reset_io_advise
55
+
56
+ @pos = 0
57
+ end
58
+
59
+ # :nocov:
60
+
61
+ # Creates raw stream.
62
+ protected def create_raw_stream
63
+ raise NotImplementedError
64
+ end
65
+
66
+ # :nocov:
67
+
68
+ # -- buffer --
69
+
70
+ # Resets internal source buffer.
71
+ protected def reset_buffer
72
+ @buffer = ::String.new :encoding => ::Encoding::BINARY
73
+ end
74
+
75
+ # -- advise --
76
+
77
+ # Resets native stream advise.
78
+ protected def reset_io_advise
79
+ # Both compressor and decompressor need sequential io access.
80
+ @io.advise :sequential if @io.respond_to? :advise
81
+ rescue ::Errno::ESPIPE
82
+ # ok
83
+ end
84
+
85
+ # Sets access mode for native stream, noop.
86
+ def advise
87
+ # Noop
88
+ nil
89
+ end
90
+
91
+ # -- encoding --
92
+
93
+ # Sets encoding for source and destination data.
94
+ # First argument: +:external_encoding+ encoding name for destination data.
95
+ # Second argument: +:internal_encoding+ encoding name for source data.
96
+ # Third argument: +:transcode_options+ transcode options for data.
97
+ def set_encoding(*args)
98
+ external_encoding, internal_encoding, transcode_options = process_set_encoding_arguments(*args)
99
+
100
+ set_target_encoding :@external_encoding, external_encoding
101
+ set_target_encoding :@internal_encoding, internal_encoding
102
+ @transcode_options = transcode_options
103
+
104
+ self
105
+ end
106
+
107
+ # Processes encoding for source and destination data.
108
+ # First argument: +:external_encoding+ encoding name for destination data.
109
+ # Second argument: +:internal_encoding+ encoding name for source data.
110
+ # Third argument: +:transcode_options+ transcode options for data.
111
+ protected def process_set_encoding_arguments(*args)
112
+ external_encoding = args[0]
113
+
114
+ unless external_encoding.nil? || external_encoding.is_a?(::Encoding)
115
+ Validation.validate_string external_encoding
116
+
117
+ # First argument can be "external_encoding:internal_encoding".
118
+ match = %r{(.+?):(.+)}.match external_encoding
119
+
120
+ unless match.nil?
121
+ external_encoding = match[0]
122
+ internal_encoding = match[1]
123
+
124
+ transcode_options = args[1]
125
+ Validation.validate_hash transcode_options unless transcode_options.nil?
126
+
127
+ return [external_encoding, internal_encoding, transcode_options]
128
+ end
129
+ end
130
+
131
+ internal_encoding = args[1]
132
+ unless internal_encoding.nil? || internal_encoding.is_a?(::Encoding)
133
+ Validation.validate_string internal_encoding
134
+ end
135
+
136
+ transcode_options = args[2]
137
+ Validation.validate_hash transcode_options unless transcode_options.nil?
138
+
139
+ [external_encoding, internal_encoding, transcode_options]
140
+ end
141
+
142
+ # Sets +value+ for encoding +name+.
143
+ protected def set_target_encoding(name, value)
144
+ unless value.nil? || value.is_a?(::Encoding)
145
+ begin
146
+ value = ::Encoding.find value
147
+ rescue ::ArgumentError
148
+ raise ValidateError, "invalid #{name} encoding"
149
+ end
150
+ end
151
+
152
+ instance_variable_set name, value
153
+ end
154
+
155
+ # Returns encoding for source data if defined.
156
+ # Returns encoding for destination data if encoding for source data is not defined.
157
+ # Returns binary encoding if encodings for source and destination dara are not defined.
158
+ protected def target_encoding
159
+ return @internal_encoding unless @internal_encoding.nil?
160
+ return @external_encoding unless @external_encoding.nil?
161
+
162
+ ::Encoding::BINARY
163
+ end
164
+
165
+ # -- etc --
166
+
167
+ # Resets stream and source position.
168
+ # Returns zero (offset for source data).
169
+ def rewind
170
+ @raw_stream = create_raw_stream
171
+
172
+ @io.rewind if @io.respond_to? :rewind
173
+
174
+ reset_buffer
175
+ reset_io_advise
176
+
177
+ @pos = 0
178
+
179
+ 0
180
+ end
181
+
182
+ # Closes stream.
183
+ def close
184
+ @io.close if @io.respond_to? :close
185
+
186
+ nil
187
+ end
188
+
189
+ # Returns whether stream is closed.
190
+ def closed?
191
+ return false unless @raw_stream.closed?
192
+
193
+ if @io.respond_to? :closed
194
+ @io.closed?
195
+ else
196
+ true
197
+ end
198
+ end
199
+
200
+ # Returns self object.
201
+ def to_io
202
+ self
203
+ end
204
+ end
205
+ end
206
+ end