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 static org.apache.commons.compress.utils.IOUtils.closeQuietly;
025
026/**
027 * Deflate64 decompressor.
028 *
029 * @since 1.16
030 * @NotThreadSafe
031 */
032public class Deflate64CompressorInputStream extends CompressorInputStream {
033    private InputStream originalStream;
034    private HuffmanDecoder decoder;
035    private final byte[] oneByte = new byte[1];
036
037    /**
038     * Constructs a Deflate64CompressorInputStream.
039     *
040     * @param in the stream to read from
041     */
042    public Deflate64CompressorInputStream(InputStream in) {
043        this(new HuffmanDecoder(in));
044        originalStream = in;
045    }
046
047    Deflate64CompressorInputStream(HuffmanDecoder decoder) {
048        this.decoder = decoder;
049    }
050
051    /**
052     * @throws java.io.EOFException if the underlying stream is exhausted before the end of defalted data was reached.
053     */
054    @Override
055    public int read() throws IOException {
056        while (true) {
057            int r = read(oneByte);
058            switch (r) {
059                case 1:
060                    return oneByte[0] & 0xFF;
061                case -1:
062                    return -1;
063                case 0:
064                    continue;
065                default:
066                    throw new IllegalStateException("Invalid return value from read: " + r);
067            }
068        }
069    }
070
071    /**
072     * @throws java.io.EOFException if the underlying stream is exhausted before the end of defalted data was reached.
073     */
074    @Override
075    public int read(byte[] b, int off, int len) throws IOException {
076        int read = -1;
077        if (decoder != null) {
078            read = decoder.decode(b, off, len);
079            count(read);
080            if (read == -1) {
081                closeDecoder();
082            }
083        }
084        return read;
085    }
086
087    @Override
088    public int available() throws IOException {
089        return decoder != null ? decoder.available() : 0;
090    }
091
092    @Override
093    public void close() throws IOException {
094        closeDecoder();
095        if (originalStream != null) {
096            originalStream.close();
097            originalStream = null;
098        }
099    }
100
101    private void closeDecoder() {
102        closeQuietly(decoder);
103        decoder = null;
104    }
105}