org.glowroot.shaded.ning.compress.gzip.OptimizedGZIPOutputStream Maven / Gradle / Ivy
The newest version!
package org.glowroot.shaded.ning.compress.gzip;
import java.io.*;
import java.util.zip.CRC32;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
/**
* Optimized variant of {@link java.util.zip.GZIPOutputStream} that
* reuses underlying {@link java.util.zip.Deflater} instance}.
*/
public class OptimizedGZIPOutputStream
extends OutputStream
{
/**
* GZIP header magic number; written out LSB like most everything
* else (i.e. as 0x1f 0x8b)
*/
private final static int GZIP_MAGIC = 0x8b1f;
/**
* For now, static header seems fine, since JDK default gzip writer
* does it too:
*/
final static byte[] DEFAULT_HEADER = new byte[] {
(byte) GZIP_MAGIC, // Magic number (short)
(byte)(GZIP_MAGIC >> 8), // Magic number (short)
Deflater.DEFLATED, // Compression method (CM)
0, // Flags (FLG)
0, // Modification time MTIME (int)
0, // Modification time MTIME (int)
0, // Modification time MTIME (int)
0, // Modification time MTIME (int)
0, // Extra flags (XFLG)
(byte) 0xff // Operating system (OS), UNKNOWN
};
/*
///////////////////////////////////////////////////////////////////////
// Helper objects
///////////////////////////////////////////////////////////////////////
*/
protected Deflater _deflater;
protected final GZIPRecycler _gzipRecycler;
protected final byte[] _eightByteBuffer = new byte[8];
/*
///////////////////////////////////////////////////////////////////////
// State
///////////////////////////////////////////////////////////////////////
*/
/**
* Underlying output stream that header, compressed content and
* footer go to
*/
protected OutputStream _rawOut;
// TODO: write this out, not strictly needed...
protected DeflaterOutputStream _deflaterOut;
protected CRC32 _crc;
/*
///////////////////////////////////////////////////////////////////////
// Construction
///////////////////////////////////////////////////////////////////////
*/
public OptimizedGZIPOutputStream(OutputStream out) throws IOException
{
super();
_gzipRecycler = GZIPRecycler.instance();
_rawOut = out;
// write header:
_rawOut.write(DEFAULT_HEADER);
_deflater = _gzipRecycler.allocDeflater();
_deflaterOut = new DeflaterOutputStream(_rawOut, _deflater, 4000);
_crc = new CRC32();
}
/*
///////////////////////////////////////////////////////////////////////
// OutputStream implementation
///////////////////////////////////////////////////////////////////////
*/
@Override
public void close() throws IOException
{
_deflaterOut.finish();
_deflaterOut = null;
_writeTrailer(_rawOut);
_rawOut.close();
Deflater d = _deflater;
if (d != null) {
_deflater = null;
_gzipRecycler.releaseDeflater(d);
}
}
@Override
public void flush() throws IOException {
_deflaterOut.flush();
}
@Override
public final void write(byte[] buf) throws IOException {
write(buf, 0, buf.length);
}
@Override
public final void write(int c) throws IOException {
_eightByteBuffer[0] = (byte) c;
write(_eightByteBuffer, 0, 1);
}
@Override
public void write(byte[] buf, int off, int len) throws IOException {
_deflaterOut.write(buf, off, len);
_crc.update(buf, off, len);
}
/*
///////////////////////////////////////////////////////////////////////
// Internal methods
///////////////////////////////////////////////////////////////////////
*/
private void _writeTrailer(OutputStream out) throws IOException
{
_putInt(_eightByteBuffer, 0, (int) _crc.getValue());
_putInt(_eightByteBuffer, 4, _deflater.getTotalIn());
out.write(_eightByteBuffer, 0, 8);
}
/**
* Stupid GZIP, writes stuff in wrong order (not network, but x86)
*/
private final static void _putInt(byte[] buf, int offset, int value)
{
buf[offset++] = (byte) (value);
buf[offset++] = (byte) (value >> 8);
buf[offset++] = (byte) (value >> 16);
buf[offset] = (byte) (value >> 24);
}
}