001: /*
002: * @(#)GZIPOutputStream.java 1.25 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package java.util.zip;
029:
030: import java.io.OutputStream;
031: import java.io.IOException;
032:
033: /**
034: * This class implements a stream filter for writing compressed data in
035: * the GZIP file format.
036: * @version 1.17, 02/02/00
037: * @author David Connelly
038: *
039: */
040: public class GZIPOutputStream extends DeflaterOutputStream {
041: /**
042: * CRC-32 of uncompressed data.
043: */
044: protected CRC32 crc = new CRC32();
045:
046: /*
047: * GZIP header magic number.
048: */
049: private final static int GZIP_MAGIC = 0x8b1f;
050:
051: /*
052: * Trailer size in bytes.
053: *
054: */
055: private final static int TRAILER_SIZE = 8;
056:
057: /**
058: * Creates a new output stream with the specified buffer size.
059: * @param out the output stream
060: * @param size the output buffer size
061: * @exception IOException If an I/O error has occurred.
062: * @exception IllegalArgumentException if size is <= 0
063: */
064: public GZIPOutputStream(OutputStream out, int size)
065: throws IOException {
066: super (out, new Deflater(Deflater.DEFAULT_COMPRESSION, true),
067: size);
068: usesDefaultDeflater = true;
069: writeHeader();
070: crc.reset();
071: }
072:
073: /**
074: * Creates a new output stream with a default buffer size.
075: * @param out the output stream
076: * @exception IOException If an I/O error has occurred.
077: */
078: public GZIPOutputStream(OutputStream out) throws IOException {
079: this (out, 512);
080: }
081:
082: /**
083: * Writes array of bytes to the compressed output stream. This method
084: * will block until all the bytes are written.
085: * @param buf the data to be written
086: * @param off the start offset of the data
087: * @param len the length of the data
088: * @exception IOException If an I/O error has occurred.
089: */
090: public synchronized void write(byte[] buf, int off, int len)
091: throws IOException {
092: super .write(buf, off, len);
093: crc.update(buf, off, len);
094: }
095:
096: /**
097: * Finishes writing compressed data to the output stream without closing
098: * the underlying stream. Use this method when applying multiple filters
099: * in succession to the same output stream.
100: * @exception IOException if an I/O error has occurred
101: */
102: public void finish() throws IOException {
103: if (!def.finished()) {
104: def.finish();
105: while (!def.finished()) {
106: int len = def.deflate(buf, 0, buf.length);
107: if (def.finished() && len <= buf.length - TRAILER_SIZE) {
108: // last deflater buffer. Fit trailer at the end
109: writeTrailer(buf, len);
110: len = len + TRAILER_SIZE;
111: out.write(buf, 0, len);
112: return;
113: }
114: if (len > 0)
115: out.write(buf, 0, len);
116: }
117: // if we can't fit the trailer at the end of the last
118: // deflater buffer, we write it separately
119: byte[] trailer = new byte[TRAILER_SIZE];
120: writeTrailer(trailer, 0);
121: out.write(trailer);
122: }
123: }
124:
125: /*
126: * Writes GZIP member header.
127: */
128:
129: private final static byte[] header = { (byte) GZIP_MAGIC, // Magic number (short)
130: (byte) (GZIP_MAGIC >> 8), // Magic number (short)
131: Deflater.DEFLATED, // Compression method (CM)
132: 0, // Flags (FLG)
133: 0, // Modification time MTIME (int)
134: 0, // Modification time MTIME (int)
135: 0, // Modification time MTIME (int)
136: 0, // Modification time MTIME (int)
137: 0, // Extra flags (XFLG)
138: 0 // Operating system (OS)
139: };
140:
141: private void writeHeader() throws IOException {
142: out.write(header);
143: }
144:
145: /*
146: * Writes GZIP member trailer to a byte array, starting at a given
147: * offset.
148: */
149: private void writeTrailer(byte[] buf, int offset)
150: throws IOException {
151: writeInt((int) crc.getValue(), buf, offset); // CRC-32 of uncompr. data
152: writeInt(def.getTotalIn(), buf, offset + 4); // Number of uncompr. bytes
153: }
154:
155: /*
156: * Writes integer in little-endian byte order to a byte array, starting
157: * at a given offset.
158: */
159: private void writeInt(int i, byte[] buf, int offset)
160: throws IOException {
161: writeShort(i & 0xffff, buf, offset);
162: writeShort((i >> 16) & 0xffff, buf, offset + 2);
163: }
164:
165: /*
166: * Writes short integer in little-endian byte order to a byte array,
167: * starting at a given offset
168: */
169: private void writeShort(int s, byte[] buf, int offset)
170: throws IOException {
171: buf[offset] = (byte) (s & 0xff);
172: buf[offset + 1] = (byte) ((s >> 8) & 0xff);
173: }
174: }
|