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.
- checksums.yaml +4 -4
- data/AUTHORS +8 -0
- data/COPYING +1 -1
- data/HISTORY.rdoc +24 -4
- data/{README.rdoc → README.md} +61 -33
- data/lib/xz.rb +290 -184
- data/lib/xz/lib_lzma.rb +28 -18
- data/lib/xz/stream.rb +24 -22
- data/lib/xz/stream_reader.rb +328 -122
- data/lib/xz/stream_writer.rb +288 -97
- data/lib/xz/version.rb +33 -0
- metadata +36 -46
data/lib/xz/stream_writer.rb
CHANGED
@@ -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
|
27
|
-
#uncompressed data to a stream which ends up as compressed data
|
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
|
-
#
|
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
|
-
#
|
37
|
-
#
|
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
|
-
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#instance and then the wrapped IO object (in *exactly* that order,
|
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 ::
|
50
|
-
#StreamWriter will open the file internally and also takes care
|
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
|
-
#
|
54
|
-
#
|
55
|
-
#
|
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
|
-
|
58
|
-
#
|
59
|
-
#
|
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
|
-
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
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
|
-
#
|
70
|
-
#
|
71
|
-
#
|
72
|
-
#Creates a new StreamWriter instance. The block form automatically
|
73
|
-
#calls the #close method when the block has finished executing.
|
74
|
-
|
75
|
-
#
|
76
|
-
#
|
77
|
-
#
|
78
|
-
#
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
|
83
|
-
#
|
84
|
-
|
85
|
-
#
|
86
|
-
#
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
90
|
-
#
|
91
|
-
#
|
92
|
-
#
|
93
|
-
#
|
94
|
-
#
|
95
|
-
#
|
96
|
-
#
|
97
|
-
#
|
98
|
-
#
|
99
|
-
#
|
100
|
-
#
|
101
|
-
|
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
|
-
|
144
|
+
# Correct use with IO
|
145
|
+
super(delegate.to_io)
|
146
|
+
@autoclose = false
|
104
147
|
else
|
105
|
-
|
106
|
-
|
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
|
-
|
124
|
-
|
125
|
-
#
|
126
|
-
#
|
127
|
-
|
128
|
-
#
|
129
|
-
#
|
130
|
-
|
131
|
-
#
|
132
|
-
|
133
|
-
#
|
134
|
-
#
|
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
|
-
|
165
|
-
|
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
|
-
#
|
172
|
-
#
|
173
|
-
#
|
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
|
-
#
|
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!
|
data/lib/xz/version.rb
ADDED
@@ -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
|