ruby-xz 0.2.1 → 0.2.2

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,9 +1,10 @@
1
1
  # -*- coding: utf-8 -*-
2
+ #--
2
3
  # (The MIT license)
3
4
  #
4
5
  # Basic liblzma-bindings for Ruby.
5
6
  #
6
- # Copyright © 2012 Marvin Gülker
7
+ # Copyright © 2012, 2015 Marvin Gülker
7
8
  #
8
9
  # Permission is hereby granted, free of charge, to any person obtaining a
9
10
  # copy of this software and associated documentation files (the ‘Software’),
@@ -22,94 +23,159 @@
22
23
  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
24
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
25
  # THE SOFTWARE.
26
+ #++
25
27
 
26
- #An IO-like writer class for XZ-compressed data, allowing you to write
27
- #uncompressed data to a stream which ends up as compressed data in
28
- #a wrapped stream such as a file.
28
+ # An IO-like writer class for XZ-compressed data, allowing you to
29
+ # write uncompressed data to a stream which ends up as compressed data
30
+ # in a wrapped stream such as a file.
29
31
  #
30
- #A StreamWriter object actually wraps another IO object it writes the
31
- #XZ-compressed data to. Here’s an ASCII art image to demonstrate
32
- #way data flows when using StreamWriter to write to a compressed
33
- #file:
32
+ # A StreamWriter object actually wraps another IO object it writes the
33
+ # XZ-compressed data to. Here’s an ASCII art image to demonstrate way
34
+ # data flows when using StreamWriter to write to a compressed file:
34
35
  #
35
- # +----------------+ +------------+
36
- # YOUR =>|StreamWriter's |=>|Wrapped IO's|=> ACTUAL
37
- # DATA =>|internal buffers|=>|buffers |=> FILE
38
- # +----------------+ +------------+
36
+ # +----------------+ +------------+
37
+ # YOUR =>|StreamWriter's |=>|Wrapped IO's|=> ACTUAL
38
+ # DATA =>|internal buffers|=>|buffers |=> FILE
39
+ # +----------------+ +------------+
39
40
  #
40
- #This graphic also illustrates why it is unlikely to see written
41
- #data directly appear in the file on your harddisk; the data is
42
- #cached at least twice before it actually gets written out. Regarding
43
- #file closing that means that before you can be sure any pending data
44
- #has been written to the file you have to close both the StreamWriter
45
- #instance and then the wrapped IO object (in *exactly* that order, otherwise
46
- #data loss and unexpected exceptions may occur!).
41
+ # This graphic also illustrates why it is unlikely to see written data
42
+ # directly appear in the file on your harddisk; the data is cached at
43
+ # least twice before it actually gets written out. Regarding file
44
+ # closing that means that before you can be sure any pending data has
45
+ # been written to the file you have to close both the StreamWriter
46
+ # instance and then the wrapped IO object (in *exactly* that order,
47
+ # otherwise data loss and unexpected exceptions may occur!).
47
48
  #
48
- #As it might be tedious to always remember the correct closing order,
49
- #it’s possible to pass a filename to the ::new method. In this case,
50
- #StreamWriter will open the file internally and also takes care closing
51
- #it when you call the #close method.
49
+ # As it might be tedious to always remember the correct closing order,
50
+ # it’s possible to pass a filename to the ::open method. In this case,
51
+ # StreamWriter will open the file internally and also takes care
52
+ # closing it when you call the #close method.
52
53
  #
53
- #See the +io-like+ gem’s documentation for the IO-writing methods
54
- #available for this class (although you’re probably familiar with
55
- #them through Ruby’s own IO class ;-)).
54
+ # *WARNING*: The closing behaviour described above is subject to
55
+ # change in the next major version. In the future, wrapped IO
56
+ # objects are automatically closed always, regardless of whether you
57
+ # passed a filename or an IO instance. This is to sync the API with
58
+ # Ruby’s own Zlib::GzipWriter. To retain the old behaviour, call
59
+ # the #finish method (which is also in sync with the Zlib API).
56
60
  #
