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

external_encoding[R]

Returns the encoding used inside the compressed data stream. Like IO#external_encoding.

internal_encoding[R]

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.

lineno[RW]

Like IO#lineno and IO#lineno=.

Public Instance Methods

<<(obj) click to toggle source

Like IO#<<.

# File lib/xz/stream.rb, line 285
def <<(obj)
  write(obj.to_s)
end
advise() click to toggle source

Like IO#advise. No-op, because not meaningful on compressed data.

# File lib/xz/stream.rb, line 290
def advise
  nil
end
close() click to toggle source

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
close_read() click to toggle source

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
close_write() click to toggle source

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
closed?() click to toggle source

True if the delegate IO has been closed.

# File lib/xz/stream.rb, line 186
def closed?
  @delegate_io.closed?
end
each(*args) { |line| ... } click to toggle source

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
Also aliased as: each_line
each_byte() { |byte| ... } click to toggle source

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
each_char() { |char| ... } click to toggle source

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
each_codepoint() { |ord| ... } click to toggle source

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
each_line(*args)
Alias for: each
eof() click to toggle source

Alias for eof?

# File lib/xz/stream.rb, line 181
def eof
  eof?
end
eof?() click to toggle source

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
finish() click to toggle source

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
finished?() click to toggle source

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
getbyte() click to toggle source

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
getc() click to toggle source

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
gets(separator = $/, limit = nil) click to toggle source

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
pos() click to toggle source

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
Also aliased as: tell
print(*objs) click to toggle source

Like IO#print.

printf(*args) click to toggle source

Like IO#printf.

# File lib/xz/stream.rb, line 396
def printf(*args)
  write(sprintf(*args))
  nil
end
putc(obj) click to toggle source

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
puts(*objs) click to toggle source
# 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
read(*args) click to toggle source

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
readbyte() click to toggle source

Like IO#readbyte.

# File lib/xz/stream.rb, line 303
def readbyte
  getbyte || raise(EOFError, "End of stream reached")
end
readchar() click to toggle source

Like IO#readchar.

# File lib/xz/stream.rb, line 330
def readchar
  getc || raise(EOFError, "End of stream reached")
end
readline(*args) click to toggle source

Like IO#readline.

# File lib/xz/stream.rb, line 356
def readline(*args)
  gets(*args) || raise(EOFError, "End of stream reached")
end
reopen(*args) click to toggle source

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
set_encoding(*args) click to toggle source

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
tell()
Alias for: pos
to_io() click to toggle source

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
write(*args) click to toggle source

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

target_encoding() click to toggle source
# File lib/xz/stream.rb, line 457
def target_encoding
  if @internal_encoding
    @internal_encoding
  else
    @external_encoding
  end
end