multi_compress 0.2.4 → 0.3.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.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MultiCompress
4
- VERSION = "0.2.4"
4
+ VERSION = "0.3.0"
5
5
  end
@@ -21,6 +21,9 @@ module MultiCompress
21
21
  DEFAULT = :default
22
22
  BEST = :best
23
23
 
24
+ DEFAULT_MAX_OUTPUT_SIZE = 512 * 1024 * 1024
25
+ DEFAULT_STREAMING_MAX_OUTPUT_SIZE = 2 * 1024 * 1024 * 1024
26
+
24
27
  EXTENSION_MAP = {
25
28
  ".zst" => :zstd,
26
29
  ".zstd" => :zstd,
@@ -30,24 +33,95 @@ module MultiCompress
30
33
 
31
34
  private_constant :EXTENSION_MAP
32
35
 
36
+ class Config
37
+ attr_reader :max_output_size, :streaming_max_output_size
38
+
39
+ def initialize
40
+ reset!
41
+ end
42
+
43
+ def max_output_size=(value)
44
+ @max_output_size = normalize_limit(value, :max_output_size, DEFAULT_MAX_OUTPUT_SIZE)
45
+ end
46
+
47
+ def streaming_max_output_size=(value)
48
+ @streaming_max_output_size = normalize_limit(
49
+ value,
50
+ :streaming_max_output_size,
51
+ DEFAULT_STREAMING_MAX_OUTPUT_SIZE
52
+ )
53
+ end
54
+
55
+ def reset!
56
+ @max_output_size = DEFAULT_MAX_OUTPUT_SIZE
57
+ @streaming_max_output_size = DEFAULT_STREAMING_MAX_OUTPUT_SIZE
58
+ self
59
+ end
60
+
61
+ def dup
62
+ copy = self.class.new
63
+ copy.max_output_size = max_output_size
64
+ copy.streaming_max_output_size = streaming_max_output_size
65
+ copy
66
+ end
67
+
68
+ private
69
+
70
+ def normalize_limit(value, name, default)
71
+ return default if value.nil?
72
+ raise TypeError, "#{name} must be an Integer" unless value.respond_to?(:to_int)
73
+
74
+ limit = value.to_int
75
+ raise ArgumentError, "#{name} must be greater than 0" if limit <= 0
76
+
77
+ limit
78
+ end
79
+ end
80
+
81
+ class << self
82
+ def config
83
+ @config ||= Config.new
84
+ end
85
+
86
+ def configure
87
+ return config unless block_given?
88
+
89
+ yield(config)
90
+ config
91
+ end
92
+
93
+ unless private_method_defined?(:_c_decompress)
94
+ alias_method :_c_decompress, :decompress
95
+ private :_c_decompress
96
+ end
97
+ end
98
+
33
99
  def self.zstd(data, level: nil)
34
100
  compress(data, algo: :zstd, **level_opts(level))
35
101
  end
36
102
 
37
- def self.lz4(data, level: nil)
38
- compress(data, algo: :lz4, **level_opts(level))
103
+ def self.lz4(data, level: nil, format: nil)
104
+ opts = level_opts(level)
105
+ opts[:format] = format if format
106
+ compress(data, algo: :lz4, **opts)
39
107
  end
40
108
 
41
109
  def self.brotli(data, level: nil)
42
110
  compress(data, algo: :brotli, **level_opts(level))
43
111
  end
44
112
 
113
+ def self.decompress(data, **opts)
114
+ _c_decompress(data, **resolved_one_shot_options(opts))
115
+ end
116
+
45
117
  def self.zstd_decompress(data)
46
118
  decompress(data, algo: :zstd)
47
119
  end
48
120
 
49
- def self.lz4_decompress(data)
50
- decompress(data, algo: :lz4)
121
+ def self.lz4_decompress(data, format: nil)
122
+ opts = { algo: :lz4 }
123
+ opts[:format] = format if format
124
+ decompress(data, **opts)
51
125
  end
52
126
 
53
127
  def self.brotli_decompress(data)
@@ -62,11 +136,24 @@ module MultiCompress
62
136
  level ? { level: level } : {}
63
137
  end
64
138
 
65
- private_class_method :level_opts
139
+ def self.resolved_one_shot_options(opts)
140
+ resolved = opts.dup
141
+ resolved[:max_output_size] = config.max_output_size unless resolved.key?(:max_output_size)
142
+ resolved
143
+ end
144
+
145
+ private_class_method :level_opts, :resolved_one_shot_options
146
+
147
+ module InflaterDefaults
148
+ def initialize(*args, **opts)
149
+ resolved = opts.dup
150
+ resolved[:max_output_size] = MultiCompress.config.streaming_max_output_size unless resolved.key?(:max_output_size)
151
+ super(*args, **resolved)
152
+ end
153
+ end
154
+
155
+ Inflater.prepend(InflaterDefaults)
66
156
 
67
- # Streaming compressed writer.
68
- #
69
- # Supports block form via +Writer.open+ or manual lifecycle management.
70
157
  class Writer
71
158
  CHUNK_BUFFER_SIZE = 8192
72
159
 
@@ -125,7 +212,7 @@ module MultiCompress
125
212
  end
126
213
 
127
214
  def puts(*args)
128
- args.each do |arg|
215
+ args.flatten.each do |arg|
129
216
  str = arg.to_s
130
217
  write(str)
131
218
  write("\n") unless str.end_with?("\n")
@@ -159,15 +246,12 @@ module MultiCompress
159
246
  private_class_method :resolve_io
160
247
  end
161
248
 
162
- # Streaming compressed reader.
163
- #
164
- # Supports block form via +Reader.open+ or manual lifecycle management.
165
249
  class Reader
166
250
  CHUNK_SIZE = 8192
167
251
 
168
- def self.open(path_or_io, algo: nil, dictionary: nil, max_output_size: nil, max_ratio: 1000, &block)
252
+ def self.open(path_or_io, algo: nil, dictionary: nil, **opts, &block)
169
253
  io, algo, owned = resolve_io(path_or_io, algo, mode: "rb")
170
- reader = new(io, algo: algo, dictionary: dictionary, max_output_size: max_output_size, max_ratio: max_ratio)
254
+ reader = new(io, algo: algo, dictionary: dictionary, **opts)
171
255
  reader.instance_variable_set(:@owned_io, owned)
172
256
 
173
257
  return reader unless block
@@ -179,9 +263,9 @@ module MultiCompress
179
263
  end
180
264
  end
181
265
 
182
- def initialize(io, algo: nil, dictionary: nil, max_output_size: nil, max_ratio: 1000)
266
+ def initialize(io, algo: nil, dictionary: nil, **opts)
183
267
  @io = io
184
- @inflater = Inflater.new(algo: algo, dictionary: dictionary, max_output_size: max_output_size, max_ratio: max_ratio)
268
+ @inflater = Inflater.new(algo: algo, dictionary: dictionary, **opts)
185
269
  @closed = false
186
270
  @owned_io = false
187
271
  @buffer = +""
@@ -237,7 +321,6 @@ module MultiCompress
237
321
 
238
322
  while (chunk = read(size))
239
323
  yield chunk
240
- break if chunk.bytesize < size
241
324
  end
242
325
  end
243
326
 
@@ -272,7 +355,7 @@ module MultiCompress
272
355
  end
273
356
 
274
357
  def read_exactly(length)
275
- return +("") if @eof && @buffer.empty?
358
+ return nil if @eof && @buffer.empty?
276
359
 
277
360
  fill_buffer_until { @buffer.bytesize >= length }
278
361
 
@@ -298,9 +381,9 @@ module MultiCompress
298
381
  end
299
382
 
300
383
  def extract_line(separator)
301
- idx = @buffer.index(separator)
302
- result = @buffer[0..idx]
303
- @buffer = @buffer[(idx + 1)..]
384
+ idx = @buffer.index(separator)
385
+ result = @buffer[0, idx + separator.bytesize]
386
+ @buffer = @buffer[(idx + separator.bytesize)..] || +""
304
387
  result
305
388
  end
306
389
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: multi_compress
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roman Haydarov
@@ -78,6 +78,7 @@ extensions:
78
78
  extra_rdoc_files: []
79
79
  files:
80
80
  - CHANGELOG.md
81
+ - GET_STARTED.md
81
82
  - LICENSE.txt
82
83
  - README.md
83
84
  - ext/multi_compress/extconf.rb