57
- #==Example
58
- #Together with the <tt>archive-tar-minitar</tt> gem, this library
59
- #can be used to create XZ-compressed TAR archives (these commonly
60
- #use a file extension of <tt>.tar.xz</tt> or rarely <tt>.txz</tt>).
61
+ # See the +io-like+ gem’s documentation for the IO-writing methods
62
+ # available for this class (although you’re probably familiar with
63
+ # them through Ruby’s own IO class ;-)).
61
64
  #
62
- # XZ::StreamWriter.open("foo.tar.xz") do |txz|
63
- # # This automatically closes txz
64
- # Archive::Tar::Minitar.pack("foo", txz)
65
- # end
65
+ # == Example
66
+ #
67
+ # Together with the <tt>archive-tar-minitar</tt> gem, this library
68
+ # can be used to create XZ-compressed TAR archives (these commonly
69
+ # use a file extension of <tt>.tar.xz</tt> or rarely <tt>.txz</tt>).
70
+ #
71
+ # XZ::StreamWriter.open("foo.tar.xz") do |txz|
72
+ # # This automatically closes txz
73
+ # Archive::Tar::Minitar.pack("foo", txz)
74
+ # end
66
75
  class XZ::StreamWriter < XZ::Stream
67
76
 
68
- #call-seq:
69
- # open(delegate, compression_level = 6, check = :crc64, extreme = false) → a_stream_writer
70
- # new(delegate, compression_level = 6, check = :crc64, extreme = false) a_stream_writer
71
- #
72
- #Creates a new StreamWriter instance. The block form automatically
73
- #calls the #close method when the block has finished executing.
74
- #==Parameters
75
- #[delegate] An IO object to write the data to or a filename
76
- # which will be opened internally. If you pass an IO,
77
- # the #close method won’t close the passed IO object;
78
- # if you passed a filename, the created internal file
79
- # of course gets closed.
80
- #The other parameters are identical to what the XZ::compress_stream
81
- #method expects.
82
- #==Return value
83
- #The newly created instance.
84
- #==Example
85
- # # Wrap it around a file
86
- # f = File.open("data.xz")
87
- # w = XZ::StreamWriter.new(f)
88
- #
89
- # # Use SHA256 as the checksum and use a higher compression level
90
- # # than the default (6)
91
- # f = File.open("data.xz")
92
- # w = XZ::StreamWriter.new(f, 8, :sha256)
93
- #
94
- # # Instruct liblzma to use ultra-really-high compression
95
- # # (may take eternity)
96
- # f = File.open("data.xz")
97
- # w = XZ::StreamWriter.new(f, 9, :crc64, true)
98
- #
99
- # # Passing a filename
100
- # w = XZ::StreamWriter.new("compressed_data.xz")
101
- def initialize(delegate, compression_level = 6, check = :crc64, extreme = false)
77
+ # call-seq:
78
+ # new(delegate, compression_level = 6, opts = {}) → writer
79
+ # new(delegate, compression_level = 6, opts = {}){|writer| …}obj
80
+ #
81
+ # Creates a new StreamWriter instance. The block form automatically
82
+ # calls the #close method when the block has finished executing.
83
+ #
84
+ # === Parameters
85
+ # [delegate]
86
+ # An IO object to write the data to
87
+ #
88
+ # [compression_level (6)]
89
+ # Compression strength. Higher values indicate a smaller result,
90
+ # but longer compression time. Maximum is 9.
91
+ #
92
+ # [opts]
93
+ # Options hash. Possible values are (defaults indicated in
94
+ # parantheses):
95
+ #
96
+ # [:check (:crc64)]
97
+ # The checksum algorithm to use for verifying
98
+ # the data inside the archive. Possible values are:
99
+ # * :none
100
+ # * :crc32
101
+ # * :crc64
102
+ # * :sha256
103
+ #
104
+ # [:extreme (false)]
105
+ # Tries to get the last bit out of the compression.
106
+ # This may succeed, but you can end up with *very*
107
+ # long computation times.
108
+ #
109
+ # [writer]
110
+ # Block argument. self of the new instance.
111
+ #
112
+ # === Return value
113
+ #
114
+ # The block form returns the block’s last expression, the nonblock
115
+ # form returns the newly created instance.
116
+ #
117
+ # === Deprecations
118
+ #
119
+ # The old API for this method as it was documented in version 0.2.1
120
+ # still works, but is deprecated. Please change to the new API as
121
+ # soon as possible.
122
+ #
123
+ # *WARNING*: The closing behaviour of the block form is subject to
124
+ # upcoming change. In the next major release the wrapped IO *will*
125
+ # be automatically closed, unless you call #finish to prevent that.
126
+ #
127
+ # === Example
128
+ #
129
+ # # Wrap it around a file
130
+ # f = File.open("data.xz")
131
+ # w = XZ::StreamWriter.new(f)
132
+ #
133
+ # # Use SHA256 as the checksum and use a higher compression level
134
+ # # than the default (6)
135
+ # f = File.open("data.xz")
136
+ # w = XZ::StreamWriter.new(f, 8, :check => :sha256)
137
+ #
138
+ # # Instruct liblzma to use ultra-really-high compression
139
+ # # (may take eternity)
140
+ # f = File.open("data.xz")
141
+ # w = XZ::StreamWriter.new(f, 9, :extreme => true)
142
+ def initialize(delegate, compression_level = 6, *args, &block)
102
143
  if delegate.respond_to?(:to_io)
