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