//============================================================================== // InflaterOutputStream.java //============================================================================== package tribble.io; // System imports import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FilterOutputStream; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import java.lang.IllegalArgumentException; import java.lang.NullPointerException; import java.lang.String; import java.lang.System; import java.lang.Throwable; import java.util.zip.DataFormatException; import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; import java.util.zip.ZipException; // Local imports // (None) /******************************************************************************* * Implements an output stream filter for uncompressing data stored in the * "deflate" compression format. * *
* This class serves as a complement to the standard * java.util.zip.InflaterInputStream and * java.util.zip.DeflaterOutputStream classes. * It decompresses ("inflates") data like the former class, but writes the * decompressed data to an output stream like the latter class. * * * @version $Revision: 1.6 $ $Date: 2006/02/14 00:05:53 $ * @since 2005-05-29 * @author David R. Tribble * (david@tribble.com). * *
* Copyright ©2005 by David R. Tribble, all rights reserved.
*
* Permission is granted to freely use and distribute this source
* code provided that the original copyright and authorship notices
* remain intact.
*
* @see DeflaterInputStream
* @see java.util.zip.DeflaterOutputStream
* @see java.util.zip.InflaterInputStream
* @see java.util.zip.InflaterOutputStream
*/
public class InflaterOutputStream
extends java.io.FilterOutputStream
{
// Identification
/** Revision information. */
static final String REV =
"@(#)tribble/io/InflaterOutputStream.java $Revision: 1.6 $ $Date: 2006/02/14 00:05:53 $\n";
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Constants
/** Default buffer size (512 bytes). */
private static final int DFL_BUFLEN = 512;
/** I/O buffer size (in bytes) (see {@link #main main()}). */
private static final int IO_BUFLEN = 4*1024;
/** Partial write block size. */
private static final int PART_LEN = 512;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Static methods
/***************************************************************************
* Uncompress a file containing data stored in the "deflate" compression
* format.
*
*
* The file to be uncompressed should contain compressed data stored in the * "deflate" compression format, such as the output from the * {@link DeflaterInputStream} class. * * *
* Usage *
* * java tribble.io.InflaterOutputStream [-option...] file * * *
* Options *
*
* Note that this method can be called multiple times with no ill effects. * * @throws IOException * Thrown if an I/O error occurs. * * @since 1.1, 2005-06-30 */ public void close() throws IOException //overrides java.io.FilterOutputStream { OutputStream outp; Inflater infl; // Sanity check if (super.out == null) return; // Complete the uncompressed output flush(); // Clean up outp = super.out; super.out = null; infl = m_infl; m_infl = null; m_buf = null; if (m_isDefault) infl.end(); outp.close(); } /*************************************************************************** * Flush this output stream, forcing any buffered output bytes to be written * to the stream. * * @throws IOException * Thrown if an I/O error occurs. * * @throws NullPointerException (unchecked) * Thrown if the output stream is closed. * * @since 1.2, 2005-07-02 */ public void flush() throws IOException //overrides java.io.FilterOutputStream { // Sanity check if (super.out == null) throw new NullPointerException(); // Finish decompressing and writing pending output data if (!m_infl.finished()) { try { while (!m_infl.finished() && !m_infl.needsInput()) { int n; // Decompress pending output data n = m_infl.inflate(m_buf, 0, m_buf.length); if (n < 1) break; // Write the decompressed output data block super.out.write(m_buf, 0, n); m_outBytes += n; } super.out.flush(); } catch (DataFormatException ex) { String msg; // Improperly formatted compressed (ZIP) data msg = ex.getMessage(); if (msg == null) msg = "Invalid ZLIB data format"; throw new ZipException(msg); } } } /*************************************************************************** * Finish writing uncompressed data to this output stream. * Does not close the underlying stream. This is useful when applying * multiple filters in succession to the same output stream. * * @throws IOException * Thrown if an I/O error occurs. * * @throws NullPointerException (unchecked) * Thrown if the output stream is closed. * * @see #flush * @see #close * * @since 1.5, 2005-07-25 */ public void finish() throws IOException { // Sanity check if (super.out == null) throw new NullPointerException(); // Finish decompressing and writing pending output data flush(); m_infl.end(); } /*************************************************************************** * Write data to this uncompressed output stream. * * @param b * A single byte of compresses data to decompress and write to the output * stream. * * @throws ZipException * Thrown if a compression (ZIP) format error occurs. * * @throws IOException * Thrown if an I/O error occurs. * * @throws NullPointerException (unchecked) * Thrown if the output stream is closed. * * @since 1.1, 2005-06-30 */ public void write(int b) throws IOException //overrides java.io.FilterOutputStream { // Write a single byte of data m_wbuf[0] = (byte) b; write(m_wbuf, 0, 1); } /*************************************************************************** * Write data to this uncompressed output stream. * * @param buf * Buffer containing compressed data to decompress and write to the output * stream. * * @throws ZipException * Thrown if a compression (ZIP) format error occurs. * * @throws IOException * Thrown if an I/O error occurs. * * @throws NullPointerException (unchecked) * Thrown if the output stream is closed. * * @since 1.1, 2005-06-30 */ public void write(/*const*/ byte[] buf) throws IOException //overrides java.io.FilterOutputStream { // Write a block of uncompressed data write(buf, 0, buf.length); } /*************************************************************************** * Write data to this uncompressed output stream. * * @param buf * Buffer containing compressed data to decompress and write to the output * stream. * * @param off * Starting offset of the compressed data within buf. * * @param len * Number of bytes to decompress from buf. * * @throws ZipException * Thrown if a compression (ZIP) format error occurs. * * @throws IOException * Thrown if an I/O error occurs. * * @throws NullPointerException (unchecked) * Thrown if the output stream is closed. * * @since 1.1, 2005-06-29 */ public void write(/*const*/ byte[] buf, int off, int len) throws IOException //overrides java.io.FilterOutputStream { // Sanity checks if (super.out == null) throw new NullPointerException(); if ((off | len | (off + len)) < 0) throw new IndexOutOfBoundsException(); if ((buf.length - (off + len)) < 0) throw new IndexOutOfBoundsException(); // Write decompressed data to the output stream try { for (;;) { int n; // Supply the decompressor with output data if (m_infl.needsInput()) { int part; // Check for the end of the decompression output if (len < 1) break; // Fill the decompression buffer with some output data; // this may reduce excessive memory allocations // within the decompressor part = (len < PART_LEN ? len : PART_LEN); m_infl.setInput(buf, off, part); off += part; len -= part; m_inBytes += part; } // Decompress blocks of output data do { // Decompress a block of output data n = m_infl.inflate(m_buf, 0, m_buf.length); if (n > 0) { // Write the decompressed output data block super.out.write(m_buf, 0, n); m_outBytes += n; } } while (n > 0); // Check the decompressor if (m_infl.finished()) break; if (m_infl.needsDictionary()) throw new ZipException("ZLIB dictionary missing"); } } catch (DataFormatException ex) { String msg; // Improperly formatted compressed (ZIP) data msg = ex.getMessage(); if (msg == null) msg = "Invalid ZLIB data format"; throw new ZipException(msg); } } /*************************************************************************** * Determine the total number of bytes written to this decompressed output * stream. * * @return * The actual number of bytes written to this output stream prior to being * decompressed. * * @since 1.2, 2005-06-30 */ public long getTotalIn() { return (m_inBytes); } /*************************************************************************** * Determine the total number of bytes written to the underlying output * stream. * * @return * The number of bytes written to the underlying output stream after being * decompressed. * * @since 1.2, 2005-06-30 */ public long getTotalOut() { return (m_outBytes); } /*************************************************************************** * Finalization. * * @since 1.6, 2006-02-13 */ protected synchronized void finalize() throws Throwable //overrides java.io.FilterOutputStream { Inflater infl; // Shut down the decompressor m_buf = null; infl = m_infl; m_infl = null; if (infl != null && m_isDefault) infl.end(); // Cascade super.finalize(); super.out = null; } } // End InflaterOutputStream.java