103
- super(delegate)
144
+ # Correct use with IO
145
+ super(delegate.to_io)
146
+ @autoclose = false
104
147
  else
105
- @file = File.open(delegate, "wb")
106
- super(@file)
148
+ # Deprecated use of filename
149
+ XZ.deprecate "Calling XZ::StreamWriter.new with a filename is deprecated, use XZ::StreamWriter.open instead"
150
+
151
+ @autoclose = true
152
+ super(File.open(delegate, "wb"))
153
+ end
154
+
155
+ # Flag for #finish method
156
+ @finish = false
157
+
158
+ opts = {}
159
+ if args[0].kind_of?(Hash) # New API
160
+ opts = args[0]
161
+ opts[:check] ||= :crc64
162
+ opts[:extreme] ||= false
163
+ else # Old API
164
+ # no arguments may also happen in new API
165
+ unless args.empty?
166
+ XZ.deprecate "Calling XZ::StreamWriter withm ore than 2 explicit arguments is deprecated, use options hash instead."
167
+ end
168
+
169
+ opts[:check] = args[0] || :crc64
170
+ opts[:extreme] = args[1] || false
107
171
  end
108
172
 
173
+ # TODO: Check argument validity...
174
+
109
175
  # Initialize the internal LZMA stream for encoding
110
176
  res = XZ::LibLZMA.lzma_easy_encoder(@lzma_stream.pointer,
111
- compression_level | (extreme ? XZ::LibLZMA::LZMA_PRESET_EXTREME : 0),
112
- XZ::LibLZMA::LZMA_CHECK[:"lzma_check_#{check}"])
177
+ compression_level | (opts[:extreme] ? XZ::LibLZMA::LZMA_PRESET_EXTREME : 0),
178
+ XZ::LibLZMA::LZMA_CHECK[:"lzma_check_#{opts[:check]}"])
113
179
  XZ::LZMAError.raise_if_necessary(res)
114
180
 
115
181
  if block_given?
@@ -120,18 +186,98 @@ class XZ::StreamWriter < XZ::Stream
120
186
  end
121
187
  end
122
188
  end
