class XZ::Stream
The base class for XZ::StreamReader
and XZ::StreamWriter
. This is an abstract class that is not meant to be used directly. You can, however, test against this class in kind_of?
tests.
XZ::StreamReader
and XZ::StreamWriter
are IO-like classes that allow you to access XZ-compressed data the same way you access an IO-object, easily allowing to fool other libraries that expect IO objects. The most noticable example for this may be reading and writing XZ-compressed tarballs using the minitar RubyGem; see the README.md file for an example.
Most of IO's methods are implemented in this class or one of the subclasses. The most notable exception is that it is not possible to seek in XZ
archives (seek and pos= are not defined). Many methods that are not expressly documented in the RDoc still exist; this class uses Ruby's Forwardable module to forward them to the underlying IO object.
Stream
and its subclasses honour Ruby's external+internal encoding system just like Ruby's own IO does. All of what the Ruby docs say about external and internal encodings applies to this class with one important difference. The “external encoding” does not refer to the encoding of the file on the hard disk (this file is always a binary file as it's compressed data), but to the encoding of the decompressed data inside the compressed file.
As with Ruby's IO class, instances of this class and its subclasses default their external encoding to Encoding.default_external and their internal encoding to Encoding.default_internal. You can use set_encoding
or pass appropriate arguments to the new
method to change these encodings per-instance.
Attributes
Returns the encoding used inside the compressed data stream. Like IO#external_encoding.
When compressed data is read, the decompressed data is transcoded from the external_encoding
to this encoding. If this encoding is nil, no transcoding happens.
Like IO#lineno and IO#lineno=.
Public Instance Methods
Like IO#<<.
# File lib/xz/stream.rb, line 285 def <<(obj) write(obj.to_s) end
Like IO#advise. No-op, because not meaningful on compressed data.
# File lib/xz/stream.rb, line 290 def advise nil end
If not done yet, call finish
. Then close the delegate IO. The latter action is going to cause the delegate IO to flush its buffer. After this method returns, it is guaranteed that all pending data has been flushed to the OS' kernel.
# File lib/xz/stream.rb, line 223 def close finish unless @finished @delegate_io.close unless @delegate_io.closed? nil end
Always raises IOError, because XZ
streams can never be duplex.
# File lib/xz/stream.rb, line 230 def close_read raise(IOError, "Not a duplex I/O stream") end
Always raises IOError, because XZ
streams can never be duplex.
# File lib/xz/stream.rb, line 235 def close_write raise(IOError, "Not a duplex I/O stream") end
True if the delegate IO has been closed.
# File lib/xz/stream.rb, line 186 def closed? @delegate_io.closed? end
Like IO#each.
# File lib/xz/stream.rb, line 361 def each(*args) return enum_for __method__ unless block_given? while line = gets(*args) yield(line) end end
Like IO#each_byte.
# File lib/xz/stream.rb, line 371 def each_byte return enum_for __method__ unless block_given? while byte = getbyte yield(byte) end end
Like IO#each_char.
# File lib/xz/stream.rb, line 380 def each_char return enum_for __method__ unless block_given? while char = getc yield(char) end end
Like IO#each_codepoint.
# File lib/xz/stream.rb, line 389 def each_codepoint return enum_for __method__ unless block_given? each_char{|c| yield(c.ord)} end
Alias for eof?
# File lib/xz/stream.rb, line 181 def eof eof? end
Overridden in StreamReader to be like IO#eof?. This abstract implementation only raises IOError.
# File lib/xz/stream.rb, line 176 def eof? raise(IOError, "Stream not opened for reading") end
Free internal libzlma memory. This needs to be called before you leave this object for the GC. If you used a block-form initializer, this done automatically for you.
Subsequent calls to read
or write
will cause an IOError.
Returns the underlying IO object. This allows you to retrieve the File instance that was automatically created when using the open
method's block form.
# File lib/xz/stream.rb, line 207 def finish return if @finished # Clean up the lzma_stream structure's internal memory. # This would belong into a destructor if Ruby had that. XZ::LibLZMA.lzma_end(@lzma_stream) @finished = true @delegate_io end
True if liblzma's internal memory has been freed. For writer instances, receiving true from this method also means that all of liblzma's compressed data has been flushed to the underlying IO object.
# File lib/xz/stream.rb, line 194 def finished? @finished end
Like IO#getbyte. Note this method isn't exactly performant, because it actually reads compressed data as a string and then needs to figure out the bytes from that again.
# File lib/xz/stream.rb, line 297 def getbyte return nil if eof? read(1).bytes.first end
Like IO#getc.
# File lib/xz/stream.rb, line 308 def getc str = String.new # Read byte-by-byte until a valid character in the external # encoding was built. loop do str.force_encoding(Encoding::BINARY) str << read(1) str.force_encoding(@external_encoding) break if str.valid_encoding? || eof? end # Transcode to internal encoding if one was requested if @internal_encoding str.encode(@internal_encoding) else str end end
Like IO#gets.
# File lib/xz/stream.rb, line 335 def gets(separator = $/, limit = nil) return nil if eof? @lineno += 1 # Mirror IO#gets' weird call-seq if separator.respond_to?(:to_int) limit = separator.to_int separator = $/ end buf = String.new buf.force_encoding(target_encoding) until eof? || (limit && buf.length >= limit) buf << getc return buf if buf[-1] == separator end buf end
Returns the position in the decompressed data (regardless of whether this is a reader or a writer instance).
# File lib/xz/stream.rb, line 253 def pos @pos end
Like IO#print.
# File lib/xz/stream.rb, line 435 def print(*objs) if objs.empty? write($_) else objs.each do |obj| write(obj.to_s) write($,) if $, end end write($\) if $\ nil end
Like IO#printf.
# File lib/xz/stream.rb, line 396 def printf(*args) write(sprintf(*args)) nil end
Like IO#putc.
# File lib/xz/stream.rb, line 402 def putc(obj) if obj.respond_to? :chr write(obj.chr) elsif obj.respond_to? :to_str write(obj.to_str) else raise(TypeError, "Can only #putc strings and numbers") end end
# File lib/xz/stream.rb, line 412 def puts(*objs) if objs.empty? write("\n") return nil end objs.each do |obj| if obj.respond_to? :to_ary puts(*obj.to_ary) else # Don't squeeze multiple subsequent trailing newlines in `obj' obj = obj.to_s if obj.end_with?("\n".encode(obj.encoding)) write(obj) else write(obj + "\n".encode(obj.encoding)) end end end nil end
Overridden in StreamReader to be like IO#read. This abstract implementation only raises IOError.
# File lib/xz/stream.rb, line 241 def read(*args) raise(IOError, "Stream not opened for reading") end
Like IO#readbyte.
# File lib/xz/stream.rb, line 303 def readbyte getbyte || raise(EOFError, "End of stream reached") end
Like IO#readchar.
# File lib/xz/stream.rb, line 330 def readchar getc || raise(EOFError, "End of stream reached") end
Like IO#readline.
# File lib/xz/stream.rb, line 356 def readline(*args) gets(*args) || raise(EOFError, "End of stream reached") end
It is not possible to reopen an lzma stream, hence this method always raises NotImplementedError.
# File lib/xz/stream.rb, line 451 def reopen(*args) raise(NotImplementedError, "Can't reopen an lzma stream") end
Like IO#set_encoding.
# File lib/xz/stream.rb, line 259 def set_encoding(*args) if args.count < 1 || args.count > 3 raise ArgumentError, "Wrong number of arguments: Expected 1-3, got #{args.count}" end # Clean `args' to [external_encoding, internal_encoding], # and @transcode_options. return set_encoding($`, $', *args[1..-1]) if args[0].respond_to?(:to_str) && args[0].to_str =~ /:/ @transcode_options = args.delete_at(-1) if args[-1].kind_of?(Hash) # `args' is always [external, internal] or [external] at this point @external_encoding = args[0].kind_of?(Encoding) ? args[0] : Encoding.find(args[0]) if args[1] @internal_encoding = args[1].kind_of?(Encoding) ? args[1] : Encoding.find(args[1]) else @internal_encoding = Encoding.default_internal # Encoding.default_internal defaults to nil end self end
You can mostly treat this as if it were an IO object. At least for subclasses. This class itself is abstract, you shouldn't be using it directly at all.
Returns the receiver.
# File lib/xz/stream.rb, line 170 def to_io self end
Overridden in StreamWriter to be like IO#write. This abstract implementation only raises IOError.
# File lib/xz/stream.rb, line 247 def write(*args) raise(IOError, "Stream not opened for writing") end
Private Instance Methods
# File lib/xz/stream.rb, line 457 def target_encoding if @internal_encoding @internal_encoding else @external_encoding end end