ruby-brs 1.1.2 → 1.2.1

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.
data/ext/extconf.rb CHANGED
@@ -3,6 +3,8 @@
3
3
 
4
4
  require "mkmf"
5
5
 
6
+ have_func "rb_thread_call_without_gvl", "ruby/thread.h"
7
+
6
8
  def require_header(name, types = [])
7
9
  abort "Can't find #{name} header" unless find_header name
8
10
 
@@ -61,7 +63,14 @@ $srcs = %w[
61
63
  .map { |name| "src/#{extension_name}/#{name}.c" }
62
64
  .freeze
63
65
 
64
- if ENV["CI"] || ENV["COVERAGE"]
66
+ # Removing library duplicates.
67
+ $libs = $libs.split(%r{\s})
68
+ .reject(&:empty?)
69
+ .sort
70
+ .uniq
71
+ .join " "
72
+
73
+ if ENV["CI"]
65
74
  $CFLAGS << " --coverage"
66
75
  $LDFLAGS << " --coverage"
67
76
  end
data/lib/brs/file.rb CHANGED
@@ -19,9 +19,11 @@ module BRS
19
19
 
20
20
  options[:size_hint] = ::File.size source
21
21
 
22
- open_files(source, destination) do |source_io, destination_io|
22
+ open_files source, destination do |source_io, destination_io|
23
23
  BRS._native_compress_io source_io, destination_io, options
24
24
  end
25
+
26
+ nil
25
27
  end
26
28
 
27
29
  def self.decompress(source, destination, options = {})
@@ -30,9 +32,11 @@ module BRS
30
32
 
31
33
  options = Option.get_decompressor_options options, BUFFER_LENGTH_NAMES
32
34
 
33
- open_files(source, destination) do |source_io, destination_io|
35
+ open_files source, destination do |source_io, destination_io|
34
36
  BRS._native_decompress_io source_io, destination_io, options
35
37
  end
38
+
39
+ nil
36
40
  end
37
41
 
38
42
  private_class_method def self.open_files(source, destination, &_block)
data/lib/brs/option.rb CHANGED
@@ -11,6 +11,7 @@ module BRS
11
11
  DEFAULT_BUFFER_LENGTH = 0
12
12
 
13
13
  COMPRESSOR_DEFAULTS = {
14
+ :gvl => false,
14
15
  :mode => nil,
15
16
  :quality => nil,
16
17
  :lgwin => nil,
@@ -21,6 +22,7 @@ module BRS
21
22
  .freeze
22
23
 
23
24
  DECOMPRESSOR_DEFAULTS = {
25
+ :gvl => false,
24
26
  :disable_ring_buffer_reallocation => nil,
25
27
  :large_window => nil
26
28
  }
@@ -29,11 +31,16 @@ module BRS
29
31
  def self.get_compressor_options(options, buffer_length_names)
30
32
  Validation.validate_hash options
31
33
 
32
- buffer_length_defaults = buffer_length_names.each_with_object({}) { |name, defaults| defaults[name] = DEFAULT_BUFFER_LENGTH }
33
- options = COMPRESSOR_DEFAULTS.merge(buffer_length_defaults).merge options
34
+ buffer_length_defaults = buffer_length_names.each_with_object({}) do |name, defaults|
35
+ defaults[name] = DEFAULT_BUFFER_LENGTH
36
+ end
37
+
38
+ options = COMPRESSOR_DEFAULTS.merge(buffer_length_defaults).merge options
34
39
 
35
40
  buffer_length_names.each { |name| Validation.validate_not_negative_integer options[name] }
36
41
 
42
+ Validation.validate_bool options[:gvl]
43
+
37
44
  mode = options[:mode]
38
45
  unless mode.nil?
39
46
  Validation.validate_symbol mode
@@ -70,11 +77,16 @@ module BRS
70
77
  def self.get_decompressor_options(options, buffer_length_names)
71
78
  Validation.validate_hash options
72
79
 
73
- buffer_length_defaults = buffer_length_names.each_with_object({}) { |name, defaults| defaults[name] = DEFAULT_BUFFER_LENGTH }
74
- options = DECOMPRESSOR_DEFAULTS.merge(buffer_length_defaults).merge options
80
+ buffer_length_defaults = buffer_length_names.each_with_object({}) do |name, defaults|
81
+ defaults[name] = DEFAULT_BUFFER_LENGTH
82
+ end
83
+
84
+ options = DECOMPRESSOR_DEFAULTS.merge(buffer_length_defaults).merge options
75
85
 
76
86
  buffer_length_names.each { |name| Validation.validate_not_negative_integer options[name] }
77
87
 
88
+ Validation.validate_bool options[:gvl]
89
+
78
90
  disable_ring_buffer_reallocation = options[:disable_ring_buffer_reallocation]
79
91
  Validation.validate_bool disable_ring_buffer_reallocation unless disable_ring_buffer_reallocation.nil?
80
92
 
@@ -12,17 +12,13 @@ module BRS
12
12
  # Native stream is not seekable by design.
13
13
  # Related methods like "seek" and "pos=" can't be implemented.
14
14
 
15
- # It is not possible to maintain correspondance between bytes consumed from source and bytes written to destination by design.
15
+ # It is not possible to maintain correspondance between bytes
16
+ # consumed from source and bytes written to destination by design.
16
17
  # We will consume all source bytes and maintain buffer with remaining destination data.
17
18
 
18
19
  include Delegates
19
20
 
20
- attr_reader :io
21
- attr_reader :stat
22
- attr_reader :external_encoding
23
- attr_reader :internal_encoding
24
- attr_reader :transcode_options
25
- attr_reader :pos
21
+ attr_reader :io, :stat, :external_encoding, :internal_encoding, :transcode_options, :pos
26
22
 
27
23
  alias tell pos
28
24
 
@@ -94,8 +90,9 @@ module BRS
94
90
  end
95
91
 
96
92
  internal_encoding = args[1]
97
- Validation.validate_string internal_encoding \
98
- unless internal_encoding.nil? || internal_encoding.is_a?(::Encoding)
93
+ unless internal_encoding.nil? || internal_encoding.is_a?(::Encoding)
94
+ Validation.validate_string internal_encoding
95
+ end
99
96
 
100
97
  transcode_options = args[2]
101
98
  Validation.validate_hash transcode_options unless transcode_options.nil?
@@ -19,11 +19,13 @@ module BRS
19
19
 
20
20
  def flush(&writer)
21
21
  write_result(&writer)
22
+
23
+ nil
22
24
  end
23
25
 
24
- protected def flush_destination_buffer(&writer)
26
+ protected def more_destination(&writer)
25
27
  result_bytesize = write_result(&writer)
26
- raise NotEnoughDestinationError, "not enough destination" if result_bytesize == 0
28
+ raise NotEnoughDestinationError, "not enough destination" if result_bytesize.zero?
27
29
  end
28
30
 
29
31
  protected def write_result(&_writer)
@@ -44,6 +46,8 @@ module BRS
44
46
 
45
47
  @native_stream.close
46
48
  @is_closed = true
49
+
50
+ nil
47
51
  end
48
52
 
49
53
  def closed?
@@ -39,7 +39,7 @@ module BRS
39
39
 
40
40
  if need_more_destination
41
41
  source = source.byteslice bytes_written, source.bytesize - bytes_written
42
- flush_destination_buffer(&writer)
42
+ more_destination(&writer)
43
43
  next
44
44
  end
45
45
 
@@ -65,7 +65,7 @@ module BRS
65
65
  need_more_destination = @native_stream.flush
66
66
 
67
67
  if need_more_destination
68
- flush_destination_buffer(&writer)
68
+ more_destination(&writer)
69
69
  next
70
70
  end
71
71
 
@@ -73,8 +73,6 @@ module BRS
73
73
  end
74
74
 
75
75
  super
76
-
77
- nil
78
76
  end
79
77
 
80
78
  def close(&writer)
@@ -86,7 +84,7 @@ module BRS
86
84
  need_more_destination = @native_stream.finish
87
85
 
88
86
  if need_more_destination
89
- flush_destination_buffer(&writer)
87
+ more_destination(&writer)
90
88
  next
91
89
  end
92
90
 
@@ -94,8 +92,6 @@ module BRS
94
92
  end
95
93
 
96
94
  super
97
-
98
- nil
99
95
  end
100
96
  end
101
97
  end
@@ -34,7 +34,7 @@ module BRS
34
34
 
35
35
  if need_more_destination
36
36
  source = source.byteslice bytes_read, source.bytesize - bytes_read
37
- flush_destination_buffer(&writer)
37
+ more_destination(&writer)
38
38
  next
39
39
  end
40
40
 
@@ -51,8 +51,6 @@ module BRS
51
51
  Validation.validate_proc writer
52
52
 
53
53
  super
54
-
55
- nil
56
54
  end
57
55
 
58
56
  def close(&writer)
@@ -61,8 +59,6 @@ module BRS
61
59
  Validation.validate_proc writer
62
60
 
63
61
  super
64
-
65
- nil
66
62
  end
67
63
  end
68
64
  end
@@ -20,79 +20,61 @@ module BRS
20
20
 
21
21
  initialize_source_buffer_length
22
22
  reset_io_remainder
23
+ reset_need_to_flush
23
24
 
24
25
  @lineno = 0
25
26
  end
26
27
 
28
+ protected def create_raw_stream
29
+ Raw::Decompressor.new @options
30
+ end
31
+
27
32
  protected def initialize_source_buffer_length
28
33
  source_buffer_length = @options[:source_buffer_length]
29
34
  Validation.validate_not_negative_integer source_buffer_length unless source_buffer_length.nil?
30
35
 
31
- source_buffer_length = Buffer::DEFAULT_SOURCE_BUFFER_LENGTH_FOR_DECOMPRESSOR \
32
- if source_buffer_length == 0 || source_buffer_length.nil?
36
+ if source_buffer_length.nil? || source_buffer_length.zero?
37
+ source_buffer_length = Buffer::DEFAULT_SOURCE_BUFFER_LENGTH_FOR_DECOMPRESSOR
38
+ end
33
39
 
34
40
  @source_buffer_length = source_buffer_length
35
41
  end
36
42
 
37
- protected def create_raw_stream
38
- Raw::Decompressor.new @options
39
- end
40
-
41
43
  protected def reset_io_remainder
42
44
  @io_remainder = ::String.new :encoding => ::Encoding::BINARY
43
45
  end
44
46
 
47
+ protected def reset_need_to_flush
48
+ @need_to_flush = false
49
+ end
50
+
45
51
  # -- synchronous --
46
52
 
47
53
  def read(bytes_to_read = nil, out_buffer = nil)
48
54
  Validation.validate_not_negative_integer bytes_to_read unless bytes_to_read.nil?
49
55
  Validation.validate_string out_buffer unless out_buffer.nil?
50
56
 
51
- return ::String.new :encoding => ::Encoding::BINARY if bytes_to_read == 0
52
-
53
57
  unless bytes_to_read.nil?
58
+ return ::String.new :encoding => ::Encoding::BINARY if bytes_to_read.zero?
54
59
  return nil if eof?
55
60
 
56
- read_more_to_buffer until @buffer.bytesize >= bytes_to_read || @io.eof?
61
+ append_io_data @io.read(@source_buffer_length) while @buffer.bytesize < bytes_to_read && !@io.eof?
62
+ flush_io_data if @buffer.bytesize < bytes_to_read
57
63
 
58
64
  return read_bytes_from_buffer bytes_to_read, out_buffer
59
65
  end
60
66
 
61
- read_more_to_buffer until @io.eof?
62
-
63
- result = @buffer
64
- reset_buffer
65
- @pos += result.bytesize
66
-
67
- result.force_encoding @external_encoding unless @external_encoding.nil?
68
- result = transcode_to_internal result
69
- result = out_buffer.replace result unless out_buffer.nil?
70
-
71
- result
72
- end
73
-
74
- protected def read_more_to_buffer
75
- io_data = @io.read @source_buffer_length
76
- append_io_data_to_buffer io_data
77
- end
78
-
79
- def readpartial(bytes_to_read = nil, out_buffer = nil)
80
- raise ::EOFError if eof?
81
-
82
- readpartial_to_buffer until @buffer.bytesize >= bytes_to_read || @io.eof?
83
-
84
- read_bytes_from_buffer bytes_to_read, out_buffer
85
- end
67
+ append_io_data @io.read(@source_buffer_length) until @io.eof?
68
+ flush_io_data
86
69
 
87
- protected def readpartial_to_buffer
88
- io_data = @io.readpartial @source_buffer_length
89
- append_io_data_to_buffer io_data
70
+ read_buffer out_buffer
90
71
  end
91
72
 
92
73
  def rewind
93
74
  raw_wrapper :close
94
75
 
95
76
  reset_io_remainder
77
+ reset_need_to_flush
96
78
 
97
79
  super
98
80
  end
@@ -103,25 +85,61 @@ module BRS
103
85
  super
104
86
  end
105
87
 
88
+ def eof?
89
+ empty? && @io.eof?
90
+ end
91
+
106
92
  # -- asynchronous --
107
93
 
94
+ def readpartial(bytes_to_read, out_buffer = nil)
95
+ read_more_nonblock(bytes_to_read, out_buffer) { @io.readpartial @source_buffer_length }
96
+ end
97
+
108
98
  def read_nonblock(bytes_to_read, out_buffer = nil, *options)
109
- raise ::EOFError if eof?
99
+ read_more_nonblock(bytes_to_read, out_buffer) { @io.read_nonblock(@source_buffer_length, *options) }
100
+ end
101
+
102
+ protected def read_more_nonblock(bytes_to_read, out_buffer, &_block)
103
+ Validation.validate_not_negative_integer bytes_to_read
104
+ Validation.validate_string out_buffer unless out_buffer.nil?
105
+
106
+ return ::String.new :encoding => ::Encoding::BINARY if bytes_to_read.zero?
107
+
108
+ io_provided_eof_error = false
110
109
 
111
- read_more_to_buffer_nonblock(*options) until @buffer.bytesize >= bytes_to_read || @io.eof?
110
+ if @buffer.bytesize < bytes_to_read
111
+ begin
112
+ append_io_data yield
113
+ rescue ::EOFError
114
+ io_provided_eof_error = true
115
+ end
116
+ end
117
+
118
+ flush_io_data if @buffer.bytesize < bytes_to_read
119
+ raise ::EOFError if empty? && io_provided_eof_error
112
120
 
113
121
  read_bytes_from_buffer bytes_to_read, out_buffer
114
122
  end
115
123
 
116
- protected def read_more_to_buffer_nonblock(*options)
117
- io_data = @io.read_nonblock @source_buffer_length, *options
118
- append_io_data_to_buffer io_data
124
+ # -- common --
125
+
126
+ protected def append_io_data(io_data)
127
+ io_portion = @io_remainder + io_data
128
+ bytes_read = raw_wrapper :read, io_portion
129
+ @io_remainder = io_portion.byteslice bytes_read, io_portion.bytesize - bytes_read
130
+
131
+ # Even empty io data may require flush.
132
+ @need_to_flush = true
119
133
  end
120
134
 
121
- # -- common --
135
+ protected def flush_io_data
136
+ raw_wrapper :flush
122
137
 
123
- def eof?
124
- @io.eof? && @buffer.bytesize == 0
138
+ @need_to_flush = false
139
+ end
140
+
141
+ protected def empty?
142
+ !@need_to_flush && @buffer.bytesize.zero?
125
143
  end
126
144
 
127
145
  protected def read_bytes_from_buffer(bytes_to_read, out_buffer)
@@ -136,14 +154,16 @@ module BRS
136
154
  result
137
155
  end
138
156
 
139
- protected def append_io_data_to_buffer(io_data)
140
- io_portion = @io_remainder + io_data
141
- bytes_read = raw_wrapper :read, io_portion
142
- @io_remainder = io_portion.byteslice bytes_read, io_portion.bytesize - bytes_read
157
+ protected def read_buffer(out_buffer)
158
+ result = @buffer
159
+ reset_buffer
160
+ @pos += result.bytesize
143
161
 
144
- # We should just ignore case when "io.eof?" appears but "io_remainder" is not empty.
145
- # Ancient compress implementations can write bytes from not initialized buffer parts to output.
146
- raw_wrapper :flush if @io.eof?
162
+ result.force_encoding @external_encoding unless @external_encoding.nil?
163
+ result = transcode_to_internal result
164
+
165
+ result = out_buffer.replace result unless out_buffer.nil?
166
+ result
147
167
  end
148
168
 
149
169
  protected def transcode_to_internal(data)
@@ -143,7 +143,7 @@ module BRS
143
143
  end
144
144
 
145
145
  protected def each_string(each_proc, &block)
146
- return enum_for __method__ unless block.is_a? ::Proc
146
+ return enum_for __method__, each_proc unless block.is_a? ::Proc
147
147
 
148
148
  loop do
149
149
  string = each_proc.call
@@ -64,7 +64,7 @@ module BRS
64
64
  end
65
65
 
66
66
  protected def write_remaining_buffer
67
- return nil if @buffer.bytesize == 0
67
+ return nil if @buffer.bytesize.zero?
68
68
 
69
69
  @io.write @buffer
70
70
 
@@ -77,6 +77,11 @@ module BRS
77
77
 
78
78
  # -- asynchronous --
79
79
 
80
+ # IO write nonblock can raise wait writable error.
81
+ # After resolving this error user may provide same content again.
82
+ # It is not possible to revert accepted content after error.
83
+ # So we have to accept content after processing IO write nonblock.
84
+ # It means that first write nonblock won't call IO write nonblock.
80
85
  def write_nonblock(object, *options)
81
86
  return 0 unless write_remaining_buffer_nonblock(*options)
82
87
 
@@ -120,14 +125,14 @@ module BRS
120
125
  end
121
126
 
122
127
  protected def write_remaining_buffer_nonblock(*options)
123
- return true if @buffer.bytesize == 0
128
+ return true if @buffer.bytesize.zero?
124
129
 
125
130
  bytes_written = @io.write_nonblock @buffer, *options
126
- return false if bytes_written == 0
131
+ return false if bytes_written.zero?
127
132
 
128
133
  @buffer = @buffer.byteslice bytes_written, @buffer.bytesize - bytes_written
129
134
 
130
- @buffer.bytesize == 0
135
+ @buffer.bytesize.zero?
131
136
  end
132
137
 
133
138
  protected def raw_nonblock_wrapper(method_name, *args)