/*
* 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 com.caucho.quercus.QuercusModuleException;
import com.caucho.quercus.lib.file.AbstractBinaryOutput;
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 ZlibOutputStream extends AbstractBinaryOutput {
private OutputStream _os;
private DeflaterOutputStream _out;
private CRC32 _crc32;
private static byte[] _header = {
(byte) 0x1f, (byte) 0x8b, // gzip file identifier (ID1, ID2)
(byte) 0x8, // Deflate compression method (CM)
0, // optional flags (FLG)
0, 0, 0, 0, // modification time (MTIME)
0, // extra optional flags (XFL)
(byte) 0x3 // operating system (OS)
};
private int _encodingMode;
private boolean _isGzip;
private long _inputSize;
/**
* Writes gzip header to OutputStream upon construction.
* XXX: set operating system (file architecure) header.
*
* @param out
* @param def
*/
private ZlibOutputStream(OutputStream os, Deflater def)
throws IOException
{
_os = os;
_out = new DeflaterOutputStream(_os, def);
_os.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 ZlibOutputStream(OutputStream os,
int compressionLevel,
int strategy,
int encodingMode)
throws IOException
{
this(os, createDeflater(compressionLevel, strategy, encodingMode));
_isGzip = (encodingMode == ZlibModule.FORCE_GZIP);
if (_isGzip)
_crc32 = new CRC32();
}
/**
* @param os
* @param compressionLevel
* @param strategy Deflate compression strategy
*/
public ZlibOutputStream(OutputStream os, int compressionLevel, int strategy)
throws IOException
{
this(os, compressionLevel, strategy, ZlibModule.FORCE_GZIP);
}
public ZlibOutputStream(OutputStream os)
throws IOException
{
this(os,
Deflater.DEFAULT_COMPRESSION,
Deflater.DEFAULT_STRATEGY,
ZlibModule.FORCE_GZIP);
}
/**
* Creates a deflater based on the Zlib arguments.
*/
private static Deflater createDeflater(int compressionLevel,
int strategy,
int encodingMode)
{
Deflater def;
if (encodingMode == ZlibModule.FORCE_GZIP)
def = new Deflater(compressionLevel, true);
else
def = new Deflater(compressionLevel, false);
def.setStrategy(strategy);
return def;
}
/**
* Writes a byte.
*
* @param input
*/
public void write(int v)
throws IOException
{
_out.write(v);
_inputSize++;
if (_isGzip)
_crc32.update(v);
}
/**
* @param input
* @param offset
* @param length
*/
public void write(byte[] buffer, int offset, int length)
throws IOException
{
_out.write(buffer, offset, length);
_inputSize += length;
if (_isGzip)
_crc32.update(buffer, offset, length);
}
private void finish(DeflaterOutputStream out)
throws IOException
{
out.finish();
if (_isGzip) {
long crcValue = _crc32.getValue();
_os.write((byte) crcValue);
_os.write((byte) (crcValue >> 8));
_os.write((byte) (crcValue >> 16));
_os.write((byte) (crcValue >> 24));
}
_os.write((byte) _inputSize);
_os.write((byte) (_inputSize >> 8));
_os.write((byte) (_inputSize >> 16));
_os.write((byte) (_inputSize >> 24));
_os.flush();
}
public void flush()
{
}
public void closeWrite()
{
close();
}
public void close()
{
try {
DeflaterOutputStream out = _out;
_out = null;
if (out != null) {
finish(out);
out.close();
}
_os.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Returns false always for a write stream.
*/
public boolean isEOF()
{
return false;
}
/**
* Tells the position in the gzip stream
*/
public long getPosition()
{
return _inputSize;
}
/**
* Sets the position.
*/
public boolean setPosition(long offset)
{
if (offset < _inputSize)
return false;
offset -= _inputSize;
byte[] buffer = new byte[128];
try {
while (offset > 0) {
int sublen = (int)Math.min(offset, buffer.length);
write(buffer, 0, sublen);
offset -= sublen;
}
return true;
} catch (IOException e) {
throw new QuercusModuleException(e);
}
}
public String toString()
{
return "ZlibOutputStream[]";
}
}