View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.commons.compress.compressors.lzma;
20  
21  import java.io.IOException;
22  import java.io.InputStream;
23  
24  import org.apache.commons.compress.MemoryLimitException;
25  import org.apache.commons.compress.compressors.CompressorInputStream;
26  import org.apache.commons.compress.utils.InputStreamStatistics;
27  import org.apache.commons.io.input.BoundedInputStream;
28  import org.tukaani.xz.LZMAInputStream;
29  
30  /**
31   * LZMA decompressor.
32   *
33   * @since 1.6
34   */
35  public class LZMACompressorInputStream extends CompressorInputStream implements InputStreamStatistics {
36  
37      /**
38       * Checks if the signature matches what is expected for an LZMA file.
39       *
40       * @param signature the bytes to check
41       * @param length    the number of bytes to check
42       * @return true, if this stream is an LZMA compressed stream, false otherwise
43       *
44       * @since 1.10
45       */
46      public static boolean matches(final byte[] signature, final int length) {
47          return signature != null && length >= 3 && signature[0] == 0x5d && signature[1] == 0 && signature[2] == 0;
48      }
49  
50      private final BoundedInputStream countingStream;
51  
52      private final InputStream in;
53  
54      /**
55       * Creates a new input stream that decompresses LZMA-compressed data from the specified input stream.
56       *
57       * @param inputStream where to read the compressed data
58       *
59       * @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
60       *                     this implementation, or the underlying {@code inputStream} throws an exception
61       */
62      public LZMACompressorInputStream(final InputStream inputStream) throws IOException {
63          in = new LZMAInputStream(countingStream = BoundedInputStream.builder().setInputStream(inputStream).get(), -1);
64      }
65  
66      /**
67       * Creates a new input stream that decompresses LZMA-compressed data from the specified input stream.
68       *
69       * @param inputStream     where to read the compressed data
70       *
71       * @param memoryLimitInKb calculated memory use threshold. Throws MemoryLimitException if calculate memory use is above this threshold
72       *
73       * @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
74       *                     this implementation, or the underlying {@code inputStream} throws an exception
75       *
76       * @since 1.14
77       */
78      public LZMACompressorInputStream(final InputStream inputStream, final int memoryLimitInKb) throws IOException {
79          try {
80              in = new LZMAInputStream(countingStream = BoundedInputStream.builder().setInputStream(inputStream).get(), memoryLimitInKb);
81          } catch (final org.tukaani.xz.MemoryLimitException e) {
82              // convert to commons-compress exception
83              throw new MemoryLimitException(e.getMemoryNeeded(), e.getMemoryLimit(), e);
84          }
85      }
86  
87      /** {@inheritDoc} */
88      @Override
89      public int available() throws IOException {
90          return in.available();
91      }
92  
93      /** {@inheritDoc} */
94      @Override
95      public void close() throws IOException {
96          in.close();
97      }
98  
99      /**
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 }