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