1. /*
  2. * @(#)GZIPInputStream.java 1.28 04/06/11
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.util.zip;
  8. import java.io.SequenceInputStream;
  9. import java.io.ByteArrayInputStream;
  10. import java.io.InputStream;
  11. import java.io.IOException;
  12. import java.io.EOFException;
  13. /**
  14. * This class implements a stream filter for reading compressed data in
  15. * the GZIP file format.
  16. *
  17. * @see InflaterInputStream
  18. * @version 1.28, 06/11/04
  19. * @author David Connelly
  20. *
  21. */
  22. public
  23. class GZIPInputStream extends InflaterInputStream {
  24. /**
  25. * CRC-32 for uncompressed data.
  26. */
  27. protected CRC32 crc = new CRC32();
  28. /**
  29. * Indicates end of input stream.
  30. */
  31. protected boolean eos;
  32. private boolean closed = false;
  33. /**
  34. * Check to make sure that this stream has not been closed
  35. */
  36. private void ensureOpen() throws IOException {
  37. if (closed) {
  38. throw new IOException("Stream closed");
  39. }
  40. }
  41. /**
  42. * Creates a new input stream with the specified buffer size.
  43. * @param in the input stream
  44. * @param size the input buffer size
  45. * @exception IOException if an I/O error has occurred
  46. * @exception IllegalArgumentException if size is <= 0
  47. */
  48. public GZIPInputStream(InputStream in, int size) throws IOException {
  49. super(in, new Inflater(true), size);
  50. usesDefaultInflater = true;
  51. readHeader();
  52. crc.reset();
  53. }
  54. /**
  55. * Creates a new input stream with a default buffer size.
  56. * @param in the input stream
  57. * @exception IOException if an I/O error has occurred
  58. */
  59. public GZIPInputStream(InputStream in) throws IOException {
  60. this(in, 512);
  61. }
  62. /**
  63. * Reads uncompressed data into an array of bytes. Blocks until enough
  64. * input is available for decompression.
  65. * @param buf the buffer into which the data is read
  66. * @param off the start offset of the data
  67. * @param len the maximum number of bytes read
  68. * @return the actual number of bytes read, or -1 if the end of the
  69. * compressed input stream is reached
  70. * @exception IOException if an I/O error has occurred or the compressed
  71. * input data is corrupt
  72. */
  73. public int read(byte[] buf, int off, int len) throws IOException {
  74. ensureOpen();
  75. if (eos) {
  76. return -1;
  77. }
  78. len = super.read(buf, off, len);
  79. if (len == -1) {
  80. readTrailer();
  81. eos = true;
  82. } else {
  83. crc.update(buf, off, len);
  84. }
  85. return len;
  86. }
  87. /**
  88. * Closes this input stream and releases any system resources associated
  89. * with the stream.
  90. * @exception IOException if an I/O error has occurred
  91. */
  92. public void close() throws IOException {
  93. if (!closed) {
  94. super.close();
  95. eos = true;
  96. closed = true;
  97. }
  98. }
  99. /**
  100. * GZIP header magic number.
  101. */
  102. public final static int GZIP_MAGIC = 0x8b1f;
  103. /*
  104. * File header flags.
  105. */
  106. private final static int FTEXT = 1; // Extra text
  107. private final static int FHCRC = 2; // Header CRC
  108. private final static int FEXTRA = 4; // Extra field
  109. private final static int FNAME = 8; // File name
  110. private final static int FCOMMENT = 16; // File comment
  111. /*
  112. * Reads GZIP member header.
  113. */
  114. private void readHeader() throws IOException {
  115. CheckedInputStream in = new CheckedInputStream(this.in, crc);
  116. crc.reset();
  117. // Check header magic
  118. if (readUShort(in) != GZIP_MAGIC) {
  119. throw new IOException("Not in GZIP format");
  120. }
  121. // Check compression method
  122. if (readUByte(in) != 8) {
  123. throw new IOException("Unsupported compression method");
  124. }
  125. // Read flags
  126. int flg = readUByte(in);
  127. // Skip MTIME, XFL, and OS fields
  128. skipBytes(in, 6);
  129. // Skip optional extra field
  130. if ((flg & FEXTRA) == FEXTRA) {
  131. skipBytes(in, readUShort(in));
  132. }
  133. // Skip optional file name
  134. if ((flg & FNAME) == FNAME) {
  135. while (readUByte(in) != 0) ;
  136. }
  137. // Skip optional file comment
  138. if ((flg & FCOMMENT) == FCOMMENT) {
  139. while (readUByte(in) != 0) ;
  140. }
  141. // Check optional header CRC
  142. if ((flg & FHCRC) == FHCRC) {
  143. int v = (int)crc.getValue() & 0xffff;
  144. if (readUShort(in) != v) {
  145. throw new IOException("Corrupt GZIP header");
  146. }
  147. }
  148. }
  149. /*
  150. * Reads GZIP member trailer.
  151. */
  152. private void readTrailer() throws IOException {
  153. InputStream in = this.in;
  154. int n = inf.getRemaining();
  155. if (n > 0) {
  156. in = new SequenceInputStream(
  157. new ByteArrayInputStream(buf, len - n, n), in);
  158. }
  159. long v = crc.getValue();
  160. if (readUInt(in) != v || readUInt(in) != inf.getTotalOut()) {
  161. throw new IOException("Corrupt GZIP trailer");
  162. }
  163. }
  164. /*
  165. * Reads unsigned integer in Intel byte order.
  166. */
  167. private long readUInt(InputStream in) throws IOException {
  168. long s = readUShort(in);
  169. return ((long)readUShort(in) << 16) | s;
  170. }
  171. /*
  172. * Reads unsigned short in Intel byte order.
  173. */
  174. private int readUShort(InputStream in) throws IOException {
  175. int b = readUByte(in);
  176. return ((int)readUByte(in) << 8) | b;
  177. }
  178. /*
  179. * Reads unsigned byte.
  180. */
  181. private int readUByte(InputStream in) throws IOException {
  182. int b = in.read();
  183. if (b == -1) {
  184. throw new EOFException();
  185. }
  186. return b;
  187. }
  188. private byte[] tmpbuf = new byte[128];
  189. /*
  190. * Skips bytes of input data blocking until all bytes are skipped.
  191. * Does not assume that the input stream is capable of seeking.
  192. */
  193. private void skipBytes(InputStream in, int n) throws IOException {
  194. while (n > 0) {
  195. int len = in.read(tmpbuf, 0, n < tmpbuf.length ? n : tmpbuf.length);
  196. if (len == -1) {
  197. throw new EOFException();
  198. }
  199. n -= len;
  200. }
  201. }
  202. }