1. /*
  2. * @(#)GZIPInputStream.java 1.21 01/11/29
  3. *
  4. * Copyright 2002 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 format.
  16. *
  17. * @see InflaterInputStream
  18. * @version 1.21, 11/29/01
  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. readHeader();
  51. crc.reset();
  52. }
  53. /**
  54. * Creates a new input stream with a default buffer size.
  55. * @param in the input stream
  56. * @exception IOException if an I/O error has occurred
  57. */
  58. public GZIPInputStream(InputStream in) throws IOException {
  59. this(in, 512);
  60. }
  61. /**
  62. * Reads uncompressed data into an array of bytes. Blocks until enough
  63. * input is available for decompression.
  64. * @param buf the buffer into which the data is read
  65. * @param off the start offset of the data
  66. * @param len the maximum number of bytes read
  67. * @return the actual number of bytes read, or -1 if the end of the
  68. * compressed input stream is reached
  69. * @exception IOException if an I/O error has occurred or the compressed
  70. * input data is corrupt
  71. */
  72. public int read(byte[] buf, int off, int len) throws IOException {
  73. ensureOpen();
  74. if (eos) {
  75. return -1;
  76. }
  77. len = super.read(buf, off, len);
  78. if (len == -1) {
  79. readTrailer();
  80. eos = true;
  81. } else {
  82. crc.update(buf, off, len);
  83. }
  84. return len;
  85. }
  86. /**
  87. * Closes the input stream.
  88. * @exception IOException if an I/O error has occurred
  89. */
  90. public void close() throws IOException {
  91. inf.end();
  92. in.close();
  93. eos = true;
  94. closed = true;
  95. }
  96. /**
  97. * GZIP header magic number.
  98. */
  99. public final static int GZIP_MAGIC = 0x8b1f;
  100. /*
  101. * File header flags.
  102. */
  103. private final static int FTEXT = 1; // Extra text
  104. private final static int FHCRC = 2; // Header CRC
  105. private final static int FEXTRA = 4; // Extra field
  106. private final static int FNAME = 8; // File name
  107. private final static int FCOMMENT = 16; // File comment
  108. /*
  109. * Reads GZIP member header.
  110. */
  111. private void readHeader() throws IOException {
  112. CheckedInputStream in = new CheckedInputStream(this.in, crc);
  113. crc.reset();
  114. // Check header magic
  115. if (readUShort(in) != GZIP_MAGIC) {
  116. throw new IOException("Not in GZIP format");
  117. }
  118. // Check compression method
  119. if (readUByte(in) != 8) {
  120. throw new IOException("Unsupported compression method");
  121. }
  122. // Read flags
  123. int flg = readUByte(in);
  124. // Skip MTIME, XFL, and OS fields
  125. skipBytes(in, 6);
  126. // Skip optional extra field
  127. if ((flg & FEXTRA) == FEXTRA) {
  128. skipBytes(in, readUShort(in));
  129. }
  130. // Skip optional file name
  131. if ((flg & FNAME) == FNAME) {
  132. while (readUByte(in) != 0) ;
  133. }
  134. // Skip optional file comment
  135. if ((flg & FCOMMENT) == FCOMMENT) {
  136. while (readUByte(in) != 0) ;
  137. }
  138. // Check optional header CRC
  139. if ((flg & FHCRC) == FHCRC) {
  140. int v = (int)crc.getValue() & 0xffff;
  141. if (readUShort(in) != v) {
  142. throw new IOException("Corrupt GZIP header");
  143. }
  144. }
  145. }
  146. /*
  147. * Reads GZIP member trailer.
  148. */
  149. private void readTrailer() throws IOException {
  150. InputStream in = this.in;
  151. int n = inf.getRemaining();
  152. if (n > 0) {
  153. in = new SequenceInputStream(
  154. new ByteArrayInputStream(buf, len - n, n), in);
  155. }
  156. long v = crc.getValue();
  157. if (readUInt(in) != v || readUInt(in) != inf.getTotalOut()) {
  158. throw new IOException("Corrupt GZIP trailer");
  159. }
  160. }
  161. /*
  162. * Reads unsigned integer in Intel byte order.
  163. */
  164. private long readUInt(InputStream in) throws IOException {
  165. long s = readUShort(in);
  166. return ((long)readUShort(in) << 16) | s;
  167. }
  168. /*
  169. * Reads unsigned short in Intel byte order.
  170. */
  171. private int readUShort(InputStream in) throws IOException {
  172. int b = readUByte(in);
  173. return ((int)readUByte(in) << 8) | b;
  174. }
  175. /*
  176. * Reads unsigned byte.
  177. */
  178. private int readUByte(InputStream in) throws IOException {
  179. int b = in.read();
  180. if (b == -1) {
  181. throw new EOFException();
  182. }
  183. return b;
  184. }
  185. /*
  186. * Skips bytes of input data blocking until all bytes are skipped.
  187. * Does not assume that the input stream is capable of seeking.
  188. */
  189. private void skipBytes(InputStream in, int n) throws IOException {
  190. byte[] buf = new byte[128];
  191. while (n > 0) {
  192. int len = in.read(buf, 0, n < buf.length ? n : buf.length);
  193. if (len == -1) {
  194. throw new EOFException();
  195. }
  196. n -= len;
  197. }
  198. }
  199. }