| 1: | // GzipInputStream.cs | |
| 2: | // Copyright (C) 2001 Mike Krueger | |
| 3: | // | |
| 4: | // This file was translated from java, it was part of the GNU Classpath | |
| 5: | // Copyright (C) 2001 Free Software Foundation, Inc. | |
| 6: | // | |
| 7: | // This program is free software; you can redistribute it and/or | |
| 8: | // modify it under the terms of the GNU General Public License | |
| 9: | // as published by the Free Software Foundation; either version 2 | |
| 10: | // of the License, or (at your option) any later version. | |
| 11: | // | |
| 12: | // This program is distributed in the hope that it will be useful, | |
| 13: | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 14: | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 15: | // GNU General Public License for more details. | |
| 16: | // | |
| 17: | // You should have received a copy of the GNU General Public License | |
| 18: | // along with this program; if not, write to the Free Software | |
| 19: | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
| 20: | // | |
| 21: | // Linking this library statically or dynamically with other modules is | |
| 22: | // making a combined work based on this library. Thus, the terms and | |
| 23: | // conditions of the GNU General Public License cover the whole | |
| 24: | // combination. | |
| 25: | // | |
| 26: | // As a special exception, the copyright holders of this library give you | |
| 27: | // permission to link this library with independent modules to produce an | |
| 28: | // executable, regardless of the license terms of these independent | |
| 29: | // modules, and to copy and distribute the resulting executable under | |
| 30: | // terms of your choice, provided that you also meet, for each linked | |
| 31: | // independent module, the terms and conditions of the license of that | |
| 32: | // module. An independent module is a module which is not derived from | |
| 33: | // or based on this library. If you modify this library, you may extend | |
| 34: | // this exception to your version of the library, but you are not | |
| 35: | // obligated to do so. If you do not wish to do so, delete this | |
| 36: | // exception statement from your version. | |
| 37: | ||
| 38: | using System; | |
| 39: | using System.IO; | |
| 40: | ||
| 41: | using ICSharpCode.SharpZipLib.Checksums; | |
| 42: | using ICSharpCode.SharpZipLib.Zip.Compression; | |
| 43: | using ICSharpCode.SharpZipLib.Zip.Compression.Streams; | |
| 44: | ||
| 45: | namespace ICSharpCode.SharpZipLib.GZip { | |
| 46: | ||
| 47: | /// <summary> | |
| 48: | /// This filter stream is used to decompress a "GZIP" format stream. | |
| 49: | /// The "GZIP" format is described baseInputStream RFC 1952. | |
| 50: | /// | |
| 51: | /// author of the original java version : John Leuner | |
| 52: | /// </summary> | |
| 53: | /// <example> This sample shows how to unzip a gzipped file | |
| 54: | /// <code> | |
| 55: | /// using System; | |
| 56: | /// using System.IO; | |
| 57: | /// | |
| 58: | /// using NZlib.GZip; | |
| 59: | /// | |
| 60: | /// class MainClass | |
| 61: | /// { | |
| 62: | /// public static void Main(string[] args) | |
| 63: | /// { | |
| 64: | /// Stream s = new GZipInputStream(File.OpenRead(args[0])); | |
| 65: | /// FileStream fs = File.Create(Path.GetFileNameWithoutExtension(args[0])); | |
| 66: | /// int size = 2048; | |
| 67: | /// byte[] writeData = new byte[2048]; | |
| 68: | /// while (true) { | |
| 69: | /// size = s.Read(writeData, 0, size); | |
| 70: | /// if (size > 0) { | |
| 71: | /// fs.Write(writeData, 0, size); | |
| 72: | /// } else { | |
| 73: | /// break; | |
| 74: | /// } | |
| 75: | /// } | |
| 76: | /// s.Close(); | |
| 77: | /// } | |
| 78: | /// } | |
| 79: | /// </code> | |
| 80: | /// </example> | |
| 81: | public class GZipInputStream : InflaterInputStream | |
| 82: | { | |
| 83: | ||
| 84: | //Variables | |
| 85: | ||
| 86: | /// <summary> | |
| 87: | /// CRC-32 value for uncompressed data | |
| 88: | /// </summary> | |
| 89: | protected Crc32 crc = new Crc32(); | |
| 90: | ||
| 91: | /// <summary> | |
| 92: | /// Indicates end of stream | |
| 93: | /// </summary> | |
| 94: | protected bool eos; | |
| 95: | ||
| 96: | /// <summary> | |
| 97: | /// Creates a GzipInputStream with the default buffer size | |
| 98: | /// </summary> | |
| 99: | /// <param name="baseInputStream"> | |
| 100: | /// The stream to read compressed data from (baseInputStream GZIP format) | |
| 101: | /// </param> | |
| 102: | public GZipInputStream(Stream baseInputStream) : this(baseInputStream, 4096) | |
| 103: | { | |
| 104: | } | |
| 105: | ||
| 106: | /// <summary> | |
| 107: | /// Creates a GZIPInputStream with the specified buffer size | |
| 108: | /// </summary> | |
| 109: | /// <param name="baseInputStream"> | |
| 110: | /// The stream to read compressed data from (baseInputStream GZIP format) | |
| 111: | /// </param> | |
| 112: | /// <param name="size"> | |
| 113: | /// Size of the buffer to use | |
| 114: | /// </param> | |
| 115: | public GZipInputStream(Stream baseInputStream, int size) : base(baseInputStream, new Inflater(true), size) | |
| 116: | { | |
| 117: | } | |
| 118: | ||
| 119: | /// <summary> | |
| 120: | /// Reads uncompressed data into an array of bytes | |
| 121: | /// </summary> | |
| 122: | /// <param name="buf"> | |
| 123: | /// the buffer to read uncompressed data into | |
| 124: | /// </param> | |
| 125: | /// <param name="offset"> | |
| 126: | /// the offset indicating where the data should be placed | |
| 127: | /// </param> | |
| 128: | /// <param name="len"> | |
| 129: | /// the number of uncompressed bytes to be read | |
| 130: | /// </param> | |
| 131: | public override int Read(byte[] buf, int offset, int len) | |
| 132: | { | |
| 133: | // We first have to slurp baseInputStream the GZIP header, then we feed all the | |
| 134: | // rest of the data to the superclass. | |
| 135: | // | |
| 136: | // As we do that we continually update the CRC32. Once the data is | |
| 137: | // finished, we check the CRC32 | |
| 138: | // | |
| 139: | // This means we don't need our own buffer, as everything is done | |
| 140: | // baseInputStream the superclass. | |
| 141: | if (!readGZIPHeader) { | |
| 142: | ReadHeader(); | |
| 143: | } | |
| 144: | ||
| 145: | if (eos) { | |
| 146: | return 0; | |
| 147: | } | |
| 148: | ||
| 149: | // System.err.println("GZIPIS.read(byte[], off, len ... " + offset + " and len " + len); | |
| 150: | //We don't have to read the header, so we just grab data from the superclass | |
| 151: | int numRead = base.Read(buf, offset, len); | |
| 152: | if (numRead > 0) { | |
| 153: | crc.Update(buf, offset, numRead); | |
| 154: | } | |
| 155: | ||
| 156: | if (inf.IsFinished) { | |
| 157: | ReadFooter(); | |
| 158: | } | |
| 159: | return numRead; | |
| 160: | } | |
| 161: | ||
| 162: | private void ReadHeader() | |
| 163: | { | |
| 164: | /* 1. Check the two magic bytes */ | |
| 165: | Crc32 headCRC = new Crc32(); | |
| 166: | int magic = baseInputStream.ReadByte(); | |
| 167: | if (magic < 0) { | |
| 168: | eos = true; | |
| 169: | return; | |
| 170: | } | |
| 171: | headCRC.Update(magic); | |
| 172: | if (magic != (GZipConstants.GZIP_MAGIC >> 8)) { | |
| 173: | throw new IOException("Error baseInputStream GZIP header, first byte doesn't match"); | |
| 174: | } | |
| 175: | ||
| 176: | magic = baseInputStream.ReadByte(); | |
| 177: | if (magic != (GZipConstants.GZIP_MAGIC & 0xFF)) { | |
| 178: | throw new IOException("Error baseInputStream GZIP header, second byte doesn't match"); | |
| 179: | } | |
| 180: | headCRC.Update(magic); | |
| 181: | ||
| 182: | /* 2. Check the compression type (must be 8) */ | |
| 183: | int CM = baseInputStream.ReadByte(); | |
| 184: | if (CM != 8) { | |
| 185: | throw new IOException("Error baseInputStream GZIP header, data not baseInputStream deflate format"); | |
| 186: | } | |
| 187: | headCRC.Update(CM); | |
| 188: | ||
| 189: | /* 3. Check the flags */ | |
| 190: | int flags = baseInputStream.ReadByte(); | |
| 191: | if (flags < 0) { | |
| 192: | throw new Exception("Early EOF baseInputStream GZIP header"); | |
| 193: | } | |
| 194: | headCRC.Update(flags); | |
| 195: | ||
| 196: | /* This flag byte is divided into individual bits as follows: | |
| 197: | ||
| 198: | bit 0 FTEXT | |
| 199: | bit 1 FHCRC | |
| 200: | bit 2 FEXTRA | |
| 201: | bit 3 FNAME | |
| 202: | bit 4 FCOMMENT | |
| 203: | bit 5 reserved | |
| 204: | bit 6 reserved | |
| 205: | bit 7 reserved | |
| 206: | */ | |
| 207: | ||
| 208: | /* 3.1 Check the reserved bits are zero */ | |
| 209: | ||
| 210: | if ((flags & 0xd0) != 0) { | |
| 211: | throw new IOException("Reserved flag bits baseInputStream GZIP header != 0"); | |
| 212: | } | |
| 213: | ||
| 214: | /* 4.-6. Skip the modification time, extra flags, and OS type */ | |
| 215: | for (int i=0; i< 6; i++) { | |
| 216: | int readByte = baseInputStream.ReadByte(); | |
| 217: | if (readByte < 0) { | |
| 218: | throw new Exception("Early EOF baseInputStream GZIP header"); | |
| 219: | } | |
| 220: | headCRC.Update(readByte); | |
| 221: | } | |
| 222: | ||
| 223: | /* 7. Read extra field */ | |
| 224: | if ((flags & GZipConstants.FEXTRA) != 0) { | |
| 225: | /* Skip subfield id */ | |
| 226: | for (int i=0; i< 2; i++) { | |
| 227: | int readByte = baseInputStream.ReadByte(); | |
| 228: | if (readByte < 0) { | |
| 229: | throw new Exception("Early EOF baseInputStream GZIP header"); | |
| 230: | } | |
| 231: | headCRC.Update(readByte); | |
| 232: | } | |
| 233: | if (baseInputStream.ReadByte() < 0 || baseInputStream.ReadByte() < 0) { | |
| 234: | throw new Exception("Early EOF baseInputStream GZIP header"); | |
| 235: | } | |
| 236: | ||
| 237: | int len1, len2, extraLen; | |
| 238: | len1 = baseInputStream.ReadByte(); | |
| 239: | len2 = baseInputStream.ReadByte(); | |
| 240: | if ((len1 < 0) || (len2 < 0)) { | |
| 241: | throw new Exception("Early EOF baseInputStream GZIP header"); | |
| 242: | } | |
| 243: | headCRC.Update(len1); | |
| 244: | headCRC.Update(len2); | |
| 245: | ||
| 246: | extraLen = (len1 << 8) | len2; | |
| 247: | for (int i = 0; i < extraLen;i++) { | |
| 248: | int readByte = baseInputStream.ReadByte(); | |
| 249: | if (readByte < 0) { | |
| 250: | throw new Exception("Early EOF baseInputStream GZIP header"); | |
| 251: | } | |
| 252: | headCRC.Update(readByte); | |
| 253: | } | |
| 254: | } | |
| 255: | ||
| 256: | /* 8. Read file name */ | |
| 257: | if ((flags & GZipConstants.FNAME) != 0) { | |
| 258: | int readByte; | |
| 259: | while ( (readByte = baseInputStream.ReadByte()) > 0) { | |
| 260: | headCRC.Update(readByte); | |
| 261: | } | |
| 262: | if (readByte < 0) { | |
| 263: | throw new Exception("Early EOF baseInputStream GZIP file name"); | |
| 264: | } | |
| 265: | headCRC.Update(readByte); | |
| 266: | } | |
| 267: | ||
| 268: | /* 9. Read comment */ | |
| 269: | if ((flags & GZipConstants.FCOMMENT) != 0) { | |
| 270: | int readByte; | |
| 271: | while ( (readByte = baseInputStream.ReadByte()) > 0) { | |
| 272: | headCRC.Update(readByte); | |
| 273: | } | |
| 274: | ||
| 275: | if (readByte < 0) { | |
| 276: | throw new Exception("Early EOF baseInputStream GZIP comment"); | |
| 277: | } | |
| 278: | headCRC.Update(readByte); | |
| 279: | } | |
| 280: | ||
| 281: | /* 10. Read header CRC */ | |
| 282: | if ((flags & GZipConstants.FHCRC) != 0) { | |
| 283: | int tempByte; | |
| 284: | int crcval = baseInputStream.ReadByte(); | |
| 285: | if (crcval < 0) { | |
| 286: | throw new Exception("Early EOF baseInputStream GZIP header"); | |
| 287: | } | |
| 288: | ||
| 289: | tempByte = baseInputStream.ReadByte(); | |
| 290: | if (tempByte < 0) { | |
| 291: | throw new Exception("Early EOF baseInputStream GZIP header"); | |
| 292: | } | |
| 293: | ||
| 294: | crcval = (crcval << 8) | tempByte; | |
| 295: | if (crcval != ((int) headCRC.Value & 0xffff)) { | |
| 296: | throw new IOException("Header CRC value mismatch"); | |
| 297: | } | |
| 298: | } | |
| 299: | ||
| 300: | readGZIPHeader = true; | |
| 301: | //System.err.println("Read GZIP header"); | |
| 302: | } | |
| 303: | ||
| 304: | private void ReadFooter() | |
| 305: | { | |
| 306: | byte[] footer = new byte[8]; | |
| 307: | int avail = inf.RemainingInput; | |
| 308: | if (avail > 8) { | |
| 309: | avail = 8; | |
| 310: | } | |
| 311: | System.Array.Copy(buf, len - inf.RemainingInput, footer, 0, avail); | |
| 312: | int needed = 8 - avail; | |
| 313: | while (needed > 0) { | |
| 314: | int count = baseInputStream.Read(footer, 8-needed, needed); | |
| 315: | if (count <= 0) { | |
| 316: | throw new Exception("Early EOF baseInputStream GZIP footer"); | |
| 317: | } | |
| 318: | needed -= count; //Jewel Jan 16 | |
| 319: | } | |
| 320: | int crcval = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8) | ((footer[2] & 0xff) << 16) | (footer[3] << 24); | |
| 321: | if (crcval != (int) crc.Value) { | |
| 322: | throw new IOException("GZIP crc sum mismatch, theirs \"" + crcval + "\" and ours \"" + (int) crc.Value); | |
| 323: | } | |
| 324: | int total = (footer[4] & 0xff) | ((footer[5] & 0xff) << 8) | ((footer[6] & 0xff) << 16) | (footer[7] << 24); | |
| 325: | if (total != inf.TotalOut) { | |
| 326: | throw new IOException("Number of bytes mismatch"); | |
| 327: | } | |
| 328: | /* XXX Should we support multiple members. | |
| 329: | * Difficult, since there may be some bytes still baseInputStream buf | |
| 330: | */ | |
| 331: | eos = true; | |
| 332: | } | |
| 333: | ||
| 334: | /* Have we read the GZIP header yet? */ | |
| 335: | private bool readGZIPHeader; | |
| 336: | } | |
| 337: | } |
This page was automatically generated by SharpDevelop.