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