Statistics
| Branch: | Revision:

root / trunk / hammock / src / net35 / ICSharpCode.SharpZipLib.Silverlight / GZip / GzipInputStream.cs @ 0eea575a

History | View | Annotate | Download (13.2 kB)

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
}