root/trunk/BazaReklam.Updater/ICSharpCode.SharpZipLib/Zip/Compression/Deflater.cs @ 851

Wersja 597, 17.8 KB (wprowadzona przez marek, 17 years temu)

re #165

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