freenet.support.compress.Bzip2Compressor.java Source code

Java tutorial

Introduction

Here is the source code for freenet.support.compress.Bzip2Compressor.java

Source

/* This code is part of Freenet. It is distributed under the GNU General
* Public License, version 2 (or at your option any later version). See
* http://www.gnu.org/ for further details of the GPL. */
package freenet.support.compress;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;

import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream;

import freenet.support.Logger;
import freenet.support.api.Bucket;
import freenet.support.api.BucketFactory;
import freenet.support.io.Closer;
import freenet.support.io.CountedOutputStream;
import freenet.support.io.HeaderStreams;

/**
** {@link Compressor} for BZip2 streams.
**
** Due to historical reasons (we used to use the ant-tools bz2 libraries,
** rather than commons-compress) the compressed streams **DO NOT** have the
** standard "BZ" header.
*/
public class Bzip2Compressor implements Compressor {

    final public static byte[] BZ_HEADER;
    static {
        try {
            BZ_HEADER = "BZ".getBytes("ISO-8859-1");
        } catch (UnsupportedEncodingException e) {
            throw new Error(e); // Impossible!
        }
    }

    @Override
    public Bucket compress(Bucket data, BucketFactory bf, long maxReadLength, long maxWriteLength)
            throws IOException, CompressionOutputSizeException {
        Bucket output = bf.makeBucket(maxWriteLength);
        InputStream is = null;
        OutputStream os = null;
        try {
            is = data.getInputStream();
            os = output.getOutputStream();
            compress(is, os, maxReadLength, maxWriteLength);
            // It is essential that the close()'s throw if there is any problem.
            is.close();
            is = null;
            os.close();
            os = null;
        } finally {
            Closer.close(is);
            Closer.close(os);
        }
        return output;
    }

    @Override
    public long compress(InputStream is, OutputStream os, long maxReadLength, long maxWriteLength)
            throws IOException, CompressionOutputSizeException {
        if (maxReadLength <= 0)
            throw new IllegalArgumentException();
        BZip2CompressorOutputStream bz2os = null;
        try {
            CountedOutputStream cos = new CountedOutputStream(os);
            bz2os = new BZip2CompressorOutputStream(HeaderStreams.dimOutput(BZ_HEADER, cos));
            long read = 0;
            // Bigger input buffer, so can compress all at once.
            // Won't hurt on I/O either, although most OSs will only return a page at a time.
            byte[] buffer = new byte[32768];
            while (true) {
                int l = (int) Math.min(buffer.length, maxReadLength - read);
                int x = l == 0 ? -1 : is.read(buffer, 0, buffer.length);
                if (x <= -1)
                    break;
                if (x == 0)
                    throw new IOException("Returned zero from read()");
                bz2os.write(buffer, 0, x);
                read += x;
                if (cos.written() > maxWriteLength)
                    throw new CompressionOutputSizeException();
            }
            bz2os.flush();
            cos.flush();
            bz2os.close();
            bz2os = null;
            if (cos.written() > maxWriteLength)
                throw new CompressionOutputSizeException();
            return cos.written();
        } finally {
            if (bz2os != null) {
                bz2os.flush();
                bz2os.close();
            }

        }
    }

    @Override
    public long decompress(InputStream is, OutputStream os, long maxLength, long maxCheckSizeBytes)
            throws IOException, CompressionOutputSizeException {
        BZip2CompressorInputStream bz2is = new BZip2CompressorInputStream(HeaderStreams.augInput(BZ_HEADER, is));
        long written = 0;
        int bufSize = 32768;
        if (maxLength > 0 && maxLength < bufSize)
            bufSize = (int) maxLength;
        byte[] buffer = new byte[bufSize];
        while (true) {
            int expectedBytesRead = (int) Math.min(buffer.length, maxLength - written);
            // We can over-read to determine whether we have over-read.
            // We enforce maximum size this way.
            // FIXME there is probably a better way to do this!
            int bytesRead = bz2is.read(buffer, 0, buffer.length);
            if (expectedBytesRead < bytesRead) {
                Logger.normal(this,
                        "expectedBytesRead=" + expectedBytesRead + ", bytesRead=" + bytesRead + ", written="
                                + written + ", maxLength=" + maxLength
                                + " throwing a CompressionOutputSizeException");
                if (maxCheckSizeBytes > 0) {
                    written += bytesRead;
                    while (true) {
                        expectedBytesRead = (int) Math.min(buffer.length, maxLength + maxCheckSizeBytes - written);
                        bytesRead = bz2is.read(buffer, 0, expectedBytesRead);
                        if (bytesRead <= -1)
                            throw new CompressionOutputSizeException(written);
                        if (bytesRead == 0)
                            throw new IOException("Returned zero from read()");
                        written += bytesRead;
                    }
                }
                throw new CompressionOutputSizeException();
            }
            if (bytesRead <= -1)
                return written;
            if (bytesRead == 0)
                throw new IOException("Returned zero from read()");
            os.write(buffer, 0, bytesRead);
            written += bytesRead;
        }
    }

    @Override
    public int decompress(byte[] dbuf, int i, int j, byte[] output) throws CompressionOutputSizeException {
        // Didn't work with Inflater.
        // FIXME fix sometimes to use Inflater - format issue?
        ByteArrayInputStream bais = new ByteArrayInputStream(dbuf, i, j);
        ByteArrayOutputStream baos = new ByteArrayOutputStream(output.length);
        int bytes = 0;
        try {
            decompress(bais, baos, output.length, -1);
            bytes = baos.size();
        } catch (IOException e) {
            // Impossible
            throw new Error("Got IOException: " + e.getMessage(), e);
        }
        byte[] buf = baos.toByteArray();
        System.arraycopy(buf, 0, output, 0, bytes);
        return bytes;
    }
}