ruby-brs 1.0.1 → 1.1.4

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.
@@ -3,46 +3,52 @@
3
3
 
4
4
  require "mkmf"
5
5
 
6
- def require_header(name)
6
+ def require_header(name, types = [])
7
7
  abort "Can't find #{name} header" unless find_header name
8
+
9
+ types.each do |type|
10
+ abort "Can't find #{type} type in #{name} header" unless find_type type, nil, name
11
+ end
8
12
  end
9
13
 
10
- require_header "brotli/encode.h"
11
- require_header "brotli/decode.h"
14
+ require_header "brotli/types.h", %w[BROTLI_BOOL]
15
+ require_header "brotli/encode.h", ["BrotliEncoderState *", "BrotliEncoderMode"]
16
+ require_header "brotli/decode.h", ["BrotliDecoderState *", "BrotliDecoderResult", "BrotliDecoderErrorCode"]
12
17
 
13
18
  def require_library(name, functions)
14
19
  functions.each do |function|
15
- abort "Can't find #{name} library and #{function} function" unless find_library name, function
20
+ abort "Can't find #{function} function in #{name} library" unless find_library name, function
16
21
  end
17
22
  end
18
23
 
19
- encoder_functions = %w[
20
- BrotliEncoderCreateInstance
21
- BrotliEncoderSetParameter
22
- BrotliEncoderCompressStream
23
- BrotliEncoderHasMoreOutput
24
- BrotliEncoderIsFinished
25
- BrotliEncoderDestroyInstance
26
- ]
27
- .freeze
24
+ require_library(
25
+ "brotlienc",
26
+ %w[
27
+ BrotliEncoderCreateInstance
28
+ BrotliEncoderSetParameter
29
+ BrotliEncoderCompressStream
30
+ BrotliEncoderHasMoreOutput
31
+ BrotliEncoderIsFinished
32
+ BrotliEncoderDestroyInstance
33
+ ]
34
+ )
28
35
 
29
- require_library "brotlienc", encoder_functions
30
-
31
- decoder_functions = %w[
32
- BrotliDecoderCreateInstance
33
- BrotliDecoderSetParameter
34
- BrotliDecoderDecompressStream
35
- BrotliDecoderGetErrorCode
36
- BrotliDecoderDestroyInstance
37
- ]
38
- .freeze
39
-
40
- require_library "brotlidec", decoder_functions
36
+ require_library(
37
+ "brotlidec",
38
+ %w[
39
+ BrotliDecoderCreateInstance
40
+ BrotliDecoderSetParameter
41
+ BrotliDecoderDecompressStream
42
+ BrotliDecoderGetErrorCode
43
+ BrotliDecoderDestroyInstance
44
+ ]
45
+ )
41
46
 
42
47
  extension_name = "brs_ext".freeze
43
48
  dir_config extension_name
44
49
 
