ruby-xz 0.2.1 → 0.2.2

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