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,219 @@
|
|
1
|
+
# Abstract data stream processor.
|
2
|
+
# Copyright (c) 2021 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
require "English"
|
5
|
+
|
6
|
+
require_relative "../validation"
|
7
|
+
|
8
|
+
module ADSP
|
9
|
+
module Stream
|
10
|
+
# ADSP::Stream::ReaderHelpers module.
|
11
|
+
module ReaderHelpers
|
12
|
+
# Returns next byte.
|
13
|
+
def getbyte
|
14
|
+
read 1
|
15
|
+
end
|
16
|
+
|
17
|
+
# Yields each byte.
|
18
|
+
def each_byte(&block)
|
19
|
+
each_string method(:getbyte), &block
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns next byte.
|
23
|
+
# Raises +::EOFError+ when no data available.
|
24
|
+
def readbyte
|
25
|
+
readstring method(:getbyte)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Pushes back +byte+.
|
29
|
+
def ungetbyte(byte)
|
30
|
+
Validation.validate_string byte
|
31
|
+
|
32
|
+
@buffer.prepend byte
|
33
|
+
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
|
37
|
+
# -- char --
|
38
|
+
|
39
|
+
# Returns next char.
|
40
|
+
def getc
|
41
|
+
if @external_encoding.nil?
|
42
|
+
byte = getbyte
|
43
|
+
return nil if byte.nil?
|
44
|
+
|
45
|
+
return transcode_to_internal byte
|
46
|
+
end
|
47
|
+
|
48
|
+
char = ::String.new :encoding => ::Encoding::BINARY
|
49
|
+
|
50
|
+
# Read one byte until valid string will appear.
|
51
|
+
loop do
|
52
|
+
byte = getbyte
|
53
|
+
return nil if byte.nil?
|
54
|
+
|
55
|
+
char << byte
|
56
|
+
|
57
|
+
char.force_encoding @external_encoding
|
58
|
+
return transcode_to_internal char if char.valid_encoding?
|
59
|
+
|
60
|
+
char.force_encoding ::Encoding::BINARY
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns next char.
|
65
|
+
# Raises +::EOFError+ when no data available.
|
66
|
+
def readchar
|
67
|
+
readstring method(:getc)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Yields each char.
|
71
|
+
def each_char(&block)
|
72
|
+
each_string method(:getc), &block
|
73
|
+
end
|
74
|
+
|
75
|
+
# Pushes back +char+.
|
76
|
+
def ungetc(char)
|
77
|
+
ungetstring char
|
78
|
+
end
|
79
|
+
|
80
|
+
# -- lines --
|
81
|
+
|
82
|
+
# Returns next line by +separator+.
|
83
|
+
# Line length is limited by +limit+.
|
84
|
+
def gets(separator = $OUTPUT_RECORD_SEPARATOR, limit = nil)
|
85
|
+
# Limit can be a first argument.
|
86
|
+
if separator.is_a? ::Numeric
|
87
|
+
limit = separator
|
88
|
+
separator = $OUTPUT_RECORD_SEPARATOR
|
89
|
+
end
|
90
|
+
|
91
|
+
line_ending =
|
92
|
+
if separator.nil?
|
93
|
+
nil
|
94
|
+
else
|
95
|
+
Validation.validate_string separator
|
96
|
+
::String.new separator, :encoding => target_encoding
|
97
|
+
end
|
98
|
+
|
99
|
+
Validation.validate_positive_integer limit unless limit.nil?
|
100
|
+
|
101
|
+
line = ::String.new :encoding => target_encoding
|
102
|
+
|
103
|
+
loop do
|
104
|
+
char = getc
|
105
|
+
|
106
|
+
if char.nil?
|
107
|
+
return nil if line.empty?
|
108
|
+
|
109
|
+
break
|
110
|
+
end
|
111
|
+
|
112
|
+
line << char
|
113
|
+
|
114
|
+
break if
|
115
|
+
(!line_ending.nil? && line.end_with?(line_ending)) ||
|
116
|
+
(!limit.nil? && line.length >= limit)
|
117
|
+
end
|
118
|
+
|
119
|
+
@lineno += 1
|
120
|
+
|
121
|
+
line
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns next line.
|
125
|
+
# Raises +::EOFError+ when no data available.
|
126
|
+
def readline
|
127
|
+
readstring method(:gets)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Returns all available lines.
|
131
|
+
def readlines
|
132
|
+
lines = []
|
133
|
+
each_line { |line| lines << line }
|
134
|
+
|
135
|
+
lines
|
136
|
+
end
|
137
|
+
|
138
|
+
# Yields each line.
|
139
|
+
def each_line(&block)
|
140
|
+
each_string method(:gets), &block
|
141
|
+
end
|
142
|
+
|
143
|
+
alias each each_line
|
144
|
+
|
145
|
+
# Pushes back +line+.
|
146
|
+
def ungetline(line)
|
147
|
+
ungetstring line
|
148
|
+
|
149
|
+
@lineno -= 1
|
150
|
+
|
151
|
+
nil
|
152
|
+
end
|
153
|
+
|
154
|
+
# -- common --
|
155
|
+
|
156
|
+
# Returns next string by +each_proc+.
|
157
|
+
# Raises +::EOFError+ when no data available.
|
158
|
+
protected def readstring(each_proc)
|
159
|
+
string = each_proc.call
|
160
|
+
raise ::EOFError if string.nil?
|
161
|
+
|
162
|
+
string
|
163
|
+
end
|
164
|
+
|
165
|
+
# Yields each string by +each_proc+.
|
166
|
+
protected def each_string(each_proc, &block)
|
167
|
+
return enum_for __method__, each_proc unless block.is_a? ::Proc
|
168
|
+
|
169
|
+
loop do
|
170
|
+
string = each_proc.call
|
171
|
+
break if string.nil?
|
172
|
+
|
173
|
+
yield string
|
174
|
+
end
|
175
|
+
|
176
|
+
nil
|
177
|
+
end
|
178
|
+
|
179
|
+
# Pushes back +string+.
|
180
|
+
protected def ungetstring(string)
|
181
|
+
Validation.validate_string string
|
182
|
+
|
183
|
+
string = ::String.new string, :encoding => @internal_encoding unless @internal_encoding.nil?
|
184
|
+
string = transcode_to_external string unless @external_encoding.nil?
|
185
|
+
|
186
|
+
string.force_encoding ::Encoding::BINARY
|
187
|
+
@buffer.prepend string
|
188
|
+
|
189
|
+
nil
|
190
|
+
end
|
191
|
+
|
192
|
+
# -- etc --
|
193
|
+
|
194
|
+
# Additional class methods for reader.
|
195
|
+
module ClassMethods
|
196
|
+
# Opens +file_path+ in binary mode, creates reader and yields it.
|
197
|
+
def open(file_path, *args, &block)
|
198
|
+
Validation.validate_string file_path
|
199
|
+
Validation.validate_proc block
|
200
|
+
|
201
|
+
::File.open file_path, "rb" do |io|
|
202
|
+
reader = new io, *args
|
203
|
+
|
204
|
+
begin
|
205
|
+
yield reader
|
206
|
+
ensure
|
207
|
+
reader.close
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# Extends target +klass+ with additional class methods.
|
214
|
+
def self.included(klass)
|
215
|
+
klass.extend ClassMethods
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
@@ -0,0 +1,80 @@
|
|
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::Stat class.
|
9
|
+
class Stat
|
10
|
+
# Libraries like minitar tries to access stat to know whether stream is seekable.
|
11
|
+
# We need to mark stream as not directory, file, etc, because it is not seekable.
|
12
|
+
|
13
|
+
extend ::Forwardable
|
14
|
+
|
15
|
+
# List of methods returning false.
|
16
|
+
METHODS_RETURNING_FALSE = %i[
|
17
|
+
blockdev?
|
18
|
+
chardev?
|
19
|
+
directory?
|
20
|
+
executable?
|
21
|
+
executable_real?
|
22
|
+
file?
|
23
|
+
grpowned?
|
24
|
+
owned?
|
25
|
+
pipe?
|
26
|
+
setgid?
|
27
|
+
setuid?
|
28
|
+
socket?
|
29
|
+
sticky?
|
30
|
+
symlink?
|
31
|
+
zero?
|
32
|
+
]
|
33
|
+
.freeze
|
34
|
+
|
35
|
+
# List of methods to be forwarded for native stream status info.
|
36
|
+
DELEGATES = %i[
|
37
|
+
<=>
|
38
|
+
atime
|
39
|
+
birthtime
|
40
|
+
blksize
|
41
|
+
blocks
|
42
|
+
ctime
|
43
|
+
dev
|
44
|
+
dev_major
|
45
|
+
dev_minor
|
46
|
+
ftype
|
47
|
+
gid
|
48
|
+
ino
|
49
|
+
inspect
|
50
|
+
mode
|
51
|
+
mtime
|
52
|
+
nlink
|
53
|
+
rdev
|
54
|
+
rdev_major
|
55
|
+
rdev_minor
|
56
|
+
readable?
|
57
|
+
readable_real?
|
58
|
+
size
|
59
|
+
size?
|
60
|
+
uid
|
61
|
+
world_readable?
|
62
|
+
world_writable?
|
63
|
+
writable?
|
64
|
+
writable_real?
|
65
|
+
]
|
66
|
+
.freeze
|
67
|
+
|
68
|
+
# Initializes status info based on native stream +stat+.
|
69
|
+
def initialize(stat)
|
70
|
+
@stat = stat
|
71
|
+
end
|
72
|
+
|
73
|
+
METHODS_RETURNING_FALSE.each do |method_name|
|
74
|
+
define_method(method_name) { false }
|
75
|
+
end
|
76
|
+
|
77
|
+
def_delegators :@stat, *DELEGATES
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,206 @@
|
|
1
|
+
# Abstract data stream processor.
|
2
|
+
# Copyright (c) 2021 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
require_relative "abstract"
|
5
|
+
require_relative "raw/compressor"
|
6
|
+
require_relative "writer_helpers"
|
7
|
+
|
8
|
+
module ADSP
|
9
|
+
module Stream
|
10
|
+
# ADSP::Stream::Writer class.
|
11
|
+
class Writer < Abstract
|
12
|
+
include WriterHelpers
|
13
|
+
|
14
|
+
# Current raw stream class.
|
15
|
+
RawCompressor = Raw::Compressor
|
16
|
+
|
17
|
+
# Initializes stream using +destination_io+ native stream and +options+.
|
18
|
+
# Option: +:external_encoding+ encoding name for destination data.
|
19
|
+
# Option: +:internal_encoding+ encoding name for source data.
|
20
|
+
# Option: +:transcode_options+ transcode options for data.
|
21
|
+
def initialize(destination_io, options = {}, *args)
|
22
|
+
@options = options
|
23
|
+
|
24
|
+
super destination_io, *args
|
25
|
+
end
|
26
|
+
|
27
|
+
# Creates raw stream.
|
28
|
+
protected def create_raw_stream
|
29
|
+
self.class::RawCompressor.new @options
|
30
|
+
end
|
31
|
+
|
32
|
+
# -- synchronous --
|
33
|
+
|
34
|
+
# Writes +objects+ to stream.
|
35
|
+
def write(*objects)
|
36
|
+
validate_write
|
37
|
+
|
38
|
+
write_remaining_buffer
|
39
|
+
|
40
|
+
bytes_written = 0
|
41
|
+
|
42
|
+
objects.each do |object|
|
43
|
+
source = transcode object.to_s
|
44
|
+
bytes_written += raw_wrapper :write, source
|
45
|
+
end
|
46
|
+
|
47
|
+
@pos += bytes_written
|
48
|
+
|
49
|
+
bytes_written
|
50
|
+
end
|
51
|
+
|
52
|
+
# Flushes stream.
|
53
|
+
def flush
|
54
|
+
validate_write
|
55
|
+
|
56
|
+
finish :flush
|
57
|
+
|
58
|
+
@io.flush if @io.respond_to? :flush
|
59
|
+
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
# Resets stream.
|
64
|
+
def rewind
|
65
|
+
validate_write
|
66
|
+
|
67
|
+
finish :close
|
68
|
+
|
69
|
+
super
|
70
|
+
end
|
71
|
+
|
72
|
+
# Closes stream.
|
73
|
+
def close
|
74
|
+
validate_write
|
75
|
+
|
76
|
+
finish :close
|
77
|
+
|
78
|
+
super
|
79
|
+
end
|
80
|
+
|
81
|
+
# Finishes stream using +method_name+.
|
82
|
+
protected def finish(method_name)
|
83
|
+
write_remaining_buffer
|
84
|
+
|
85
|
+
raw_wrapper method_name
|
86
|
+
end
|
87
|
+
|
88
|
+
# Writes remaining buffer and resets it.
|
89
|
+
protected def write_remaining_buffer
|
90
|
+
return nil if @buffer.bytesize.zero?
|
91
|
+
|
92
|
+
@io.write @buffer
|
93
|
+
|
94
|
+
reset_buffer
|
95
|
+
end
|
96
|
+
|
97
|
+
# Wraps +method_name+ for raw stream.
|
98
|
+
protected def raw_wrapper(method_name, *args)
|
99
|
+
@raw_stream.send(method_name, *args) { |portion| @io.write portion }
|
100
|
+
end
|
101
|
+
|
102
|
+
# Validates native stream responsibility to +write+ method.
|
103
|
+
protected def validate_write
|
104
|
+
raise ValidateError, "io should be responsible to write" unless @io.respond_to? :write
|
105
|
+
end
|
106
|
+
|
107
|
+
# -- asynchronous --
|
108
|
+
|
109
|
+
# Writes +object+ nonblock.
|
110
|
+
# +options+ will be passed to native stream.
|
111
|
+
# Native stream +write_nonblock+ can raise +IO::WaitWritable+ error.
|
112
|
+
# After resolving this error user may provide same content again.
|
113
|
+
# It is not possible to revert accepted content after error.
|
114
|
+
# So we have to accept content after processing native stream +write_nonblock+.
|
115
|
+
# It means that first write nonblock won't call native stream +write_nonblock+.
|
116
|
+
def write_nonblock(object, *options)
|
117
|
+
validate_write_nonblock
|
118
|
+
|
119
|
+
return 0 unless write_remaining_buffer_nonblock(*options)
|
120
|
+
|
121
|
+
source = transcode object.to_s
|
122
|
+
bytes_written = raw_nonblock_wrapper :write, source
|
123
|
+
@pos += bytes_written
|
124
|
+
|
125
|
+
bytes_written
|
126
|
+
end
|
127
|
+
|
128
|
+
# Flushes stream nonblock.
|
129
|
+
# +options+ will be passed to native stream.
|
130
|
+
def flush_nonblock(*options)
|
131
|
+
validate_write_nonblock
|
132
|
+
|
133
|
+
return false unless finish_nonblock :flush, *options
|
134
|
+
|
135
|
+
@io.flush if @io.respond_to? :flush
|
136
|
+
|
137
|
+
true
|
138
|
+
end
|
139
|
+
|
140
|
+
# Resets stream nonblock.
|
141
|
+
# +options+ will be passed to native stream.
|
142
|
+
def rewind_nonblock(*options)
|
143
|
+
validate_write_nonblock
|
144
|
+
|
145
|
+
return false unless finish_nonblock :close, *options
|
146
|
+
|
147
|
+
method(:rewind).super_method.call
|
148
|
+
|
149
|
+
true
|
150
|
+
end
|
151
|
+
|
152
|
+
# Closes stream nonblock.
|
153
|
+
# +options+ will be passed to native stream.
|
154
|
+
def close_nonblock(*options)
|
155
|
+
validate_write_nonblock
|
156
|
+
|
157
|
+
return false unless finish_nonblock :close, *options
|
158
|
+
|
159
|
+
method(:close).super_method.call
|
160
|
+
|
161
|
+
true
|
162
|
+
end
|
163
|
+
|
164
|
+
# Finishes stream using +method_name+ nonblock.
|
165
|
+
# +options+ will be passed to native stream.
|
166
|
+
protected def finish_nonblock(method_name, *options)
|
167
|
+
return false unless write_remaining_buffer_nonblock(*options)
|
168
|
+
|
169
|
+
raw_nonblock_wrapper method_name
|
170
|
+
|
171
|
+
write_remaining_buffer_nonblock(*options)
|
172
|
+
end
|
173
|
+
|
174
|
+
# Writes remaining buffer nonblock.
|
175
|
+
# +options+ will be passed to native stream.
|
176
|
+
protected def write_remaining_buffer_nonblock(*options)
|
177
|
+
return true if @buffer.bytesize.zero?
|
178
|
+
|
179
|
+
bytes_written = @io.write_nonblock @buffer, *options
|
180
|
+
return false if bytes_written.zero?
|
181
|
+
|
182
|
+
@buffer = @buffer.byteslice bytes_written, @buffer.bytesize - bytes_written
|
183
|
+
|
184
|
+
@buffer.bytesize.zero?
|
185
|
+
end
|
186
|
+
|
187
|
+
# Wraps nonblock +method_name+ for raw stream.
|
188
|
+
protected def raw_nonblock_wrapper(method_name, *args)
|
189
|
+
@raw_stream.send(method_name, *args) { |portion| @buffer << portion }
|
190
|
+
end
|
191
|
+
|
192
|
+
# Validates native stream responsibility to +write_nonblock+ method.
|
193
|
+
protected def validate_write_nonblock
|
194
|
+
raise ValidateError, "io should be responsible to write nonblock" unless @io.respond_to? :write_nonblock
|
195
|
+
end
|
196
|
+
|
197
|
+
# -- common --
|
198
|
+
|
199
|
+
# Transcodes +data+ to external encoding.
|
200
|
+
protected def transcode(data)
|
201
|
+
data = data.encode @external_encoding, **@transcode_options unless @external_encoding.nil?
|
202
|
+
data
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# Abstract data stream processor.
|
2
|
+
# Copyright (c) 2021 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
require "English"
|
5
|
+
|
6
|
+
require_relative "../validation"
|
7
|
+
|
8
|
+
module ADSP
|
9
|
+
module Stream
|
10
|
+
# ADSP::Stream::WriterHelpers module.
|
11
|
+
module WriterHelpers
|
12
|
+
# Writes +object+ to stream.
|
13
|
+
def <<(object)
|
14
|
+
write object
|
15
|
+
end
|
16
|
+
|
17
|
+
# Writes +objects+ to stream.
|
18
|
+
# Uses +field_separator+ for each object.
|
19
|
+
# Uses +record_separator+ for group of objects.
|
20
|
+
def print(*objects, field_separator: $OUTPUT_FIELD_SEPARATOR, record_separator: $OUTPUT_RECORD_SEPARATOR)
|
21
|
+
objects.each do |object|
|
22
|
+
write object
|
23
|
+
write field_separator unless field_separator.nil?
|
24
|
+
end
|
25
|
+
|
26
|
+
write record_separator unless record_separator.nil?
|
27
|
+
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
31
|
+
# Formats each argument and writes to stream.
|
32
|
+
def printf(*args)
|
33
|
+
write sprintf(*args)
|
34
|
+
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
|
38
|
+
# Writes first char of +object+ to stream.
|
39
|
+
# Numeric object uses +encoding+ for providing first char.
|
40
|
+
def putc(object, encoding: ::Encoding::BINARY)
|
41
|
+
case object
|
42
|
+
when ::Numeric
|
43
|
+
write object.chr(encoding)
|
44
|
+
when ::String
|
45
|
+
write object[0]
|
46
|
+
else
|
47
|
+
raise ValidateError, "invalid object: \"#{object}\" for putc"
|
48
|
+
end
|
49
|
+
|
50
|
+
object
|
51
|
+
end
|
52
|
+
|
53
|
+
# Writes +objects+ to stream.
|
54
|
+
def puts(*objects)
|
55
|
+
objects.each do |object|
|
56
|
+
if object.is_a? ::Array
|
57
|
+
puts(*object)
|
58
|
+
next
|
59
|
+
end
|
60
|
+
|
61
|
+
source = object.to_s
|
62
|
+
newline = "\n".encode source.encoding
|
63
|
+
|
64
|
+
# Do not add newline if source ends with newline.
|
65
|
+
if source.end_with? newline
|
66
|
+
write source
|
67
|
+
else
|
68
|
+
write source + newline
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
nil
|
73
|
+
end
|
74
|
+
|
75
|
+
# -- etc --
|
76
|
+
|
77
|
+
# Additional class methods for writer.
|
78
|
+
module ClassMethods
|
79
|
+
# Opens +file_path+ in binary mode, creates writer and yields it.
|
80
|
+
def open(file_path, *args, &block)
|
81
|
+
Validation.validate_string file_path
|
82
|
+
Validation.validate_proc block
|
83
|
+
|
84
|
+
::File.open file_path, "wb" do |io|
|
85
|
+
writer = new io, *args
|
86
|
+
|
87
|
+
begin
|
88
|
+
yield writer
|
89
|
+
ensure
|
90
|
+
writer.close
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Extends target +klass+ with additional class methods.
|
97
|
+
def self.included(klass)
|
98
|
+
klass.extend ClassMethods
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/lib/adsp/string.rb
ADDED
@@ -0,0 +1,58 @@
|
|
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::String class.
|
10
|
+
class String
|
11
|
+
# Current option class.
|
12
|
+
Option = ADSP::Option
|
13
|
+
|
14
|
+
# Current buffer length names.
|
15
|
+
# It is a part of decompressor options.
|
16
|
+
BUFFER_LENGTH_NAMES = %i[destination_buffer_length].freeze
|
17
|
+
|
18
|
+
# Compresses +source+ string using +options+.
|
19
|
+
# Option: +:destination_buffer_length+ destination buffer length.
|
20
|
+
# Returns compressed string.
|
21
|
+
def self.compress(source, options = {})
|
22
|
+
Validation.validate_string source
|
23
|
+
|
24
|
+
options = self::Option.get_compressor_options options, BUFFER_LENGTH_NAMES
|
25
|
+
|
26
|
+
native_compress_string source, options
|
27
|
+
end
|
28
|
+
|
29
|
+
# :nocov:
|
30
|
+
|
31
|
+
# Internal method for compressing +source+ string using +options+.
|
32
|
+
def self.native_compress_string(source, options)
|
33
|
+
raise NotImplementedError
|
34
|
+
end
|
35
|
+
|
36
|
+
# :nocov:
|
37
|
+
|
38
|
+
# Decompresses +source+ string using +options+.
|
39
|
+
# Option: +:destination_buffer_length+ destination buffer length.
|
40
|
+
# Returns decompressed string.
|
41
|
+
def self.decompress(source, options = {})
|
42
|
+
Validation.validate_string source
|
43
|
+
|
44
|
+
options = self::Option.get_decompressor_options options, BUFFER_LENGTH_NAMES
|
45
|
+
|
46
|
+
native_decompress_string source, options
|
47
|
+
end
|
48
|
+
|
49
|
+
# :nocov:
|
50
|
+
|
51
|
+
# Internal method for decompressing +source+ string using +options+.
|
52
|
+
def self.native_decompress_string(source, options)
|
53
|
+
raise NotImplementedError
|
54
|
+
end
|
55
|
+
|
56
|
+
# :nocov:
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# Abstract data stream processor.
|
2
|
+
# Copyright (c) 2021 AUTHORS, MIT License.
|
3
|
+
|
4
|
+
require_relative "error"
|
5
|
+
|
6
|
+
module ADSP
|
7
|
+
# ADSP::Validation class.
|
8
|
+
class Validation
|
9
|
+
# Raises error when +value+ is not array.
|
10
|
+
def self.validate_array(value)
|
11
|
+
raise ValidateError, "invalid array" unless value.is_a? ::Array
|
12
|
+
end
|
13
|
+
|
14
|
+
# Raises error when +value+ is not hash.
|
15
|
+
def self.validate_hash(value)
|
16
|
+
raise ValidateError, "invalid hash" unless value.is_a? ::Hash
|
17
|
+
end
|
18
|
+
|
19
|
+
# Raises error when +value+ is not negative integer.
|
20
|
+
def self.validate_not_negative_integer(value)
|
21
|
+
raise ValidateError, "invalid not negative integer" unless value.is_a?(::Integer) && value >= 0
|
22
|
+
end
|
23
|
+
|
24
|
+
# Raises error when +value+ is not positive integer.
|
25
|
+
def self.validate_positive_integer(value)
|
26
|
+
raise ValidateError, "invalid positive integer" unless value.is_a?(::Integer) && value.positive?
|
27
|
+
end
|
28
|
+
|
29
|
+
# Raises error when +value+ is not proc.
|
30
|
+
def self.validate_proc(value)
|
31
|
+
unless value.is_a?(::Proc) || value.is_a?(::Method) || value.is_a?(::UnboundMethod)
|
32
|
+
raise ValidateError, "invalid proc"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Raises error when +value+ is not string.
|
37
|
+
def self.validate_string(value)
|
38
|
+
raise ValidateError, "invalid string" unless value.is_a? ::String
|
39
|
+
end
|
40
|
+
|
41
|
+
# Raises error when +value+ is not symbol.
|
42
|
+
def self.validate_symbol(value)
|
43
|
+
raise ValidateError, "invalid symbol" unless value.is_a? ::Symbol
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|