45
- sources = %w[
50
+ # rubocop:disable Style/GlobalVars
51
+ $srcs = %w[
46
52
  stream/compressor
47
53
  stream/decompressor
48
54
  buffer
@@ -52,14 +58,23 @@ sources = %w[
52
58
  option
53
59
  string
54
60
  ]
61
+ .map { |name| "src/#{extension_name}/#{name}.c" }
55
62
  .freeze
56
63
 
57
- # rubocop:disable Style/GlobalVars
58
- $srcs = sources
59
- .map { |name| "src/#{extension_name}/#{name}.c" }
60
- .freeze
64
+ # Removing library duplicates.
65
+ $libs = $libs.split(%r{\s})
66
+ .reject(&:empty?)
67
+ .sort
68
+ .uniq
69
+ .join " "
70
+
71
+ if ENV["CI"] || ENV["COVERAGE"]
72
+ $CFLAGS << " --coverage"
73
+ $LDFLAGS << " --coverage"
74
+ end
61
75
 
62
76
  $CFLAGS << " -Wno-declaration-after-statement"
77
+
63
78
  $VPATH << "$(srcdir)/#{extension_name}:$(srcdir)/#{extension_name}/stream"
64
79
  # rubocop:enable Style/GlobalVars
65
80
 
data/lib/brs.rb CHANGED
@@ -5,3 +5,4 @@ require_relative "brs/stream/reader"
5
5
  require_relative "brs/stream/writer"
6
6
  require_relative "brs/file"
7
7
  require_relative "brs/string"
8
+ require_relative "brs/version"
@@ -17,9 +17,13 @@ module BRS
17
17
 
18
18
  options = Option.get_compressor_options options, BUFFER_LENGTH_NAMES
19
19
 
20
+ options[:size_hint] = ::File.size source
21
+
20
22
  open_files(source, destination) do |source_io, destination_io|
21
23
  BRS._native_compress_io source_io, destination_io, options
22
24
  end
25
+
26
+ nil
23
27
  end
24
28
 
25
29
  def self.decompress(source, destination, options = {})
@@ -31,6 +35,8 @@ module BRS
31
35
  open_files(source, destination) do |source_io, destination_io|
32
36
  BRS._native_decompress_io source_io, destination_io, options
33
37
  end
38
+
39
+ nil
34
40
  end
35
41
 
36
42
  private_class_method def self.open_files(source, destination, &_block)
@@ -1,6 +1,8 @@
1
1
  # Ruby bindings for brotli library.
2
2
  # Copyright (c) 2019 AUTHORS, MIT License.
3
3
 
4
+ require "brs_ext"
5
+
4
6
  require_relative "error"
5
7
  require_relative "validation"
6
8
 
@@ -14,7 +16,6 @@ module BRS
14
16
  :lgwin => nil,
15
17
  :lgblock => nil,
16
18
  :disable_literal_context_modeling => nil,
17
- :size_hint => nil,
18
19
  :large_window => nil
19
20
  }
20
21
  .freeze
@@ -40,20 +41,26 @@ module BRS
40
41
  end
41
42
 
42
43
  quality = options[:quality]
43
- Validation.validate_not_negative_integer quality unless quality.nil?
44
+ unless quality.nil?
45
+ Validation.validate_not_negative_integer quality
46
+ raise ValidateError, "invalid quality" if quality < MIN_QUALITY || quality > MAX_QUALITY
47
+ end
44
48
 
45
49
  lgwin = options[:lgwin]
46
- Validation.validate_not_negative_integer lgwin unless lgwin.nil?
50
+ unless lgwin.nil?
51
+ Validation.validate_not_negative_integer lgwin
52
+ raise ValidateError, "invalid lgwin" if lgwin < MIN_LGWIN || lgwin > MAX_LGWIN
53
+ end
47
54
 
48
55
  lgblock = options[:lgblock]
49
- Validation.validate_not_negative_integer lgblock unless lgblock.nil?
56
+ unless lgblock.nil?
57
+ Validation.validate_not_negative_integer lgblock
58
+ raise ValidateError, "invalid lgblock" if lgblock < MIN_LGBLOCK || lgblock > MAX_LGBLOCK
59
+ end
50
60
 
51
61
  disable_literal_context_modeling = options[:disable_literal_context_modeling]
52
62
  Validation.validate_bool disable_literal_context_modeling unless disable_literal_context_modeling.nil?
53
63
 
54
- size_hint = options[:size_hint]
55
- Validation.validate_not_negative_integer size_hint unless size_hint.nil?
56
-
57
64
  large_window = options[:large_window]
58
65
  Validation.validate_bool large_window unless large_window.nil?
59
66
 
@@ -17,23 +17,19 @@ module BRS
17
17
 
18
18
  include Delegates
19
19
 
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
20
+ attr_reader :io, :stat, :external_encoding, :internal_encoding, :transcode_options, :pos
21
+
26
22
  alias tell pos
27
23
 
28
- def initialize(io, external_encoding: nil, internal_encoding: nil, transcode_options: {})
24
+ def initialize(io, options = {})
29
25
  @raw_stream = create_raw_stream
30
26
 
31
27
  Validation.validate_io io
32
28
  @io = io
33
29
 
34
- @stat = Stat.new @io.stat
30
+ @stat = Stat.new @io.stat if @io.respond_to? :stat
35
31
 
36
- set_encoding external_encoding, internal_encoding, transcode_options
32
+ set_encoding options[:external_encoding], options[:internal_encoding], options[:transcode_options]
37
33
  reset_buffer
38
34
  reset_io_advise
39
35
 
@@ -50,8 +46,8 @@ module BRS
50
46
 
51
47
  protected def reset_io_advise
52
48
  # Both compressor and decompressor need sequential io access.
53
- @io.advise :sequential
54
- rescue ::Errno::ESPIPE # rubocop:disable Lint/HandleExceptions
49
+ @io.advise :sequential if @io.respond_to? :advise
50
+ rescue ::Errno::ESPIPE
55
51
  # ok
56
52
  end
57
53
 
@@ -126,7 +122,8 @@ module BRS
126
122
  def rewind
127
123
  @raw_stream = create_raw_stream
128
124
 
129
- @io.rewind
125
+ @io.rewind if @io.respond_to? :rewind
126
+
130
127
  reset_buffer
131
128
  reset_io_advise
132
129
 
@@ -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?
@@ -15,7 +15,11 @@ module BRS
15
15
  BUFFER_LENGTH_NAMES = %i[destination_buffer_length].freeze
16
16
 
17
17
  def initialize(options = {})
18
- options = Option.get_compressor_options options, BUFFER_LENGTH_NAMES
18
+ options = Option.get_compressor_options options, BUFFER_LENGTH_NAMES
19
+
20
+ size_hint = options[:size_hint]
21
+ Validation.validate_not_negative_integer size_hint unless size_hint.nil?
22
+
19
23
  native_stream = NativeCompressor.new options
20
24
 
21
25
  super native_stream
@@ -35,13 +39,15 @@ module BRS
35
39
 
36
40
  if need_more_destination
37
41
  source = source.byteslice bytes_written, source.bytesize - bytes_written
38
- flush_destination_buffer(&writer)
42
+ more_destination(&writer)
39
43
  next
40
44
  end
41
45
 
42
46
  unless bytes_written == source.bytesize
47
+ # :nocov:
43
48
  # Compressor write should eat all provided "source" without remainder.
44
49
  raise UnexpectedError, "unexpected error"
50
+ # :nocov:
45
51
  end
46
52
 
47
53
  break
@@ -59,7 +65,7 @@ module BRS
59
65
  need_more_destination = @native_stream.flush
60
66
 
61
67
  if need_more_destination
62
- flush_destination_buffer(&writer)
68
+ more_destination(&writer)
63
69
  next
64
70
  end
65
71
 
@@ -67,8 +73,6 @@ module BRS
67
73
  end
68
74
 
69
75
  super
70
-
71
- nil
72
76
  end
73
77
 
74
78
  def close(&writer)
@@ -80,7 +84,7 @@ module BRS
80
84
  need_more_destination = @native_stream.finish
81
85
 
82
86
  if need_more_destination
83
- flush_destination_buffer(&writer)
87
+ more_destination(&writer)
84
88
  next
85
89
  end
86
90
 
@@ -88,8 +92,6 @@ module BRS
88
92
  end
89
93
 
90
94
  super
91
-
92
- nil
93
95
  end
94
96
  end
95
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,60 @@ 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
36
  source_buffer_length = Buffer::DEFAULT_SOURCE_BUFFER_LENGTH_FOR_DECOMPRESSOR \
32
- if source_buffer_length == 0 || source_buffer_length.nil?
37
+ if source_buffer_length.nil? || source_buffer_length.zero?
33
38
 
34
39
  @source_buffer_length = source_buffer_length
35
40
  end
36
41
 
37
- protected def create_raw_stream
38
- Raw::Decompressor.new @options
39
- end
40
-
41
42
  protected def reset_io_remainder
42
43
  @io_remainder = ::String.new :encoding => ::Encoding::BINARY
43
44
  end
44
45
 
46
+ protected def reset_need_to_flush
47
+ @need_to_flush = false
48
+ end
49
+
45
50
  # -- synchronous --
46
51
 
47
52
  def read(bytes_to_read = nil, out_buffer = nil)
48
53
  Validation.validate_not_negative_integer bytes_to_read unless bytes_to_read.nil?
49
54
  Validation.validate_string out_buffer unless out_buffer.nil?
50
55
 
51
- return ::String.new :encoding => ::Encoding::BINARY if bytes_to_read == 0
52
-
53
56
  unless bytes_to_read.nil?
57
+ return ::String.new :encoding => ::Encoding::BINARY if bytes_to_read.zero?
54
58
  return nil if eof?
55
59
 
56
- read_more_from_buffer until @buffer.bytesize >= bytes_to_read || @io.eof?
60
+ append_io_data @io.read(@source_buffer_length) while @buffer.bytesize < bytes_to_read && !@io.eof?
61
+ flush_io_data if @buffer.bytesize < bytes_to_read
57
62
 
58
63
  return read_bytes_from_buffer bytes_to_read, out_buffer
59
64
  end
60
65
 
61
- read_more_from_buffer until @io.eof?
62
-
63
- result = @buffer
64
- reset_buffer
65
- @pos += result.bytesize
66
+ append_io_data @io.read(@source_buffer_length) until @io.eof?
67
+ flush_io_data
66
68
 
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_from_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_from_buffer until @buffer.bytesize >= bytes_to_read || @io.eof?
83
-
84
- read_bytes_from_buffer bytes_to_read, out_buffer
85
- end
86
-
87
- protected def readpartial_from_buffer
88
- io_data = @io.readpartial @source_buffer_length
89
- append_io_data_to_buffer io_data
69
+ read_buffer out_buffer
90
70
  end
91
71
 
92
72
  def rewind
93
73
  raw_wrapper :close
94
74
 
95
75
  reset_io_remainder
76
+ reset_need_to_flush
96
77
 
97
78
  super
98
79
  end
@@ -103,25 +84,61 @@ module BRS
103
84
  super
104
85
  end
105
86
 
87
+ def eof?
88
+ empty? && @io.eof?
89
+ end
90
+
106
91
  # -- asynchronous --
107
92
 
93
+ def readpartial(bytes_to_read, out_buffer = nil)
94
+ read_more_nonblock(bytes_to_read, out_buffer) { @io.readpartial @source_buffer_length }
95
+ end
96
+
108
97
  def read_nonblock(bytes_to_read, out_buffer = nil, *options)
109
- raise ::EOFError if eof?
98
+ read_more_nonblock(bytes_to_read, out_buffer) { @io.read_nonblock(@source_buffer_length, *options) }
99
+ end
100
+
101
+ protected def read_more_nonblock(bytes_to_read, out_buffer, &_block)
102
+ Validation.validate_not_negative_integer bytes_to_read
103
+ Validation.validate_string out_buffer unless out_buffer.nil?
104
+
105
+ return ::String.new :encoding => ::Encoding::BINARY if bytes_to_read.zero?
106
+
107
+ io_provided_eof_error = false
110
108
 
111
- read_more_from_buffer_nonblock(*options) until @buffer.bytesize >= bytes_to_read || @io.eof?
109
+ if @buffer.bytesize < bytes_to_read
110
+ begin
111
+ append_io_data yield
112
+ rescue ::EOFError
113
+ io_provided_eof_error = true
114
+ end
115
+ end
116
+
117
+ flush_io_data if @buffer.bytesize < bytes_to_read
118
+ raise ::EOFError if empty? && io_provided_eof_error
112
119
 
113
120
  read_bytes_from_buffer bytes_to_read, out_buffer
114
121
  end
115
122
 
116
- protected def read_more_from_buffer_nonblock(*options)
117
- io_data = @io.read_nonblock @source_buffer_length, *options
118
- append_io_data_to_buffer io_data
123
+ # -- common --
124
+
125
+ protected def append_io_data(io_data)
126
+ io_portion = @io_remainder + io_data
127
+ bytes_read = raw_wrapper :read, io_portion
128
+ @io_remainder = io_portion.byteslice bytes_read, io_portion.bytesize - bytes_read
129
+
130
+ # Even empty io data may require flush.
131
+ @need_to_flush = true
119
132
  end
120
133
 
121
- # -- common --
134
+ protected def flush_io_data
135
+ raw_wrapper :flush
122
136
 
123
- def eof?
124
- @io.eof? && @buffer.bytesize == 0
137
+ @need_to_flush = false
138
+ end
139
+
140
+ protected def empty?
141
+ !@need_to_flush && @buffer.bytesize.zero?
125
142
  end
126
143
 
127
144
  protected def read_bytes_from_buffer(bytes_to_read, out_buffer)
@@ -136,18 +153,20 @@ module BRS
136
153
  result
137
154
  end
138
155
 
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
156
+ protected def read_buffer(out_buffer)
157
+ result = @buffer
158
+ reset_buffer
159
+ @pos += result.bytesize
143
160
 
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?
161
+ result.force_encoding @external_encoding unless @external_encoding.nil?
162
+ result = transcode_to_internal result
163
+
164
+ result = out_buffer.replace result unless out_buffer.nil?
165
+ result
147
166
  end
148
167
 
149
168
  protected def transcode_to_internal(data)
150
- data = data.encode @internal_encoding, @transcode_options unless @internal_encoding.nil?
169
+ data = data.encode @internal_encoding, **@transcode_options unless @internal_encoding.nil?
151
170
  data
152
171
  end
153
172