/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Nam Nguyen */ package com.caucho.quercus.lib.zlib; import java.io.IOException; import java.io.OutputStream; import java.util.zip.CRC32; import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; /** * As opposed to java's GZIPOutputStream, this class allows for more control on * what is written to the underlying OutputStream. * * @see java.util.zip.GZIPOutputStream */ public class GZIPOutputStream extends DeflaterOutputStream { private CRC32 _crc32; private byte[] _header = { (byte) 0x1f, (byte) 0x8b, // gzip file identifier (ID1, ID2) 8, // Deflate compression method (CM) 0, // optional flags (FLG) 0, 0, 0, 0, // modification time (MTIME) 0, // extra optional flags (XFL) 0 // operating system (OS) }; private int _encodingMode; private boolean _isGzip; /** * Writes gzip header to OutputStream upon construction. * XXX: set operating system (file architecure) header. * * @param out * @param def */ private GZIPOutputStream(OutputStream out, Deflater def) throws IOException { super(out, def); out.write(_header, 0, _header.length); } /** * @param out * @param compressionLevel * @param strategy Deflate compression strategy * @param encodingMode FORCE_GZIP to write gzwrite compatible output; * FORCE_DEFLATE to write gzip header and zlib header, * but do not write crc32 trailer */ public GZIPOutputStream(OutputStream out, int compressionLevel, int strategy, int encodingMode) throws IOException { this(out, createDeflater(compressionLevel, strategy, encodingMode)); _isGzip = (encodingMode == ZlibModule.FORCE_GZIP); if (_isGzip) _crc32 = new CRC32(); _encodingMode = encodingMode; } /** * Creates a deflater based on the Zlib arguments. */ private static Deflater createDeflater(int compressionLevel, int strategy, int encodingMode) { Deflater defl; if (encodingMode == ZlibModule.FORCE_GZIP) defl = new Deflater(compressionLevel, true); else defl = new Deflater(compressionLevel, false); defl.setStrategy(strategy); return defl; } /** * @param out * @param compressionLevel * @param strategy Deflate compression strategy */ public GZIPOutputStream(OutputStream out, int compressionLevel, int strategy) throws IOException { this(out, compressionLevel, strategy, ZlibModule.FORCE_GZIP); } /** * @param out */ public GZIPOutputStream(OutputStream out) throws IOException { this(out, Deflater.DEFAULT_COMPRESSION, Deflater.DEFAULT_STRATEGY); } /** * Writes a byte. * * @param input */ public void write(int v) throws IOException { super.write(v); if (_isGzip) _crc32.update(v); } /** * @param input * @param offset * @param length */ public void write(byte[] buffer, int offset, int length) throws IOException { super.write(buffer, offset, length); if (_isGzip) _crc32.update(buffer, offset, length); } public void finish() throws IOException { super.finish(); if (_isGzip) { long crcValue = _crc32.getValue(); byte[] trailerCRC = new byte[4]; trailerCRC[0] = (byte) crcValue; trailerCRC[1] = (byte) (crcValue >> 8); trailerCRC[2] = (byte) (crcValue >> 16); trailerCRC[3] = (byte) (crcValue >> 24); out.write(trailerCRC, 0, trailerCRC.length); } long inputSize = def.getBytesRead(); byte[] trailerInputSize = new byte[4]; trailerInputSize[0] = (byte) inputSize; trailerInputSize[1] = (byte) (inputSize >> 8); trailerInputSize[2] = (byte) (inputSize >> 16); trailerInputSize[3] = (byte) (inputSize >> 24); out.write(trailerInputSize, 0, trailerInputSize.length); out.flush(); } /** * Calls super function, which in turn closes the underlying 'in' stream */ public void close() throws IOException { if (! def.finished()) finish(); super.close(); } }