123
- self.class.send(:alias_method, :open, :new)
124
-
125
- #Closes this StreamWriter instance and flushes all internal buffers.
126
- #Don’t use it afterwards anymore.
127
- #==Return vaule
128
- #The total number of bytes written, i.e. the size of the compressed
129
- #data.
130
- #==Example
131
- # w.close #=> 424
132
- #==Remarks
133
- #If you passed an IO object to ::new, this method doesn’t close it,
134
- #you have to do that yourself.
189
+
190
+ # call-seq:
191
+ # open(filename, compression_level = 6, opts = {}) writer
192
+ # open(filename, compression_level = 6, opts = {}){|writer| …} → obj
193
+ #
194
+ # Opens a file from disk and wraps an XZ::StreamWriter instance
195
+ # around the resulting file IO object. This is a convenience method
196
+ # mostly equivalent to
197
+ #
198
+ # file = File.open(filename, "wb")
199
+ # writer = XZ::StreamWriter.new(file, compression_level, opts)
200
+ #
201
+ # , except that you don’t have to explicitely close the File
202
+ # instance, this is done automatically for you when you call #close.
203
+ # Beware the Deprecations section in this regard.
204
+ #
205
+ # === Parameters
206
+ #
207
+ # [filename]
208
+ # Path to a file on the disk to open. This file should exist and be
209
+ # writable, otherwise you may get Errno exceptions.
210
+ #
211
+ # [opts]
212
+ # Options hash. See ::new for a description of the possible
213
+ # options.
214
+ #
215
+ # [writer]
216
+ # Block argument. self of the new instance.
217
+ #
218
+ # === Return value
219
+ #
220
+ # The block form returns the blocks last expression, the nonblock
221
+ # form returns the newly created XZ::StreamWriter instance.
222
+ #
223
+ # === Deprecations
224
+ #
225
+ # In the API up to and including version 0.2.1 this method was an
226
+ # alias for ::new. This continues to work for now, but using it
227
+ # as an alias for ::new is deprecated. The next major version will
228
+ # only accept a string as a parameter for this method.
229
+ #
230
+ # *WARNING*: Future versions of ruby-xz will always close the
231
+ # wrapped IO, regardless of whether you pass in your own IO or use
232
+ # this convenience method, unless you call #finish to prevent that.
233
+ #
234
+ # === Example
235
+ #
236
+ # w = XZ::StreamWriter.new("compressed_data.xz")
237
+ def self.open(filename, compression_level = 6, *args, &block)
238
+ if filename.respond_to?(:to_io)
239
+ # Deprecated use of IO
240
+ XZ.deprecate "Calling XZ::StreamWriter.open with an IO is deprecated, use XZ::StreamReader.new instead."
241
+ new(filename.to_io, compression_level, *args, &block)
242
+ else
243
+ # Correct use with filename
244
+ file = File.open(filename, "wb")
245
+
246
+ obj = new(file, compression_level, *args)
247
+ obj.instance_variable_set(:@autoclose, true) # Only needed during deprecation phase, see #close
248
+
249
+ if block_given?
250
+ begin
251
+ block.call(obj)
252
+ ensure
253
+ obj.close unless obj.closed?
254
+ end
255
+ else
256
+ obj
257
+ end
258
+ end
259
+ end
260
+
261
+ # Closes this StreamWriter instance and flushes all internal buffers.
262
+ # Don’t use it afterwards anymore.
263
+ #
264
+ # === Return value
265
+ #
266
+ # The total number of bytes written, i.e. the size of the compressed
267
+ # data.
268
+ #
269
+ # === Example
270
+ #
271
+ # w.close #=> 424
272
+ #
273
+ # === Remarks
274
+ #
275
+ # If you passed an IO object to ::new, this method doesn’t close it,
276
+ # you have to do that yourself.
277
+ #
278
+ # *WARNING*: The next major release will change this behaviour.
279
+ # In the future, the wrapped IO object will always be closed.
280
+ # Use the #finish method for keeping it open.
135
281
  def close
136
282
  super
137
283
 
@@ -157,23 +303,68 @@ class XZ::StreamWriter < XZ::Stream
157
303
  break unless @lzma_stream[:avail_out] == 0
158
304
  end
159
305
 
160
- #2. Close the whole XZ stream.
306
+ # 2. Close the whole XZ stream.
161
307
  res = XZ::LibLZMA.lzma_end(@lzma_stream.pointer)
162
308
  XZ::LZMAError.raise_if_necessary(res)
163
309
 
