    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.lzma;
    020
    021import java.io.IOException;
    022import java.io.InputStream;
    023
    024import org.apache.commons.compress.MemoryLimitException;
    025import org.apache.commons.compress.compressors.CompressorInputStream;
    026import org.apache.commons.compress.utils.CountingInputStream;
    027import org.apache.commons.compress.utils.IOUtils;
    028import org.apache.commons.compress.utils.InputStreamStatistics;
    029import org.tukaani.xz.LZMAInputStream;
    030
    031/**
    032 * LZMA decompressor.
    033 * @since 1.6
    034 */
    035public class LZMACompressorInputStream extends CompressorInputStream
    036    implements InputStreamStatistics {
    037
    038    /**
    039     * Checks if the signature matches what is expected for an lzma file.
    040     *
    041     * @param signature
    042     *            the bytes to check
    043     * @param length
    044     *            the number of bytes to check
    045     * @return true, if this stream is an lzma  compressed stream, false otherwise
    046     *
    047     * @since 1.10
    048     */
    049    public static boolean matches(final byte[] signature, final int length) {
    050        return signature != null && length >= 3 &&
    051                signature[0] == 0x5d && signature[1] == 0 &&
    052                signature[2] == 0;
    053    }
    054    private final CountingInputStream countingStream;
    055
    056    private final InputStream in;
    057
    058    /**
    059     * Creates a new input stream that decompresses LZMA-compressed data
    060     * from the specified input stream.
    061     *
    062     * @param       inputStream where to read the compressed data
    063     *
    064     * @throws      IOException if the input is not in the .lzma format,
    065     *                          the input is corrupt or truncated, the .lzma
    066     *                          headers specify sizes that are not supported
    067     *                          by this implementation, or the underlying
    068     *                          {@code inputStream} throws an exception
    069     */
    070    public LZMACompressorInputStream(final InputStream inputStream)
    071            throws IOException {
    072        in = new LZMAInputStream(countingStream = new CountingInputStream(inputStream), -1);
    073    }
    074
    075    /**
    076     * Creates a new input stream that decompresses LZMA-compressed data
    077     * from the specified input stream.
    078     *
    079     * @param       inputStream where to read the compressed data
    080     *
    081     * @param       memoryLimitInKb calculated memory use threshold.  Throws MemoryLimitException
    082     *                            if calculate memory use is above this threshold
    083     *
    084     * @throws      IOException if the input is not in the .lzma format,
    085     *                          the input is corrupt or truncated, the .lzma
    086     *                          headers specify sizes that are not supported
    087     *                          by this implementation, or the underlying
    088     *                          {@code inputStream} throws an exception
    089     *
    090     * @since 1.14
    091     */
    092    public LZMACompressorInputStream(final InputStream inputStream, final int memoryLimitInKb)
    093            throws IOException {
    094        try {
    095            in = new LZMAInputStream(countingStream = new CountingInputStream(inputStream), memoryLimitInKb);
    096        } catch (final org.tukaani.xz.MemoryLimitException e) {
    097            //convert to commons-compress exception
    098            throw new MemoryLimitException(e.getMemoryNeeded(), e.getMemoryLimit(), e);
    099        }
    100    }
    101
    102    /** {@inheritDoc} */
    103    @Override
    104    public int available() throws IOException {
    105        return in.available();
    106    }
    107
    108    /** {@inheritDoc} */
    109    @Override
    110    public void close() throws IOException {
    111        in.close();
    112    }
    113
    114    /**
    115     * @since 1.17
    116     */
    117    @Override
    118    public long getCompressedCount() {
    119        return countingStream.getBytesRead();
    120    }
    121
    122    /** {@inheritDoc} */
    123    @Override
    124    public int read() throws IOException {
    125        final int ret = in.read();
    126        count(ret == -1 ? 0 : 1);
    127        return ret;
    128    }
    129
    130    /** {@inheritDoc} */
    131    @Override
    132    public int read(final byte[] buf, final int off, final int len) throws IOException {
    133        final int ret = in.read(buf, off, len);
    134        count(ret);
    135        return ret;
    136    }
    137
    138    /** {@inheritDoc} */
    139    @Override
    140    public long skip(final long n) throws IOException {
    141        return IOUtils.skip(in, n);
    142    }
    143}