Class: Zlib::ZReader

Inherits:
Object
  • Object
show all
Includes:
IO::Like
Defined in:
lib/archive/support/zlib.rb

Overview

Zlib::ZReader is a readable, IO-like object (includes IO::Like) which wraps other readable, IO-like objects in order to facilitate reading data from those objects using the inflate method of decompression.

Constant Summary collapse

DEFAULT_DELEGATE_READ_SIZE =

The number of bytes to read from the delegate object each time the internal read buffer is filled.

4096

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(delegate, window_bits = nil) ⇒ ZReader

Creates a new instance of this class. delegate must respond to the read method as an IO instance would. window_bits is passed directly to Zlib::Inflate.new().

The following description of window_bits is based on the description found in zlib.h version 1.2.3. Some of the statements concerning default settings or value ranges may not be accurate depending on the version of the zlib library used by a given Ruby interpreter.

The window_bits parameter specifies the size of the history buffer, the format of the compressed stream, and the kind of checksum returned by the checksum method. The size of the history buffer is specified by setting the value of window_bits in the range of 8..15, inclusive. It must be at least as large as the setting used to create the stream or a Zlib::DataError will be raised. Modification of this base value for window_bits as noted below dictates what kind of compressed stream is expected and what kind of checksum will be produced while preserving the setting for the history buffer.

If nothing else is done to the base value of window_bits, a zlib stream is expected with an appropriate header and trailer. In this case the checksum method of this object will be an adler32.

Adding 16 to the base value of window_bits indicates that a gzip stream is expected with an appropriate header and trailer. In this case the checksum method of this object will be a crc32.

Adding 32 to the base value of window_bits indicates that an automatic detection of the stream format should be made based on the header in the stream. In this case the checksum method of this object will depend on whether a zlib or a gzip stream is detected.

Finally, negating the base value of window_bits indicates that a raw zlib stream is expected without any header or trailer. In this case the checksum method of this object will always return nil. This is for use with other formats that use the deflate compressed data format such as zip. Such formats should provide their own check values.

If unspecified or nil, window_bits defaults to 15.

In all cases, Zlib::DataError is raised if the wrong stream format is found when reading.

This class has extremely limited seek capabilities. It is possible to seek with an offset of 0 and a whence of IO::SEEK_CUR. As a result, the pos and tell methods also work as expected.

Due to certain optimizations within IO::Like#seek and if there is data in the read buffer, the seek method can be used to seek forward from the current stream position up to the end of the buffer. Unless it is known definitively how much data is in the buffer, it is best to avoid relying on this behavior.

If delegate also responds to rewind, then the rewind method of this class can be used to reset the whole stream back to the beginning. Using seek of this class to seek directly to offset 0 using IO::SEEK_SET for whence will also work in this case.

Any other seeking attempts, will raise Errno::EINVAL exceptions.

NOTE: Due to limitations in Ruby’s finalization capabilities, the #close method is not automatically called when this object is garbage collected. Make sure to call #close when finished with this object.



348
349
350
351
352
353
354
355
356
357
# File 'lib/archive/support/zlib.rb', line 348

def initialize(delegate, window_bits = nil)
  @delegate = delegate
  @delegate_read_size = DEFAULT_DELEGATE_READ_SIZE
  @window_bits = window_bits
  @inflater = Zlib::Inflate.new(@window_bits)
  @inflate_buffer = ''
  @checksum = nil
  @compressed_size = nil
  @uncompressed_size = nil
end

Instance Attribute Details

#delegateObject (readonly, protected)

The delegate object from which compressed data is read.



366
367
368
# File 'lib/archive/support/zlib.rb', line 366

def delegate
  @delegate
end

#delegate_read_sizeObject

The number of bytes to read from the delegate object each time the internal read buffer is filled.



361
362
363
# File 'lib/archive/support/zlib.rb', line 361

def delegate_read_size
  @delegate_read_size
end

Class Method Details

.open(delegate, window_bits = nil) ⇒ Object

Creates a new instance of this class with the given arguments using #new and then passes the instance to the given block. The #close method is guaranteed to be called after the block completes.

Equivalent to #new if no block is given.



272
273
274
275
276
277
278
279
280
281
# File 'lib/archive/support/zlib.rb', line 272

def self.open(delegate, window_bits = nil)
  zr = new(delegate, window_bits)
  return zr unless block_given?

  begin
    yield(zr)
  ensure
    zr.close unless zr.closed?
  end
end

Instance Method Details

#checksumObject

Returns the checksum computed over the data read from this stream.

NOTE: Refer to the documentation of #new concerning window_bits to learn what kind of checksum will be returned.

NOTE: The contents of the internal read buffer are immediately processed any time the internal buffer is filled, so this checksum is only accurate if all data has been read out of this object.



378
379
380
381
# File 'lib/archive/support/zlib.rb', line 378

def checksum
  return nil if @window_bits < 0
  @inflater.closed? ? @checksum : @inflater.adler
end

#closeObject

Closes the reader.

Raises IOError if called more than once.



386
387
388
389
390
391
392
393
# File 'lib/archive/support/zlib.rb', line 386

def close
  super()
  @checksum = @inflater.adler
  @compressed_size = @inflater.total_in
  @uncompressed_size = @inflater.total_out
  @inflater.close
  nil
end

#compressed_sizeObject

Returns the number of bytes sent to be compressed so far.

NOTE: This value is updated whenever the internal read buffer needs to be filled, not when data is read out of this stream.



399
400
401
# File 'lib/archive/support/zlib.rb', line 399

def compressed_size
  @inflater.closed? ? @compressed_size : @inflater.total_in
end

#unbuffered_read(length) ⇒ Object (private)



413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
# File 'lib/archive/support/zlib.rb', line 413

def unbuffered_read(length)
  if @inflate_buffer.empty? && @inflater.finished? then
    raise EOFError, 'end of file reached'
  end

  begin
    while @inflate_buffer.length < length && ! @inflater.finished? do
      @inflate_buffer <<
        @inflater.inflate(delegate.read(@delegate_read_size))
    end
  rescue Errno::EINTR, Errno::EAGAIN
    raise if @inflate_buffer.empty?
  end
  @inflate_buffer.slice!(0, length)
end

#unbuffered_seek(offset, whence = IO::SEEK_SET) ⇒ Object (private)

Allows resetting this object and the delegate object back to the beginning of the stream or reporting the current position in the stream.

Raises Errno::EINVAL unless offset is 0 and whence is either IO::SEEK_SET or IO::SEEK_CUR. Raises Errno::EINVAL if whence is IO::SEEK_SEK and the delegate object does not respond to the rewind method.



436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
# File 'lib/archive/support/zlib.rb', line 436

def unbuffered_seek(offset, whence = IO::SEEK_SET)
  unless offset == 0 &&
         ((whence == IO::SEEK_SET && delegate.respond_to?(:rewind)) ||
          whence == IO::SEEK_CUR) then
    raise Errno::EINVAL
  end

  case whence
  when IO::SEEK_SET
    delegate.rewind
    @inflater.close
    @inflater = Zlib::Inflate.new(@window_bits)
    @inflate_buffer = ''
    0
  when IO::SEEK_CUR
    @inflater.total_out - @inflate_buffer.length
  end
end

#uncompressed_sizeObject

Returns the number of bytes of decompressed data produced so far.

NOTE: This value is updated whenever the internal read buffer needs to be filled, not when data is read out of this stream.



407
408
409
# File 'lib/archive/support/zlib.rb', line 407

def uncompressed_size
  @inflater.closed? ? @uncompressed_size : @inflater.total_out
end