package org.biomart.common.utils; import java.io.EOFException; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.util.zip.DataFormatException; import java.util.zip.Inflater; /** * Input stream that decompresses data. * * 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: CompressedBlockInputStream.java,v 1.1 2007-11-13 11:40:04 rh4 Exp $ */ public class CompressedBlockInputStream extends FilterInputStream { /** * Buffer of compressed data read from the stream */ private byte[] inBuf = null; /** * Length of data in the input data */ private int inLength = 0; /** * Buffer of uncompressed data */ private byte[] outBuf = null; /** * Offset and length of uncompressed data */ private int outOffs = 0; private int outLength = 0; /** * Inflater for decompressing */ private Inflater inflater = null; /** * Wrap an input stream and decompress the data from it. * * @param is * the input stream. * @throws IOException * if anything went wrong. */ public CompressedBlockInputStream(final InputStream is) throws IOException { super(is); this.inflater = new Inflater(); } private void readAndDecompress() throws IOException { // Read the length of the compressed block int ch1 = this.in.read(); int ch2 = this.in.read(); int ch3 = this.in.read(); int ch4 = this.in.read(); if ((ch1 | ch2 | ch3 | ch4) < 0) throw new EOFException(); this.inLength = (ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0); ch1 = this.in.read(); ch2 = this.in.read(); ch3 = this.in.read(); ch4 = this.in.read(); if ((ch1 | ch2 | ch3 | ch4) < 0) throw new EOFException(); this.outLength = (ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0); // Make sure we've got enough space to read the block if (this.inBuf == null || this.inLength > this.inBuf.length) this.inBuf = new byte[this.inLength]; if (this.outBuf == null || this.outLength > this.outBuf.length) this.outBuf = new byte[this.outLength]; // Read until we're got the entire compressed buffer. // read(...) will not necessarily block until all // requested data has been read, so we loop until // we're done. int inOffs = 0; while (inOffs < this.inLength) { final int inCount = this.in.read(this.inBuf, inOffs, this.inLength - inOffs); if (inCount == -1) throw new EOFException(); inOffs += inCount; } this.inflater.setInput(this.inBuf, 0, this.inLength); try { this.inflater.inflate(this.outBuf); } catch (final DataFormatException dfe) { throw new IOException("Data format exception - " + dfe.getMessage()); } // Reset the inflator so we can re-use it for the // next block this.inflater.reset(); this.outOffs = 0; } public int read() throws IOException { if (this.outOffs >= this.outLength) try { this.readAndDecompress(); } catch (final EOFException eof) { return -1; } return this.outBuf[this.outOffs++] & 0xff; } public int read(final byte[] b, final int off, final int len) throws IOException { int count = 0; while (count < len) { if (this.outOffs >= this.outLength) try { // If we've read at least one decompressed // byte and further decompression would // require blocking, return the count. if (count > 0 && this.in.available() == 0) return count; else this.readAndDecompress(); } catch (final EOFException eof) { if (count == 0) count = -1; return count; } final int toCopy = Math.min(this.outLength - this.outOffs, len - count); System.arraycopy(this.outBuf, this.outOffs, b, off + count, toCopy); this.outOffs += toCopy; count += toCopy; } return count; } public int available() throws IOException { // This isn't precise, but should be an adequate // lower bound on the actual amount of available data return this.outLength - this.outOffs + this.in.available(); } }