using System; namespace Invensys.Compression; internal class AADeflaterManaged : IDisposable { private enum DeflaterState { NotStarted, SlowDownForIncompressible1, SlowDownForIncompressible2, StartingSmallData, CompressThenCheck, CheckingForIncompressible, HandlingSmallData } private const int MinBlockSize = 256; private const int MaxHeaderFooterGoo = 120; private const int CleanCopySize = 8072; private const double BadCompressionThreshold = 1.0; private AAFastEncoder deflateEncoder; private AACopyEncoder copyEncoder; private AADeflateInput input; private AAOutputBuffer output; private DeflaterState processingState; private AADeflateInput inputFromHistory; internal AADeflaterManaged() { deflateEncoder = new AAFastEncoder(); copyEncoder = new AACopyEncoder(); input = new AADeflateInput(); output = new AAOutputBuffer(); processingState = DeflaterState.NotStarted; } public bool NeedsInput() { if (input.Count == 0) { return deflateEncoder.BytesInHistory == 0; } return false; } public void SetInput(byte[] inputBuffer, int startIndex, int count) { input.Buffer = inputBuffer; input.Count = count; input.StartIndex = startIndex; if (count > 0 && count < 256) { switch (processingState) { case DeflaterState.NotStarted: case DeflaterState.CheckingForIncompressible: processingState = DeflaterState.StartingSmallData; break; case DeflaterState.CompressThenCheck: processingState = DeflaterState.HandlingSmallData; break; } } } public int GetDeflateOutput(byte[] outputBuffer) { output.UpdateBuffer(outputBuffer); switch (processingState) { case DeflaterState.NotStarted: { AADeflateInput.InputState state3 = input.DumpState(); AAOutputBuffer.BufferState state4 = output.DumpState(); deflateEncoder.GetBlockHeader(output); deflateEncoder.GetCompressedData(input, output); if (!UseCompressed(deflateEncoder.LastCompressionRatio)) { input.RestoreState(state3); output.RestoreState(state4); copyEncoder.GetBlock(input, output, isFinal: false); FlushInputWindows(); processingState = DeflaterState.CheckingForIncompressible; } else { processingState = DeflaterState.CompressThenCheck; } break; } case DeflaterState.CompressThenCheck: deflateEncoder.GetCompressedData(input, output); if (!UseCompressed(deflateEncoder.LastCompressionRatio)) { processingState = DeflaterState.SlowDownForIncompressible1; inputFromHistory = deflateEncoder.UnprocessedInput; } break; case DeflaterState.SlowDownForIncompressible1: deflateEncoder.GetBlockFooter(output); processingState = DeflaterState.SlowDownForIncompressible2; goto case DeflaterState.SlowDownForIncompressible2; case DeflaterState.SlowDownForIncompressible2: if (inputFromHistory.Count > 0) { copyEncoder.GetBlock(inputFromHistory, output, isFinal: false); } if (inputFromHistory.Count == 0) { deflateEncoder.FlushInput(); processingState = DeflaterState.CheckingForIncompressible; } break; case DeflaterState.CheckingForIncompressible: { AADeflateInput.InputState state = input.DumpState(); AAOutputBuffer.BufferState state2 = output.DumpState(); deflateEncoder.GetBlock(input, output, 8072); if (!UseCompressed(deflateEncoder.LastCompressionRatio)) { input.RestoreState(state); output.RestoreState(state2); copyEncoder.GetBlock(input, output, isFinal: false); FlushInputWindows(); } break; } case DeflaterState.StartingSmallData: deflateEncoder.GetBlockHeader(output); processingState = DeflaterState.HandlingSmallData; goto case DeflaterState.HandlingSmallData; case DeflaterState.HandlingSmallData: deflateEncoder.GetCompressedData(input, output); break; } return output.BytesWritten; } public bool Finish(byte[] outputBuffer, out int bytesRead) { if (processingState == DeflaterState.NotStarted) { bytesRead = 0; return true; } output.UpdateBuffer(outputBuffer); if (processingState == DeflaterState.CompressThenCheck || processingState == DeflaterState.HandlingSmallData || processingState == DeflaterState.SlowDownForIncompressible1) { deflateEncoder.GetBlockFooter(output); } WriteFinal(); bytesRead = output.BytesWritten; return true; } void IDisposable.Dispose() { } protected void Dispose(bool disposing) { } private bool UseCompressed(double ratio) { return ratio <= 1.0; } private void FlushInputWindows() { deflateEncoder.FlushInput(); } private void WriteFinal() { copyEncoder.GetBlock(null, output, isFinal: true); } }