adsp 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/AUTHORS +1 -0
- data/LICENSE +21 -0
- data/README.md +300 -0
- data/lib/adsp/error.rb +14 -0
- data/lib/adsp/file.rb +76 -0
- data/lib/adsp/option.rb +51 -0
- data/lib/adsp/stream/abstract.rb +206 -0
- data/lib/adsp/stream/delegates.rb +39 -0
- data/lib/adsp/stream/raw/abstract.rb +69 -0
- data/lib/adsp/stream/raw/compressor.rb +110 -0
- data/lib/adsp/stream/raw/decompressor.rb +80 -0
- data/lib/adsp/stream/raw/native_compressor.rb +58 -0
- data/lib/adsp/stream/raw/native_decompressor.rb +44 -0
- data/lib/adsp/stream/reader.rb +234 -0
- data/lib/adsp/stream/reader_helpers.rb +219 -0
- data/lib/adsp/stream/stat.rb +80 -0
- data/lib/adsp/stream/writer.rb +206 -0
- data/lib/adsp/stream/writer_helpers.rb +102 -0
- data/lib/adsp/string.rb +58 -0
- data/lib/adsp/validation.rb +46 -0
- data/lib/adsp/version.rb +7 -0
- data/lib/adsp.rb +8 -0
- data/test/common.rb +108 -0
- data/test/coverage_helper.rb +18 -0
- data/test/file.test.rb +120 -0
- data/test/minitest.rb +20 -0
- data/test/mock/common.rb +57 -0
- data/test/mock/file.rb +60 -0
- data/test/mock/stream/raw/compressor.rb +20 -0
- data/test/mock/stream/raw/decompressor.rb +20 -0
- data/test/mock/stream/raw/native_compressor.rb +82 -0
- data/test/mock/stream/raw/native_decompressor.rb +70 -0
- data/test/mock/stream/reader.rb +18 -0
- data/test/mock/stream/writer.rb +18 -0
- data/test/mock/string.rb +44 -0
- data/test/option.rb +66 -0
- data/test/stream/abstract.rb +125 -0
- data/test/stream/minitar.test.rb +50 -0
- data/test/stream/raw/abstract.rb +45 -0
- data/test/stream/raw/compressor.test.rb +166 -0
- data/test/stream/raw/decompressor.test.rb +166 -0
- data/test/stream/reader.test.rb +643 -0
- data/test/stream/reader_helpers.test.rb +421 -0
- data/test/stream/writer.test.rb +610 -0
- data/test/stream/writer_helpers.test.rb +267 -0
- data/test/string.test.rb +95 -0
- data/test/validation.rb +71 -0
- data/test/version.test.rb +18 -0
- metadata +274 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
# Abstract data stream processor.
|
2
|
+
# Copyright (c) 2021 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
require "forwardable"
|
5
|
+
|
6
|
+
module ADSP
|
7
|
+
module Stream
|
8
|
+
# ADSP::Stream::Delegates class.
|
9
|
+
module Delegates
|
10
|
+
# List of methods to be forwarded for native stream.
|
11
|
+
DELEGATES = %i[
|
12
|
+
autoclose=
|
13
|
+
autoclose?
|
14
|
+
binmode
|
15
|
+
binmode?
|
16
|
+
close_on_exec=
|
17
|
+
close_on_exec?
|
18
|
+
fcntl
|
19
|
+
fdatasync
|
20
|
+
fileno
|
21
|
+
fsync
|
22
|
+
ioctl
|
23
|
+
isatty
|
24
|
+
pid
|
25
|
+
sync
|
26
|
+
sync=
|
27
|
+
to_i
|
28
|
+
tty?
|
29
|
+
]
|
30
|
+
.freeze
|
31
|
+
|
32
|
+
# Defines delegates for native stream after module included.
|
33
|
+
def self.included(klass)
|
34
|
+
klass.extend ::Forwardable
|
35
|
+
klass.def_delegators :@io, *DELEGATES
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# Abstract data stream processor.
|
2
|
+
# Copyright (c) 2021 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
require_relative "../../error"
|
5
|
+
require_relative "../../validation"
|
6
|
+
|
7
|
+
module ADSP
|
8
|
+
module Stream
|
9
|
+
module Raw
|
10
|
+
# ADSP::Stream::Raw::Abstract class.
|
11
|
+
class Abstract
|
12
|
+
# Initializes raw stream using +native_stream+.
|
13
|
+
def initialize(native_stream)
|
14
|
+
@native_stream = native_stream
|
15
|
+
@is_closed = false
|
16
|
+
end
|
17
|
+
|
18
|
+
# -- write --
|
19
|
+
|
20
|
+
# Flushes raw stream and writes next result using +writer+ proc.
|
21
|
+
def flush(&writer)
|
22
|
+
do_not_use_after_close
|
23
|
+
|
24
|
+
Validation.validate_proc writer
|
25
|
+
|
26
|
+
write_result(&writer)
|
27
|
+
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
31
|
+
# Writes next result using +writer+ proc and frees destination buffer.
|
32
|
+
protected def more_destination(&writer)
|
33
|
+
result_bytesize = write_result(&writer)
|
34
|
+
raise NotEnoughDestinationError, "not enough destination" if result_bytesize.zero?
|
35
|
+
end
|
36
|
+
|
37
|
+
# Writes next result using block.
|
38
|
+
protected def write_result(&_writer)
|
39
|
+
result = @native_stream.read_result
|
40
|
+
yield result
|
41
|
+
|
42
|
+
result.bytesize
|
43
|
+
end
|
44
|
+
|
45
|
+
# -- close --
|
46
|
+
|
47
|
+
# Raises error when raw stream is closed.
|
48
|
+
protected def do_not_use_after_close
|
49
|
+
raise UsedAfterCloseError, "used after close" if closed?
|
50
|
+
end
|
51
|
+
|
52
|
+
# Writes next result using +writer+ proc and closes raw stream.
|
53
|
+
def close(&writer)
|
54
|
+
write_result(&writer)
|
55
|
+
|
56
|
+
@native_stream.close
|
57
|
+
@is_closed = true
|
58
|
+
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns whether raw stream is closed.
|
63
|
+
def closed?
|
64
|
+
@is_closed
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# Abstract data stream processor.
|
2
|
+
# Copyright (c) 2021 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
require_relative "abstract"
|
5
|
+
require_relative "native_compressor"
|
6
|
+
require_relative "../../error"
|
7
|
+
require_relative "../../option"
|
8
|
+
require_relative "../../validation"
|
9
|
+
|
10
|
+
module ADSP
|
11
|
+
module Stream
|
12
|
+
module Raw
|
13
|
+
# ADSP::Stream::Raw::Compressor class.
|
14
|
+
class Compressor < Abstract
|
15
|
+
# Current native compressor class.
|
16
|
+
NativeCompressor = Raw::NativeCompressor
|
17
|
+
|
18
|
+
# Current option class.
|
19
|
+
Option = ADSP::Option
|
20
|
+
|
21
|
+
# Current buffer length names.
|
22
|
+
# It is a part of compressor options.
|
23
|
+
BUFFER_LENGTH_NAMES = %i[destination_buffer_length].freeze
|
24
|
+
|
25
|
+
# Initializes compressor.
|
26
|
+
# Option: +:destination_buffer_length+ destination buffer length.
|
27
|
+
def initialize(options = {})
|
28
|
+
options = self.class::Option.get_compressor_options options, BUFFER_LENGTH_NAMES
|
29
|
+
native_stream = self.class::NativeCompressor.new options
|
30
|
+
|
31
|
+
super native_stream
|
32
|
+
end
|
33
|
+
|
34
|
+
# Writes +source+ string, writes result using +writer+ proc.
|
35
|
+
# Returns amount of bytes written from +source+.
|
36
|
+
def write(source, &writer)
|
37
|
+
do_not_use_after_close
|
38
|
+
|
39
|
+
Validation.validate_string source
|
40
|
+
Validation.validate_proc writer
|
41
|
+
|
42
|
+
total_bytes_written = 0
|
43
|
+
|
44
|
+
loop do
|
45
|
+
bytes_written, need_more_destination = @native_stream.write source
|
46
|
+
total_bytes_written += bytes_written
|
47
|
+
|
48
|
+
if need_more_destination
|
49
|
+
source = source.byteslice bytes_written, source.bytesize - bytes_written
|
50
|
+
more_destination(&writer)
|
51
|
+
next
|
52
|
+
end
|
53
|
+
|
54
|
+
unless bytes_written == source.bytesize
|
55
|
+
# Compressor write should eat all provided "source" without remainder.
|
56
|
+
# :nocov:
|
57
|
+
raise UnexpectedError, "unexpected error"
|
58
|
+
# :nocov:
|
59
|
+
end
|
60
|
+
|
61
|
+
break
|
62
|
+
end
|
63
|
+
|
64
|
+
total_bytes_written
|
65
|
+
end
|
66
|
+
|
67
|
+
# Flushes compressor, writes result using +writer+ proc and closes compressor.
|
68
|
+
def flush(&writer)
|
69
|
+
do_not_use_after_close
|
70
|
+
|
71
|
+
Validation.validate_proc writer
|
72
|
+
|
73
|
+
loop do
|
74
|
+
need_more_destination = @native_stream.flush
|
75
|
+
|
76
|
+
if need_more_destination
|
77
|
+
more_destination(&writer)
|
78
|
+
next
|
79
|
+
end
|
80
|
+
|
81
|
+
break
|
82
|
+
end
|
83
|
+
|
84
|
+
super
|
85
|
+
end
|
86
|
+
|
87
|
+
# Finishes compressor, writes result using +writer+ proc and closes compressor.
|
88
|
+
# Raises +UsedAfterCloseError+ when used after close.
|
89
|
+
def close(&writer)
|
90
|
+
return nil if closed?
|
91
|
+
|
92
|
+
Validation.validate_proc writer
|
93
|
+
|
94
|
+
loop do
|
95
|
+
need_more_destination = @native_stream.finish
|
96
|
+
|
97
|
+
if need_more_destination
|
98
|
+
more_destination(&writer)
|
99
|
+
next
|
100
|
+
end
|
101
|
+
|
102
|
+
break
|
103
|
+
end
|
104
|
+
|
105
|
+
super
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# Abstract data stream processor.
|
2
|
+
# Copyright (c) 2021 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
require_relative "abstract"
|
5
|
+
require_relative "native_decompressor"
|
6
|
+
require_relative "../../option"
|
7
|
+
require_relative "../../validation"
|
8
|
+
|
9
|
+
module ADSP
|
10
|
+
module Stream
|
11
|
+
module Raw
|
12
|
+
# ADSP::Stream::Raw::Decompressor class.
|
13
|
+
class Decompressor < Abstract
|
14
|
+
# Current native decompressor class.
|
15
|
+
NativeDecompressor = Raw::NativeDecompressor
|
16
|
+
|
17
|
+
# Current option class.
|
18
|
+
Option = ADSP::Option
|
19
|
+
|
20
|
+
# Current buffer length names.
|
21
|
+
# It is a part of decompressor options.
|
22
|
+
BUFFER_LENGTH_NAMES = %i[destination_buffer_length].freeze
|
23
|
+
|
24
|
+
# Initializes decompressor.
|
25
|
+
# Option: +:destination_buffer_length+ destination buffer length.
|
26
|
+
def initialize(options = {})
|
27
|
+
options = self.class::Option.get_decompressor_options options, BUFFER_LENGTH_NAMES
|
28
|
+
native_stream = self.class::NativeDecompressor.new options
|
29
|
+
|
30
|
+
super native_stream
|
31
|
+
end
|
32
|
+
|
33
|
+
# Reads +source+ string, writes result using +writer+ proc.
|
34
|
+
# Returns amount of bytes read from +source+.
|
35
|
+
def read(source, &writer)
|
36
|
+
do_not_use_after_close
|
37
|
+
|
38
|
+
Validation.validate_string source
|
39
|
+
Validation.validate_proc writer
|
40
|
+
|
41
|
+
total_bytes_read = 0
|
42
|
+
|
43
|
+
loop do
|
44
|
+
bytes_read, need_more_destination = @native_stream.read source
|
45
|
+
total_bytes_read += bytes_read
|
46
|
+
|
47
|
+
if need_more_destination
|
48
|
+
source = source.byteslice bytes_read, source.bytesize - bytes_read
|
49
|
+
more_destination(&writer)
|
50
|
+
next
|
51
|
+
end
|
52
|
+
|
53
|
+
break
|
54
|
+
end
|
55
|
+
|
56
|
+
total_bytes_read
|
57
|
+
end
|
58
|
+
|
59
|
+
# Flushes decompressor, writes result using +writer+ proc and closes decompressor.
|
60
|
+
def flush(&writer)
|
61
|
+
do_not_use_after_close
|
62
|
+
|
63
|
+
Validation.validate_proc writer
|
64
|
+
|
65
|
+
super
|
66
|
+
end
|
67
|
+
|
68
|
+
# Writes result using +writer+ proc and closes decompressor.
|
69
|
+
# Raises +UsedAfterCloseError+ when used after close.
|
70
|
+
def close(&writer)
|
71
|
+
return nil if closed?
|
72
|
+
|
73
|
+
Validation.validate_proc writer
|
74
|
+
|
75
|
+
super
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# Abstract data stream processor.
|
2
|
+
# Copyright (c) 2021 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
require_relative "../../error"
|
5
|
+
|
6
|
+
module ADSP
|
7
|
+
module Stream
|
8
|
+
module Raw
|
9
|
+
# ADSP::Stream::Raw::NativeCompressor class.
|
10
|
+
class NativeCompressor
|
11
|
+
# :nocov:
|
12
|
+
|
13
|
+
# Initializes native compressor.
|
14
|
+
# Option: +:destination_buffer_length+ destination buffer length.
|
15
|
+
def initialize(options)
|
16
|
+
raise NotImplementedError
|
17
|
+
end
|
18
|
+
|
19
|
+
# Writes part of +source+ string.
|
20
|
+
# Returns array of 2 values:
|
21
|
+
# 1. number of bytes written from +source+.
|
22
|
+
# 2. boolean that can be named as "need more destination".
|
23
|
+
# User needs to call +read_result+ if "need more destination" is true.
|
24
|
+
def write(source)
|
25
|
+
raise NotImplementedError
|
26
|
+
end
|
27
|
+
|
28
|
+
# Provides next part of unread result.
|
29
|
+
# Returns empty string if there is no unread result.
|
30
|
+
def read_result
|
31
|
+
raise NotImplementedError
|
32
|
+
end
|
33
|
+
|
34
|
+
# Flushes internal buffers and prepares result for +read_result+.
|
35
|
+
# Returns boolean that can be named as "need more destination".
|
36
|
+
# User needs to call +read_result+ if "need more destination" is true.
|
37
|
+
def flush
|
38
|
+
raise NotImplementedError
|
39
|
+
end
|
40
|
+
|
41
|
+
# Finishes compressor and prepares result for +read_result+.
|
42
|
+
# Returns boolean that can be named as "need more destination".
|
43
|
+
# User needs to call +read_result+ if "need more destination" is true.
|
44
|
+
def finish
|
45
|
+
raise NotImplementedError
|
46
|
+
end
|
47
|
+
|
48
|
+
# Closes compressor and cleans up internal resources.
|
49
|
+
# Raises +UsedAfterCloseError+ when used after close.
|
50
|
+
def close
|
51
|
+
raise NotImplementedError
|
52
|
+
end
|
53
|
+
|
54
|
+
# :nocov:
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Abstract data stream processor.
|
2
|
+
# Copyright (c) 2021 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
require_relative "../../error"
|
5
|
+
|
6
|
+
module ADSP
|
7
|
+
module Stream
|
8
|
+
module Raw
|
9
|
+
# ADSP::Stream::Raw::NativeDecompressor class.
|
10
|
+
class NativeDecompressor
|
11
|
+
# :nocov:
|
12
|
+
|
13
|
+
# Initializes native decompressor.
|
14
|
+
# Option: +:destination_buffer_length+ destination buffer length.
|
15
|
+
def initialize(options)
|
16
|
+
raise NotImplementedError
|
17
|
+
end
|
18
|
+
|
19
|
+
# Reads part of +source+ string.
|
20
|
+
# Returns array of 2 values:
|
21
|
+
# 1. number of bytes read from +source+.
|
22
|
+
# 2. boolean that can be named as "need more destination".
|
23
|
+
# User needs to call +read_result+ if "need more destination" is true.
|
24
|
+
def read(source)
|
25
|
+
raise NotImplementedError
|
26
|
+
end
|
27
|
+
|
28
|
+
# Provides next part of unread result.
|
29
|
+
# Returns empty string if there is no unread result.
|
30
|
+
def read_result
|
31
|
+
raise NotImplementedError
|
32
|
+
end
|
33
|
+
|
34
|
+
# Closes decompressor and cleans up internal resources.
|
35
|
+
# Raises +UsedAfterCloseError+ when used after close.
|
36
|
+
def close
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
|
40
|
+
# :nocov:
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,234 @@
|
|
1
|
+
# Abstract data stream processor.
|
2
|
+
# Copyright (c) 2021 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
require_relative "abstract"
|
5
|
+
require_relative "raw/decompressor"
|
6
|
+
require_relative "reader_helpers"
|
7
|
+
require_relative "../validation"
|
8
|
+
|
9
|
+
module ADSP
|
10
|
+
module Stream
|
11
|
+
# ADSP::Stream::Reader class.
|
12
|
+
class Reader < Abstract
|
13
|
+
include ReaderHelpers
|
14
|
+
|
15
|
+
# Default source buffer length.
|
16
|
+
DEFAULT_SOURCE_BUFFER_LENGTH = 1 << 18 # 256 KB
|
17
|
+
|
18
|
+
# Current raw stream class.
|
19
|
+
RawDecompressor = Raw::Decompressor
|
20
|
+
|
21
|
+
# Current line for source data.
|
22
|
+
attr_accessor :lineno
|
23
|
+
|
24
|
+
# Initializes stream using +source_io+ native stream and +options+.
|
25
|
+
# Option: +:external_encoding+ encoding name for destination data.
|
26
|
+
# Option: +:internal_encoding+ encoding name for source data.
|
27
|
+
# Option: +:transcode_options+ transcode options for data.
|
28
|
+
def initialize(source_io, options = {}, *args)
|
29
|
+
@options = options
|
30
|
+
|
31
|
+
super source_io, *args
|
32
|
+
|
33
|
+
initialize_source_buffer_length
|
34
|
+
reset_io_remainder
|
35
|
+
reset_need_to_flush
|
36
|
+
|
37
|
+
@lineno = 0
|
38
|
+
end
|
39
|
+
|
40
|
+
# Creates raw stream.
|
41
|
+
protected def create_raw_stream
|
42
|
+
self.class::RawDecompressor.new @options
|
43
|
+
end
|
44
|
+
|
45
|
+
# Initializes source buffer length.
|
46
|
+
protected def initialize_source_buffer_length
|
47
|
+
source_buffer_length = @options[:source_buffer_length]
|
48
|
+
Validation.validate_not_negative_integer source_buffer_length unless source_buffer_length.nil?
|
49
|
+
|
50
|
+
if source_buffer_length.nil? || source_buffer_length.zero?
|
51
|
+
source_buffer_length = self.class::DEFAULT_SOURCE_BUFFER_LENGTH
|
52
|
+
end
|
53
|
+
|
54
|
+
@source_buffer_length = source_buffer_length
|
55
|
+
end
|
56
|
+
|
57
|
+
# Resets io remainder.
|
58
|
+
protected def reset_io_remainder
|
59
|
+
@io_remainder = ::String.new :encoding => ::Encoding::BINARY
|
60
|
+
end
|
61
|
+
|
62
|
+
# Resets need to flush flag.
|
63
|
+
protected def reset_need_to_flush
|
64
|
+
@need_to_flush = false
|
65
|
+
end
|
66
|
+
|
67
|
+
# -- synchronous --
|
68
|
+
|
69
|
+
# Reads +bytes_to_read+ bytes from stream.
|
70
|
+
# If +out_buffer+ is defined than it will be used as output destination.
|
71
|
+
def read(bytes_to_read = nil, out_buffer = nil)
|
72
|
+
Validation.validate_not_negative_integer bytes_to_read unless bytes_to_read.nil?
|
73
|
+
Validation.validate_string out_buffer unless out_buffer.nil?
|
74
|
+
|
75
|
+
raise ValidateError, "io should be responsible to read and eof" unless
|
76
|
+
@io.respond_to?(:read) && @io.respond_to?(:eof?)
|
77
|
+
|
78
|
+
unless bytes_to_read.nil?
|
79
|
+
return ::String.new :encoding => ::Encoding::BINARY if bytes_to_read.zero?
|
80
|
+
return nil if eof?
|
81
|
+
|
82
|
+
append_io_data @io.read(@source_buffer_length) while @buffer.bytesize < bytes_to_read && !@io.eof?
|
83
|
+
flush_io_data if @buffer.bytesize < bytes_to_read
|
84
|
+
|
85
|
+
return read_bytes_from_buffer bytes_to_read, out_buffer
|
86
|
+
end
|
87
|
+
|
88
|
+
append_io_data @io.read(@source_buffer_length) until @io.eof?
|
89
|
+
flush_io_data
|
90
|
+
|
91
|
+
read_buffer out_buffer
|
92
|
+
end
|
93
|
+
|
94
|
+
# Resets stream.
|
95
|
+
def rewind
|
96
|
+
raw_wrapper :close
|
97
|
+
|
98
|
+
reset_io_remainder
|
99
|
+
reset_need_to_flush
|
100
|
+
|
101
|
+
super
|
102
|
+
end
|
103
|
+
|
104
|
+
# Closes stream.
|
105
|
+
def close
|
106
|
+
raw_wrapper :close
|
107
|
+
|
108
|
+
super
|
109
|
+
end
|
110
|
+
|
111
|
+
# Returns whether we are at the end of stream.
|
112
|
+
def eof?
|
113
|
+
raise ValidateError, "io should be responsible to eof" unless @io.respond_to? :eof?
|
114
|
+
|
115
|
+
empty? && @io.eof?
|
116
|
+
end
|
117
|
+
|
118
|
+
# -- asynchronous --
|
119
|
+
|
120
|
+
# Reads +bytes_to_read+ bytes from stream.
|
121
|
+
# If +out_buffer+ is defined than it will be used as output destination.
|
122
|
+
# Raises +::EOFError+ when no data available.
|
123
|
+
def readpartial(bytes_to_read, out_buffer = nil)
|
124
|
+
raise ValidateError, "io should be responsible to readpartial" unless @io.respond_to? :readpartial
|
125
|
+
|
126
|
+
read_more_nonblock(bytes_to_read, out_buffer) { @io.readpartial @source_buffer_length }
|
127
|
+
end
|
128
|
+
|
129
|
+
# Reads +bytes_to_read+ bytes from stream.
|
130
|
+
# If +out_buffer+ is defined than it will be used as output destination.
|
131
|
+
# +options+ will be passed to native stream.
|
132
|
+
def read_nonblock(bytes_to_read, out_buffer = nil, *options)
|
133
|
+
raise ValidateError, "io should be responsible to read nonblock" unless @io.respond_to? :read_nonblock
|
134
|
+
|
135
|
+
read_more_nonblock(bytes_to_read, out_buffer) { @io.read_nonblock(@source_buffer_length, *options) }
|
136
|
+
end
|
137
|
+
|
138
|
+
# Reads +bytes_to_read+ bytes from stream.
|
139
|
+
# If +out_buffer+ is defined than it will be used as output destination.
|
140
|
+
protected def read_more_nonblock(bytes_to_read, out_buffer, &_block)
|
141
|
+
Validation.validate_not_negative_integer bytes_to_read
|
142
|
+
Validation.validate_string out_buffer unless out_buffer.nil?
|
143
|
+
|
144
|
+
return ::String.new :encoding => ::Encoding::BINARY if bytes_to_read.zero?
|
145
|
+
|
146
|
+
io_provided_eof_error = false
|
147
|
+
|
148
|
+
if @buffer.bytesize < bytes_to_read
|
149
|
+
begin
|
150
|
+
append_io_data yield
|
151
|
+
rescue ::EOFError
|
152
|
+
io_provided_eof_error = true
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
flush_io_data if @buffer.bytesize < bytes_to_read
|
157
|
+
raise ::EOFError if empty? && io_provided_eof_error
|
158
|
+
|
159
|
+
read_bytes_from_buffer bytes_to_read, out_buffer
|
160
|
+
end
|
161
|
+
|
162
|
+
# -- common --
|
163
|
+
|
164
|
+
# Appends +io_data+ from native stream to internal storage.
|
165
|
+
protected def append_io_data(io_data)
|
166
|
+
io_portion = @io_remainder + io_data
|
167
|
+
bytes_read = raw_wrapper :read, io_portion
|
168
|
+
@io_remainder = io_portion.byteslice bytes_read, io_portion.bytesize - bytes_read
|
169
|
+
|
170
|
+
# Even empty io data may require flush.
|
171
|
+
@need_to_flush = true
|
172
|
+
end
|
173
|
+
|
174
|
+
# Triggers flush method for native stream.
|
175
|
+
protected def flush_io_data
|
176
|
+
raw_wrapper :flush
|
177
|
+
|
178
|
+
@need_to_flush = false
|
179
|
+
end
|
180
|
+
|
181
|
+
# Returns whether stream is empty.
|
182
|
+
protected def empty?
|
183
|
+
!@need_to_flush && @buffer.bytesize.zero?
|
184
|
+
end
|
185
|
+
|
186
|
+
# Reads +bytes_to_read+ bytes from buffer.
|
187
|
+
# If +out_buffer+ is defined than it will be used as output destination.
|
188
|
+
protected def read_bytes_from_buffer(bytes_to_read, out_buffer)
|
189
|
+
bytes_read = [@buffer.bytesize, bytes_to_read].min
|
190
|
+
|
191
|
+
# Result uses buffer binary encoding.
|
192
|
+
result = @buffer.byteslice 0, bytes_read
|
193
|
+
@buffer = @buffer.byteslice bytes_read, @buffer.bytesize - bytes_read
|
194
|
+
@pos += bytes_read
|
195
|
+
|
196
|
+
result = out_buffer.replace result unless out_buffer.nil?
|
197
|
+
result
|
198
|
+
end
|
199
|
+
|
200
|
+
# Reads data from buffer.
|
201
|
+
# If +out_buffer+ is defined than it will be used as output destination.
|
202
|
+
protected def read_buffer(out_buffer)
|
203
|
+
result = @buffer
|
204
|
+
reset_buffer
|
205
|
+
@pos += result.bytesize
|
206
|
+
|
207
|
+
result.force_encoding @external_encoding unless @external_encoding.nil?
|
208
|
+
result = transcode_to_internal result
|
209
|
+
|
210
|
+
result = out_buffer.replace result unless out_buffer.nil?
|
211
|
+
result
|
212
|
+
end
|
213
|
+
|
214
|
+
# Transcodes +data+ to internal encoding.
|
215
|
+
protected def transcode_to_internal(data)
|
216
|
+
data = data.encode @internal_encoding, **@transcode_options unless @internal_encoding.nil?
|
217
|
+
data
|
218
|
+
end
|
219
|
+
|
220
|
+
# Transcodes +data+ to external encoding.
|
221
|
+
# We should be able to return data back to buffer.
|
222
|
+
# We won't use any transcode options because transcoded data should be backward compatible.
|
223
|
+
protected def transcode_to_external(data)
|
224
|
+
data = data.encode @external_encoding unless @external_encoding.nil?
|
225
|
+
data
|
226
|
+
end
|
227
|
+
|
228
|
+
# Wraps +method_name+ for raw stream.
|
229
|
+
protected def raw_wrapper(method_name, *args)
|
230
|
+
@raw_stream.send(method_name, *args) { |portion| @buffer << portion }
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|