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 */
019package org.apache.commons.compress.compressors.deflate64;
020
021import java.io.Closeable;
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;
028
029/**
030 * Deflate64 decompressor.
031 *
032 * @since 1.16
033 * @NotThreadSafe
034 */
035public class Deflate64CompressorInputStream extends CompressorInputStream implements InputStreamStatistics {
036    private InputStream originalStream;
037    private HuffmanDecoder decoder;
038    private long compressedBytesRead;
039    private final byte[] oneByte = new byte[1];
040
041    Deflate64CompressorInputStream(final HuffmanDecoder decoder) {
042        this.decoder = decoder;
043    }
044
045    /**
046     * Constructs a Deflate64CompressorInputStream.
047     *
048     * @param in the stream to read from
049     */
050    public Deflate64CompressorInputStream(final InputStream in) {
051        this(new HuffmanDecoder(in));
052        originalStream = in;
053    }
054
055    @Override
056    public int available() throws IOException {
057        return decoder != null ? decoder.available() : 0;
058    }
059
060    @Override
061    public void close() throws IOException {
062        try {
063            closeDecoder();
064        } finally {
065            IOUtils.close(originalStream);
066            originalStream = null;
067        }
068    }
069
070    private void closeDecoder() {
071        final Closeable c = decoder;
072        IOUtils.closeQuietly(c);
073        decoder = null;
074    }
075
076    /**
077     * @since 1.17
078     */
079    @Override
080    public long getCompressedCount() {
081        return compressedBytesRead;
082    }
083
084    /**
085     * @throws java.io.EOFException if the underlying stream is exhausted before the end of deflated data was reached.
086     */
087    @Override
088    public int read() throws IOException {
089        while (true) {
090            final int r = read(oneByte);
091            switch (r) {
092            case 1:
093                return oneByte[0] & 0xFF;
094            case -1:
095                return -1;
096            case 0:
097                continue;
098            default:
099                throw new IllegalStateException("Invalid return value from read: " + r);
100            }
101        }
102    }
103
104    /**
105     * @throws java.io.EOFException if the underlying stream is exhausted before the end of deflated data was reached.
106     */
107    @Override
108    public int read(final byte[] b, final int off, final int len) throws IOException {
109        if (len == 0) {
110            return 0;
111        }
112        int read = -1;
113        if (decoder != null) {
114            try {
115                read = decoder.decode(b, off, len);
116            } catch (final RuntimeException ex) {
117                throw new IOException("Invalid Deflate64 input", ex);
118            }
119            compressedBytesRead = decoder.getBytesRead();
120            count(read);
121            if (read == -1) {
122                closeDecoder();
123            }
124        }
125        return read;
126    }
127}