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 * https://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
20 package org.apache.commons.compress.compressors.zstandard;
21
22 import java.io.IOException;
23 import java.io.InputStream;
24
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
29 import com.github.luben.zstd.BufferPool;
30 import com.github.luben.zstd.ZstdInputStream;
31
32 /**
33 * {@link CompressorInputStream} implementation to decode Zstandard encoded stream.
34 *
35 * <p>
36 * This class avoids making the underlying {@code zstd} classes part of the public or protected API. The underlying implementation is provided through the
37 * <a href="https://github.com/luben/zstd-jni/">Zstandard JNI</a> library which is based on <a href="https://github.com/facebook/zstd/">zstd</a>.
38 * </p>
39 *
40 * @see <a href="https://github.com/luben/zstd-jni/">Zstandard JNI</a>
41 * @see <a href="https://github.com/facebook/zstd/">zstd</a>
42 * @since 1.16
43 */
44 public class ZstdCompressorInputStream extends CompressorInputStream implements InputStreamStatistics {
45
46 private final BoundedInputStream countingStream;
47 private final ZstdInputStream decIS;
48
49 /**
50 * Constructs a new input stream that decompresses zstd-compressed data from the specific input stream.
51 *
52 * @param in the input stream of compressed data.
53 * @throws IOException if an I/O error occurs.
54 */
55 public ZstdCompressorInputStream(final InputStream in) throws IOException {
56 this.decIS = new ZstdInputStream(countingStream = BoundedInputStream.builder().setInputStream(in).get());
57 }
58
59 /**
60 * Constructs a new input stream that decompresses zstd-compressed data from the specific input stream.
61 *
62 * @param in the input stream of compressed data.
63 * @param bufferPool a configuration of zstd-jni that allows users to customize how buffers are recycled. Either a {@link com.github.luben.zstd.NoPool} or a
64 * {@link com.github.luben.zstd.RecyclingBufferPool} is allowed here.
65 * @throws IOException if an I/O error occurs.
66 */
67 public ZstdCompressorInputStream(final InputStream in, final BufferPool bufferPool) throws IOException {
68 this.decIS = new ZstdInputStream(countingStream = BoundedInputStream.builder().setInputStream(in).get(), bufferPool);
69 }
70
71 @Override
72 public int available() throws IOException {
73 return decIS.available();
74 }
75
76 @Override
77 public void close() throws IOException {
78 decIS.close();
79 }
80
81 /**
82 *
83 * {@inheritDoc}
84 *
85 * @since 1.17
86 */
87 @Override
88 public long getCompressedCount() {
89 return countingStream.getCount();
90 }
91
92 @Override
93 public synchronized void mark(final int readLimit) {
94 decIS.mark(readLimit);
95 }
96
97 @Override
98 public boolean markSupported() {
99 return decIS.markSupported();
100 }
101
102 @Override
103 public int read() throws IOException {
104 final int ret = decIS.read();
105 count(ret == -1 ? 0 : 1);
106 return ret;
107 }
108
109 @Override
110 public int read(final byte[] b) throws IOException {
111 return read(b, 0, b.length);
112 }
113
114 @Override
115 public int read(final byte[] buf, final int off, final int len) throws IOException {
116 if (len == 0) {
117 return 0;
118 }
119 final int ret = decIS.read(buf, off, len);
120 count(ret);
121 return ret;
122 }
123
124 @Override
125 public synchronized void reset() throws IOException {
126 decIS.reset();
127 }
128
129 @Override
130 public long skip(final long n) throws IOException {
131 return org.apache.commons.io.IOUtils.skip(decIS, n);
132 }
133
134 @Override
135 public String toString() {
136 return decIS.toString();
137 }
138 }