ruby-brs 1.1.2 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
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)