1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package java.util.zip; 19 20import dalvik.system.CloseGuard; 21import java.io.FileDescriptor; 22import java.util.Arrays; 23 24/** 25 * This class decompresses data that was compressed using the <i>DEFLATE</i> 26 * algorithm (see <a href="http://www.gzip.org/algorithm.txt">specification</a>). 27 * 28 * <p>It is usually more convenient to use {@link InflaterInputStream}. 29 * 30 * <p>To decompress an in-memory {@code byte[]} to another in-memory {@code byte[]} manually: 31 * <pre> 32 * byte[] compressedBytes = ... 33 * int decompressedByteCount = ... // From your format's metadata. 34 * Inflater inflater = new Inflater(); 35 * inflater.setInput(compressedBytes, 0, compressedBytes.length); 36 * byte[] decompressedBytes = new byte[decompressedByteCount]; 37 * if (inflater.inflate(decompressedBytes) != decompressedByteCount) { 38 * throw new AssertionError(); 39 * } 40 * inflater.end(); 41 * </pre> 42 * <p>In situations where you don't have all the input in one array (or have so much 43 * input that you want to feed it to the inflater in chunks), it's possible to call 44 * {@link #setInput} repeatedly, but you're much better off using {@link InflaterInputStream} 45 * to handle all this for you. 46 * 47 * <p>If you don't know how big the decompressed data will be, you can call {@link #inflate} 48 * repeatedly on a temporary buffer, copying the bytes to a {@link java.io.ByteArrayOutputStream}, 49 * but this is probably another sign you'd be better off using {@link InflaterInputStream}. 50 */ 51public class Inflater { 52 53 private int inLength; 54 55 private int inRead; // Set by inflateImpl. 56 private boolean finished; // Set by inflateImpl. 57 private boolean needsDictionary; // Set by inflateImpl. 58 59 private long streamHandle = -1; 60 61 private final CloseGuard guard = CloseGuard.get(); 62 63 /** 64 * This constructor creates an inflater that expects a header from the input 65 * stream. Use {@link #Inflater(boolean)} if the input comes without a ZLIB 66 * header. 67 */ 68 public Inflater() { 69 this(false); 70 } 71 72 /** 73 * This constructor allows to create an inflater that expects no header from 74 * the input stream. 75 * 76 * @param noHeader 77 * {@code true} indicates that no ZLIB header comes with the 78 * input. 79 */ 80 public Inflater(boolean noHeader) { 81 streamHandle = createStream(noHeader); 82 guard.open("end"); 83 } 84 85 private native long createStream(boolean noHeader1); 86 87 /** 88 * Releases resources associated with this {@code Inflater}. Any unused 89 * input or output is discarded. This method should be called explicitly in 90 * order to free native resources as soon as possible. After {@code end()} is 91 * called, other methods will typically throw {@code IllegalStateException}. 92 */ 93 public synchronized void end() { 94 guard.close(); 95 if (streamHandle != -1) { 96 endImpl(streamHandle); 97 inRead = 0; 98 inLength = 0; 99 streamHandle = -1; 100 } 101 } 102 103 private native void endImpl(long handle); 104 105 @Override protected void finalize() { 106 try { 107 if (guard != null) { 108 guard.warnIfOpen(); 109 } 110 end(); 111 } finally { 112 try { 113 super.finalize(); 114 } catch (Throwable t) { 115 throw new AssertionError(t); 116 } 117 } 118 } 119 120 /** 121 * Indicates if the {@code Inflater} has inflated the entire deflated 122 * stream. If deflated bytes remain and {@link #needsInput} returns {@code 123 * true} this method will return {@code false}. This method should be 124 * called after all deflated input is supplied to the {@code Inflater}. 125 * 126 * @return {@code true} if all input has been inflated, {@code false} 127 * otherwise. 128 */ 129 public synchronized boolean finished() { 130 return finished; 131 } 132 133 /** 134 * Returns the {@link Adler32} checksum of the bytes inflated so far, or the 135 * checksum of the preset dictionary if {@link #needsDictionary} returns true. 136 */ 137 public synchronized int getAdler() { 138 checkOpen(); 139 return getAdlerImpl(streamHandle); 140 } 141 142 private native int getAdlerImpl(long handle); 143 144 /** 145 * Returns the total number of bytes read by the {@code Inflater}. This 146 * method is the same as {@link #getTotalIn} except that it returns a 147 * {@code long} value instead of an integer. 148 */ 149 public synchronized long getBytesRead() { 150 checkOpen(); 151 return getTotalInImpl(streamHandle); 152 } 153 154 /** 155 * Returns a the total number of bytes written by this {@code Inflater}. This 156 * method is the same as {@code getTotalOut} except it returns a 157 * {@code long} value instead of an integer. 158 */ 159 public synchronized long getBytesWritten() { 160 checkOpen(); 161 return getTotalOutImpl(streamHandle); 162 } 163 164 /** 165 * Returns the number of bytes of current input remaining to be read by this 166 * inflater. 167 */ 168 public synchronized int getRemaining() { 169 return inLength - inRead; 170 } 171 172 /** 173 * Returns the total number of bytes of input read by this {@code Inflater}. This 174 * method is limited to 32 bits; use {@link #getBytesRead} instead. 175 */ 176 public synchronized int getTotalIn() { 177 checkOpen(); 178 return (int) Math.min(getTotalInImpl(streamHandle), (long) Integer.MAX_VALUE); 179 } 180 181 private native long getTotalInImpl(long handle); 182 183 /** 184 * Returns the total number of bytes written to the output buffer by this {@code 185 * Inflater}. The method is limited to 32 bits; use {@link #getBytesWritten} instead. 186 */ 187 public synchronized int getTotalOut() { 188 checkOpen(); 189 return (int) Math.min(getTotalOutImpl(streamHandle), (long) Integer.MAX_VALUE); 190 } 191 192 private native long getTotalOutImpl(long handle); 193 194 /** 195 * Inflates bytes from the current input and stores them in {@code buf}. 196 * 197 * @param buf 198 * the buffer where decompressed data bytes are written. 199 * @return the number of bytes inflated. 200 * @throws DataFormatException 201 * if the underlying stream is corrupted or was not compressed 202 * using a {@code Deflater}. 203 */ 204 public int inflate(byte[] buf) throws DataFormatException { 205 return inflate(buf, 0, buf.length); 206 } 207 208 /** 209 * Inflates up to {@code byteCount} bytes from the current input and stores them in 210 * {@code buf} starting at {@code offset}. 211 * 212 * @throws DataFormatException 213 * if the underlying stream is corrupted or was not compressed 214 * using a {@code Deflater}. 215 * @return the number of bytes inflated. 216 */ 217 public synchronized int inflate(byte[] buf, int offset, int byteCount) throws DataFormatException { 218 Arrays.checkOffsetAndCount(buf.length, offset, byteCount); 219 220 checkOpen(); 221 222 if (needsInput()) { 223 return 0; 224 } 225 226 boolean neededDict = needsDictionary; 227 needsDictionary = false; 228 int result = inflateImpl(buf, offset, byteCount, streamHandle); 229 if (needsDictionary && neededDict) { 230 throw new DataFormatException("Needs dictionary"); 231 } 232 return result; 233 } 234 235 private native int inflateImpl(byte[] buf, int offset, int byteCount, long handle); 236 237 /** 238 * Returns true if the input bytes were compressed with a preset 239 * dictionary. This method should be called if the first call to {@link #inflate} returns 0, 240 * to determine whether a dictionary is required. If so, {@link #setDictionary} 241 * should be called with the appropriate dictionary before calling {@code 242 * inflate} again. Use {@link #getAdler} to determine which dictionary is required. 243 */ 244 public synchronized boolean needsDictionary() { 245 return needsDictionary; 246 } 247 248 /** 249 * Returns true if {@link #setInput} must be called before inflation can continue. 250 */ 251 public synchronized boolean needsInput() { 252 return inRead == inLength; 253 } 254 255 /** 256 * Resets this {@code Inflater}. Should be called prior to inflating a new 257 * set of data. 258 */ 259 public synchronized void reset() { 260 checkOpen(); 261 finished = false; 262 needsDictionary = false; 263 inLength = inRead = 0; 264 resetImpl(streamHandle); 265 } 266 267 private native void resetImpl(long handle); 268 269 /** 270 * Sets the preset dictionary to be used for inflation to {@code dictionary}. 271 * See {@link #needsDictionary} for details. 272 */ 273 public synchronized void setDictionary(byte[] dictionary) { 274 setDictionary(dictionary, 0, dictionary.length); 275 } 276 277 /** 278 * Sets the preset dictionary to be used for inflation to a subsequence of {@code dictionary} 279 * starting at {@code offset} and continuing for {@code byteCount} bytes. See {@link 280 * #needsDictionary} for details. 281 */ 282 public synchronized void setDictionary(byte[] dictionary, int offset, int byteCount) { 283 checkOpen(); 284 Arrays.checkOffsetAndCount(dictionary.length, offset, byteCount); 285 setDictionaryImpl(dictionary, offset, byteCount, streamHandle); 286 } 287 288 private native void setDictionaryImpl(byte[] dictionary, int offset, int byteCount, long handle); 289 290 /** 291 * Sets the current input to to be decompressed. This method should only be 292 * called if {@link #needsInput} returns {@code true}. 293 */ 294 public synchronized void setInput(byte[] buf) { 295 setInput(buf, 0, buf.length); 296 } 297 298 /** 299 * Sets the current input to to be decompressed. This method should only be 300 * called if {@link #needsInput} returns {@code true}. 301 */ 302 public synchronized void setInput(byte[] buf, int offset, int byteCount) { 303 checkOpen(); 304 Arrays.checkOffsetAndCount(buf.length, offset, byteCount); 305 inRead = 0; 306 inLength = byteCount; 307 setInputImpl(buf, offset, byteCount, streamHandle); 308 } 309 310 private native void setInputImpl(byte[] buf, int offset, int byteCount, long handle); 311 312 synchronized int setFileInput(FileDescriptor fd, long offset, int byteCount) { 313 checkOpen(); 314 inRead = 0; 315 inLength = setFileInputImpl(fd, offset, byteCount, streamHandle); 316 return inLength; 317 } 318 319 private native int setFileInputImpl(FileDescriptor fd, long offset, int byteCount, long handle); 320 321 private void checkOpen() { 322 if (streamHandle == -1) { 323 throw new IllegalStateException("attempt to use Inflater after calling end"); 324 } 325 } 326} 327