164
- #2b. If we wrapped a file automatically, close it.
165
- @file.close if @file
310
+ unless @finish
311
+ # New API: Close the wrapped IO
312
+ #@delegate_io.close
313
+
314
+ # Old API:
315
+ # 2b. If we wrapped a file automatically, close it.
316
+ if @autoclose
317
+ @delegate_io.close
318
+ else
319
+ XZ.deprecate "XZ::StreamWriter#close will automatically close the wrapped IO in the future. Use #finish to prevent that."
320
+ end
321
+ end
166
322
 
167
- #3. Return the number of bytes written in total.
323
+ # 3. Return the number of bytes written in total.
168
324
  @lzma_stream[:total_out]
169
325
  end
170
326
 
171
- #call-seq:
172
- # pos() → an_integer
173
- # tell() an_integer
327
+ # If called in the block form of ::new or ::open, prevents the
328
+ # wrapped IO from being closed, only the LZMA stream is closed
329
+ # then. If called outside the block form of ::new and open, behaves
330
+ # like #close, but only closes the underlying LZMA stream. The
331
+ # wrapped IO object is kept open.
332
+ #
333
+ # === Return value
334
+ #
335
+ # Returns the wrapped IO object. This allows you to wire the File
336
+ # instance out of a StreamReader instance that was created with
337
+ # ::open.
338
+ #
339
+ # === Example
340
+ #
341
+ # # Nonblock form
342
+ # f = File.open("foo.xz", "wb")
343
+ # w = XZ::StreamReader.new(f)
344
+ # # ...
345
+ # w.finish
346
+ # # f is still open here!
347
+ #
348
+ # # Block form
349
+ # f = XZ::StreamReader.open("foo.xz") do |w|
350
+ # # ...
351
+ # w.finish
352
+ # end
353
+ # # f now is an *open* File instance of mode "wb".
354
+ def finish
355
+ # Do not close wrapped IO object in #close
356
+ @finish = true
357
+ close
358
+
359
+ @delegate_io
360
+ end
361
+
362
+ # call-seq:
363
+ # pos() → an_integer
364
+ # tell() → an_integer
174
365
  #
175
- #Total number of input bytes read so far from what you
176
- #supplied to any writer method.
366
+ # Total number of input bytes read so far from what you supplied to
367
+ # any writer method.
177
368
  def pos
178
369
  @lzma_stream[:total_in]
179
370
  end
@@ -181,9 +372,9 @@ class XZ::StreamWriter < XZ::Stream
181
372
 
182
373
  private
183
374
 
184
- #Called by io-like’s write methods such as #write. Does the heavy
185
- #work of feeding liblzma the uncompressed data and reading the
186
- #returned compressed data.
375
+ # Called by io-like’s write methods such as #write. Does the heavy
376
+ # work of feeding liblzma the uncompressed data and reading the
377
+ # returned compressed data.
187
378
  def unbuffered_write(data)
188
379
  output_buffer_p = FFI::MemoryPointer.new(XZ::CHUNK_SIZE)
189
380
  input_buffer_p = FFI::MemoryPointer.from_string(data) # This adds a terminating NUL byte we don’t want to compress!
@@ -0,0 +1,33 @@
1
+ # -*- coding: utf-8 -*-
2
+ #--
3
+ # (The MIT License)
4
+ #
5
+ # Basic liblzma-bindings for Ruby.
6
+ #
7
+ # Copyright © 2015 Marvin Gülker
8
+ #
9
+ # Permission is hereby granted, free of charge, to any person obtaining a
10
+ # copy of this software and associated documentation files (the ‘Software’),
11
+ # to deal in the Software without restriction, including without limitation
12
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
13
+ # and/or sell copies of the Software, and to permit persons to whom the Software
14
+ # is furnished to do so, subject to the following conditions:
15
+ #
16
+ # The above copyright notice and this permission notice shall be included in all
17
+ # copies or substantial portions of the Software.
18
+ #
19
+ # THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
+ # THE SOFTWARE.
26
+ #++
27
+
28
+ module XZ
29
+
30
+ # The version of this library.
31
+ VERSION = "0.2.2".freeze
32
+
33
+ end