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 *   https://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 */
019
020package org.apache.commons.compress.compressors.brotli;
021
022import java.io.IOException;
023import java.io.InputStream;
024
025import org.apache.commons.compress.compressors.CompressorInputStream;
026import org.apache.commons.compress.utils.InputStreamStatistics;
027import org.apache.commons.io.IOUtils;
028import org.apache.commons.io.input.BoundedInputStream;
029import org.brotli.dec.BrotliInputStream;
030
031/**
032 * {@link CompressorInputStream} implementation to decode Brotli encoded stream. Library relies on <a href="https://github.com/google/brotli">Google brotli</a>
033 *
034 * @since 1.14
035 */
036public class BrotliCompressorInputStream extends CompressorInputStream implements InputStreamStatistics {
037
038    private final BoundedInputStream countingInputStream;
039    private final BrotliInputStream brotliInputStream;
040
041    /**
042     * Construct a new instance.
043     *
044     * @param inputStream underlying data source.
045     * @throws IOException in case of corrupted data or source stream problems.
046     */
047    public BrotliCompressorInputStream(final InputStream inputStream) throws IOException {
048        brotliInputStream = new BrotliInputStream(countingInputStream = BoundedInputStream.builder().setInputStream(inputStream).get());
049    }
050
051    @Override
052    public int available() throws IOException {
053        return brotliInputStream.available();
054    }
055
056    @Override
057    public void close() throws IOException {
058        brotliInputStream.close();
059    }
060
061    /**
062     * @since 1.17
063     */
064    @Override
065    public long getCompressedCount() {
066        return countingInputStream.getCount();
067    }
068
069    @Override
070    public synchronized void mark(final int readLimit) {
071        brotliInputStream.mark(readLimit);
072    }
073
074    @Override
075    public boolean markSupported() {
076        return brotliInputStream.markSupported();
077    }
078
079    @Override
080    public int read() throws IOException {
081        final int ret = brotliInputStream.read();
082        count(ret == -1 ? 0 : 1);
083        return ret;
084    }
085
086    @Override
087    public int read(final byte[] b) throws IOException {
088        return brotliInputStream.read(b);
089    }
090
091    @Override
092    public int read(final byte[] buf, final int off, final int len) throws IOException {
093        final int ret = brotliInputStream.read(buf, off, len);
094        count(ret);
095        return ret;
096    }
097
098    @Override
099    public synchronized void reset() throws IOException {
100        brotliInputStream.reset();
101    }
102
103    @Override
104    public long skip(final long n) throws IOException {
105        return IOUtils.skip(brotliInputStream, n);
106    }
107
108    @Override
109    public String toString() {
110        return brotliInputStream.toString();
111    }
112}