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 |
} |