1. /*
  2. * @(#)GZIPInputStream.java 1.22 00/02/02
  3. *
  4. * Copyright 1996-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package java.util.zip;
  11. import java.io.SequenceInputStream;
  12. import java.io.ByteArrayInputStream;
  13. import java.io.InputStream;
  14. import java.io.IOException;
  15. import java.io.EOFException;
  16. /**
  17. * This class implements a stream filter for reading compressed data in
  18. * the GZIP format.
  19. *
  20. * @see InflaterInputStream
  21. * @version 1.22, 02/02/00
  22. * @author David Connelly
  23. *
  24. */
  25. public
  26. class GZIPInputStream extends InflaterInputStream {
  27. /**
  28. * CRC-32 for uncompressed data.
  29. */
  30. protected CRC32 crc = new CRC32();
  31. /**
  32. * Indicates end of input stream.
  33. */
  34. protected boolean eos;
  35. private boolean closed = false;
  36. /**
  37. * Check to make sure that this stream has not been closed
  38. */
  39. private void ensureOpen() throws IOException {
  40. if (closed) {
  41. throw new IOException("Stream closed");
  42. }
  43. }
  44. /**
  45. * Creates a new input stream with the specified buffer size.
  46. * @param in the input stream
  47. * @param size the input buffer size
  48. * @exception IOException if an I/O error has occurred
  49. * @exception IllegalArgumentException if size is <= 0
  50. */
  51. public GZIPInputStream(InputStream in, int size) throws IOException {
  52. super(in, new Inflater(true), size);
  53. readHeader();
  54. crc.reset();
  55. }
  56. /**
  57. * Creates a new input stream with a default buffer size.
  58. * @param in the input stream
  59. * @exception IOException if an I/O error has occurred
  60. */
  61. public GZIPInputStream(InputStream in) throws IOException {
  62. this(in, 512);
  63. }
  64. /**
  65. * Reads uncompressed data into an array of bytes. Blocks until enough
  66. * input is available for decompression.
  67. * @param buf the buffer into which the data is read
  68. * @param off the start offset of the data
  69. * @param len the maximum number of bytes read
  70. * @return the actual number of bytes read, or -1 if the end of the
  71. * compressed input stream is reached
  72. * @exception IOException if an I/O error has occurred or the compressed
  73. * input data is corrupt
  74. */
  75. public int read(byte[] buf, int off, int len) throws IOException {
  76. ensureOpen();
  77. if (eos) {
  78. return -1;
  79. }
  80. len = super.read(buf, off, len);
  81. if (len == -1) {
  82. readTrailer();
  83. eos = true;
  84. } else {
  85. crc.update(buf, off, len);
  86. }
  87. return len;
  88. }
  89. /**
  90. * Closes the input stream.
  91. * @exception IOException if an I/O error has occurred
  92. */
  93. public void close() throws IOException {
  94. inf.end();
  95. in.close();
  96. eos = true;
  97. closed = true;
  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. /*
  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. byte[] buf = new byte[128];
  194. while (n > 0) {
  195. int len = in.read(buf, 0, n < buf.length ? n : buf.length);
  196. if (len == -1) {
  197. throw new EOFException();
  198. }
  199. n -= len;
  200. }
  201. }
  202. }