    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}