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}