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}