Summary

Class:ICSharpCode.SharpZipLib.Zip.Compression.Deflater
Assembly:ICSharpCode.SharpZipLib
File(s):C:\Users\Neil\Documents\Visual Studio 2015\Projects\icsharpcode\SZL_master\ICSharpCode.SharpZipLib\Zip\Compression\Deflater.cs
Covered lines:69
Uncovered lines:29
Coverable lines:98
Total lines:521
Line coverage:70.4%
Branch coverage:68%

Metrics

MethodCyclomatic ComplexitySequence CoverageBranch Coverage
.ctor()1100100
.ctor(...)100
.ctor(...)492.3171.43
Reset()3100100
Flush()1100100
Finish()1100100
SetInput(...)100
SetInput(...)27566.67
SetLevel(...)588.8977.78
GetLevel()1100100
SetStrategy(...)1100100
Deflate(...)100
Deflate(...)1669.0570.97
SetDictionary(...)100
SetDictionary(...)200

File(s)

C:\Users\Neil\Documents\Visual Studio 2015\Projects\icsharpcode\SZL_master\ICSharpCode.SharpZipLib\Zip\Compression\Deflater.cs

#LineLine coverage
 1using System;
 2
 3namespace ICSharpCode.SharpZipLib.Zip.Compression
 4{
 5  /// <summary>
 6  /// This is the Deflater class.  The deflater class compresses input
 7  /// with the deflate algorithm described in RFC 1951.  It has several
 8  /// compression levels and three different strategies described below.
 9  ///
 10  /// This class is <i>not</i> thread safe.  This is inherent in the API, due
 11  /// to the split of deflate and setInput.
 12  ///
 13  /// author of the original java version : Jochen Hoenicke
 14  /// </summary>
 15  public class Deflater
 16  {
 17    #region Deflater Documentation
 18    /*
 19    * The Deflater can do the following state transitions:
 20    *
 21    * (1) -> INIT_STATE   ----> INIT_FINISHING_STATE ---.
 22    *        /  | (2)      (5)                          |
 23    *       /   v          (5)                          |
 24    *   (3)| SETDICT_STATE ---> SETDICT_FINISHING_STATE |(3)
 25    *       \   | (3)                 |        ,--------'
 26    *        |  |                     | (3)   /
 27    *        v  v          (5)        v      v
 28    * (1) -> BUSY_STATE   ----> FINISHING_STATE
 29    *                                | (6)
 30    *                                v
 31    *                           FINISHED_STATE
 32    *    \_____________________________________/
 33    *                    | (7)
 34    *                    v
 35    *               CLOSED_STATE
 36    *
 37    * (1) If we should produce a header we start in INIT_STATE, otherwise
 38    *     we start in BUSY_STATE.
 39    * (2) A dictionary may be set only when we are in INIT_STATE, then
 40    *     we change the state as indicated.
 41    * (3) Whether a dictionary is set or not, on the first call of deflate
 42    *     we change to BUSY_STATE.
 43    * (4) -- intentionally left blank -- :)
 44    * (5) FINISHING_STATE is entered, when flush() is called to indicate that
 45    *     there is no more INPUT.  There are also states indicating, that
 46    *     the header wasn't written yet.
 47    * (6) FINISHED_STATE is entered, when everything has been flushed to the
 48    *     internal pending output buffer.
 49    * (7) At any time (7)
 50    *
 51    */
 52    #endregion
 53    #region Public Constants
 54    /// <summary>
 55    /// The best and slowest compression level.  This tries to find very
 56    /// long and distant string repetitions.
 57    /// </summary>
 58    public const int BEST_COMPRESSION = 9;
 59
 60    /// <summary>
 61    /// The worst but fastest compression level.
 62    /// </summary>
 63    public const int BEST_SPEED = 1;
 64
 65    /// <summary>
 66    /// The default compression level.
 67    /// </summary>
 68    public const int DEFAULT_COMPRESSION = -1;
 69
 70    /// <summary>
 71    /// This level won't compress at all but output uncompressed blocks.
 72    /// </summary>
 73    public const int NO_COMPRESSION = 0;
 74
 75    /// <summary>
 76    /// The compression method.  This is the only method supported so far.
 77    /// There is no need to use this constant at all.
 78    /// </summary>
 79    public const int DEFLATED = 8;
 80    #endregion
 81    #region Local Constants
 82    private const int IS_SETDICT = 0x01;
 83    private const int IS_FLUSHING = 0x04;
 84    private const int IS_FINISHING = 0x08;
 85
 86    private const int INIT_STATE = 0x00;
 87    private const int SETDICT_STATE = 0x01;
 88    //    private static  int INIT_FINISHING_STATE    = 0x08;
 89    //    private static  int SETDICT_FINISHING_STATE = 0x09;
 90    private const int BUSY_STATE = 0x10;
 91    private const int FLUSHING_STATE = 0x14;
 92    private const int FINISHING_STATE = 0x1c;
 93    private const int FINISHED_STATE = 0x1e;
 94    private const int CLOSED_STATE = 0x7f;
 95    #endregion
 96    #region Constructors
 97    /// <summary>
 98    /// Creates a new deflater with default compression level.
 99    /// </summary>
 4100    public Deflater() : this(DEFAULT_COMPRESSION, false)
 101    {
 102
 4103    }
 104
 105    /// <summary>
 106    /// Creates a new deflater with given compression level.
 107    /// </summary>
 108    /// <param name="level">
 109    /// the compression level, a value between NO_COMPRESSION
 110    /// and BEST_COMPRESSION, or DEFAULT_COMPRESSION.
 111    /// </param>
 112    /// <exception cref="System.ArgumentOutOfRangeException">if lvl is out of range.</exception>
 0113    public Deflater(int level) : this(level, false)
 114    {
 115
 0116    }
 117
 118    /// <summary>
 119    /// Creates a new deflater with given compression level.
 120    /// </summary>
 121    /// <param name="level">
 122    /// the compression level, a value between NO_COMPRESSION
 123    /// and BEST_COMPRESSION.
 124    /// </param>
 125    /// <param name="noZlibHeaderOrFooter">
 126    /// true, if we should suppress the Zlib/RFC1950 header at the
 127    /// beginning and the adler checksum at the end of the output.  This is
 128    /// useful for the GZIP/PKZIP formats.
 129    /// </param>
 130    /// <exception cref="System.ArgumentOutOfRangeException">if lvl is out of range.</exception>
 273131    public Deflater(int level, bool noZlibHeaderOrFooter)
 132    {
 273133       if (level == DEFAULT_COMPRESSION) {
 100134        level = 6;
 273135       } else if (level < NO_COMPRESSION || level > BEST_COMPRESSION) {
 0136        throw new ArgumentOutOfRangeException(nameof(level));
 137      }
 138
 273139      pending = new DeflaterPending();
 273140      engine = new DeflaterEngine(pending);
 273141      this.noZlibHeaderOrFooter = noZlibHeaderOrFooter;
 273142      SetStrategy(DeflateStrategy.Default);
 273143      SetLevel(level);
 273144      Reset();
 273145    }
 146    #endregion
 147
 148    /// <summary>
 149    /// Resets the deflater.  The deflater acts afterwards as if it was
 150    /// just created with the same compression level and strategy as it
 151    /// had before.
 152    /// </summary>
 153    public void Reset()
 154    {
 392155       state = (noZlibHeaderOrFooter ? BUSY_STATE : INIT_STATE);
 392156      totalOut = 0;
 392157      pending.Reset();
 392158      engine.Reset();
 392159    }
 160
 161    /// <summary>
 162    /// Gets the current adler checksum of the data that was processed so far.
 163    /// </summary>
 164    public int Adler {
 165      get {
 0166        return engine.Adler;
 167      }
 168    }
 169
 170    /// <summary>
 171    /// Gets the number of input bytes processed so far.
 172    /// </summary>
 173    public long TotalIn {
 174      get {
 9175        return engine.TotalIn;
 176      }
 177    }
 178
 179    /// <summary>
 180    /// Gets the number of output bytes so far.
 181    /// </summary>
 182    public long TotalOut {
 183      get {
 119184        return totalOut;
 185      }
 186    }
 187
 188    /// <summary>
 189    /// Flushes the current input block.  Further calls to deflate() will
 190    /// produce enough output to inflate everything in the current input
 191    /// block.  This is not part of Sun's JDK so I have made it package
 192    /// private.  It is used by DeflaterOutputStream to implement
 193    /// flush().
 194    /// </summary>
 195    public void Flush()
 196    {
 30197      state |= IS_FLUSHING;
 30198    }
 199
 200    /// <summary>
 201    /// Finishes the deflater with the current input block.  It is an error
 202    /// to give more input after this method was called.  This method must
 203    /// be called to force all bytes to be flushed.
 204    /// </summary>
 205    public void Finish()
 206    {
 325207      state |= (IS_FLUSHING | IS_FINISHING);
 325208    }
 209
 210    /// <summary>
 211    /// Returns true if the stream was finished and no more output bytes
 212    /// are available.
 213    /// </summary>
 214    public bool IsFinished {
 215      get {
 1898216        return (state == FINISHED_STATE) && pending.IsFlushed;
 217      }
 218    }
 219
 220    /// <summary>
 221    /// Returns true, if the input buffer is empty.
 222    /// You should then call setInput().
 223    /// NOTE: This method can also return true when the stream
 224    /// was finished.
 225    /// </summary>
 226    public bool IsNeedingInput {
 227      get {
 16258228        return engine.NeedsInput();
 229      }
 230    }
 231
 232    /// <summary>
 233    /// Sets the data which should be compressed next.  This should be only
 234    /// called when needsInput indicates that more input is needed.
 235    /// If you call setInput when needsInput() returns false, the
 236    /// previous input that is still pending will be thrown away.
 237    /// The given byte array should not be changed, before needsInput() returns
 238    /// true again.
 239    /// This call is equivalent to <code>setInput(input, 0, input.length)</code>.
 240    /// </summary>
 241    /// <param name="input">
 242    /// the buffer containing the input data.
 243    /// </param>
 244    /// <exception cref="System.InvalidOperationException">
 245    /// if the buffer was finished() or ended().
 246    /// </exception>
 247    public void SetInput(byte[] input)
 248    {
 0249      SetInput(input, 0, input.Length);
 0250    }
 251
 252    /// <summary>
 253    /// Sets the data which should be compressed next.  This should be
 254    /// only called when needsInput indicates that more input is needed.
 255    /// The given byte array should not be changed, before needsInput() returns
 256    /// true again.
 257    /// </summary>
 258    /// <param name="input">
 259    /// the buffer containing the input data.
 260    /// </param>
 261    /// <param name="offset">
 262    /// the start of the data.
 263    /// </param>
 264    /// <param name="count">
 265    /// the number of data bytes of input.
 266    /// </param>
 267    /// <exception cref="System.InvalidOperationException">
 268    /// if the buffer was Finish()ed or if previous input is still pending.
 269    /// </exception>
 270    public void SetInput(byte[] input, int offset, int count)
 271    {
 4479272       if ((state & IS_FINISHING) != 0) {
 0273        throw new InvalidOperationException("Finish() already called");
 274      }
 4479275      engine.SetInput(input, offset, count);
 4479276    }
 277
 278    /// <summary>
 279    /// Sets the compression level.  There is no guarantee of the exact
 280    /// position of the change, but if you call this when needsInput is
 281    /// true the change of compression level will occur somewhere near
 282    /// before the end of the so far given input.
 283    /// </summary>
 284    /// <param name="level">
 285    /// the new compression level.
 286    /// </param>
 287    public void SetLevel(int level)
 288    {
 447289       if (level == DEFAULT_COMPRESSION) {
 59290        level = 6;
 447291       } else if (level < NO_COMPRESSION || level > BEST_COMPRESSION) {
 0292        throw new ArgumentOutOfRangeException(nameof(level));
 293      }
 294
 447295       if (this.level != level) {
 327296        this.level = level;
 327297        engine.SetLevel(level);
 298      }
 447299    }
 300
 301    /// <summary>
 302    /// Get current compression level
 303    /// </summary>
 304    /// <returns>Returns the current compression level</returns>
 305    public int GetLevel()
 306    {
 3307      return level;
 308    }
 309
 310    /// <summary>
 311    /// Sets the compression strategy. Strategy is one of
 312    /// DEFAULT_STRATEGY, HUFFMAN_ONLY and FILTERED.  For the exact
 313    /// position where the strategy is changed, the same as for
 314    /// SetLevel() applies.
 315    /// </summary>
 316    /// <param name="strategy">
 317    /// The new compression strategy.
 318    /// </param>
 319    public void SetStrategy(DeflateStrategy strategy)
 320    {
 273321      engine.Strategy = strategy;
 273322    }
 323
 324    /// <summary>
 325    /// Deflates the current input block with to the given array.
 326    /// </summary>
 327    /// <param name="output">
 328    /// The buffer where compressed data is stored
 329    /// </param>
 330    /// <returns>
 331    /// The number of compressed bytes added to the output, or 0 if either
 332    /// IsNeedingInput() or IsFinished returns true or length is zero.
 333    /// </returns>
 334    public int Deflate(byte[] output)
 335    {
 0336      return Deflate(output, 0, output.Length);
 337    }
 338
 339    /// <summary>
 340    /// Deflates the current input block to the given array.
 341    /// </summary>
 342    /// <param name="output">
 343    /// Buffer to store the compressed data.
 344    /// </param>
 345    /// <param name="offset">
 346    /// Offset into the output array.
 347    /// </param>
 348    /// <param name="length">
 349    /// The maximum number of bytes that may be stored.
 350    /// </param>
 351    /// <returns>
 352    /// The number of compressed bytes added to the output, or 0 if either
 353    /// needsInput() or finished() returns true or length is zero.
 354    /// </returns>
 355    /// <exception cref="System.InvalidOperationException">
 356    /// If Finish() was previously called.
 357    /// </exception>
 358    /// <exception cref="System.ArgumentOutOfRangeException">
 359    /// If offset or length don't match the array length.
 360    /// </exception>
 361    public int Deflate(byte[] output, int offset, int length)
 362    {
 12717363      int origLength = length;
 364
 12717365       if (state == CLOSED_STATE) {
 0366        throw new InvalidOperationException("Deflater closed");
 367      }
 368
 12717369       if (state < BUSY_STATE) {
 370        // output header
 14371        int header = (DEFLATED +
 14372          ((DeflaterConstants.MAX_WBITS - 8) << 4)) << 8;
 14373        int level_flags = (level - 1) >> 1;
 14374         if (level_flags < 0 || level_flags > 3) {
 2375          level_flags = 3;
 376        }
 14377        header |= level_flags << 6;
 14378         if ((state & IS_SETDICT) != 0) {
 379          // Dictionary was set
 0380          header |= DeflaterConstants.PRESET_DICT;
 381        }
 14382        header += 31 - (header % 31);
 383
 14384        pending.WriteShortMSB(header);
 14385         if ((state & IS_SETDICT) != 0) {
 0386          int chksum = engine.Adler;
 0387          engine.ResetAdler();
 0388          pending.WriteShortMSB(chksum >> 16);
 0389          pending.WriteShortMSB(chksum & 0xffff);
 390        }
 391
 14392        state = BUSY_STATE | (state & (IS_FLUSHING | IS_FINISHING));
 393      }
 394
 395      for (;;) {
 13248396        int count = pending.Flush(output, offset, length);
 13248397        offset += count;
 13248398        totalOut += count;
 13248399        length -= count;
 400
 13248401         if (length == 0 || state == FINISHED_STATE) {
 402          break;
 403        }
 404
 4878405         if (!engine.Deflate((state & IS_FLUSHING) != 0, (state & IS_FINISHING) != 0)) {
 4652406           switch (state) {
 407            case BUSY_STATE:
 408              // We need more input now
 4347409              return origLength - length;
 410            case FLUSHING_STATE:
 0411               if (level != NO_COMPRESSION) {
 412                /* We have to supply some lookahead.  8 bit lookahead
 413                 * is needed by the zlib inflater, and we must fill
 414                 * the next byte, so that all bits are flushed.
 415                 */
 0416                int neededbits = 8 + ((-pending.BitCount) & 7);
 0417                 while (neededbits > 0) {
 418                  /* write a static tree block consisting solely of
 419                   * an EOF:
 420                   */
 0421                  pending.WriteBits(2, 10);
 0422                  neededbits -= 10;
 423                }
 424              }
 0425              state = BUSY_STATE;
 0426              break;
 427            case FINISHING_STATE:
 305428              pending.AlignToByte();
 429
 430              // Compressed data is complete.  Write footer information if required.
 305431               if (!noZlibHeaderOrFooter) {
 14432                int adler = engine.Adler;
 14433                pending.WriteShortMSB(adler >> 16);
 14434                pending.WriteShortMSB(adler & 0xffff);
 435              }
 305436              state = FINISHED_STATE;
 305437              break;
 438          }
 439        }
 440      }
 8370441      return origLength - length;
 442    }
 443
 444    /// <summary>
 445    /// Sets the dictionary which should be used in the deflate process.
 446    /// This call is equivalent to <code>setDictionary(dict, 0, dict.Length)</code>.
 447    /// </summary>
 448    /// <param name="dictionary">
 449    /// the dictionary.
 450    /// </param>
 451    /// <exception cref="System.InvalidOperationException">
 452    /// if SetInput () or Deflate () were already called or another dictionary was already set.
 453    /// </exception>
 454    public void SetDictionary(byte[] dictionary)
 455    {
 0456      SetDictionary(dictionary, 0, dictionary.Length);
 0457    }
 458
 459    /// <summary>
 460    /// Sets the dictionary which should be used in the deflate process.
 461    /// The dictionary is a byte array containing strings that are
 462    /// likely to occur in the data which should be compressed.  The
 463    /// dictionary is not stored in the compressed output, only a
 464    /// checksum.  To decompress the output you need to supply the same
 465    /// dictionary again.
 466    /// </summary>
 467    /// <param name="dictionary">
 468    /// The dictionary data
 469    /// </param>
 470    /// <param name="index">
 471    /// The index where dictionary information commences.
 472    /// </param>
 473    /// <param name="count">
 474    /// The number of bytes in the dictionary.
 475    /// </param>
 476    /// <exception cref="System.InvalidOperationException">
 477    /// If SetInput () or Deflate() were already called or another dictionary was already set.
 478    /// </exception>
 479    public void SetDictionary(byte[] dictionary, int index, int count)
 480    {
 0481       if (state != INIT_STATE) {
 0482        throw new InvalidOperationException();
 483      }
 484
 0485      state = SETDICT_STATE;
 0486      engine.SetDictionary(dictionary, index, count);
 0487    }
 488
 489    #region Instance Fields
 490    /// <summary>
 491    /// Compression level.
 492    /// </summary>
 493    int level;
 494
 495    /// <summary>
 496    /// If true no Zlib/RFC1950 headers or footers are generated
 497    /// </summary>
 498    bool noZlibHeaderOrFooter;
 499
 500    /// <summary>
 501    /// The current state.
 502    /// </summary>
 503    int state;
 504
 505    /// <summary>
 506    /// The total bytes of output written.
 507    /// </summary>
 508    long totalOut;
 509
 510    /// <summary>
 511    /// The pending output.
 512    /// </summary>
 513    DeflaterPending pending;
 514
 515    /// <summary>
 516    /// The deflater engine.
 517    /// </summary>
 518    DeflaterEngine engine;
 519    #endregion
 520  }
 521}