001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one or more
003 *  contributor license agreements.  See the NOTICE file distributed with
004 *  this work for additional information regarding copyright ownership.
005 *  The ASF licenses this file to You under the Apache License, Version 2.0
006 *  (the "License"); you may not use this file except in compliance with
007 *  the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 *
017 */
018package org.apache.commons.compress.compressors.deflate64;
019
020import static org.apache.commons.compress.utils.IOUtils.closeQuietly;
021
022import java.io.IOException;
023import java.io.InputStream;
024
025import org.apache.commons.compress.compressors.CompressorInputStream;
026import org.apache.commons.compress.utils.InputStreamStatistics;
027
028/**
029 * Deflate64 decompressor.
030 *
031 * @since 1.16
032 * @NotThreadSafe
033 */
034public class Deflate64CompressorInputStream extends CompressorInputStream implements InputStreamStatistics {
035    private InputStream originalStream;
036    private HuffmanDecoder decoder;
037    private long compressedBytesRead;
038    private final byte[] oneByte = new byte[1];
039
040    Deflate64CompressorInputStream(final HuffmanDecoder decoder) {
041        this.decoder = decoder;
042    }
043
044    /**
045     * Constructs a Deflate64CompressorInputStream.
046     *
047     * @param in the stream to read from
048     */
049    public Deflate64CompressorInputStream(final InputStream in) {
050        this(new HuffmanDecoder(in));
051        originalStream = in;
052    }
053
054    @Override
055    public int available() throws IOException {
056        return decoder != null ? decoder.available() : 0;
057    }
058
059    @Override
060    public void close() throws IOException {
061        try {
062            closeDecoder();
063        } finally {
064            if (originalStream != null) {
065                originalStream.close();
066                originalStream = null;
067            }
068        }
069    }
070
071    private void closeDecoder() {
072        closeQuietly(decoder);
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}