using System; using System.IO; using System.IO.Compression; using System.Security.Permissions; using System.Threading; namespace Invensys.Compression; public class AADeflateStream : Stream { internal delegate void AsyncWriteDelegate(byte[] array, int offset, int count, bool isAsync); internal const int DefaultBufferSize = 8192; private Stream _stream; private CompressionMode _mode; private bool _leaveOpen; private AADeflaterManaged deflater; private byte[] buffer; private int asyncOperations; private readonly AsyncCallback m_CallBack; private readonly AsyncWriteDelegate m_AsyncWriterDelegate; private bool wroteBytes; public Stream BaseStream => _stream; public override bool CanRead { get { if (_stream == null) { return false; } if (_mode == CompressionMode.Decompress) { return _stream.CanRead; } return false; } } public override bool CanWrite { get { if (_stream == null) { return false; } if (_mode == CompressionMode.Compress) { return _stream.CanWrite; } return false; } } public override bool CanSeek => false; public override long Length { get { throw new NotSupportedException(); } } public override long Position { get { throw new NotSupportedException(); } set { throw new NotSupportedException(); } } public AADeflateStream(Stream stream, CompressionMode mode) : this(stream, mode, leaveOpen: false) { } public AADeflateStream(Stream stream, CompressionMode mode, bool leaveOpen) { if (stream == null) { throw new ArgumentNullException("stream"); } if (CompressionMode.Compress != mode && mode != CompressionMode.Decompress) { throw new ArgumentException("mode"); } _stream = stream; _mode = mode; _leaveOpen = leaveOpen; switch (_mode) { case CompressionMode.Decompress: throw new ArgumentException("CompressionMode.Decompress - Unsupported"); case CompressionMode.Compress: if (!_stream.CanWrite) { throw new ArgumentException("stream"); } deflater = new AADeflaterManaged(); m_AsyncWriterDelegate = InternalWrite; m_CallBack = WriteCallback; break; } buffer = new byte[8192]; } public override void Flush() { EnsureNotDisposed(); } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } public override void SetLength(long value) { throw new NotSupportedException(); } public override int Read(byte[] array, int offset, int count) { throw new ArgumentException("Read - Unsupported"); } private void ValidateParameters(byte[] array, int offset, int count) { if (array == null) { throw new ArgumentNullException("array"); } if (offset < 0) { throw new ArgumentOutOfRangeException("offset"); } if (count < 0) { throw new ArgumentOutOfRangeException("count"); } if (array.Length - offset < count) { throw new ArgumentException(); } } private void EnsureNotDisposed() { if (_stream == null) { throw new ObjectDisposedException(string.Empty); } } private void EnsureDecompressionMode() { if (_mode != CompressionMode.Decompress) { throw new InvalidOperationException(); } } private void EnsureCompressionMode() { if (_mode != CompressionMode.Compress) { throw new InvalidOperationException(); } } [HostProtection(SecurityAction.LinkDemand, ExternalThreading = true)] public override IAsyncResult BeginRead(byte[] array, int offset, int count, AsyncCallback asyncCallback, object asyncState) { throw new ArgumentException("BeginRead - Unsupported"); } private void ReadCallback(IAsyncResult baseStreamResult) { throw new ArgumentException("ReadCallback - Unsupported"); } public override int EndRead(IAsyncResult asyncResult) { EnsureDecompressionMode(); CheckEndXxxxLegalStateAndParams(asyncResult); DeflateStreamAsyncResult deflateStreamAsyncResult = (DeflateStreamAsyncResult)asyncResult; AwaitAsyncResultCompletion(deflateStreamAsyncResult); if (deflateStreamAsyncResult.Result is Exception ex) { throw ex; } return (int)deflateStreamAsyncResult.Result; } public override void Write(byte[] array, int offset, int count) { EnsureCompressionMode(); ValidateParameters(array, offset, count); EnsureNotDisposed(); InternalWrite(array, offset, count, isAsync: false); } internal void InternalWrite(byte[] array, int offset, int count, bool isAsync) { DoMaintenance(array, offset, count); WriteDeflaterOutput(isAsync); deflater.SetInput(array, offset, count); WriteDeflaterOutput(isAsync); } private void WriteDeflaterOutput(bool isAsync) { while (!deflater.NeedsInput()) { int deflateOutput = deflater.GetDeflateOutput(buffer); if (deflateOutput > 0) { DoWrite(buffer, 0, deflateOutput, isAsync); } } } private void DoWrite(byte[] array, int offset, int count, bool isAsync) { if (isAsync) { IAsyncResult asyncResult = _stream.BeginWrite(array, offset, count, null, null); _stream.EndWrite(asyncResult); } else { _stream.Write(array, offset, count); } } private void DoMaintenance(byte[] array, int offset, int count) { if (count > 0) { wroteBytes = true; } } private void PurgeBuffers(bool disposing) { if (!disposing || _stream == null) { return; } Flush(); if (_mode != CompressionMode.Compress || !wroteBytes) { return; } WriteDeflaterOutput(isAsync: false); bool flag; do { flag = deflater.Finish(buffer, out var bytesRead); if (bytesRead > 0) { DoWrite(buffer, 0, bytesRead, isAsync: false); } } while (!flag); } protected override void Dispose(bool disposing) { try { PurgeBuffers(disposing); } finally { try { if (disposing && !_leaveOpen && _stream != null) { _stream.Close(); } } finally { _stream = null; try { } finally { base.Dispose(disposing); } } } } [HostProtection(SecurityAction.LinkDemand, ExternalThreading = true)] public override IAsyncResult BeginWrite(byte[] array, int offset, int count, AsyncCallback asyncCallback, object asyncState) { EnsureCompressionMode(); if (asyncOperations != 0) { throw new InvalidOperationException(); } ValidateParameters(array, offset, count); EnsureNotDisposed(); Interlocked.Increment(ref asyncOperations); try { DeflateStreamAsyncResult deflateStreamAsyncResult = new DeflateStreamAsyncResult(this, asyncState, asyncCallback, array, offset, count); deflateStreamAsyncResult.isWrite = true; m_AsyncWriterDelegate.BeginInvoke(array, offset, count, isAsync: true, m_CallBack, deflateStreamAsyncResult); deflateStreamAsyncResult.m_CompletedSynchronously &= deflateStreamAsyncResult.IsCompleted; return deflateStreamAsyncResult; } catch { Interlocked.Decrement(ref asyncOperations); throw; } } private void WriteCallback(IAsyncResult asyncResult) { DeflateStreamAsyncResult deflateStreamAsyncResult = (DeflateStreamAsyncResult)asyncResult.AsyncState; deflateStreamAsyncResult.m_CompletedSynchronously &= asyncResult.CompletedSynchronously; try { m_AsyncWriterDelegate.EndInvoke(asyncResult); } catch (Exception result) { deflateStreamAsyncResult.InvokeCallback(result); return; } deflateStreamAsyncResult.InvokeCallback(null); } public override void EndWrite(IAsyncResult asyncResult) { EnsureCompressionMode(); CheckEndXxxxLegalStateAndParams(asyncResult); DeflateStreamAsyncResult deflateStreamAsyncResult = (DeflateStreamAsyncResult)asyncResult; AwaitAsyncResultCompletion(deflateStreamAsyncResult); if (deflateStreamAsyncResult.Result is Exception ex) { throw ex; } } private void CheckEndXxxxLegalStateAndParams(IAsyncResult asyncResult) { if (asyncOperations != 1) { throw new InvalidOperationException(); } if (asyncResult == null) { throw new ArgumentNullException("asyncResult"); } EnsureNotDisposed(); if (!(asyncResult is DeflateStreamAsyncResult)) { throw new ArgumentNullException("asyncResult"); } } private void AwaitAsyncResultCompletion(DeflateStreamAsyncResult asyncResult) { try { if (!asyncResult.IsCompleted) { asyncResult.AsyncWaitHandle.WaitOne(); } } finally { Interlocked.Decrement(ref asyncOperations); asyncResult.Close(); } } }