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