root / trunk / hammock / src / net35 / ICSharpCode.SharpZipLib.Silverlight / Zip / Compression / Inflater.cs @ 0eea575a
History | View | Annotate | Download (30.8 kB)
1 |
// Inflater.cs |
---|---|
2 |
// |
3 |
// Copyright (C) 2001 Mike Krueger |
4 |
// Copyright (C) 2004 John Reilly |
5 |
// |
6 |
// This file was translated from java, it was part of the GNU Classpath |
7 |
// Copyright (C) 2001 Free Software Foundation, Inc. |
8 |
// |
9 |
// This program is free software; you can redistribute it and/or |
10 |
// modify it under the terms of the GNU General Public License |
11 |
// as published by the Free Software Foundation; either version 2 |
12 |
// of the License, or (at your option) any later version. |
13 |
// |
14 |
// This program is distributed in the hope that it will be useful, |
15 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 |
// GNU General Public License for more details. |
18 |
// |
19 |
// You should have received a copy of the GNU General Public License |
20 |
// along with this program; if not, write to the Free Software |
21 |
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
22 |
// |
23 |
// Linking this library statically or dynamically with other modules is |
24 |
// making a combined work based on this library. Thus, the terms and |
25 |
// conditions of the GNU General Public License cover the whole |
26 |
// combination. |
27 |
// |
28 |
// As a special exception, the copyright holders of this library give you |
29 |
// permission to link this library with independent modules to produce an |
30 |
// executable, regardless of the license terms of these independent |
31 |
// modules, and to copy and distribute the resulting executable under |
32 |
// terms of your choice, provided that you also meet, for each linked |
33 |
// independent module, the terms and conditions of the license of that |
34 |
// module. An independent module is a module which is not derived from |
35 |
// or based on this library. If you modify this library, you may extend |
36 |
// this exception to your version of the library, but you are not |
37 |
// obligated to do so. If you do not wish to do so, delete this |
38 |
// exception statement from your version. |
39 |
|
40 |
using System; |
41 |
using ICSharpCode.SharpZipLib.Silverlight.Checksums; |
42 |
using ICSharpCode.SharpZipLib.Silverlight.Zip.Compression; |
43 |
using ICSharpCode.SharpZipLib.Silverlight.Zip.Compression.Streams; |
44 |
|
45 |
namespace ICSharpCode.SharpZipLib.Silverlight.Zip.Compression |
46 |
{ |
47 |
/// <summary> |
48 |
/// Inflater is used to decompress data that has been compressed according |
49 |
/// to the "deflate" standard described in rfc1951. |
50 |
/// |
51 |
/// By default Zlib (rfc1950) headers and footers are expected in the input. |
52 |
/// You can use constructor <code> public Inflater(bool noHeader)</code> passing true |
53 |
/// if there is no Zlib header information |
54 |
/// |
55 |
/// The usage is as following. First you have to set some input with |
56 |
/// <code>SetInput()</code>, then Inflate() it. If inflate doesn't |
57 |
/// inflate any bytes there may be three reasons: |
58 |
/// <ul> |
59 |
/// <li>IsNeedingInput() returns true because the input buffer is empty. |
60 |
/// You have to provide more input with <code>SetInput()</code>. |
61 |
/// NOTE: IsNeedingInput() also returns true when, the stream is finished. |
62 |
/// </li> |
63 |
/// <li>IsNeedingDictionary() returns true, you have to provide a preset |
64 |
/// dictionary with <code>SetDictionary()</code>.</li> |
65 |
/// <li>IsFinished returns true, the inflater has finished.</li> |
66 |
/// </ul> |
67 |
/// Once the first output byte is produced, a dictionary will not be |
68 |
/// needed at a later stage. |
69 |
/// |
70 |
/// author of the original java version : John Leuner, Jochen Hoenicke |
71 |
/// </summary> |
72 |
public class Inflater |
73 |
{ |
74 |
#region Constants/Readonly |
75 |
/// <summary> |
76 |
/// Copy lengths for literal codes 257..285 |
77 |
/// </summary> |
78 |
static readonly int[] CPLENS = { |
79 |
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, |
80 |
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 |
81 |
}; |
82 |
|
83 |
/// <summary> |
84 |
/// Extra bits for literal codes 257..285 |
85 |
/// </summary> |
86 |
static readonly int[] CPLEXT = { |
87 |
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, |
88 |
3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 |
89 |
}; |
90 |
|
91 |
/// <summary> |
92 |
/// Copy offsets for distance codes 0..29 |
93 |
/// </summary> |
94 |
static readonly int[] CPDIST = { |
95 |
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, |
96 |
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, |
97 |
8193, 12289, 16385, 24577 |
98 |
}; |
99 |
|
100 |
/// <summary> |
101 |
/// Extra bits for distance codes |
102 |
/// </summary> |
103 |
static readonly int[] CPDEXT = { |
104 |
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, |
105 |
7, 7, 8, 8, 9, 9, 10, 10, 11, 11, |
106 |
12, 12, 13, 13 |
107 |
}; |
108 |
|
109 |
/// <summary> |
110 |
/// These are the possible states for an inflater |
111 |
/// </summary> |
112 |
const int DECODE_HEADER = 0; |
113 |
const int DECODE_DICT = 1; |
114 |
const int DECODE_BLOCKS = 2; |
115 |
const int DECODE_STORED_LEN1 = 3; |
116 |
const int DECODE_STORED_LEN2 = 4; |
117 |
const int DECODE_STORED = 5; |
118 |
const int DECODE_DYN_HEADER = 6; |
119 |
const int DECODE_HUFFMAN = 7; |
120 |
const int DECODE_HUFFMAN_LENBITS = 8; |
121 |
const int DECODE_HUFFMAN_DIST = 9; |
122 |
const int DECODE_HUFFMAN_DISTBITS = 10; |
123 |
const int DECODE_CHKSUM = 11; |
124 |
const int FINISHED = 12; |
125 |
#endregion |
126 |
|
127 |
#region Instance Fields |
128 |
/// <summary> |
129 |
/// This variable contains the current state. |
130 |
/// </summary> |
131 |
int mode; |
132 |
|
133 |
/// <summary> |
134 |
/// The adler checksum of the dictionary or of the decompressed |
135 |
/// stream, as it is written in the header resp. footer of the |
136 |
/// compressed stream. |
137 |
/// Only valid if mode is DECODE_DICT or DECODE_CHKSUM. |
138 |
/// </summary> |
139 |
int readAdler; |
140 |
|
141 |
/// <summary> |
142 |
/// The number of bits needed to complete the current state. This |
143 |
/// is valid, if mode is DECODE_DICT, DECODE_CHKSUM, |
144 |
/// DECODE_HUFFMAN_LENBITS or DECODE_HUFFMAN_DISTBITS. |
145 |
/// </summary> |
146 |
int neededBits; |
147 |
int repLength; |
148 |
int repDist; |
149 |
int uncomprLen; |
150 |
|
151 |
/// <summary> |
152 |
/// True, if the last block flag was set in the last block of the |
153 |
/// inflated stream. This means that the stream ends after the |
154 |
/// current block. |
155 |
/// </summary> |
156 |
bool isLastBlock; |
157 |
|
158 |
/// <summary> |
159 |
/// The total number of inflated bytes. |
160 |
/// </summary> |
161 |
long totalOut; |
162 |
|
163 |
/// <summary> |
164 |
/// The total number of bytes set with setInput(). This is not the |
165 |
/// value returned by the TotalIn property, since this also includes the |
166 |
/// unprocessed input. |
167 |
/// </summary> |
168 |
long totalIn; |
169 |
|
170 |
/// <summary> |
171 |
/// This variable stores the noHeader flag that was given to the constructor. |
172 |
/// True means, that the inflated stream doesn't contain a Zlib header or |
173 |
/// footer. |
174 |
/// </summary> |
175 |
bool noHeader; |
176 |
|
177 |
StreamManipulator input; |
178 |
OutputWindow outputWindow; |
179 |
InflaterDynHeader dynHeader; |
180 |
InflaterHuffmanTree litlenTree, distTree; |
181 |
Adler32 adler; |
182 |
#endregion |
183 |
|
184 |
#region Constructors |
185 |
/// <summary> |
186 |
/// Creates a new inflater or RFC1951 decompressor |
187 |
/// RFC1950/Zlib headers and footers will be expected in the input data |
188 |
/// </summary> |
189 |
public Inflater() : this(false) |
190 |
{ |
191 |
} |
192 |
|
193 |
/// <summary> |
194 |
/// Creates a new inflater. |
195 |
/// </summary> |
196 |
/// <param name="noHeader"> |
197 |
/// True if no RFC1950/Zlib header and footer fields are expected in the input data |
198 |
/// |
199 |
/// This is used for GZIPed/Zipped input. |
200 |
/// |
201 |
/// For compatibility with |
202 |
/// Sun JDK you should provide one byte of input more than needed in |
203 |
/// this case. |
204 |
/// </param> |
205 |
public Inflater(bool noHeader) |
206 |
{ |
207 |
this.noHeader = noHeader; |
208 |
this.adler = new Adler32(); |
209 |
input = new StreamManipulator(); |
210 |
outputWindow = new OutputWindow(); |
211 |
mode = noHeader ? DECODE_BLOCKS : DECODE_HEADER; |
212 |
} |
213 |
#endregion |
214 |
|
215 |
/// <summary> |
216 |
/// Resets the inflater so that a new stream can be decompressed. All |
217 |
/// pending input and output will be discarded. |
218 |
/// </summary> |
219 |
public void Reset() |
220 |
{ |
221 |
mode = noHeader ? DECODE_BLOCKS : DECODE_HEADER; |
222 |
totalIn = 0; |
223 |
totalOut = 0; |
224 |
input.Reset(); |
225 |
outputWindow.Reset(); |
226 |
dynHeader = null; |
227 |
litlenTree = null; |
228 |
distTree = null; |
229 |
isLastBlock = false; |
230 |
adler.Reset(); |
231 |
} |
232 |
|
233 |
/// <summary> |
234 |
/// Decodes a zlib/RFC1950 header. |
235 |
/// </summary> |
236 |
/// <returns> |
237 |
/// False if more input is needed. |
238 |
/// </returns> |
239 |
/// <exception cref="SharpZipBaseException"> |
240 |
/// The header is invalid. |
241 |
/// </exception> |
242 |
private bool DecodeHeader() |
243 |
{ |
244 |
int header = input.PeekBits(16); |
245 |
if (header < 0) { |
246 |
return false; |
247 |
} |
248 |
input.DropBits(16); |
249 |
|
250 |
// The header is written in "wrong" byte order |
251 |
header = ((header << 8) | (header >> 8)) & 0xffff; |
252 |
if (header % 31 != 0) { |
253 |
throw new SharpZipBaseException("Header checksum illegal"); |
254 |
} |
255 |
|
256 |
if ((header & 0x0f00) != (Deflater.DEFLATED << 8)) { |
257 |
throw new SharpZipBaseException("Compression Method unknown"); |
258 |
} |
259 |
|
260 |
/* Maximum size of the backwards window in bits. |
261 |
* We currently ignore this, but we could use it to make the |
262 |
* inflater window more space efficient. On the other hand the |
263 |
* full window (15 bits) is needed most times, anyway. |
264 |
int max_wbits = ((header & 0x7000) >> 12) + 8; |
265 |
*/ |
266 |
|
267 |
if ((header & 0x0020) == 0) { // Dictionary flag? |
268 |
mode = DECODE_BLOCKS; |
269 |
} else { |
270 |
mode = DECODE_DICT; |
271 |
neededBits = 32; |
272 |
} |
273 |
return true; |
274 |
} |
275 |
|
276 |
/// <summary> |
277 |
/// Decodes the dictionary checksum after the deflate header. |
278 |
/// </summary> |
279 |
/// <returns> |
280 |
/// False if more input is needed. |
281 |
/// </returns> |
282 |
private bool DecodeDict() |
283 |
{ |
284 |
while (neededBits > 0) { |
285 |
int dictByte = input.PeekBits(8); |
286 |
if (dictByte < 0) { |
287 |
return false; |
288 |
} |
289 |
input.DropBits(8); |
290 |
readAdler = (readAdler << 8) | dictByte; |
291 |
neededBits -= 8; |
292 |
} |
293 |
return false; |
294 |
} |
295 |
|
296 |
/// <summary> |
297 |
/// Decodes the huffman encoded symbols in the input stream. |
298 |
/// </summary> |
299 |
/// <returns> |
300 |
/// false if more input is needed, true if output window is |
301 |
/// full or the current block ends. |
302 |
/// </returns> |
303 |
/// <exception cref="SharpZipBaseException"> |
304 |
/// if deflated stream is invalid. |
305 |
/// </exception> |
306 |
private bool DecodeHuffman() |
307 |
{ |
308 |
int free = outputWindow.GetFreeSpace(); |
309 |
while (free >= 258) |
310 |
{ |
311 |
int symbol; |
312 |
switch (mode) |
313 |
{ |
314 |
case DECODE_HUFFMAN: |
315 |
// This is the inner loop so it is optimized a bit |
316 |
while (((symbol = litlenTree.GetSymbol(input)) & ~0xff) == 0) |
317 |
{ |
318 |
outputWindow.Write(symbol); |
319 |
if (--free < 258) |
320 |
{ |
321 |
return true; |
322 |
} |
323 |
} |
324 |
|
325 |
if (symbol < 257) |
326 |
{ |
327 |
if (symbol < 0) |
328 |
{ |
329 |
return false; |
330 |
} |
331 |
else |
332 |
{ |
333 |
// symbol == 256: end of block |
334 |
distTree = null; |
335 |
litlenTree = null; |
336 |
mode = DECODE_BLOCKS; |
337 |
return true; |
338 |
} |
339 |
} |
340 |
|
341 |
try |
342 |
{ |
343 |
repLength = CPLENS[symbol - 257]; |
344 |
neededBits = CPLEXT[symbol - 257]; |
345 |
} |
346 |
catch (Exception) |
347 |
{ |
348 |
throw new SharpZipBaseException("Illegal rep length code"); |
349 |
} |
350 |
goto case DECODE_HUFFMAN_LENBITS; // fall through |
351 |
|
352 |
case DECODE_HUFFMAN_LENBITS: |
353 |
if (neededBits > 0) |
354 |
{ |
355 |
mode = DECODE_HUFFMAN_LENBITS; |
356 |
int i = input.PeekBits(neededBits); |
357 |
if (i < 0) |
358 |
{ |
359 |
return false; |
360 |
} |
361 |
input.DropBits(neededBits); |
362 |
repLength += i; |
363 |
} |
364 |
mode = DECODE_HUFFMAN_DIST; |
365 |
goto case DECODE_HUFFMAN_DIST; // fall through |
366 |
|
367 |
case DECODE_HUFFMAN_DIST: |
368 |
symbol = distTree.GetSymbol(input); |
369 |
if (symbol < 0) |
370 |
{ |
371 |
return false; |
372 |
} |
373 |
|
374 |
try |
375 |
{ |
376 |
repDist = CPDIST[symbol]; |
377 |
neededBits = CPDEXT[symbol]; |
378 |
} |
379 |
catch (Exception) |
380 |
{ |
381 |
throw new SharpZipBaseException("Illegal rep dist code"); |
382 |
} |
383 |
|
384 |
goto case DECODE_HUFFMAN_DISTBITS; // fall through |
385 |
|
386 |
case DECODE_HUFFMAN_DISTBITS: |
387 |
if (neededBits > 0) |
388 |
{ |
389 |
mode = DECODE_HUFFMAN_DISTBITS; |
390 |
int i = input.PeekBits(neededBits); |
391 |
if (i < 0) |
392 |
{ |
393 |
return false; |
394 |
} |
395 |
input.DropBits(neededBits); |
396 |
repDist += i; |
397 |
} |
398 |
|
399 |
outputWindow.Repeat(repLength, repDist); |
400 |
free -= repLength; |
401 |
mode = DECODE_HUFFMAN; |
402 |
break; |
403 |
|
404 |
default: |
405 |
throw new SharpZipBaseException("Inflater unknown mode"); |
406 |
} |
407 |
} |
408 |
return true; |
409 |
} |
410 |
|
411 |
/// <summary> |
412 |
/// Decodes the adler checksum after the deflate stream. |
413 |
/// </summary> |
414 |
/// <returns> |
415 |
/// false if more input is needed. |
416 |
/// </returns> |
417 |
/// <exception cref="SharpZipBaseException"> |
418 |
/// If checksum doesn't match. |
419 |
/// </exception> |
420 |
private bool DecodeChksum() |
421 |
{ |
422 |
while (neededBits > 0) { |
423 |
int chkByte = input.PeekBits(8); |
424 |
if (chkByte < 0) { |
425 |
return false; |
426 |
} |
427 |
input.DropBits(8); |
428 |
readAdler = (readAdler << 8) | chkByte; |
429 |
neededBits -= 8; |
430 |
} |
431 |
|
432 |
if ((int) adler.Value != readAdler) { |
433 |
throw new SharpZipBaseException("Adler chksum doesn't match: " + (int)adler.Value + " vs. " + readAdler); |
434 |
} |
435 |
|
436 |
mode = FINISHED; |
437 |
return false; |
438 |
} |
439 |
|
440 |
/// <summary> |
441 |
/// Decodes the deflated stream. |
442 |
/// </summary> |
443 |
/// <returns> |
444 |
/// false if more input is needed, or if finished. |
445 |
/// </returns> |
446 |
/// <exception cref="SharpZipBaseException"> |
447 |
/// if deflated stream is invalid. |
448 |
/// </exception> |
449 |
private bool Decode() |
450 |
{ |
451 |
switch (mode) { |
452 |
case DECODE_HEADER: |
453 |
return DecodeHeader(); |
454 |
|
455 |
case DECODE_DICT: |
456 |
return DecodeDict(); |
457 |
|
458 |
case DECODE_CHKSUM: |
459 |
return DecodeChksum(); |
460 |
|
461 |
case DECODE_BLOCKS: |
462 |
if (isLastBlock) { |
463 |
if (noHeader) { |
464 |
mode = FINISHED; |
465 |
return false; |
466 |
} else { |
467 |
input.SkipToByteBoundary(); |
468 |
neededBits = 32; |
469 |
mode = DECODE_CHKSUM; |
470 |
return true; |
471 |
} |
472 |
} |
473 |
|
474 |
int type = input.PeekBits(3); |
475 |
if (type < 0) { |
476 |
return false; |
477 |
} |
478 |
input.DropBits(3); |
479 |
|
480 |
if ((type & 1) != 0) { |
481 |
isLastBlock = true; |
482 |
} |
483 |
switch (type >> 1){ |
484 |
case DeflaterConstants.STORED_BLOCK: |
485 |
input.SkipToByteBoundary(); |
486 |
mode = DECODE_STORED_LEN1; |
487 |
break; |
488 |
case DeflaterConstants.STATIC_TREES: |
489 |
litlenTree = InflaterHuffmanTree.defLitLenTree; |
490 |
distTree = InflaterHuffmanTree.defDistTree; |
491 |
mode = DECODE_HUFFMAN; |
492 |
break; |
493 |
case DeflaterConstants.DYN_TREES: |
494 |
dynHeader = new InflaterDynHeader(); |
495 |
mode = DECODE_DYN_HEADER; |
496 |
break; |
497 |
default: |
498 |
throw new SharpZipBaseException("Unknown block type " + type); |
499 |
} |
500 |
return true; |
501 |
|
502 |
case DECODE_STORED_LEN1: |
503 |
{ |
504 |
if ((uncomprLen = input.PeekBits(16)) < 0) { |
505 |
return false; |
506 |
} |
507 |
input.DropBits(16); |
508 |
mode = DECODE_STORED_LEN2; |
509 |
} |
510 |
goto case DECODE_STORED_LEN2; // fall through |
511 |
|
512 |
case DECODE_STORED_LEN2: |
513 |
{ |
514 |
int nlen = input.PeekBits(16); |
515 |
if (nlen < 0) { |
516 |
return false; |
517 |
} |
518 |
input.DropBits(16); |
519 |
if (nlen != (uncomprLen ^ 0xffff)) { |
520 |
throw new SharpZipBaseException("broken uncompressed block"); |
521 |
} |
522 |
mode = DECODE_STORED; |
523 |
} |
524 |
goto case DECODE_STORED; // fall through |
525 |
|
526 |
case DECODE_STORED: |
527 |
{ |
528 |
int more = outputWindow.CopyStored(input, uncomprLen); |
529 |
uncomprLen -= more; |
530 |
if (uncomprLen == 0) { |
531 |
mode = DECODE_BLOCKS; |
532 |
return true; |
533 |
} |
534 |
return !input.IsNeedingInput; |
535 |
} |
536 |
|
537 |
case DECODE_DYN_HEADER: |
538 |
if (!dynHeader.Decode(input)) { |
539 |
return false; |
540 |
} |
541 |
|
542 |
litlenTree = dynHeader.BuildLitLenTree(); |
543 |
distTree = dynHeader.BuildDistTree(); |
544 |
mode = DECODE_HUFFMAN; |
545 |
goto case DECODE_HUFFMAN; // fall through |
546 |
|
547 |
case DECODE_HUFFMAN: |
548 |
case DECODE_HUFFMAN_LENBITS: |
549 |
case DECODE_HUFFMAN_DIST: |
550 |
case DECODE_HUFFMAN_DISTBITS: |
551 |
return DecodeHuffman(); |
552 |
|
553 |
case FINISHED: |
554 |
return false; |
555 |
|
556 |
default: |
557 |
throw new SharpZipBaseException("Inflater.Decode unknown mode"); |
558 |
} |
559 |
} |
560 |
|
561 |
/// <summary> |
562 |
/// Sets the preset dictionary. This should only be called, if |
563 |
/// needsDictionary() returns true and it should set the same |
564 |
/// dictionary, that was used for deflating. The getAdler() |
565 |
/// function returns the checksum of the dictionary needed. |
566 |
/// </summary> |
567 |
/// <param name="buffer"> |
568 |
/// The dictionary. |
569 |
/// </param> |
570 |
public void SetDictionary(byte[] buffer) |
571 |
{ |
572 |
SetDictionary(buffer, 0, buffer.Length); |
573 |
} |
574 |
|
575 |
/// <summary> |
576 |
/// Sets the preset dictionary. This should only be called, if |
577 |
/// needsDictionary() returns true and it should set the same |
578 |
/// dictionary, that was used for deflating. The getAdler() |
579 |
/// function returns the checksum of the dictionary needed. |
580 |
/// </summary> |
581 |
/// <param name="buffer"> |
582 |
/// The dictionary. |
583 |
/// </param> |
584 |
/// <param name="index"> |
585 |
/// The index into buffer where the dictionary starts. |
586 |
/// </param> |
587 |
/// <param name="count"> |
588 |
/// The number of bytes in the dictionary. |
589 |
/// </param> |
590 |
/// <exception cref="System.InvalidOperationException"> |
591 |
/// No dictionary is needed. |
592 |
/// </exception> |
593 |
/// <exception cref="SharpZipBaseException"> |
594 |
/// The adler checksum for the buffer is invalid |
595 |
/// </exception> |
596 |
public void SetDictionary(byte[] buffer, int index, int count) |
597 |
{ |
598 |
if ( buffer == null ) { |
599 |
throw new ArgumentNullException("buffer"); |
600 |
} |
601 |
|
602 |
if ( index < 0 ) { |
603 |
throw new ArgumentOutOfRangeException("index"); |
604 |
} |
605 |
|
606 |
if ( count < 0 ) { |
607 |
throw new ArgumentOutOfRangeException("count"); |
608 |
} |
609 |
|
610 |
if (!IsNeedingDictionary) { |
611 |
throw new InvalidOperationException("Dictionary is not needed"); |
612 |
} |
613 |
|
614 |
adler.Update(buffer, index, count); |
615 |
|
616 |
if ((int)adler.Value != readAdler) { |
617 |
throw new SharpZipBaseException("Wrong adler checksum"); |
618 |
} |
619 |
adler.Reset(); |
620 |
outputWindow.CopyDict(buffer, index, count); |
621 |
mode = DECODE_BLOCKS; |
622 |
} |
623 |
|
624 |
/// <summary> |
625 |
/// Sets the input. This should only be called, if needsInput() |
626 |
/// returns true. |
627 |
/// </summary> |
628 |
/// <param name="buffer"> |
629 |
/// the input. |
630 |
/// </param> |
631 |
public void SetInput(byte[] buffer) |
632 |
{ |
633 |
SetInput(buffer, 0, buffer.Length); |
634 |
} |
635 |
|
636 |
/// <summary> |
637 |
/// Sets the input. This should only be called, if needsInput() |
638 |
/// returns true. |
639 |
/// </summary> |
640 |
/// <param name="buffer"> |
641 |
/// The source of input data |
642 |
/// </param> |
643 |
/// <param name="index"> |
644 |
/// The index into buffer where the input starts. |
645 |
/// </param> |
646 |
/// <param name="count"> |
647 |
/// The number of bytes of input to use. |
648 |
/// </param> |
649 |
/// <exception cref="System.InvalidOperationException"> |
650 |
/// No input is needed. |
651 |
/// </exception> |
652 |
/// <exception cref="System.ArgumentOutOfRangeException"> |
653 |
/// The index and/or count are wrong. |
654 |
/// </exception> |
655 |
public void SetInput(byte[] buffer, int index, int count) |
656 |
{ |
657 |
input.SetInput(buffer, index, count); |
658 |
totalIn += (long)count; |
659 |
} |
660 |
|
661 |
/// <summary> |
662 |
/// Inflates the compressed stream to the output buffer. If this |
663 |
/// returns 0, you should check, whether IsNeedingDictionary(), |
664 |
/// IsNeedingInput() or IsFinished() returns true, to determine why no |
665 |
/// further output is produced. |
666 |
/// </summary> |
667 |
/// <param name="buffer"> |
668 |
/// the output buffer. |
669 |
/// </param> |
670 |
/// <returns> |
671 |
/// The number of bytes written to the buffer, 0 if no further |
672 |
/// output can be produced. |
673 |
/// </returns> |
674 |
/// <exception cref="System.ArgumentOutOfRangeException"> |
675 |
/// if buffer has length 0. |
676 |
/// </exception> |
677 |
/// <exception cref="System.FormatException"> |
678 |
/// if deflated stream is invalid. |
679 |
/// </exception> |
680 |
public int Inflate(byte[] buffer) |
681 |
{ |
682 |
if ( buffer == null ) |
683 |
{ |
684 |
throw new ArgumentNullException("buffer"); |
685 |
} |
686 |
|
687 |
return Inflate(buffer, 0, buffer.Length); |
688 |
} |
689 |
|
690 |
/// <summary> |
691 |
/// Inflates the compressed stream to the output buffer. If this |
692 |
/// returns 0, you should check, whether needsDictionary(), |
693 |
/// needsInput() or finished() returns true, to determine why no |
694 |
/// further output is produced. |
695 |
/// </summary> |
696 |
/// <param name="buffer"> |
697 |
/// the output buffer. |
698 |
/// </param> |
699 |
/// <param name="offset"> |
700 |
/// the offset in buffer where storing starts. |
701 |
/// </param> |
702 |
/// <param name="count"> |
703 |
/// the maximum number of bytes to output. |
704 |
/// </param> |
705 |
/// <returns> |
706 |
/// the number of bytes written to the buffer, 0 if no further output can be produced. |
707 |
/// </returns> |
708 |
/// <exception cref="System.ArgumentOutOfRangeException"> |
709 |
/// if count is less than 0. |
710 |
/// </exception> |
711 |
/// <exception cref="System.ArgumentOutOfRangeException"> |
712 |
/// if the index and / or count are wrong. |
713 |
/// </exception> |
714 |
/// <exception cref="System.FormatException"> |
715 |
/// if deflated stream is invalid. |
716 |
/// </exception> |
717 |
public int Inflate(byte[] buffer, int offset, int count) |
718 |
{ |
719 |
if ( buffer == null ) |
720 |
{ |
721 |
throw new ArgumentNullException("buffer"); |
722 |
} |
723 |
|
724 |
if ( count < 0 ) { |
725 |
throw new ArgumentOutOfRangeException("count", "count cannot be negative"); |
726 |
} |
727 |
|
728 |
if ( offset < 0 ) { |
729 |
throw new ArgumentOutOfRangeException("offset", "offset cannot be negative"); |
730 |
} |
731 |
|
732 |
if ( offset + count > buffer.Length ) { |
733 |
throw new ArgumentException("count exceeds buffer bounds"); |
734 |
} |
735 |
|
736 |
// Special case: count may be zero |
737 |
if (count == 0) |
738 |
{ |
739 |
if (!IsFinished) { // -jr- 08-Nov-2003 INFLATE_BUG fix.. |
740 |
Decode(); |
741 |
} |
742 |
return 0; |
743 |
} |
744 |
|
745 |
int bytesCopied = 0; |
746 |
|
747 |
do { |
748 |
if (mode != DECODE_CHKSUM) { |
749 |
/* Don't give away any output, if we are waiting for the |
750 |
* checksum in the input stream. |
751 |
* |
752 |
* With this trick we have always: |
753 |
* IsNeedingInput() and not IsFinished() |
754 |
* implies more output can be produced. |
755 |
*/ |
756 |
int more = outputWindow.CopyOutput(buffer, offset, count); |
757 |
if ( more > 0 ) { |
758 |
adler.Update(buffer, offset, more); |
759 |
offset += more; |
760 |
bytesCopied += more; |
761 |
totalOut += (long)more; |
762 |
count -= more; |
763 |
if (count == 0) { |
764 |
return bytesCopied; |
765 |
} |
766 |
} |
767 |
} |
768 |
} while (Decode() || ((outputWindow.GetAvailable() > 0) && (mode != DECODE_CHKSUM))); |
769 |
return bytesCopied; |
770 |
} |
771 |
|
772 |
/// <summary> |
773 |
/// Returns true, if the input buffer is empty. |
774 |
/// You should then call setInput(). |
775 |
/// NOTE: This method also returns true when the stream is finished. |
776 |
/// </summary> |
777 |
public bool IsNeedingInput { |
778 |
get { |
779 |
return input.IsNeedingInput; |
780 |
} |
781 |
} |
782 |
|
783 |
/// <summary> |
784 |
/// Returns true, if a preset dictionary is needed to inflate the input. |
785 |
/// </summary> |
786 |
public bool IsNeedingDictionary { |
787 |
get { |
788 |
return mode == DECODE_DICT && neededBits == 0; |
789 |
} |
790 |
} |
791 |
|
792 |
/// <summary> |
793 |
/// Returns true, if the inflater has finished. This means, that no |
794 |
/// input is needed and no output can be produced. |
795 |
/// </summary> |
796 |
public bool IsFinished { |
797 |
get { |
798 |
return mode == FINISHED && outputWindow.GetAvailable() == 0; |
799 |
} |
800 |
} |
801 |
|
802 |
/// <summary> |
803 |
/// Gets the adler checksum. This is either the checksum of all |
804 |
/// uncompressed bytes returned by inflate(), or if needsDictionary() |
805 |
/// returns true (and thus no output was yet produced) this is the |
806 |
/// adler checksum of the expected dictionary. |
807 |
/// </summary> |
808 |
/// <returns> |
809 |
/// the adler checksum. |
810 |
/// </returns> |
811 |
public int Adler { |
812 |
get { |
813 |
return IsNeedingDictionary ? readAdler : (int) adler.Value; |
814 |
} |
815 |
} |
816 |
|
817 |
/// <summary> |
818 |
/// Gets the total number of output bytes returned by Inflate(). |
819 |
/// </summary> |
820 |
/// <returns> |
821 |
/// the total number of output bytes. |
822 |
/// </returns> |
823 |
public long TotalOut { |
824 |
get { |
825 |
return totalOut; |
826 |
} |
827 |
} |
828 |
|
829 |
/// <summary> |
830 |
/// Gets the total number of processed compressed input bytes. |
831 |
/// </summary> |
832 |
/// <returns> |
833 |
/// The total number of bytes of processed input bytes. |
834 |
/// </returns> |
835 |
public long TotalIn { |
836 |
get { |
837 |
return totalIn - (long)RemainingInput; |
838 |
} |
839 |
} |
840 |
|
841 |
/// <summary> |
842 |
/// Gets the number of unprocessed input bytes. Useful, if the end of the |
843 |
/// stream is reached and you want to further process the bytes after |
844 |
/// the deflate stream. |
845 |
/// </summary> |
846 |
/// <returns> |
847 |
/// The number of bytes of the input which have not been processed. |
848 |
/// </returns> |
849 |
public int RemainingInput { |
850 |
// TODO: This should be a long? |
851 |
get { |
852 |
return input.AvailableBytes; |
853 |
} |
854 |
} |
855 |
} |
856 |
} |