context: using System;
  using System.IO;
  using System.Security.Cryptography;
  using ICSharpCode.SharpZipLib.Encryption;
  
  namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams
  {
  	/// <summary>
  	/// A special stream deflating or compressing the bytes that are
  	/// written to it.  It uses a Deflater to perform actual deflating.<br/>
  	/// Authors of the original java version : Tom Tromey, Jochen Hoenicke 
  	/// </summary>
  	public class DeflaterOutputStream : Stream
  	{
  		#region Constructors
  		/// <summary>
  		/// Creates a new DeflaterOutputStream with a default Deflater and default buffer size.
  		/// </summary>
  		/// <param name="baseOutputStream">
  		/// the output stream where deflated output should be written.
  		/// </param>
  		public DeflaterOutputStream(Stream baseOutputStream)
  			: this(baseOutputStream, new Deflater(), 512)
  		{
  		}
  
  		/// <summary>
  		/// Creates a new DeflaterOutputStream with the given Deflater and
  		/// default buffer size.
  		/// </summary>
  		/// <param name="baseOutputStream">
  		/// the output stream where deflated output should be written.
  		/// </param>
  		/// <param name="deflater">
  		/// the underlying deflater.
  		/// </param>
  		public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater)
  			: this(baseOutputStream, deflater, 512)
  		{
  		}
  
  		/// <summary>
  		/// Creates a new DeflaterOutputStream with the given Deflater and
  		/// buffer size.
  		/// </summary>
  		/// <param name="baseOutputStream">
  		/// The output stream where deflated output is written.
  		/// </param>
  		/// <param name="deflater">
  		/// The underlying deflater to use
  		/// </param>
  		/// <param name="bufferSize">
  		/// The buffer size in bytes to use when deflating (minimum value 512)
  		/// </param>
  		/// <exception cref="ArgumentOutOfRangeException">
  		/// bufsize is less than or equal to zero.
  		/// </exception>
  		/// <exception cref="ArgumentException">
  		/// baseOutputStream does not support writing
  		/// </exception>
  		/// <exception cref="ArgumentNullException">
  		/// deflater instance is null
  		/// </exception>
  		public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater, int bufferSize)
  		{
  			if (baseOutputStream == null) {
  				throw new ArgumentNullException(nameof(baseOutputStream));
  			}
  
  			if (baseOutputStream.CanWrite == false) {
  				throw new ArgumentException("Must support writing", nameof(baseOutputStream));
  			}
  
  			if (deflater == null) {
  				throw new ArgumentNullException(nameof(deflater));
  			}
  
  			if (bufferSize < 512) {
  				throw new ArgumentOutOfRangeException(nameof(bufferSize));
  			}
  
  			baseOutputStream_ = baseOutputStream;
  			buffer_ = new byte[bufferSize];
  			deflater_ = deflater;
  		}
  		#endregion
  
  		#region Public API
  		/// <summary>
  		/// Finishes the stream by calling finish() on the deflater. 
  		/// </summary>
  		/// <exception cref="SharpZipBaseException">
  		/// Not all input is deflated
  		/// </exception>
  		public virtual void Finish()
  		{
  			deflater_.Finish();
  			while (!deflater_.IsFinished) {
  				int len = deflater_.Deflate(buffer_, 0, buffer_.Length);
  				if (len <= 0) {
  					break;
  				}
  
  				if (cryptoTransform_ != null) {
  					EncryptBlock(buffer_, 0, len);
  				}
  
  				baseOutputStream_.Write(buffer_, 0, len);
  			}
  
  			if (!deflater_.IsFinished) {
  				throw new SharpZipBaseException("Can't deflate all input?");
  			}
  
  			baseOutputStream_.Flush();
  
  			if (cryptoTransform_ != null) {
  				if (cryptoTransform_ is ZipAESTransform) {
  					AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode();
  				}
  				cryptoTransform_.Dispose();
  				cryptoTransform_ = null;
  			}
  		}
  
  		/// <summary>
  		/// Gets or sets a flag indicating ownership of underlying stream.
  		/// When the flag is true <see cref="Stream.Dispose()" /> will close the underlying stream also.
  		/// </summary>
  		/// <remarks>The default value is true.</remarks>
  		public bool IsStreamOwner { get; set; } = true;
  
  		///	<summary>
  		/// Allows client to determine if an entry can be patched after its added
  		/// </summary>
  		public bool CanPatchEntries {
  			get {
  				return baseOutputStream_.CanSeek;
  			}
  		}
  
  		#endregion
  
  		#region Encryption
  
  		string password;
  
  		ICryptoTransform cryptoTransform_;
  
  		/// <summary>
  		/// Returns the 10 byte AUTH CODE to be appended immediately following the AES data stream.
  		/// </summary>
  		protected byte[] AESAuthCode;
  
  		/// <summary>
  		/// Get/set the password used for encryption.
  		/// </summary>
  		/// <remarks>When set to null or if the password is empty no encryption is performed</remarks>
  		public string Password {
  			get {
  				return password;
  			}
  			set {
  				if ((value != null) && (value.Length == 0)) {
  					password = null;
  				} else {
  					password = value;
  				}
  			}
  		}
  
  		/// <summary>
  		/// Encrypt a block of data
  		/// </summary>
  		/// <param name="buffer">
  		/// Data to encrypt.  NOTE the original contents of the buffer are lost
  		/// </param>
  		/// <param name="offset">
  		/// Offset of first byte in buffer to encrypt
  		/// </param>
  		/// <param name="length">
  		/// Number of bytes in buffer to encrypt
  		/// </param>
  		protected void EncryptBlock(byte[] buffer, int offset, int length)
  		{
  			cryptoTransform_.TransformBlock(buffer, 0, length, buffer, 0);
  		}
  
  		/// <summary>
  		/// Initializes encryption keys based on given <paramref name="password"/>.
  		/// </summary>
  		/// <param name="password">The password.</param>
  		protected void InitializePassword(string password)
  		{
  			var pkManaged = new PkzipClassicManaged();
  			byte[] key = PkzipClassic.GenerateKeys(ZipConstants.ConvertToArray(password));
  			cryptoTransform_ = pkManaged.CreateEncryptor(key, null);
  		}
  
  		/// <summary>
  		/// Initializes encryption keys based on given password.
  		/// </summary>
  		protected void InitializeAESPassword(ZipEntry entry, string rawPassword,
  											out byte[] salt, out byte[] pwdVerifier)
  		{
  
gt: 			salt = new byte[entry.AESSaltLen];