package org.biomart.common.utils; import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.zip.Deflater; /** * Output stream that compresses data. A compressed block is generated and * transmitted once a given number of bytes have been written, or when the flush * method is invoked. * * Copyright 2005 - Philip Isenhour - http://javatechniques.com/ * * This software is provided 'as-is', without any express or implied warranty. * In no event will the authors be held liable for any damages arising from the * use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not claim * that you wrote the original software. If you use this software in a product, * an acknowledgment in the product documentation would be appreciated but is * not required. * * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source distribution. * * $Id: CompressedBlockOutputStream.java,v 1.1 2007-11-13 11:40:04 rh4 Exp $ */ public class CompressedBlockOutputStream extends FilterOutputStream { /** * Buffer for input data */ private byte[] inBuf = null; /** * Buffer for compressed data to be written */ private byte[] outBuf = null; /** * Number of bytes in the buffer */ private int len = 0; /** * Deflater for compressing data */ private Deflater deflater = null; /** * Constructs a CompressedBlockOutputStream that writes to the given * underlying output stream 'os' and sends a compressed block once 'size' * byte have been written. The default compression strategy and level are * used. * * @param os * the outputstream to write to. * @param size * the buffer size to use. * @throws IOException * if anything went wrong. */ public CompressedBlockOutputStream(final OutputStream os, final int size) throws IOException { this(os, size, Deflater.DEFAULT_COMPRESSION, Deflater.DEFAULT_STRATEGY); } /** * Constructs a CompressedBlockOutputStream that writes to the given * underlying output stream 'os' and sends a compressed block once 'size' * byte have been written. The compression level and strategy should be * specified using the constants defined in {#link #Deflator}. * * @param os * the outputstream to write to. * @param size * the buffer size to use. * @param level * the compression level. * @param strategy * the compression strategy. * @throws IOException * if anything went wrong. */ public CompressedBlockOutputStream(final OutputStream os, final int size, final int level, final int strategy) throws IOException { super(os); this.inBuf = new byte[size]; this.outBuf = new byte[size + 64]; this.deflater = new Deflater(level); this.deflater.setStrategy(strategy); } /** * Compresses any existing data and sends it. * * @throws IOException * if anything went wrong. */ protected void compressAndSend() throws IOException { if (this.len > 0) { this.deflater.setInput(this.inBuf, 0, this.len); this.deflater.finish(); final int size = this.deflater.deflate(this.outBuf); // Write the size of the compressed data, followed // by the size of the uncompressed data this.out.write(size >> 24 & 0xFF); this.out.write(size >> 16 & 0xFF); this.out.write(size >> 8 & 0xFF); this.out.write(size >> 0 & 0xFF); this.out.write(this.len >> 24 & 0xFF); this.out.write(this.len >> 16 & 0xFF); this.out.write(this.len >> 8 & 0xFF); this.out.write(this.len >> 0 & 0xFF); this.out.write(this.outBuf, 0, size); this.out.flush(); this.len = 0; this.deflater.reset(); } } public void write(final int b) throws IOException { this.inBuf[this.len++] = (byte) b; if (this.len == this.inBuf.length) this.compressAndSend(); } public void write(final byte[] b, int boff, int blen) throws IOException { while (this.len + blen > this.inBuf.length) { final int toCopy = this.inBuf.length - this.len; System.arraycopy(b, boff, this.inBuf, this.len, toCopy); this.len += toCopy; this.compressAndSend(); boff += toCopy; blen -= toCopy; } System.arraycopy(b, boff, this.inBuf, this.len, blen); this.len += blen; } public void flush() throws IOException { this.compressAndSend(); this.out.flush(); } public void close() throws IOException { this.compressAndSend(); this.out.close(); } }