Summary

Class:ICSharpCode.SharpZipLib.Zip.Compression.Streams.DeflaterOutputStream
Assembly:ICSharpCode.SharpZipLib
File(s):C:\Users\Neil\Documents\Visual Studio 2015\Projects\icsharpcode\SZL_master\ICSharpCode.SharpZipLib\Zip\Compression\Streams\DeflaterOutputStream.cs
Covered lines:76
Uncovered lines:28
Coverable lines:104
Total lines:476
Line coverage:73%
Branch coverage:71.4%

Metrics

MethodCyclomatic ComplexitySequence CoverageBranch Coverage
.ctor(...)1100100
.ctor(...)1100100
.ctor(...)571.4355.56
Finish()787.576.92
EncryptBlock(...)1100100
InitializePassword(...)1100100
InitializeAESPassword(...)200
Deflate()588.8988.89
Seek(...)100
SetLength(...)100
ReadByte()100
Read(...)100
BeginRead(...)100
BeginWrite(...)100
Flush()1100100
Close()4100100
GetAuthCodeIfAES()266.6766.67
WriteByte(...)1100100
Write(...)1100100

File(s)

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

#LineLine coverage
 1using System;
 2using System.IO;
 3using System.Security.Cryptography;
 4using ICSharpCode.SharpZipLib.Encryption;
 5
 6namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams
 7{
 8  /// <summary>
 9  /// A special stream deflating or compressing the bytes that are
 10  /// written to it.  It uses a Deflater to perform actual deflating.<br/>
 11  /// Authors of the original java version : Tom Tromey, Jochen Hoenicke
 12  /// </summary>
 13  public class DeflaterOutputStream : Stream
 14  {
 15    #region Constructors
 16    /// <summary>
 17    /// Creates a new DeflaterOutputStream with a default Deflater and default buffer size.
 18    /// </summary>
 19    /// <param name="baseOutputStream">
 20    /// the output stream where deflated output should be written.
 21    /// </param>
 22    public DeflaterOutputStream(Stream baseOutputStream)
 423      : this(baseOutputStream, new Deflater(), 512)
 24    {
 425    }
 26
 27    /// <summary>
 28    /// Creates a new DeflaterOutputStream with the given Deflater and
 29    /// default buffer size.
 30    /// </summary>
 31    /// <param name="baseOutputStream">
 32    /// the output stream where deflated output should be written.
 33    /// </param>
 34    /// <param name="deflater">
 35    /// the underlying deflater.
 36    /// </param>
 37    public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater)
 26038      : this(baseOutputStream, deflater, 512)
 39    {
 26040    }
 41
 42    /// <summary>
 43    /// Creates a new DeflaterOutputStream with the given Deflater and
 44    /// buffer size.
 45    /// </summary>
 46    /// <param name="baseOutputStream">
 47    /// The output stream where deflated output is written.
 48    /// </param>
 49    /// <param name="deflater">
 50    /// The underlying deflater to use
 51    /// </param>
 52    /// <param name="bufferSize">
 53    /// The buffer size in bytes to use when deflating (minimum value 512)
 54    /// </param>
 55    /// <exception cref="ArgumentOutOfRangeException">
 56    /// bufsize is less than or equal to zero.
 57    /// </exception>
 58    /// <exception cref="ArgumentException">
 59    /// baseOutputStream does not support writing
 60    /// </exception>
 61    /// <exception cref="ArgumentNullException">
 62    /// deflater instance is null
 63    /// </exception>
 27364    public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater, int bufferSize)
 65    {
 27366       if (baseOutputStream == null) {
 067        throw new ArgumentNullException(nameof(baseOutputStream));
 68      }
 69
 27370       if (baseOutputStream.CanWrite == false) {
 071        throw new ArgumentException("Must support writing", nameof(baseOutputStream));
 72      }
 73
 27374       if (deflater == null) {
 075        throw new ArgumentNullException(nameof(deflater));
 76      }
 77
 27378       if (bufferSize < 512) {
 079        throw new ArgumentOutOfRangeException(nameof(bufferSize));
 80      }
 81
 27382      baseOutputStream_ = baseOutputStream;
 27383      buffer_ = new byte[bufferSize];
 27384      deflater_ = deflater;
 27385    }
 86    #endregion
 87
 88    #region Public API
 89    /// <summary>
 90    /// Finishes the stream by calling finish() on the deflater.
 91    /// </summary>
 92    /// <exception cref="SharpZipBaseException">
 93    /// Not all input is deflated
 94    /// </exception>
 95    public virtual void Finish()
 96    {
 32597      deflater_.Finish();
 157398       while (!deflater_.IsFinished) {
 124899        int len = deflater_.Deflate(buffer_, 0, buffer_.Length);
 1248100         if (len <= 0) {
 101          break;
 102        }
 103
 1248104         if (cryptoTransform_ != null) {
 196105          EncryptBlock(buffer_, 0, len);
 106        }
 107
 1248108        baseOutputStream_.Write(buffer_, 0, len);
 109      }
 110
 325111       if (!deflater_.IsFinished) {
 0112        throw new SharpZipBaseException("Can't deflate all input?");
 113      }
 114
 325115      baseOutputStream_.Flush();
 116
 325117       if (cryptoTransform_ != null) {
 31118         if (cryptoTransform_ is ZipAESTransform) {
 0119          AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode();
 120        }
 31121        cryptoTransform_.Dispose();
 31122        cryptoTransform_ = null;
 123      }
 325124    }
 125
 126    /// <summary>
 127    /// Get/set flag indicating ownership of the underlying stream.
 128    /// When the flag is true <see cref="Close"></see> will close the underlying stream also.
 129    /// </summary>
 130    public bool IsStreamOwner {
 11131      get { return isStreamOwner_; }
 386132      set { isStreamOwner_ = value; }
 133    }
 134
 135    ///  <summary>
 136    /// Allows client to determine if an entry can be patched after its added
 137    /// </summary>
 138    public bool CanPatchEntries {
 139      get {
 141140        return baseOutputStream_.CanSeek;
 141      }
 142    }
 143
 144    #endregion
 145
 146    #region Encryption
 147
 148    string password;
 149
 150    ICryptoTransform cryptoTransform_;
 151
 152    /// <summary>
 153    /// Returns the 10 byte AUTH CODE to be appended immediately following the AES data stream.
 154    /// </summary>
 155    protected byte[] AESAuthCode;
 156
 157    /// <summary>
 158    /// Get/set the password used for encryption.
 159    /// </summary>
 160    /// <remarks>When set to null or if the password is empty no encryption is performed</remarks>
 161    public string Password {
 162      get {
 361163        return password;
 164      }
 165      set {
 69166         if ((value != null) && (value.Length == 0)) {
 0167          password = null;
 0168        } else {
 69169          password = value;
 170        }
 69171      }
 172    }
 173
 174    /// <summary>
 175    /// Encrypt a block of data
 176    /// </summary>
 177    /// <param name="buffer">
 178    /// Data to encrypt.  NOTE the original contents of the buffer are lost
 179    /// </param>
 180    /// <param name="offset">
 181    /// Offset of first byte in buffer to encrypt
 182    /// </param>
 183    /// <param name="length">
 184    /// Number of bytes in buffer to encrypt
 185    /// </param>
 186    protected void EncryptBlock(byte[] buffer, int offset, int length)
 187    {
 2305188      cryptoTransform_.TransformBlock(buffer, 0, length, buffer, 0);
 2305189    }
 190
 191    /// <summary>
 192    /// Initializes encryption keys based on given <paramref name="password"/>.
 193    /// </summary>
 194    /// <param name="password">The password.</param>
 195    protected void InitializePassword(string password)
 196    {
 33197      var pkManaged = new PkzipClassicManaged();
 33198      byte[] key = PkzipClassic.GenerateKeys(ZipConstants.ConvertToArray(password));
 33199      cryptoTransform_ = pkManaged.CreateEncryptor(key, null);
 33200    }
 201
 202    /// <summary>
 203    /// Initializes encryption keys based on given password.
 204    /// </summary>
 205    protected void InitializeAESPassword(ZipEntry entry, string rawPassword,
 206                      out byte[] salt, out byte[] pwdVerifier)
 207    {
 0208      salt = new byte[entry.AESSaltLen];
 209      // Salt needs to be cryptographically random, and unique per file
 0210       if (_aesRnd == null)
 0211        _aesRnd = new RNGCryptoServiceProvider();
 0212      _aesRnd.GetBytes(salt);
 0213      int blockSize = entry.AESKeySize / 8;   // bits to bytes
 214
 0215      cryptoTransform_ = new ZipAESTransform(rawPassword, salt, blockSize, true);
 0216      pwdVerifier = ((ZipAESTransform)cryptoTransform_).PwdVerifier;
 0217    }
 218
 219    #endregion
 220
 221    #region Deflation Support
 222    /// <summary>
 223    /// Deflates everything in the input buffers.  This will call
 224    /// <code>def.deflate()</code> until all bytes from the input buffers
 225    /// are processed.
 226    /// </summary>
 227    protected void Deflate()
 228    {
 11749229       while (!deflater_.IsNeedingInput) {
 11469230        int deflateCount = deflater_.Deflate(buffer_, 0, buffer_.Length);
 231
 11469232         if (deflateCount <= 0) {
 233          break;
 234        }
 7240235         if (cryptoTransform_ != null) {
 1977236          EncryptBlock(buffer_, 0, deflateCount);
 237        }
 238
 7240239        baseOutputStream_.Write(buffer_, 0, deflateCount);
 240      }
 241
 4509242       if (!deflater_.IsNeedingInput) {
 0243        throw new SharpZipBaseException("DeflaterOutputStream can't deflate all input?");
 244      }
 4509245    }
 246    #endregion
 247
 248    #region Stream Overrides
 249    /// <summary>
 250    /// Gets value indicating stream can be read from
 251    /// </summary>
 252    public override bool CanRead {
 253      get {
 0254        return false;
 255      }
 256    }
 257
 258    /// <summary>
 259    /// Gets a value indicating if seeking is supported for this stream
 260    /// This property always returns false
 261    /// </summary>
 262    public override bool CanSeek {
 263      get {
 2264        return false;
 265      }
 266    }
 267
 268    /// <summary>
 269    /// Get value indicating if this stream supports writing
 270    /// </summary>
 271    public override bool CanWrite {
 272      get {
 5273        return baseOutputStream_.CanWrite;
 274      }
 275    }
 276
 277    /// <summary>
 278    /// Get current length of stream
 279    /// </summary>
 280    public override long Length {
 281      get {
 0282        return baseOutputStream_.Length;
 283      }
 284    }
 285
 286    /// <summary>
 287    /// Gets the current position within the stream.
 288    /// </summary>
 289    /// <exception cref="NotSupportedException">Any attempt to set position</exception>
 290    public override long Position {
 291      get {
 0292        return baseOutputStream_.Position;
 293      }
 294      set {
 0295        throw new NotSupportedException("Position property not supported");
 296      }
 297    }
 298
 299    /// <summary>
 300    /// Sets the current position of this stream to the given value. Not supported by this class!
 301    /// </summary>
 302    /// <param name="offset">The offset relative to the <paramref name="origin"/> to seek.</param>
 303    /// <param name="origin">The <see cref="SeekOrigin"/> to seek from.</param>
 304    /// <returns>The new position in the stream.</returns>
 305    /// <exception cref="NotSupportedException">Any access</exception>
 306    public override long Seek(long offset, SeekOrigin origin)
 307    {
 0308      throw new NotSupportedException("DeflaterOutputStream Seek not supported");
 309    }
 310
 311    /// <summary>
 312    /// Sets the length of this stream to the given value. Not supported by this class!
 313    /// </summary>
 314    /// <param name="value">The new stream length.</param>
 315    /// <exception cref="NotSupportedException">Any access</exception>
 316    public override void SetLength(long value)
 317    {
 0318      throw new NotSupportedException("DeflaterOutputStream SetLength not supported");
 319    }
 320
 321    /// <summary>
 322    /// Read a byte from stream advancing position by one
 323    /// </summary>
 324    /// <returns>The byte read cast to an int.  THe value is -1 if at the end of the stream.</returns>
 325    /// <exception cref="NotSupportedException">Any access</exception>
 326    public override int ReadByte()
 327    {
 0328      throw new NotSupportedException("DeflaterOutputStream ReadByte not supported");
 329    }
 330
 331    /// <summary>
 332    /// Read a block of bytes from stream
 333    /// </summary>
 334    /// <param name="buffer">The buffer to store read data in.</param>
 335    /// <param name="offset">The offset to start storing at.</param>
 336    /// <param name="count">The maximum number of bytes to read.</param>
 337    /// <returns>The actual number of bytes read.  Zero if end of stream is detected.</returns>
 338    /// <exception cref="NotSupportedException">Any access</exception>
 339    public override int Read(byte[] buffer, int offset, int count)
 340    {
 0341      throw new NotSupportedException("DeflaterOutputStream Read not supported");
 342    }
 343
 344    /// <summary>
 345    /// Asynchronous reads are not supported a NotSupportedException is always thrown
 346    /// </summary>
 347    /// <param name="buffer">The buffer to read into.</param>
 348    /// <param name="offset">The offset to start storing data at.</param>
 349    /// <param name="count">The number of bytes to read</param>
 350    /// <param name="callback">The async callback to use.</param>
 351    /// <param name="state">The state to use.</param>
 352    /// <returns>Returns an <see cref="IAsyncResult"/></returns>
 353    /// <exception cref="NotSupportedException">Any access</exception>
 354    public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
 355    {
 0356      throw new NotSupportedException("DeflaterOutputStream BeginRead not currently supported");
 357    }
 358
 359    /// <summary>
 360    /// Asynchronous writes arent supported, a NotSupportedException is always thrown
 361    /// </summary>
 362    /// <param name="buffer">The buffer to write.</param>
 363    /// <param name="offset">The offset to begin writing at.</param>
 364    /// <param name="count">The number of bytes to write.</param>
 365    /// <param name="callback">The <see cref="AsyncCallback"/> to use.</param>
 366    /// <param name="state">The state object.</param>
 367    /// <returns>Returns an IAsyncResult.</returns>
 368    /// <exception cref="NotSupportedException">Any access</exception>
 369    public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
 370    {
 0371      throw new NotSupportedException("BeginWrite is not supported");
 372    }
 373
 374    /// <summary>
 375    /// Flushes the stream by calling <see cref="DeflaterOutputStream.Flush">Flush</see> on the deflater and then
 376    /// on the underlying stream.  This ensures that all bytes are flushed.
 377    /// </summary>
 378    public override void Flush()
 379    {
 30380      deflater_.Flush();
 30381      Deflate();
 30382      baseOutputStream_.Flush();
 30383    }
 384
 385    /// <summary>
 386    /// Calls <see cref="Finish"/> and closes the underlying
 387    /// stream when <see cref="IsStreamOwner"></see> is true.
 388    /// </summary>
 389    public override void Close()
 390    {
 267391       if (!isClosed_) {
 259392        isClosed_ = true;
 393
 394        try {
 259395          Finish();
 258396           if (cryptoTransform_ != null) {
 1397            GetAuthCodeIfAES();
 1398            cryptoTransform_.Dispose();
 1399            cryptoTransform_ = null;
 400          }
 258401        } finally {
 259402           if (isStreamOwner_) {
 68403            baseOutputStream_.Close();
 404          }
 259405        }
 406      }
 266407    }
 408
 409    private void GetAuthCodeIfAES()
 410    {
 1411       if (cryptoTransform_ is ZipAESTransform) {
 0412        AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode();
 413      }
 1414    }
 415
 416    /// <summary>
 417    /// Writes a single byte to the compressed output stream.
 418    /// </summary>
 419    /// <param name="value">
 420    /// The byte value.
 421    /// </param>
 422    public override void WriteByte(byte value)
 423    {
 24424      byte[] b = new byte[1];
 24425      b[0] = value;
 24426      Write(b, 0, 1);
 22427    }
 428
 429    /// <summary>
 430    /// Writes bytes from an array to the compressed stream.
 431    /// </summary>
 432    /// <param name="buffer">
 433    /// The byte array
 434    /// </param>
 435    /// <param name="offset">
 436    /// The offset into the byte array where to start.
 437    /// </param>
 438    /// <param name="count">
 439    /// The number of bytes to write.
 440    /// </param>
 441    public override void Write(byte[] buffer, int offset, int count)
 442    {
 4479443      deflater_.SetInput(buffer, offset, count);
 4479444      Deflate();
 4479445    }
 446    #endregion
 447
 448    #region Instance Fields
 449    /// <summary>
 450    /// This buffer is used temporarily to retrieve the bytes from the
 451    /// deflater and write them to the underlying output stream.
 452    /// </summary>
 453    byte[] buffer_;
 454
 455    /// <summary>
 456    /// The deflater which is used to deflate the stream.
 457    /// </summary>
 458    protected Deflater deflater_;
 459
 460    /// <summary>
 461    /// Base stream the deflater depends on.
 462    /// </summary>
 463    protected Stream baseOutputStream_;
 464
 465    bool isClosed_;
 466
 273467    bool isStreamOwner_ = true;
 468    #endregion
 469
 470    #region Static Fields
 471
 472    // Static to help ensure that multiple files within a zip will get different random salt
 473    private static RNGCryptoServiceProvider _aesRnd;
 474    #endregion
 475  }
 476}