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.
- 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
|