3 // Copyright (C) 2001 Mike Krueger
5 // This file was translated from java, it was part of the GNU Classpath
6 // Copyright (C) 2001 Free Software Foundation, Inc.
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License
10 // as published by the Free Software Foundation; either version 2
11 // of the License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU General Public License for more details.
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 // Linking this library statically or dynamically with other modules is
23 // making a combined work based on this library. Thus, the terms and
24 // conditions of the GNU General Public License cover the whole
27 // As a special exception, the copyright holders of this library give you
28 // permission to link this library with independent modules to produce an
29 // executable, regardless of the license terms of these independent
30 // modules, and to copy and distribute the resulting executable under
31 // terms of your choice, provided that you also meet, for each linked
32 // independent module, the terms and conditions of the license of that
33 // module. An independent module is a module which is not derived from
34 // or based on this library. If you modify this library, you may extend
35 // this exception to your version of the library, but you are not
36 // obligated to do so. If you do not wish to do so, delete this
37 // exception statement from your version.
41 using ICSharpCode.SharpZipLib.GZip;
42 using ICSharpCode.SharpZipLib.Silverlight.Checksums;
43 using ICSharpCode.SharpZipLib.Silverlight.Zip.Compression;
44 using ICSharpCode.SharpZipLib.Silverlight.Zip.Compression.Streams;
46 namespace ICSharpCode.SharpZipLib.Silverlight.GZip
49 /// This filter stream is used to decompress a "GZIP" format stream.
50 /// The "GZIP" format is described baseInputStream RFC 1952.
52 /// author of the original java version : John Leuner
54 /// <example> This sample shows how to unzip a gzipped file
59 /// using ICSharpCode.SharpZipLib.Core;
60 /// using ICSharpCode.SharpZipLib.GZip;
64 /// public static void Main(string[] args)
66 /// using (Stream inStream = new GZipInputStream(File.OpenRead(args[0])))
67 /// using (FileStream outStream = File.Create(Path.GetFileNameWithoutExtension(args[0]))) {
68 /// byte[] buffer = new byte[4096];
69 /// StreamUtils.Copy(inStream, outStream, buffer);
75 public class GZipInputStream : InflaterInputStream
77 #region Instance Fields
80 /// CRC-32 value for uncompressed data
82 protected Crc32 crc = new Crc32();
85 /// Indicates end of stream
89 // Have we read the GZIP header yet?
90 private bool readGZIPHeader;
97 /// Creates a GZipInputStream with the default buffer size
99 /// <param name="baseInputStream">
100 /// The stream to read compressed data from (baseInputStream GZIP format)
102 public GZipInputStream(Stream baseInputStream)
103 : this(baseInputStream, 4096)
108 /// Creates a GZIPInputStream with the specified buffer size
110 /// <param name="baseInputStream">
111 /// The stream to read compressed data from (baseInputStream GZIP format)
113 /// <param name="size">
114 /// Size of the buffer to use
116 public GZipInputStream(Stream baseInputStream, int size)
117 : base(baseInputStream, new Inflater(true), size)
123 #region Stream overrides
126 /// Reads uncompressed data into an array of bytes
128 /// <param name="buffer">
129 /// The buffer to read uncompressed data into
131 /// <param name="offset">
132 /// The offset indicating where the data should be placed
134 /// <param name="count">
135 /// The number of uncompressed bytes to be read
137 /// <returns>Returns the number of bytes actually read.</returns>
138 public override int Read(byte[] buffer, int offset, int count)
140 // We first have to read the GZIP header, then we feed all the
141 // rest of the data to the base class.
143 // As we do that we continually update the CRC32. Once the data is
144 // finished, we check the CRC32
146 // This means we don't need our own buffer, as everything is done
147 // in baseInputStream the superclass.
158 // We don't have to read the header, so we just grab data from the superclass
159 var bytesRead = base.Read(buffer, offset, count);
162 crc.Update(buffer, offset, bytesRead);
174 #region Support routines
176 private void ReadHeader()
178 // 1. Check the two magic bytes
179 var headCRC = new Crc32();
180 var magic = baseInputStream.ReadByte();
184 throw new EndOfStreamException("EOS reading GZIP header");
187 headCRC.Update(magic);
188 if (magic != (GZipConstants.GZIP_MAGIC >> 8))
190 throw new GZipException("Error GZIP header, first magic byte doesn't match");
193 magic = baseInputStream.ReadByte();
197 throw new EndOfStreamException("EOS reading GZIP header");
200 if (magic != (GZipConstants.GZIP_MAGIC & 0xFF))
202 throw new GZipException("Error GZIP header, second magic byte doesn't match");
205 headCRC.Update(magic);
207 // 2. Check the compression type (must be 8)
208 var compressionType = baseInputStream.ReadByte();
210 if (compressionType < 0)
212 throw new EndOfStreamException("EOS reading GZIP header");
215 if (compressionType != 8)
217 throw new GZipException("Error GZIP header, data not in deflate format");
219 headCRC.Update(compressionType);
221 // 3. Check the flags
222 var flags = baseInputStream.ReadByte();
225 throw new EndOfStreamException("EOS reading GZIP header");
227 headCRC.Update(flags);
229 /* This flag byte is divided into individual bits as follows:
241 // 3.1 Check the reserved bits are zero
243 if ((flags & 0xd0) != 0)
245 throw new GZipException("Reserved flag bits in GZIP header != 0");
248 // 4.-6. Skip the modification time, extra flags, and OS type
249 for (var i = 0; i < 6; i++)
251 var readByte = baseInputStream.ReadByte();
254 throw new EndOfStreamException("EOS reading GZIP header");
256 headCRC.Update(readByte);
259 // 7. Read extra field
260 if ((flags & GZipConstants.FEXTRA) != 0)
263 for (var i = 0; i < 2; i++)
265 var readByte = baseInputStream.ReadByte();
268 throw new EndOfStreamException("EOS reading GZIP header");
270 headCRC.Update(readByte);
273 if (baseInputStream.ReadByte() < 0 || baseInputStream.ReadByte() < 0)
275 throw new EndOfStreamException("EOS reading GZIP header");
278 var len1 = baseInputStream.ReadByte();
279 var len2 = baseInputStream.ReadByte();
280 if ((len1 < 0) || (len2 < 0))
282 throw new EndOfStreamException("EOS reading GZIP header");
284 headCRC.Update(len1);
285 headCRC.Update(len2);
287 var extraLen = (len1 << 8) | len2;
288 for (var i = 0; i < extraLen; i++)
290 var readByte = baseInputStream.ReadByte();
293 throw new EndOfStreamException("EOS reading GZIP header");
295 headCRC.Update(readByte);
300 if ((flags & GZipConstants.FNAME) != 0)
303 while ((readByte = baseInputStream.ReadByte()) > 0)
305 headCRC.Update(readByte);
310 throw new EndOfStreamException("EOS reading GZIP header");
312 headCRC.Update(readByte);
316 if ((flags & GZipConstants.FCOMMENT) != 0)
319 while ((readByte = baseInputStream.ReadByte()) > 0)
321 headCRC.Update(readByte);
326 throw new EndOfStreamException("EOS reading GZIP header");
329 headCRC.Update(readByte);
332 // 10. Read header CRC
333 if ((flags & GZipConstants.FHCRC) != 0)
335 var crcval = baseInputStream.ReadByte();
338 throw new EndOfStreamException("EOS reading GZIP header");
341 int tempByte = baseInputStream.ReadByte();
344 throw new EndOfStreamException("EOS reading GZIP header");
347 crcval = (crcval << 8) | tempByte;
348 if (crcval != ((int) headCRC.Value & 0xffff))
350 throw new GZipException("Header CRC value mismatch");
354 readGZIPHeader = true;
357 private void ReadFooter()
359 var footer = new byte[8];
360 var avail = inf.RemainingInput;
367 Array.Copy(inputBuffer.RawData, inputBuffer.RawLength - inf.RemainingInput, footer, 0, avail);
368 var needed = 8 - avail;
372 var count = baseInputStream.Read(footer, 8 - needed, needed);
375 throw new EndOfStreamException("EOS reading GZIP footer");
377 needed -= count; // Jewel Jan 16
380 var crcval = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8) | ((footer[2] & 0xff) << 16) | (footer[3] << 24);
381 if (crcval != (int) crc.Value)
383 throw new GZipException("GZIP crc sum mismatch, theirs \"" + crcval + "\" and ours \"" + (int) crc.Value);
386 // NOTE The total here is the original total modulo 2 ^ 32.
388 ((uint) footer[4] & 0xff) |
389 (((uint) footer[5] & 0xff) << 8) |
390 (((uint) footer[6] & 0xff) << 16) |
391 ((uint) footer[7] << 24);
393 if ((inf.TotalOut & 0xffffffff) != total)
395 throw new GZipException("Number of bytes mismatch in footer");
398 // Should we support multiple gzip members.
399 // Difficult, since there may be some bytes still in baseInputStream dataBuffer