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 static org.apache.commons.compress.utils.IOUtils.closeQuietly; 021 022import java.io.IOException; 023import java.io.InputStream; 024 025import org.apache.commons.compress.compressors.CompressorInputStream; 026import org.apache.commons.compress.utils.InputStreamStatistics; 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 Deflate64CompressorInputStream(final HuffmanDecoder decoder) { 041 this.decoder = decoder; 042 } 043 044 /** 045 * Constructs a Deflate64CompressorInputStream. 046 * 047 * @param in the stream to read from 048 */ 049 public Deflate64CompressorInputStream(final InputStream in) { 050 this(new HuffmanDecoder(in)); 051 originalStream = in; 052 } 053 054 @Override 055 public int available() throws IOException { 056 return decoder != null ? decoder.available() : 0; 057 } 058 059 @Override 060 public void close() throws IOException { 061 try { 062 closeDecoder(); 063 } finally { 064 if (originalStream != null) { 065 originalStream.close(); 066 originalStream = null; 067 } 068 } 069 } 070 071 private void closeDecoder() { 072 closeQuietly(decoder); 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}