Statistics
| Branch: | Revision:

root / trunk / hammock / src / net35 / ICSharpCode.SharpZipLib.Silverlight / Zip / Compression / Deflater.cs @ 0eea575a

History | View | Annotate | Download (20.5 kB)

1
// Deflater.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

    
42
namespace ICSharpCode.SharpZipLib.Silverlight.Zip.Compression
43
{
44
    /// <summary>
45
    /// This is the Deflater class.  The deflater class compresses input
46
    /// with the deflate algorithm described in RFC 1951.  It has several
47
    /// compression levels and three different strategies described below.
48
    ///
49
    /// This class is <i>not</i> thread safe.  This is inherent in the API, due
50
    /// to the split of deflate and setInput.
51
    /// 
52
    /// author of the original java version : Jochen Hoenicke
53
    /// </summary>
54
    public class Deflater
55
    {
56
        #region Deflater Documentation
57
        /*
58
		* The Deflater can do the following state transitions:
59
		*
60
		* (1) -> INIT_STATE   ----> INIT_FINISHING_STATE ---.
61
		*        /  | (2)      (5)                          |
62
		*       /   v          (5)                          |
63
		*   (3)| SETDICT_STATE ---> SETDICT_FINISHING_STATE |(3)
64
		*       \   | (3)                 |        ,--------'
65
		*        |  |                     | (3)   /
66
		*        v  v          (5)        v      v
67
		* (1) -> BUSY_STATE   ----> FINISHING_STATE
68
		*                                | (6)
69
		*                                v
70
		*                           FINISHED_STATE
71
		*    \_____________________________________/
72
		*                    | (7)
73
		*                    v
74
		*               CLOSED_STATE
75
		*
76
		* (1) If we should produce a header we start in INIT_STATE, otherwise
77
		*     we start in BUSY_STATE.
78
		* (2) A dictionary may be set only when we are in INIT_STATE, then
79
		*     we change the state as indicated.
80
		* (3) Whether a dictionary is set or not, on the first call of deflate
81
		*     we change to BUSY_STATE.
82
		* (4) -- intentionally left blank -- :)
83
		* (5) FINISHING_STATE is entered, when flush() is called to indicate that
84
		*     there is no more INPUT.  There are also states indicating, that
85
		*     the header wasn't written yet.
86
		* (6) FINISHED_STATE is entered, when everything has been flushed to the
87
		*     internal pending output buffer.
88
		* (7) At any time (7)
89
		*
90
		*/
91
        #endregion
92
        #region Public Constants
93
        /// <summary>
94
        /// The best and slowest compression level.  This tries to find very
95
        /// long and distant string repetitions.
96
        /// </summary>
97
        public const  int BEST_COMPRESSION = 9;
98
		
99
        /// <summary>
100
        /// The worst but fastest compression level.
101
        /// </summary>
102
        public const  int BEST_SPEED = 1;
103
		
104
        /// <summary>
105
        /// The default compression level.
106
        /// </summary>
107
        public const  int DEFAULT_COMPRESSION = -1;
108
		
109
        /// <summary>
110
        /// This level won't compress at all but output uncompressed blocks.
111
        /// </summary>
112
        public const  int NO_COMPRESSION = 0;
113
				
114
        /// <summary>
115
        /// The compression method.  This is the only method supported so far.
116
        /// There is no need to use this constant at all.
117
        /// </summary>
118
        public const  int DEFLATED = 8;
119
        #endregion
120
        #region Local Constants
121
        private const  int IS_SETDICT              = 0x01;
122
        private const  int IS_FLUSHING             = 0x04;
123
        private const  int IS_FINISHING            = 0x08;
124
		
125
        private const  int INIT_STATE              = 0x00;
126
        private const  int SETDICT_STATE           = 0x01;
127
        //		private static  int INIT_FINISHING_STATE    = 0x08;
128
        //		private static  int SETDICT_FINISHING_STATE = 0x09;
129
        private const  int BUSY_STATE              = 0x10;
130
        private const  int FLUSHING_STATE          = 0x14;
131
        private const  int FINISHING_STATE         = 0x1c;
132
        private const  int FINISHED_STATE          = 0x1e;
133
        private const  int CLOSED_STATE            = 0x7f;
134
        #endregion
135
        #region Constructors
136
        /// <summary>
137
        /// Creates a new deflater with default compression level.
138
        /// </summary>
139
        public Deflater() : this(DEFAULT_COMPRESSION, false)
140
        {
141
			
142
        }
143
		
144
        /// <summary>
145
        /// Creates a new deflater with given compression level.
146
        /// </summary>
147
        /// <param name="level">
148
        /// the compression level, a value between NO_COMPRESSION
149
        /// and BEST_COMPRESSION, or DEFAULT_COMPRESSION.
150
        /// </param>
151
        /// <exception cref="System.ArgumentOutOfRangeException">if lvl is out of range.</exception>
152
        public Deflater(int level) : this(level, false)
153
        {
154
			
155
        }
156
		
157
        /// <summary>
158
        /// Creates a new deflater with given compression level.
159
        /// </summary>
160
        /// <param name="level">
161
        /// the compression level, a value between NO_COMPRESSION
162
        /// and BEST_COMPRESSION.
163
        /// </param>
164
        /// <param name="noZlibHeaderOrFooter">
165
        /// true, if we should suppress the Zlib/RFC1950 header at the
166
        /// beginning and the adler checksum at the end of the output.  This is
167
        /// useful for the GZIP/PKZIP formats.
168
        /// </param>
169
        /// <exception cref="System.ArgumentOutOfRangeException">if lvl is out of range.</exception>
170
        public Deflater(int level, bool noZlibHeaderOrFooter)
171
        {
172
            if (level == DEFAULT_COMPRESSION) {
173
                level = 6;
174
            } else if (level < NO_COMPRESSION || level > BEST_COMPRESSION) {
175
                throw new ArgumentOutOfRangeException("level");
176
            }
177
			
178
            pending = new DeflaterPending();
179
            engine = new DeflaterEngine(pending);
180
            this.noZlibHeaderOrFooter = noZlibHeaderOrFooter;
181
            SetStrategy(DeflateStrategy.Default);
182
            SetLevel(level);
183
            Reset();
184
        }
185
        #endregion
186
		
187
        /// <summary>
188
        /// Resets the deflater.  The deflater acts afterwards as if it was
189
        /// just created with the same compression level and strategy as it
190
        /// had before.
191
        /// </summary>
192
        public void Reset()
193
        {
194
            state = (noZlibHeaderOrFooter ? BUSY_STATE : INIT_STATE);
195
            totalOut = 0;
196
            pending.Reset();
197
            engine.Reset();
198
        }
199
		
200
        /// <summary>
201
        /// Gets the current adler checksum of the data that was processed so far.
202
        /// </summary>
203
        public int Adler {
204
            get {
205
                return engine.Adler;
206
            }
207
        }
208
		
209
        /// <summary>
210
        /// Gets the number of input bytes processed so far.
211
        /// </summary>
212
        public int TotalIn {
213
            get {
214
                return engine.TotalIn;
215
            }
216
        }
217
		
218
        /// <summary>
219
        /// Gets the number of output bytes so far.
220
        /// </summary>
221
        public long TotalOut {
222
            get {
223
                return totalOut;
224
            }
225
        }
226
		
227
        /// <summary>
228
        /// Flushes the current input block.  Further calls to deflate() will
229
        /// produce enough output to inflate everything in the current input
230
        /// block.  This is not part of Sun's JDK so I have made it package
231
        /// private.  It is used by DeflaterOutputStream to implement
232
        /// flush().
233
        /// </summary>
234
        public void Flush() 
235
        {
236
            state |= IS_FLUSHING;
237
        }
238
		
239
        /// <summary>
240
        /// Finishes the deflater with the current input block.  It is an error
241
        /// to give more input after this method was called.  This method must
242
        /// be called to force all bytes to be flushed.
243
        /// </summary>
244
        public void Finish() 
245
        {
246
            state |= (IS_FLUSHING | IS_FINISHING);
247
        }
248
		
249
        /// <summary>
250
        /// Returns true if the stream was finished and no more output bytes
251
        /// are available.
252
        /// </summary>
253
        public bool IsFinished {
254
            get {
255
                return (state == FINISHED_STATE) && pending.IsFlushed;
256
            }
257
        }
258
		
259
        /// <summary>
260
        /// Returns true, if the input buffer is empty.
261
        /// You should then call setInput(). 
262
        /// NOTE: This method can also return true when the stream
263
        /// was finished.
264
        /// </summary>
265
        public bool IsNeedingInput {
266
            get {
267
                return engine.NeedsInput();
268
            }
269
        }
270
		
271
        /// <summary>
272
        /// Sets the data which should be compressed next.  This should be only
273
        /// called when needsInput indicates that more input is needed.
274
        /// If you call setInput when needsInput() returns false, the
275
        /// previous input that is still pending will be thrown away.
276
        /// The given byte array should not be changed, before needsInput() returns
277
        /// true again.
278
        /// This call is equivalent to <code>setInput(input, 0, input.length)</code>.
279
        /// </summary>
280
        /// <param name="input">
281
        /// the buffer containing the input data.
282
        /// </param>
283
        /// <exception cref="System.InvalidOperationException">
284
        /// if the buffer was finished() or ended().
285
        /// </exception>
286
        public void SetInput(byte[] input)
287
        {
288
            SetInput(input, 0, input.Length);
289
        }
290
		
291
        /// <summary>
292
        /// Sets the data which should be compressed next.  This should be
293
        /// only called when needsInput indicates that more input is needed.
294
        /// The given byte array should not be changed, before needsInput() returns
295
        /// true again.
296
        /// </summary>
297
        /// <param name="input">
298
        /// the buffer containing the input data.
299
        /// </param>
300
        /// <param name="offset">
301
        /// the start of the data.
302
        /// </param>
303
        /// <param name="count">
304
        /// the number of data bytes of input.
305
        /// </param>
306
        /// <exception cref="System.InvalidOperationException">
307
        /// if the buffer was Finish()ed or if previous input is still pending.
308
        /// </exception>
309
        public void SetInput(byte[] input, int offset, int count)
310
        {
311
            if ((state & IS_FINISHING) != 0) {
312
                throw new InvalidOperationException("Finish() already called");
313
            }
314
            engine.SetInput(input, offset, count);
315
        }
316
		
317
        /// <summary>
318
        /// Sets the compression level.  There is no guarantee of the exact
319
        /// position of the change, but if you call this when needsInput is
320
        /// true the change of compression level will occur somewhere near
321
        /// before the end of the so far given input.
322
        /// </summary>
323
        /// <param name="level">
324
        /// the new compression level.
325
        /// </param>
326
        public void SetLevel(int level)
327
        {
328
            if (level == DEFAULT_COMPRESSION) {
329
                level = 6;
330
            } else if (level < NO_COMPRESSION || level > BEST_COMPRESSION) {
331
                throw new ArgumentOutOfRangeException("level");
332
            }
333
			
334
            if (this.level != level) {
335
                this.level = level;
336
                engine.SetLevel(level);
337
            }
338
        }
339
		
340
        /// <summary>
341
        /// Get current compression level
342
        /// </summary>
343
        /// <returns>Returns the current compression level</returns>
344
        public int GetLevel() {
345
            return level;
346
        }
347
		
348
        /// <summary>
349
        /// Sets the compression strategy. Strategy is one of
350
        /// DEFAULT_STRATEGY, HUFFMAN_ONLY and FILTERED.  For the exact
351
        /// position where the strategy is changed, the same as for
352
        /// SetLevel() applies.
353
        /// </summary>
354
        /// <param name="strategy">
355
        /// The new compression strategy.
356
        /// </param>
357
        public void SetStrategy(DeflateStrategy strategy)
358
        {
359
            engine.Strategy = strategy;
360
        }
361
		
362
        /// <summary>
363
        /// Deflates the current input block with to the given array.
364
        /// </summary>
365
        /// <param name="output">
366
        /// The buffer where compressed data is stored
367
        /// </param>
368
        /// <returns>
369
        /// The number of compressed bytes added to the output, or 0 if either
370
        /// IsNeedingInput() or IsFinished returns true or length is zero.
371
        /// </returns>
372
        public int Deflate(byte[] output)
373
        {
374
            return Deflate(output, 0, output.Length);
375
        }
376
		
377
        /// <summary>
378
        /// Deflates the current input block to the given array.
379
        /// </summary>
380
        /// <param name="output">
381
        /// Buffer to store the compressed data.
382
        /// </param>
383
        /// <param name="offset">
384
        /// Offset into the output array.
385
        /// </param>
386
        /// <param name="length">
387
        /// The maximum number of bytes that may be stored.
388
        /// </param>
389
        /// <returns>
390
        /// The number of compressed bytes added to the output, or 0 if either
391
        /// needsInput() or finished() returns true or length is zero.
392
        /// </returns>
393
        /// <exception cref="System.InvalidOperationException">
394
        /// If Finish() was previously called.
395
        /// </exception>
396
        /// <exception cref="System.ArgumentOutOfRangeException">
397
        /// If offset or length don't match the array length.
398
        /// </exception>
399
        public int Deflate(byte[] output, int offset, int length)
400
        {
401
            int origLength = length;
402
			
403
            if (state == CLOSED_STATE) {
404
                throw new InvalidOperationException("Deflater closed");
405
            }
406
			
407
            if (state < BUSY_STATE) {
408
                // output header
409
                int header = (DEFLATED +
410
                              ((DeflaterConstants.MAX_WBITS - 8) << 4)) << 8;
411
                int level_flags = (level - 1) >> 1;
412
                if (level_flags < 0 || level_flags > 3) {
413
                    level_flags = 3;
414
                }
415
                header |= level_flags << 6;
416
                if ((state & IS_SETDICT) != 0) {
417
                    // Dictionary was set
418
                    header |= DeflaterConstants.PRESET_DICT;
419
                }
420
                header += 31 - (header % 31);
421
				
422
                pending.WriteShortMSB(header);
423
                if ((state & IS_SETDICT) != 0) {
424
                    int chksum = engine.Adler;
425
                    engine.ResetAdler();
426
                    pending.WriteShortMSB(chksum >> 16);
427
                    pending.WriteShortMSB(chksum & 0xffff);
428
                }
429
				
430
                state = BUSY_STATE | (state & (IS_FLUSHING | IS_FINISHING));
431
            }
432
			
433
            for (;;) {
434
                int count = pending.Flush(output, offset, length);
435
                offset   += count;
436
                totalOut += count;
437
                length   -= count;
438
				
439
                if (length == 0 || state == FINISHED_STATE) {
440
                    break;
441
                }
442
				
443
                if (!engine.Deflate((state & IS_FLUSHING) != 0, (state & IS_FINISHING) != 0)) {
444
                    if (state == BUSY_STATE) {
445
                        // We need more input now
446
                        return origLength - length;
447
                    } else if (state == FLUSHING_STATE) {
448
                        if (level != NO_COMPRESSION) {
449
                            /* We have to supply some lookahead.  8 bit lookahead
450
							 * is needed by the zlib inflater, and we must fill
451
							 * the next byte, so that all bits are flushed.
452
							 */
453
                            int neededbits = 8 + ((-pending.BitCount) & 7);
454
                            while (neededbits > 0) {
455
                                /* write a static tree block consisting solely of
456
								 * an EOF:
457
								 */
458
                                pending.WriteBits(2, 10);
459
                                neededbits -= 10;
460
                            }
461
                        }
462
                        state = BUSY_STATE;
463
                    } else if (state == FINISHING_STATE) {
464
                        pending.AlignToByte();
465

    
466
                        // Compressed data is complete.  Write footer information if required.
467
                        if (!noZlibHeaderOrFooter) {
468
                            int adler = engine.Adler;
469
                            pending.WriteShortMSB(adler >> 16);
470
                            pending.WriteShortMSB(adler & 0xffff);
471
                        }
472
                        state = FINISHED_STATE;
473
                    }
474
                }
475
            }
476
            return origLength - length;
477
        }
478
		
479
        /// <summary>
480
        /// Sets the dictionary which should be used in the deflate process.
481
        /// This call is equivalent to <code>setDictionary(dict, 0, dict.Length)</code>.
482
        /// </summary>
483
        /// <param name="dictionary">
484
        /// the dictionary.
485
        /// </param>
486
        /// <exception cref="System.InvalidOperationException">
487
        /// if SetInput () or Deflate () were already called or another dictionary was already set.
488
        /// </exception>
489
        public void SetDictionary(byte[] dictionary)
490
        {
491
            SetDictionary(dictionary, 0, dictionary.Length);
492
        }
493
		
494
        /// <summary>
495
        /// Sets the dictionary which should be used in the deflate process.
496
        /// The dictionary is a byte array containing strings that are
497
        /// likely to occur in the data which should be compressed.  The
498
        /// dictionary is not stored in the compressed output, only a
499
        /// checksum.  To decompress the output you need to supply the same
500
        /// dictionary again.
501
        /// </summary>
502
        /// <param name="dictionary">
503
        /// The dictionary data
504
        /// </param>
505
        /// <param name="index">
506
        /// The index where dictionary information commences.
507
        /// </param>
508
        /// <param name="count">
509
        /// The number of bytes in the dictionary.
510
        /// </param>
511
        /// <exception cref="System.InvalidOperationException">
512
        /// If SetInput () or Deflate() were already called or another dictionary was already set.
513
        /// </exception>
514
        public void SetDictionary(byte[] dictionary, int index, int count)
515
        {
516
            if (state != INIT_STATE) {
517
                throw new InvalidOperationException();
518
            }
519
			
520
            state = SETDICT_STATE;
521
            engine.SetDictionary(dictionary, index, count);
522
        }
523
        #region Instance Fields
524
        /// <summary>
525
        /// Compression level.
526
        /// </summary>
527
        int level;
528
		
529
        /// <summary>
530
        /// If true no Zlib/RFC1950 headers or footers are generated
531
        /// </summary>
532
        bool noZlibHeaderOrFooter;
533
		
534
        /// <summary>
535
        /// The current state.
536
        /// </summary>
537
        int state;
538
		
539
        /// <summary>
540
        /// The total bytes of output written.
541
        /// </summary>
542
        long totalOut;
543
		
544
        /// <summary>
545
        /// The pending output.
546
        /// </summary>
547
        DeflaterPending pending;
548
		
549
        /// <summary>
550
        /// The deflater engine.
551
        /// </summary>
552
        DeflaterEngine engine;
553
        #endregion
554
    }
555
}