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.
- 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
|