Added hammock project to debug streaming issues
[pithos-ms-client] / trunk / hammock / src / net35 / ICSharpCode.SharpZipLib.Silverlight / GZip / GzipInputStream.cs
1 // GzipInputStream.cs
2 //
3 // Copyright (C) 2001 Mike Krueger
4 //
5 // This file was translated from java, it was part of the GNU Classpath
6 // Copyright (C) 2001 Free Software Foundation, Inc.
7 //
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.
12 //
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.
17 //
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.
21 //
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
25 // combination.
26 // 
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.
38
39 using System;
40 using System.IO;
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;
45
46 namespace ICSharpCode.SharpZipLib.Silverlight.GZip
47 {
48     /// <summary>
49     /// This filter stream is used to decompress a "GZIP" format stream.
50     /// The "GZIP" format is described baseInputStream RFC 1952.
51     /// 
52     /// author of the original java version : John Leuner
53     /// </summary>
54     /// <example> This sample shows how to unzip a gzipped file
55     /// <code>
56     /// using System;
57     /// using System.IO;
58     /// 
59     /// using ICSharpCode.SharpZipLib.Core;
60     /// using ICSharpCode.SharpZipLib.GZip;
61     /// 
62     /// class MainClass
63     /// {
64     ///         public static void Main(string[] args)
65     ///         {
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);
70     ///                 }
71     ///         }
72     /// }       
73     /// </code>
74     /// </example>
75     public class GZipInputStream : InflaterInputStream
76     {
77         #region Instance Fields
78
79         /// <summary>
80         /// CRC-32 value for uncompressed data
81         /// </summary>
82         protected Crc32 crc = new Crc32();
83
84         /// <summary>
85         /// Indicates end of stream
86         /// </summary>
87         protected bool eos;
88
89         // Have we read the GZIP header yet?
90         private bool readGZIPHeader;
91
92         #endregion
93
94         #region Constructors
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)
103             : this(baseInputStream, 4096)
104         {
105         }
106
107         /// <summary>
108         /// Creates a GZIPInputStream with the specified buffer size
109         /// </summary>
110         /// <param name="baseInputStream">
111         /// The stream to read compressed data from (baseInputStream GZIP format)
112         /// </param>
113         /// <param name="size">
114         /// Size of the buffer to use
115         /// </param>
116         public GZipInputStream(Stream baseInputStream, int size)
117             : base(baseInputStream, new Inflater(true), size)
118         {
119         }
120
121         #endregion
122
123         #region Stream overrides
124
125         /// <summary>
126         /// Reads uncompressed data into an array of bytes
127         /// </summary>
128         /// <param name="buffer">
129         /// The buffer to read uncompressed data into
130         /// </param>
131         /// <param name="offset">
132         /// The offset indicating where the data should be placed
133         /// </param>
134         /// <param name="count">
135         /// The number of uncompressed bytes to be read
136         /// </param>
137         /// <returns>Returns the number of bytes actually read.</returns>
138         public override int Read(byte[] buffer, int offset, int count)
139         {
140             // We first have to read the GZIP header, then we feed all the
141             // rest of the data to the base class.
142             //
143             // As we do that we continually update the CRC32. Once the data is
144             // finished, we check the CRC32
145             //
146             // This means we don't need our own buffer, as everything is done
147             // in baseInputStream the superclass.
148             if (!readGZIPHeader)
149             {
150                 ReadHeader();
151             }
152
153             if (eos)
154             {
155                 return 0;
156             }
157
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);
160             if (bytesRead > 0)
161             {
162                 crc.Update(buffer, offset, bytesRead);
163             }
164
165             if (inf.IsFinished)
166             {
167                 ReadFooter();
168             }
169             return bytesRead;
170         }
171
172         #endregion
173
174         #region Support routines
175
176         private void ReadHeader()
177         {
178             // 1. Check the two magic bytes
179             var headCRC = new Crc32();
180             var magic = baseInputStream.ReadByte();
181
182             if (magic < 0)
183             {
184                 throw new EndOfStreamException("EOS reading GZIP header");
185             }
186
187             headCRC.Update(magic);
188             if (magic != (GZipConstants.GZIP_MAGIC >> 8))
189             {
190                 throw new GZipException("Error GZIP header, first magic byte doesn't match");
191             }
192
193             magic = baseInputStream.ReadByte();
194
195             if (magic < 0)
196             {
197                 throw new EndOfStreamException("EOS reading GZIP header");
198             }
199
200             if (magic != (GZipConstants.GZIP_MAGIC & 0xFF))
201             {
202                 throw new GZipException("Error GZIP header,  second magic byte doesn't match");
203             }
204
205             headCRC.Update(magic);
206
207             // 2. Check the compression type (must be 8)
208             var compressionType = baseInputStream.ReadByte();
209
210             if (compressionType < 0)
211             {
212                 throw new EndOfStreamException("EOS reading GZIP header");
213             }
214
215             if (compressionType != 8)
216             {
217                 throw new GZipException("Error GZIP header, data not in deflate format");
218             }
219             headCRC.Update(compressionType);
220
221             // 3. Check the flags
222             var flags = baseInputStream.ReadByte();
223             if (flags < 0)
224             {
225                 throw new EndOfStreamException("EOS reading GZIP header");
226             }
227             headCRC.Update(flags);
228
229             /*    This flag byte is divided into individual bits as follows:
230                                 
231                                 bit 0   FTEXT
232                                 bit 1   FHCRC
233                                 bit 2   FEXTRA
234                                 bit 3   FNAME
235                                 bit 4   FCOMMENT
236                                 bit 5   reserved
237                                 bit 6   reserved
238                                 bit 7   reserved
239                         */
240
241             // 3.1 Check the reserved bits are zero
242
243             if ((flags & 0xd0) != 0)
244             {
245                 throw new GZipException("Reserved flag bits in GZIP header != 0");
246             }
247
248             // 4.-6. Skip the modification time, extra flags, and OS type
249             for (var i = 0; i < 6; i++)
250             {
251                 var readByte = baseInputStream.ReadByte();
252                 if (readByte < 0)
253                 {
254                     throw new EndOfStreamException("EOS reading GZIP header");
255                 }
256                 headCRC.Update(readByte);
257             }
258
259             // 7. Read extra field
260             if ((flags & GZipConstants.FEXTRA) != 0)
261             {
262                 // Skip subfield id
263                 for (var i = 0; i < 2; i++)
264                 {
265                     var readByte = baseInputStream.ReadByte();
266                     if (readByte < 0)
267                     {
268                         throw new EndOfStreamException("EOS reading GZIP header");
269                     }
270                     headCRC.Update(readByte);
271                 }
272
273                 if (baseInputStream.ReadByte() < 0 || baseInputStream.ReadByte() < 0)
274                 {
275                     throw new EndOfStreamException("EOS reading GZIP header");
276                 }
277
278                 var len1 = baseInputStream.ReadByte();
279                 var len2 = baseInputStream.ReadByte();
280                 if ((len1 < 0) || (len2 < 0))
281                 {
282                     throw new EndOfStreamException("EOS reading GZIP header");
283                 }
284                 headCRC.Update(len1);
285                 headCRC.Update(len2);
286
287                 var extraLen = (len1 << 8) | len2;
288                 for (var i = 0; i < extraLen; i++)
289                 {
290                     var readByte = baseInputStream.ReadByte();
291                     if (readByte < 0)
292                     {
293                         throw new EndOfStreamException("EOS reading GZIP header");
294                     }
295                     headCRC.Update(readByte);
296                 }
297             }
298
299             // 8. Read file name
300             if ((flags & GZipConstants.FNAME) != 0)
301             {
302                 int readByte;
303                 while ((readByte = baseInputStream.ReadByte()) > 0)
304                 {
305                     headCRC.Update(readByte);
306                 }
307
308                 if (readByte < 0)
309                 {
310                     throw new EndOfStreamException("EOS reading GZIP header");
311                 }
312                 headCRC.Update(readByte);
313             }
314
315             // 9. Read comment
316             if ((flags & GZipConstants.FCOMMENT) != 0)
317             {
318                 int readByte;
319                 while ((readByte = baseInputStream.ReadByte()) > 0)
320                 {
321                     headCRC.Update(readByte);
322                 }
323
324                 if (readByte < 0)
325                 {
326                     throw new EndOfStreamException("EOS reading GZIP header");
327                 }
328
329                 headCRC.Update(readByte);
330             }
331
332             // 10. Read header CRC
333             if ((flags & GZipConstants.FHCRC) != 0)
334             {
335                 var crcval = baseInputStream.ReadByte();
336                 if (crcval < 0)
337                 {
338                     throw new EndOfStreamException("EOS reading GZIP header");
339                 }
340
341                 int tempByte = baseInputStream.ReadByte();
342                 if (tempByte < 0)
343                 {
344                     throw new EndOfStreamException("EOS reading GZIP header");
345                 }
346
347                 crcval = (crcval << 8) | tempByte;
348                 if (crcval != ((int) headCRC.Value & 0xffff))
349                 {
350                     throw new GZipException("Header CRC value mismatch");
351                 }
352             }
353
354             readGZIPHeader = true;
355         }
356
357         private void ReadFooter()
358         {
359             var footer = new byte[8];
360             var avail = inf.RemainingInput;
361
362             if (avail > 8)
363             {
364                 avail = 8;
365             }
366
367             Array.Copy(inputBuffer.RawData, inputBuffer.RawLength - inf.RemainingInput, footer, 0, avail);
368             var needed = 8 - avail;
369
370             while (needed > 0)
371             {
372                 var count = baseInputStream.Read(footer, 8 - needed, needed);
373                 if (count <= 0)
374                 {
375                     throw new EndOfStreamException("EOS reading GZIP footer");
376                 }
377                 needed -= count; // Jewel Jan 16
378             }
379
380             var crcval = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8) | ((footer[2] & 0xff) << 16) | (footer[3] << 24);
381             if (crcval != (int) crc.Value)
382             {
383                 throw new GZipException("GZIP crc sum mismatch, theirs \"" + crcval + "\" and ours \"" + (int) crc.Value);
384             }
385
386             // NOTE The total here is the original total modulo 2 ^ 32.
387             var total =
388                 ((uint) footer[4] & 0xff) |
389                 (((uint) footer[5] & 0xff) << 8) |
390                 (((uint) footer[6] & 0xff) << 16) |
391                 ((uint) footer[7] << 24);
392
393             if ((inf.TotalOut & 0xffffffff) != total)
394             {
395                 throw new GZipException("Number of bytes mismatch in footer");
396             }
397
398             // Should we support multiple gzip members.
399             // Difficult, since there may be some bytes still in baseInputStream dataBuffer
400             eos = true;
401         }
402
403         #endregion
404     }
405 }