    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 static org.apache.commons.compress.utils.IOUtils.closeQuietly;
    020
    021import java.io.IOException;
    022import java.io.InputStream;
    023
    024import org.apache.commons.compress.compressors.CompressorInputStream;
    025import org.apache.commons.compress.utils.InputStreamStatistics;
    026
    027/**
    028 * Deflate64 decompressor.
    029 *
    030 * @since 1.16
    031 * @NotThreadSafe
    032 */
    033public class Deflate64CompressorInputStream extends CompressorInputStream implements InputStreamStatistics {
    034    private InputStream originalStream;
    035    private HuffmanDecoder decoder;
    036    private long compressedBytesRead;
    037    private final byte[] oneByte = new byte[1];
    038
    039    Deflate64CompressorInputStream(final HuffmanDecoder decoder) {
    040        this.decoder = decoder;
    041    }
    042
    043    /**
    044     * Constructs a Deflate64CompressorInputStream.
    045     *
    046     * @param in the stream to read from
    047     */
    048    public Deflate64CompressorInputStream(final InputStream in) {
    049        this(new HuffmanDecoder(in));
    050        originalStream = in;
    051    }
    052
    053    @Override
    054    public int available() throws IOException {
    055        return decoder != null ? decoder.available() : 0;
    056    }
    057
    058    @Override
    059    public void close() throws IOException {
    060        try {
    061            closeDecoder();
    062        } finally {
    063            if (originalStream != null) {
    064                originalStream.close();
    065                originalStream = null;
    066            }
    067        }
    068    }
    069
    070    private void closeDecoder() {
    071        closeQuietly(decoder);
    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}