1d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden// Copyright 2015 Google Inc. All rights reserved. 2d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden// 3d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden// Licensed under the Apache License, Version 2.0 (the "License"); 4d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden// you may not use this file except in compliance with the License. 5d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden// You may obtain a copy of the License at 6d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden// 7d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden// http://www.apache.org/licenses/LICENSE-2.0 8d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden// 9d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden// Unless required by applicable law or agreed to in writing, software 10d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden// distributed under the License is distributed on an "AS IS" BASIS, 11d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden// See the License for the specific language governing permissions and 13d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden// limitations under the License. 14d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 15d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Haydenpackage com.google.archivepatcher.shared; 16d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 17d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Haydenimport java.io.IOException; 18d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Haydenimport java.io.InputStream; 19d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Haydenimport java.io.OutputStream; 20d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Haydenimport java.util.zip.Inflater; 21d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Haydenimport java.util.zip.InflaterInputStream; 22d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 23d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden/** 24d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * Implementation of {@link Uncompressor} based on Java's built-in {@link Inflater}. Uses no-wrap by 25d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * default along with a 32k read buffer and a 32k write buffer. Buffers are allocated on-demand and 26d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * discarded after use. {@link Inflater} instances, which may be expensive, are also created 27d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * on-demand; This can be changed by using {@link #setCaching(boolean)}. 28d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 29d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Haydenpublic class DeflateUncompressor implements Uncompressor { 30d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden /** 31d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * Whether to skip the standard zlib header and checksum fields when 32d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * reading. Defaults to true. 33d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 34d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden private boolean nowrap = true; 35d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 36d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden /** 37d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * The size of the buffer used for reading data in during 38d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * {@link #uncompress(InputStream, OutputStream)}. 39d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 40d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden private int inputBufferSize = 32768; 41d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 42d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden /** 43d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * The size of the buffer used for writing data out during 44d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * {@link #uncompress(InputStream, OutputStream)}. 45d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 46d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden private int outputBufferSize = 32768; 47d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 48d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden /** 49d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * Cached {@link Inflater} to be used. 50d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 51d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden private Inflater inflater = null; 52d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 53d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden /** 54d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * Whether or not to cache {@link Inflater} instances, which is a major performance tradeoff. 55d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 56d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden private boolean caching = false; 57d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 58d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden /** 59d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * Returns whether to skip the standard zlib header and checksum fields when reading. 60d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * @return the value 61d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * @see Inflater#Inflater(boolean) 62d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 63d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden public boolean isNowrap() { 64d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden return nowrap; 65d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden } 66d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 67d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden /** 68d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * Returns the size of the buffer used for reading from the input stream in 69d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * {@link #uncompress(InputStream, OutputStream)}. 70d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * @return the size (default is 32768) 71d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 72d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden public int getInputBufferSize() { 73d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden return inputBufferSize; 74d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden } 75d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 76d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden /** 77d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * Sets the size of the buffer used for reading from the input stream in 78d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * {@link #uncompress(InputStream, OutputStream)}. 79d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * NB: {@link Inflater} uses an <em>internal</em> buffer and this method adjusts the size of that 80d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * buffer. This buffer is important for performance, <em>even if the {@link InputStream} is 81d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * is already buffered</em>. 82d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * @param inputBufferSize the size to set (default is 32768) 83d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 84d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden public void setInputBufferSize(int inputBufferSize) { 85d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden this.inputBufferSize = inputBufferSize; 86d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden } 87d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 88d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden /** 89d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * Returns the size of the buffer used for writing to the output stream in 90d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * {@link #uncompress(InputStream, OutputStream)}. 91d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * @return the size (default is 32768) 92d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 93d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden public int getOutputBufferSize() { 94d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden return outputBufferSize; 95d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden } 96d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 97d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden /** 98d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * Sets the size of the buffer used for writing to the output stream in 99d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * {@link #uncompress(InputStream, OutputStream)}. 100d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * @param outputBufferSize the size to set (default is 32768) 101d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 102d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden public void setOutputBufferSize(int outputBufferSize) { 103d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden this.outputBufferSize = outputBufferSize; 104d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden } 105d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 106d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden /** 107d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * Sets whether or not to suppress wrapping the deflate output with the standard zlib header and 108d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * checksum fields. Defaults to false. 109d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * @param nowrap see {@link Inflater#Inflater(boolean)} 110d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 111d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden public void setNowrap(boolean nowrap) { 112d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden if (nowrap != this.nowrap) { 113d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden release(); // Cannot re-use the inflater any more. 114d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden this.nowrap = nowrap; 115d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden } 116d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden } 117d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 118d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden /** 119d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * Returns if caching is enabled. 120d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * @return true if enabled, otherwise false 121d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * @see #setCaching(boolean) 122d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 123d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden public boolean isCaching() { 124d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden return caching; 125d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden } 126d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 127d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden /** 128d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * Sets whether or not to cache the {@link Inflater} instance. Defaults to false. If set to true, 129d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * the {@link Inflater} is kept until this object is finalized or until {@link #release()} is 130d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * called. Instances of {@link Inflater} can be surprisingly expensive, so caching is advised in 131d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * situations where many resources need to be inflated. 132d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * @param caching whether to enable caching 133d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 134d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden public void setCaching(boolean caching) { 135d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden this.caching = caching; 136d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden } 137d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 138d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden /** 139d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * Returns the {@link Inflater} to be used, creating a new one if necessary and caching it for 140d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * future use. 141d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * @return the inflater 142d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 143d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden protected Inflater createOrResetInflater() { 144d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden Inflater result = inflater; 145d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden if (result == null) { 146d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden result = new Inflater(nowrap); 147d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden if (caching) { 148d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden inflater = result; 149d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden } 150d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden } else { 151d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden result.reset(); 152d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden } 153d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden return result; 154d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden } 155d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 156d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden /** 157d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * Immediately releases any cached {@link Inflater} instance. 158d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 159d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden public void release() { 160d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden if (inflater != null) { 161d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden inflater.end(); 162d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden inflater = null; 163d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden } 164d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden } 165d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 166d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden @Override 167d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden public void uncompress(InputStream compressedIn, OutputStream uncompressedOut) 168d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden throws IOException { 169d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden InflaterInputStream inflaterIn = 170d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden new InflaterInputStream(compressedIn, createOrResetInflater(), inputBufferSize); 171d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden byte[] buffer = new byte[outputBufferSize]; 172d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden int numRead = 0; 173d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden while ((numRead = inflaterIn.read(buffer)) >= 0) { 174d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden uncompressedOut.write(buffer, 0, numRead); 175d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden } 176d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden if (!isCaching()) { 177d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden release(); 178d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden } 179d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden } 180d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden} 181