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