int IDeflater.GetDeflateOutput(byte[] outputBuffer) { Debug.Assert(outputBuffer != null, "Can't pass in a null output buffer!"); Debug.Assert(!NeedsInput(), "GetDeflateOutput should only be called after providing input"); output.UpdateBuffer(outputBuffer); switch (processingState) { case DeflaterState.NotStarted: { // first call. Try to compress but if we get bad compression ratio, switch to uncompressed blocks. Debug.Assert(deflateEncoder.BytesInHistory == 0, "have leftover bytes in window"); // save these in case we need to switch to uncompressed format DeflateInput.InputState initialInputState = input.DumpState(); OutputBuffer.BufferState initialOutputState = output.DumpState(); deflateEncoder.GetBlockHeader(output); deflateEncoder.GetCompressedData(input, output); if (!UseCompressed(deflateEncoder.LastCompressionRatio)) { // we're expanding; restore state and switch to uncompressed input.RestoreState(initialInputState); output.RestoreState(initialOutputState); copyEncoder.GetBlock(input, output, false); FlushInputWindows(); processingState = DeflaterState.CheckingForIncompressible; } else { processingState = DeflaterState.CompressThenCheck; } break; } case DeflaterState.CompressThenCheck: { // continue assuming data is compressible. If we reach data that indicates otherwise // finish off remaining data in history and decide whether to compress on a // block-by-block basis deflateEncoder.GetCompressedData(input, output); if (!UseCompressed(deflateEncoder.LastCompressionRatio)) { processingState = DeflaterState.SlowDownForIncompressible1; inputFromHistory = deflateEncoder.UnprocessedInput; } break; } case DeflaterState.SlowDownForIncompressible1: { // finish off previous compressed block deflateEncoder.GetBlockFooter(output); processingState = DeflaterState.SlowDownForIncompressible2; goto case DeflaterState.SlowDownForIncompressible2; // yeah I know, but there's no fallthrough } case DeflaterState.SlowDownForIncompressible2: { // clear out data from history, but add them as uncompressed blocks if (inputFromHistory.Count > 0) { copyEncoder.GetBlock(inputFromHistory, output, false); } if (inputFromHistory.Count == 0) { // now we're clean deflateEncoder.FlushInput(); processingState = DeflaterState.CheckingForIncompressible; } break; } case DeflaterState.CheckingForIncompressible: { // decide whether to compress on a block-by-block basis Debug.Assert(deflateEncoder.BytesInHistory == 0, "have leftover bytes in window"); // save these in case we need to store as uncompressed DeflateInput.InputState initialInputState = input.DumpState(); OutputBuffer.BufferState initialOutputState = output.DumpState(); // enforce max so we can ensure state between calls deflateEncoder.GetBlock(input, output, CleanCopySize); if (!UseCompressed(deflateEncoder.LastCompressionRatio)) { // we're expanding; restore state and switch to uncompressed input.RestoreState(initialInputState); output.RestoreState(initialOutputState); copyEncoder.GetBlock(input, output, false); FlushInputWindows(); } break; } case DeflaterState.StartingSmallData: { // add compressed header and data, but not footer. Subsequent calls will keep // adding compressed data (no header and no footer). We're doing this to // avoid overhead of header and footer size relative to compressed payload. deflateEncoder.GetBlockHeader(output); processingState = DeflaterState.HandlingSmallData; goto case DeflaterState.HandlingSmallData; // yeah I know, but there's no fallthrough } case DeflaterState.HandlingSmallData: { // continue adding compressed data deflateEncoder.GetCompressedData(input, output); break; } } return(output.BytesWritten); }
internal void RestoreState(OutputBuffer.BufferState state) { pos = state.pos; bitBuf = state.bitBuf; bitCount = state.bitCount; }