1. /*
  2. * @(#)GZIPInputStream.java 1.25 03/01/23
  3. *
  4. * Copyright 2003 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.25, 01/23/03
  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 the input stream.
  89. * @exception IOException if an I/O error has occurred
  90. */
  91. public void close() throws IOException {
  92. if (!closed) {
  93. super.close();
  94. eos = true;
  95. closed = true;
  96. }
  97. }
  98. /**
  99. * GZIP header magic number.
  100. */
  101. public final static int GZIP_MAGIC = 0x8b1f;
  102. /*
  103. * File header flags.
  104. */
  105. private final static int FTEXT = 1; // Extra text
  106. private final static int FHCRC = 2; // Header CRC
  107. private final static int FEXTRA = 4; // Extra field
  108. private final static int FNAME = 8; // File name
  109. private final static int FCOMMENT = 16; // File comment
  110. /*
  111. * Reads GZIP member header.
  112. */
  113. private void readHeader() throws IOException {
  114. CheckedInputStream in = new CheckedInputStream(this.in, crc);
  115. crc.reset();
  116. // Check header magic
  117. if (readUShort(in) != GZIP_MAGIC) {
  118. throw new IOException("Not in GZIP format");
  119. }
  120. // Check compression method
  121. if (readUByte(in) != 8) {
  122. throw new IOException("Unsupported compression method");
  123. }
  124. // Read flags
  125. int flg = readUByte(in);
  126. // Skip MTIME, XFL, and OS fields
  127. skipBytes(in, 6);
  128. // Skip optional extra field
  129. if ((flg & FEXTRA) == FEXTRA) {
  130. skipBytes(in, readUShort(in));
  131. }
  132. // Skip optional file name
  133. if ((flg & FNAME) == FNAME) {
  134. while (readUByte(in) != 0) ;
  135. }
  136. // Skip optional file comment
  137. if ((flg & FCOMMENT) == FCOMMENT) {
  138. while (readUByte(in) != 0) ;
  139. }
  140. // Check optional header CRC
  141. if ((flg & FHCRC) == FHCRC) {
  142. int v = (int)crc.getValue() & 0xffff;
  143. if (readUShort(in) != v) {
  144. throw new IOException("Corrupt GZIP header");
  145. }
  146. }
  147. }
  148. /*
  149. * Reads GZIP member trailer.
  150. */
  151. private void readTrailer() throws IOException {
  152. InputStream in = this.in;
  153. int n = inf.getRemaining();
  154. if (n > 0) {
  155. in = new SequenceInputStream(
  156. new ByteArrayInputStream(buf, len - n, n), in);
  157. }
  158. long v = crc.getValue();
  159. if (readUInt(in) != v || readUInt(in) != inf.getTotalOut()) {
  160. throw new IOException("Corrupt GZIP trailer");
  161. }
  162. }
  163. /*
  164. * Reads unsigned integer in Intel byte order.
  165. */
  166. private long readUInt(InputStream in) throws IOException {
  167. long s = readUShort(in);
  168. return ((long)readUShort(in) << 16) | s;
  169. }
  170. /*
  171. * Reads unsigned short in Intel byte order.
  172. */
  173. private int readUShort(InputStream in) throws IOException {
  174. int b = readUByte(in);
  175. return ((int)readUByte(in) << 8) | b;
  176. }
  177. /*
  178. * Reads unsigned byte.
  179. */
  180. private int readUByte(InputStream in) throws IOException {
  181. int b = in.read();
  182. if (b == -1) {
  183. throw new EOFException();
  184. }
  185. return b;
  186. }
  187. private byte[] tmpbuf = new byte[128];
  188. /*
  189. * Skips bytes of input data blocking until all bytes are skipped.
  190. * Does not assume that the input stream is capable of seeking.
  191. */
  192. private void skipBytes(InputStream in, int n) throws IOException {
  193. while (n > 0) {
  194. int len = in.read(tmpbuf, 0, n < tmpbuf.length ? n : tmpbuf.length);
  195. if (len == -1) {
  196. throw new EOFException();
  197. }
  198. n -= len;
  199. }
  200. }
  201. }