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
data/lib/adsp.rb ADDED
@@ -0,0 +1,8 @@
1
+ # Abstract data stream processor.
2
+ # Copyright (c) 2021 AUTHORS, MIT License.
3
+
4
+ require_relative "adsp/stream/reader"
5
+ require_relative "adsp/stream/writer"
6
+ require_relative "adsp/file"
7
+ require_relative "adsp/string"
8
+ require_relative "adsp/version"
data/test/common.rb ADDED
@@ -0,0 +1,108 @@
1
+ # Abstract data stream processor.
2
+ # Copyright (c) 2021 AUTHORS, MIT License.
3
+
4
+ require "parallel"
5
+ require "securerandom"
6
+ require "tempfile"
7
+
8
+ require_relative "coverage_helper"
9
+
10
+ module ADSP
11
+ module Test
12
+ module Common
13
+ BASE_PATH = ::File.expand_path(::File.join(::File.dirname(__FILE__), "..")).freeze
14
+ TEMP_PATH = ::File.join(BASE_PATH, "tmp").freeze
15
+
16
+ SOURCE_PATH = ::File.join(TEMP_PATH, "source").freeze
17
+ ARCHIVE_PATH = ::File.join(TEMP_PATH, "archive").freeze
18
+
19
+ [
20
+ SOURCE_PATH,
21
+ ARCHIVE_PATH
22
+ ]
23
+ .each { |path| FileUtils.touch path }
24
+
25
+ def self.get_path(path, suffix)
26
+ "#{path}_#{suffix}"
27
+ end
28
+
29
+ ENCODINGS = %w[
30
+ binary
31
+ UTF-8
32
+ UTF-16LE
33
+ ]
34
+ .map { |encoding_name| ::Encoding.find encoding_name }
35
+ .freeze
36
+
37
+ TRANSCODE_OPTIONS = {
38
+ :invalid => :replace,
39
+ :undef => :replace,
40
+ :replace => "?"
41
+ }
42
+ .freeze
43
+
44
+ def self.generate_texts(*sources)
45
+ sources.flat_map do |source|
46
+ ENCODINGS.map do |encoding|
47
+ source.encode encoding, **TRANSCODE_OPTIONS
48
+ end
49
+ end
50
+ end
51
+
52
+ TEXTS = generate_texts(
53
+ "",
54
+ ::SecureRandom.random_bytes(1 << 8) # 256 B
55
+ )
56
+ .freeze
57
+
58
+ LARGE_TEXTS = generate_texts(
59
+ ::SecureRandom.random_bytes(1 << 21) # 2 MB
60
+ )
61
+ .freeze
62
+
63
+ # It is better to have text lengths not divisible by portion lengths.
64
+ PORTION_LENGTHS = [
65
+ 10**2,
66
+ 5 * (10**2)
67
+ ]
68
+ .freeze
69
+
70
+ # It is better to have large text lengths not divisible by large portion lengths.
71
+ LARGE_PORTION_LENGTHS = [
72
+ 10**6,
73
+ 5 * (10**6)
74
+ ]
75
+ .freeze
76
+
77
+ # We need at least 2 threads for testing multiple threads support.
78
+ THREADS_COUNT = [Parallel.processor_count, 2].max.freeze
79
+
80
+ def self.parallel(producer, &_block)
81
+ Parallel.each producer, :in_threads => THREADS_COUNT do |item|
82
+ yield item, Parallel.worker_number
83
+ end
84
+ end
85
+
86
+ def self.parallel_options(generator, &block)
87
+ producer = proc do
88
+ next Parallel::Stop if generator.finished?
89
+
90
+ generator.next
91
+ end
92
+
93
+ parallel producer, &block
94
+ end
95
+
96
+ def self.file_can_be_used_nonblock?
97
+ ::File.open(::Tempfile.new, "w") do |file|
98
+ file.write_nonblock "text"
99
+ end
100
+ rescue Errno::EBADF
101
+ # Nonblock operations with files are not available on Windows.
102
+ false
103
+ else
104
+ true
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,18 @@
1
+ # Abstract data stream processor.
2
+ # Copyright (c) 2021 AUTHORS, MIT License.
3
+
4
+ if ENV["CI"]
5
+ require "codecov"
6
+ require "simplecov"
7
+
8
+ SimpleCov.formatters = SimpleCov::Formatter::MultiFormatter.new(
9
+ [
10
+ SimpleCov::Formatter::HTMLFormatter,
11
+ SimpleCov::Formatter::Codecov
12
+ ]
13
+ )
14
+
15
+ SimpleCov.start do
16
+ add_filter %r{^/test/}
17
+ end
18
+ end
data/test/file.test.rb ADDED
@@ -0,0 +1,120 @@
1
+ # Abstract data stream processor.
2
+ # Copyright (c) 2021 AUTHORS, MIT License.
3
+
4
+ require "adsp/file"
5
+
6
+ require_relative "common"
7
+ require_relative "minitest"
8
+ require_relative "mock/file"
9
+ require_relative "option"
10
+ require_relative "validation"
11
+
12
+ module ADSP
13
+ module Test
14
+ class File < Minitest::Test
15
+ Option = Test::Option
16
+ Target = Mock::File
17
+
18
+ SOURCE_PATH = Common::SOURCE_PATH
19
+ ARCHIVE_PATH = Common::ARCHIVE_PATH
20
+ TEXTS = Common::TEXTS
21
+ LARGE_TEXTS = Common::LARGE_TEXTS
22
+
23
+ BUFFER_LENGTH_NAMES = %i[source_buffer_length destination_buffer_length].freeze
24
+ BUFFER_LENGTH_MAPPING = {
25
+ :source_buffer_length => :destination_buffer_length,
26
+ :destination_buffer_length => :source_buffer_length
27
+ }
28
+ .freeze
29
+
30
+ def test_invalid_arguments
31
+ Validation::INVALID_STRINGS.each do |invalid_path|
32
+ assert_raises ValidateError do
33
+ Target.compress invalid_path, ARCHIVE_PATH
34
+ end
35
+
36
+ assert_raises ValidateError do
37
+ Target.compress SOURCE_PATH, invalid_path
38
+ end
39
+
40
+ assert_raises ValidateError do
41
+ Target.decompress invalid_path, SOURCE_PATH
42
+ end
43
+
44
+ assert_raises ValidateError do
45
+ Target.decompress ARCHIVE_PATH, invalid_path
46
+ end
47
+ end
48
+
49
+ get_invalid_compressor_options do |invalid_options|
50
+ assert_raises ValidateError do
51
+ Target.compress SOURCE_PATH, ARCHIVE_PATH, invalid_options
52
+ end
53
+ end
54
+
55
+ get_invalid_decompressor_options do |invalid_options|
56
+ assert_raises ValidateError do
57
+ Target.decompress ARCHIVE_PATH, SOURCE_PATH, invalid_options
58
+ end
59
+ end
60
+ end
61
+
62
+ def test_texts
63
+ parallel_compressor_options do |compressor_options, worker_index|
64
+ source_path = Common.get_path SOURCE_PATH, worker_index
65
+ archive_path = Common.get_path ARCHIVE_PATH, worker_index
66
+
67
+ TEXTS.each do |text|
68
+ ::File.write source_path, text, :mode => "wb"
69
+ Target.compress source_path, archive_path, compressor_options
70
+
71
+ get_compatible_decompressor_options compressor_options do |decompressor_options|
72
+ Target.decompress archive_path, source_path, decompressor_options
73
+
74
+ decompressed_text = ::File.read source_path, :mode => "rb"
75
+ decompressed_text.force_encoding text.encoding
76
+
77
+ assert_equal text, decompressed_text
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ def test_large_texts
84
+ Common.parallel LARGE_TEXTS do |text, worker_index|
85
+ source_path = Common.get_path SOURCE_PATH, worker_index
86
+ archive_path = Common.get_path ARCHIVE_PATH, worker_index
87
+
88
+ ::File.write source_path, text, :mode => "wb"
89
+ Target.compress source_path, archive_path
90
+ Target.decompress archive_path, source_path
91
+
92
+ decompressed_text = ::File.read source_path, :mode => "rb"
93
+ decompressed_text.force_encoding text.encoding
94
+
95
+ assert_equal text, decompressed_text
96
+ end
97
+ end
98
+
99
+ # -----
100
+
101
+ def get_invalid_compressor_options(&block)
102
+ self.class::Option.get_invalid_compressor_options BUFFER_LENGTH_NAMES, &block
103
+ end
104
+
105
+ def get_invalid_decompressor_options(&block)
106
+ self.class::Option.get_invalid_decompressor_options BUFFER_LENGTH_NAMES, &block
107
+ end
108
+
109
+ def parallel_compressor_options(&block)
110
+ Common.parallel_options self.class::Option.get_compressor_options_generator(BUFFER_LENGTH_NAMES), &block
111
+ end
112
+
113
+ def get_compatible_decompressor_options(compressor_options, &block)
114
+ self.class::Option.get_compatible_decompressor_options compressor_options, BUFFER_LENGTH_MAPPING, &block
115
+ end
116
+ end
117
+
118
+ Minitest << File
119
+ end
120
+ end
data/test/minitest.rb ADDED
@@ -0,0 +1,20 @@
1
+ # Abstract data stream processor.
2
+ # Copyright (c) 2021 AUTHORS, MIT License.
3
+
4
+ require "minitest"
5
+ require "minitest/autorun"
6
+
7
+ module Minitest
8
+ Minitest.module_eval do
9
+ class << self
10
+ def <<(klass)
11
+ Runnable.runnables << klass unless Runnable.runnables.include? klass
12
+ nil
13
+ end
14
+ end
15
+ end
16
+
17
+ Runnable.instance_eval do
18
+ def self.inherited(_klass); end # rubocop:disable Lint/MissingSuper
19
+ end
20
+ end
@@ -0,0 +1,57 @@
1
+ # Abstract data stream processor.
2
+ # Copyright (c) 2021 AUTHORS, MIT License.
3
+
4
+ require "adsp/string"
5
+
6
+ require_relative "../common"
7
+
8
+ module ADSP
9
+ module Test
10
+ module Mock
11
+ module Common
12
+ DEFAULT_DESTINATION_BUFFER_LENGTH = 1 << 18 # 256 KB
13
+
14
+ # Flipping bytes and packing them as 16-bit values in network byte order.
15
+ # This method will make compressed data bytesize more than original bytesize.
16
+ # It will help to improve testing coverage.
17
+ def self.native_compress(source, destination_buffer_length)
18
+ source_length = source.bytesize
19
+ destination_buffer_length = DEFAULT_DESTINATION_BUFFER_LENGTH if destination_buffer_length.zero?
20
+
21
+ bytes_read =
22
+ if destination_buffer_length.nil?
23
+ source_length
24
+ else
25
+ [source_length, destination_buffer_length / 2].min
26
+ end
27
+
28
+ result = source
29
+ .byteslice(0, bytes_read)
30
+ .unpack("C*")
31
+ .map { |byte| byte ^ 0xFF }
32
+ .pack "n*"
33
+ [result, bytes_read]
34
+ end
35
+
36
+ def self.native_decompress(source, destination_buffer_length)
37
+ source_length = source.bytesize / 2 * 2
38
+ destination_buffer_length = DEFAULT_DESTINATION_BUFFER_LENGTH if destination_buffer_length.zero?
39
+
40
+ bytes_read =
41
+ if destination_buffer_length.nil?
42
+ source_length
43
+ else
44
+ [source_length, destination_buffer_length * 2].min
45
+ end
46
+
47
+ result = source
48
+ .byteslice(0, bytes_read)
49
+ .unpack("n*")
50
+ .map { |byte| byte ^ 0xFF }
51
+ .pack "C*"
52
+ [result, bytes_read]
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
data/test/mock/file.rb ADDED
@@ -0,0 +1,60 @@
1
+ # Abstract data stream processor.
2
+ # Copyright (c) 2021 AUTHORS, MIT License.
3
+
4
+ require "adsp/file"
5
+
6
+ require_relative "./common"
7
+
8
+ module ADSP
9
+ module Test
10
+ module Mock
11
+ class File < ADSP::File
12
+ PORTION_LENGTH = 10**6
13
+
14
+ def self.native_compress_io(source_io, destination_io, options)
15
+ destination_buffer_length = options[:destination_buffer_length]
16
+ source = "".b
17
+
18
+ loop do
19
+ begin
20
+ source << source_io.readpartial(PORTION_LENGTH)
21
+ rescue ::EOFError
22
+ break
23
+ end
24
+
25
+ until source.empty?
26
+ data, bytes_read = Common.native_compress source, destination_buffer_length
27
+ source = source.byteslice bytes_read, source.bytesize - bytes_read
28
+
29
+ destination_io.write data
30
+ end
31
+ end
32
+
33
+ nil
34
+ end
35
+
36
+ def self.native_decompress_io(source_io, destination_io, options)
37
+ destination_buffer_length = options[:destination_buffer_length]
38
+ source = "".b
39
+
40
+ loop do
41
+ begin
42
+ source << source_io.readpartial(PORTION_LENGTH)
43
+ rescue ::EOFError
44
+ break
45
+ end
46
+
47
+ until source.empty?
48
+ data, bytes_read = Common.native_decompress source, destination_buffer_length
49
+ source = source.byteslice bytes_read, source.bytesize - bytes_read
50
+
51
+ destination_io.write data
52
+ end
53
+ end
54
+
55
+ nil
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,20 @@
1
+ # Abstract data stream processor.
2
+ # Copyright (c) 2021 AUTHORS, MIT License.
3
+
4
+ require "adsp/stream/raw/compressor"
5
+
6
+ require_relative "native_compressor"
7
+
8
+ module ADSP
9
+ module Test
10
+ module Mock
11
+ module Stream
12
+ module Raw
13
+ class Compressor < ADSP::Stream::Raw::Compressor
14
+ NativeCompressor = Raw::NativeCompressor
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # Abstract data stream processor.
2
+ # Copyright (c) 2021 AUTHORS, MIT License.
3
+
4
+ require "adsp/stream/raw/decompressor"
5
+
6
+ require_relative "native_decompressor"
7
+
8
+ module ADSP
9
+ module Test
10
+ module Mock
11
+ module Stream
12
+ module Raw
13
+ class Decompressor < ADSP::Stream::Raw::Decompressor
14
+ NativeDecompressor = Raw::NativeDecompressor
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,82 @@
1
+ # Abstract data stream processor.
2
+ # Copyright (c) 2021 AUTHORS, MIT License.
3
+
4
+ require "adsp/error"
5
+ require "adsp/validation"
6
+
7
+ require_relative "../../common"
8
+
9
+ module ADSP
10
+ module Test
11
+ module Mock
12
+ module Stream
13
+ module Raw
14
+ class NativeCompressor
15
+ def initialize(options)
16
+ ADSP::Validation.validate_hash options
17
+
18
+ destination_buffer_length = options[:destination_buffer_length]
19
+ ADSP::Validation.validate_not_negative_integer destination_buffer_length
20
+
21
+ destination_buffer_length = Common::DEFAULT_DESTINATION_BUFFER_LENGTH \
22
+ if destination_buffer_length.zero?
23
+
24
+ @destination_buffer = "".b
25
+ @destination_buffer_length = destination_buffer_length
26
+
27
+ @is_closed = false
28
+ end
29
+
30
+ def write(source)
31
+ ADSP::Validation.validate_string source
32
+
33
+ do_not_use_after_close
34
+
35
+ remaining_destination_buffer_length = @destination_buffer_length - @destination_buffer.bytesize
36
+ return [0, true] if remaining_destination_buffer_length.zero?
37
+
38
+ data, bytes_read = Common.native_compress source, remaining_destination_buffer_length
39
+ needs_more_destination = bytes_read < source.bytesize
40
+ @destination_buffer << data
41
+
42
+ [bytes_read, needs_more_destination]
43
+ end
44
+
45
+ def read_result
46
+ do_not_use_after_close
47
+
48
+ result = @destination_buffer
49
+ @destination_buffer = "".b
50
+
51
+ result
52
+ end
53
+
54
+ def flush
55
+ do_not_use_after_close
56
+
57
+ nil
58
+ end
59
+
60
+ def finish
61
+ do_not_use_after_close
62
+
63
+ nil
64
+ end
65
+
66
+ def close
67
+ do_not_use_after_close
68
+
69
+ @is_closed = true
70
+
71
+ nil
72
+ end
73
+
74
+ protected def do_not_use_after_close
75
+ raise UsedAfterCloseError if @is_closed
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,70 @@
1
+ # Abstract data stream processor.
2
+ # Copyright (c) 2021 AUTHORS, MIT License.
3
+
4
+ require "adsp/error"
5
+ require "adsp/validation"
6
+
7
+ require_relative "../../common"
8
+
9
+ module ADSP
10
+ module Test
11
+ module Mock
12
+ module Stream
13
+ module Raw
14
+ class NativeDecompressor
15
+ def initialize(options)
16
+ ADSP::Validation.validate_hash options
17
+
18
+ destination_buffer_length = options[:destination_buffer_length]
19
+ ADSP::Validation.validate_not_negative_integer destination_buffer_length
20
+
21
+ destination_buffer_length = Common::DEFAULT_DESTINATION_BUFFER_LENGTH \
22
+ if destination_buffer_length.zero?
23
+
24
+ @destination_buffer = "".b
25
+ @destination_buffer_length = destination_buffer_length
26
+
27
+ @is_closed = false
28
+ end
29
+
30
+ def read(source)
31
+ ADSP::Validation.validate_string source
32
+
33
+ do_not_use_after_close
34
+
35
+ remaining_destination_buffer_length = @destination_buffer_length - @destination_buffer.bytesize
36
+ return [0, true] if remaining_destination_buffer_length.zero?
37
+
38
+ data, bytes_read = Common.native_decompress source, remaining_destination_buffer_length
39
+ needs_more_destination = bytes_read < source.bytesize
40
+ @destination_buffer << data
41
+
42
+ [bytes_read, needs_more_destination]
43
+ end
44
+
45
+ def read_result
46
+ do_not_use_after_close
47
+
48
+ result = @destination_buffer
49
+ @destination_buffer = "".b
50
+
51
+ result
52
+ end
53
+
54
+ def close
55
+ do_not_use_after_close
56
+
57
+ @is_closed = true
58
+
59
+ nil
60
+ end
61
+
62
+ protected def do_not_use_after_close
63
+ raise UsedAfterCloseError if @is_closed
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,18 @@
1
+ # Abstract data stream processor.
2
+ # Copyright (c) 2021 AUTHORS, MIT License.
3
+
4
+ require "adsp/stream/reader"
5
+
6
+ require_relative "raw/decompressor"
7
+
8
+ module ADSP
9
+ module Test
10
+ module Mock
11
+ module Stream
12
+ class Reader < ADSP::Stream::Reader
13
+ RawDecompressor = Mock::Stream::Raw::Decompressor
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # Abstract data stream processor.
2
+ # Copyright (c) 2021 AUTHORS, MIT License.
3
+
4
+ require "adsp/stream/writer"
5
+
6
+ require_relative "raw/compressor"
7
+
8
+ module ADSP
9
+ module Test
10
+ module Mock
11
+ module Stream
12
+ class Writer < ADSP::Stream::Writer
13
+ RawCompressor = Mock::Stream::Raw::Compressor
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,44 @@
1
+ # Abstract data stream processor.
2
+ # Copyright (c) 2021 AUTHORS, MIT License.
3
+
4
+ require "adsp/string"
5
+
6
+ require_relative "./common"
7
+
8
+ module ADSP
9
+ module Test
10
+ module Mock
11
+ class String < ADSP::String
12
+ def self.native_compress_string(source, options)
13
+ destination_buffer_length = options[:destination_buffer_length]
14
+ result = "".b
15
+
16
+ loop do
17
+ data, bytes_read = Common.native_compress source, destination_buffer_length
18
+ source = source.byteslice bytes_read, source.bytesize - bytes_read
19
+ result << data
20
+
21
+ break if source.bytesize.zero?
22
+ end
23
+
24
+ result
25
+ end
26
+
27
+ def self.native_decompress_string(source, options)
28
+ destination_buffer_length = options[:destination_buffer_length]
29
+ result = "".b
30
+
31
+ loop do
32
+ data, bytes_read = Common.native_decompress source, destination_buffer_length
33
+ source = source.byteslice bytes_read, source.bytesize - bytes_read
34
+ result << data
35
+
36
+ break if source.bytesize.zero?
37
+ end
38
+
39
+ result
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end