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