    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one
    003 * or more contributor license agreements.  See the NOTICE file
    004 * distributed with this work for additional information
    005 * regarding copyright ownership.  The ASF licenses this file
    006 * to you under the Apache License, Version 2.0 (the
    007 * "License"); you may not use this file except in compliance
    008 * with the License.  You may obtain a copy of the License at
    009 *
    010 * http://www.apache.org/licenses/LICENSE-2.0
    011 *
    012 * Unless required by applicable law or agreed to in writing,
    013 * software distributed under the License is distributed on an
    014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015 * KIND, either express or implied.  See the License for the
    016 * specific language governing permissions and limitations
    017 * under the License.
    018 */
    019package org.apache.commons.compress.compressors.deflate;
    020
    021import java.io.IOException;
    022import java.io.InputStream;
    023import java.util.zip.Inflater;
    024import java.util.zip.InflaterInputStream;
    025
    026import org.apache.commons.compress.compressors.CompressorInputStream;
    027import org.apache.commons.compress.utils.CountingInputStream;
    028import org.apache.commons.compress.utils.IOUtils;
    029import org.apache.commons.compress.utils.InputStreamStatistics;
    030
    031/**
    032 * Deflate decompressor.
    033 * @since 1.9
    034 */
    035public class DeflateCompressorInputStream extends CompressorInputStream
    036    implements InputStreamStatistics {
    037
    038    private static final int MAGIC_1 = 0x78;
    039    private static final int MAGIC_2a = 0x01;
    040    private static final int MAGIC_2b = 0x5e;
    041    private static final int MAGIC_2c = 0x9c;
    042    private static final int MAGIC_2d = 0xda;
    043
    044    /**
    045     * Checks if the signature matches what is expected for a zlib / deflated file
    046     *  with the zlib header.
    047     *
    048     * @param signature
    049     *            the bytes to check
    050     * @param length
    051     *            the number of bytes to check
    052     * @return true, if this stream is zlib / deflate compressed with a header
    053     * stream, false otherwise
    054     *
    055     * @since 1.10
    056     */
    057    public static boolean matches(final byte[] signature, final int length) {
    058        return length > 3 && signature[0] == MAGIC_1 && (
    059                signature[1] == (byte) MAGIC_2a ||
    060                signature[1] == (byte) MAGIC_2b ||
    061                signature[1] == (byte) MAGIC_2c ||
    062                signature[1] == (byte) MAGIC_2d);
    063    }
    064    private final CountingInputStream countingStream;
    065    private final InputStream in;
    066
    067    private final Inflater inflater;
    068
    069    /**
    070     * Creates a new input stream that decompresses Deflate-compressed data
    071     * from the specified input stream.
    072     *
    073     * @param       inputStream where to read the compressed data
    074     *
    075     */
    076    public DeflateCompressorInputStream(final InputStream inputStream) {
    077        this(inputStream, new DeflateParameters());
    078    }
    079
    080    /**
    081     * Creates a new input stream that decompresses Deflate-compressed data
    082     * from the specified input stream.
    083     *
    084     * @param       inputStream where to read the compressed data
    085     * @param       parameters parameters
    086     */
    087    public DeflateCompressorInputStream(final InputStream inputStream,
    088                                        final DeflateParameters parameters) {
    089        inflater = new Inflater(!parameters.withZlibHeader());
    090        in = new InflaterInputStream(countingStream = new CountingInputStream(inputStream), inflater);
    091    }
    092
    093    /** {@inheritDoc} */
    094    @Override
    095    public int available() throws IOException {
    096        return in.available();
    097    }
    098
    099    /** {@inheritDoc} */
    100    @Override
    101    public void close() throws IOException {
    102        try {
    103            in.close();
    104        } finally {
    105            inflater.end();
    106        }
    107    }
    108
    109    /**
    110     * @since 1.17
    111     */
    112    @Override
    113    public long getCompressedCount() {
    114        return countingStream.getBytesRead();
    115    }
    116
    117    /** {@inheritDoc} */
    118    @Override
    119    public int read() throws IOException {
    120        final int ret = in.read();
    121        count(ret == -1 ? 0 : 1);
    122        return ret;
    123    }
    124
    125    /** {@inheritDoc} */
    126    @Override
    127    public int read(final byte[] buf, final int off, final int len) throws IOException {
    128        if (len == 0) {
    129            return 0;
    130        }
    131        final int ret = in.read(buf, off, len);
    132        count(ret);
    133        return ret;
    134    }
    135
    136    /** {@inheritDoc} */
    137    @Override
    138    public long skip(final long n) throws IOException {
    139        return IOUtils.skip(in, n);
    140    }
    141}