Statistics
| Branch: | Revision:

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
}