Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Sys / System / IO / compression / Deflater.cs / 1305376 / Deflater.cs
// ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // zlib.h -- interface of the 'zlib' general purpose compression library // version 1.2.1, November 17th, 2003 // // Copyright (C) 1995-2003 Jean-loup Gailly and Mark Adler // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // // // ==--== // Compression engine namespace System.IO.Compression { using System; using System.Diagnostics; internal class Deflater { private const int MinBlockSize = 256; private const int MaxHeaderFooterGoo = 120; private const int CleanCopySize = DeflateStream.DefaultBufferSize - MaxHeaderFooterGoo; private const double BadCompressionThreshold = 1.0; private FastEncoder deflateEncoder; private CopyEncoder copyEncoder; private DeflateInput input; private OutputBuffer output; private DeflaterState processingState; private DeflateInput inputFromHistory; public Deflater() { deflateEncoder = new FastEncoder(); copyEncoder = new CopyEncoder(); input = new DeflateInput(); output = new OutputBuffer(); processingState = DeflaterState.NotStarted; } public bool NeedsInput() { return input.Count == 0 && deflateEncoder.BytesInHistory == 0; } // Sets the input to compress. The only buffer copy occurs when the input is copied // to the FastEncoderWindow public void SetInput(byte[] inputBuffer, int startIndex, int count) { Debug.Assert(input.Count == 0, "We have something left in previous input!"); input.Buffer = inputBuffer; input.Count = count; input.StartIndex = startIndex; if (count > 0 && count < MinBlockSize) { // user is writing small buffers. If buffer size is below MinBlockSize, we // need to switch to a small data mode, to avoid block headers and footers // dominating the output. switch (processingState) { case DeflaterState.NotStarted : case DeflaterState.CheckingForIncompressible: // clean states, needs a block header first processingState = DeflaterState.StartingSmallData; break; case DeflaterState.CompressThenCheck: // already has correct block header processingState = DeflaterState.HandlingSmallData; break; } } } public int 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; } public int Finish(byte[] outputBuffer) { Debug.Assert(outputBuffer != null, "Can't pass in a null output buffer!"); Debug.Assert(processingState == DeflaterState.NotStarted || processingState == DeflaterState.CheckingForIncompressible || processingState == DeflaterState.HandlingSmallData || processingState == DeflaterState.CompressThenCheck || processingState == DeflaterState.SlowDownForIncompressible1, "got unexpected processing state = " + processingState); Debug.Assert(NeedsInput()); // no need to add end of block info if we didn't write anything if (processingState == DeflaterState.NotStarted) { return 0; } output.UpdateBuffer(outputBuffer); if (processingState == DeflaterState.CompressThenCheck || processingState == DeflaterState.HandlingSmallData || processingState == DeflaterState.SlowDownForIncompressible1) { // need to finish off block deflateEncoder.GetBlockFooter(output); } // write final block WriteFinal(); return output.BytesWritten; } // Is compression ratio under threshold? private bool UseCompressed(double ratio) { return (ratio <= BadCompressionThreshold); } private void FlushInputWindows() { deflateEncoder.FlushInput(); } private void WriteFinal() { copyEncoder.GetBlock(null, output, true); } // These states allow us to assume that data is compressible and keep compression ratios at least // as good as historical values, but switch to different handling if that approach may increase the // data. If we detect we're getting a bad compression ratio, we switch to CheckingForIncompressible // state and decide to compress on a block by block basis. // // If we're getting small data buffers, we want to avoid overhead of excessive header and footer // info, so we add one header and keep adding blocks as compressed. This means that if the user uses // small buffers, they won't get the "don't increase size" improvements. // // An earlier iteration of this fix handled that data separately by buffering this data until it // reached a reasonable size, but given that Flush is not implemented on DeflateStream, this meant // data could be flushed only on Dispose. In the future, it would be reasonable to revisit this, in // case this isn't breaking. // // NotStarted -> CheckingForIncompressible, CompressThenCheck, StartingSmallData // CompressThenCheck -> SlowDownForIncompressible1 // SlowDownForIncompressible1 -> SlowDownForIncompressible2 // SlowDownForIncompressible2 -> CheckingForIncompressible // StartingSmallData -> HandlingSmallData internal enum DeflaterState { // no bytes to write yet NotStarted, // transient states SlowDownForIncompressible1, SlowDownForIncompressible2, StartingSmallData, // stable state: may transition to CheckingForIncompressible (via transient states) if it // appears we're expanding data CompressThenCheck, // sink states CheckingForIncompressible, HandlingSmallData } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // zlib.h -- interface of the 'zlib' general purpose compression library // version 1.2.1, November 17th, 2003 // // Copyright (C) 1995-2003 Jean-loup Gailly and Mark Adler // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // // // ==--== // Compression engine namespace System.IO.Compression { using System; using System.Diagnostics; internal class Deflater { private const int MinBlockSize = 256; private const int MaxHeaderFooterGoo = 120; private const int CleanCopySize = DeflateStream.DefaultBufferSize - MaxHeaderFooterGoo; private const double BadCompressionThreshold = 1.0; private FastEncoder deflateEncoder; private CopyEncoder copyEncoder; private DeflateInput input; private OutputBuffer output; private DeflaterState processingState; private DeflateInput inputFromHistory; public Deflater() { deflateEncoder = new FastEncoder(); copyEncoder = new CopyEncoder(); input = new DeflateInput(); output = new OutputBuffer(); processingState = DeflaterState.NotStarted; } public bool NeedsInput() { return input.Count == 0 && deflateEncoder.BytesInHistory == 0; } // Sets the input to compress. The only buffer copy occurs when the input is copied // to the FastEncoderWindow public void SetInput(byte[] inputBuffer, int startIndex, int count) { Debug.Assert(input.Count == 0, "We have something left in previous input!"); input.Buffer = inputBuffer; input.Count = count; input.StartIndex = startIndex; if (count > 0 && count < MinBlockSize) { // user is writing small buffers. If buffer size is below MinBlockSize, we // need to switch to a small data mode, to avoid block headers and footers // dominating the output. switch (processingState) { case DeflaterState.NotStarted : case DeflaterState.CheckingForIncompressible: // clean states, needs a block header first processingState = DeflaterState.StartingSmallData; break; case DeflaterState.CompressThenCheck: // already has correct block header processingState = DeflaterState.HandlingSmallData; break; } } } public int 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; } public int Finish(byte[] outputBuffer) { Debug.Assert(outputBuffer != null, "Can't pass in a null output buffer!"); Debug.Assert(processingState == DeflaterState.NotStarted || processingState == DeflaterState.CheckingForIncompressible || processingState == DeflaterState.HandlingSmallData || processingState == DeflaterState.CompressThenCheck || processingState == DeflaterState.SlowDownForIncompressible1, "got unexpected processing state = " + processingState); Debug.Assert(NeedsInput()); // no need to add end of block info if we didn't write anything if (processingState == DeflaterState.NotStarted) { return 0; } output.UpdateBuffer(outputBuffer); if (processingState == DeflaterState.CompressThenCheck || processingState == DeflaterState.HandlingSmallData || processingState == DeflaterState.SlowDownForIncompressible1) { // need to finish off block deflateEncoder.GetBlockFooter(output); } // write final block WriteFinal(); return output.BytesWritten; } // Is compression ratio under threshold? private bool UseCompressed(double ratio) { return (ratio <= BadCompressionThreshold); } private void FlushInputWindows() { deflateEncoder.FlushInput(); } private void WriteFinal() { copyEncoder.GetBlock(null, output, true); } // These states allow us to assume that data is compressible and keep compression ratios at least // as good as historical values, but switch to different handling if that approach may increase the // data. If we detect we're getting a bad compression ratio, we switch to CheckingForIncompressible // state and decide to compress on a block by block basis. // // If we're getting small data buffers, we want to avoid overhead of excessive header and footer // info, so we add one header and keep adding blocks as compressed. This means that if the user uses // small buffers, they won't get the "don't increase size" improvements. // // An earlier iteration of this fix handled that data separately by buffering this data until it // reached a reasonable size, but given that Flush is not implemented on DeflateStream, this meant // data could be flushed only on Dispose. In the future, it would be reasonable to revisit this, in // case this isn't breaking. // // NotStarted -> CheckingForIncompressible, CompressThenCheck, StartingSmallData // CompressThenCheck -> SlowDownForIncompressible1 // SlowDownForIncompressible1 -> SlowDownForIncompressible2 // SlowDownForIncompressible2 -> CheckingForIncompressible // StartingSmallData -> HandlingSmallData internal enum DeflaterState { // no bytes to write yet NotStarted, // transient states SlowDownForIncompressible1, SlowDownForIncompressible2, StartingSmallData, // stable state: may transition to CheckingForIncompressible (via transient states) if it // appears we're expanding data CompressThenCheck, // sink states CheckingForIncompressible, HandlingSmallData } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- PersonalizationStateQuery.cs
- PointKeyFrameCollection.cs
- GroupBox.cs
- ProfessionalColors.cs
- _TransmitFileOverlappedAsyncResult.cs
- GeneralTransform2DTo3DTo2D.cs
- BindingBase.cs
- Math.cs
- ConditionBrowserDialog.cs
- CalendarButton.cs
- ToolStripContextMenu.cs
- DataGridAddNewRow.cs
- SchemaImporterExtensionsSection.cs
- StylusTouchDevice.cs
- GreenMethods.cs
- BamlBinaryWriter.cs
- TaskFactory.cs
- WebPartConnectionsConnectVerb.cs
- VideoDrawing.cs
- DefaultPropertyAttribute.cs
- SettingsPropertyWrongTypeException.cs
- XPathExpr.cs
- log.cs
- LoadWorkflowByInstanceKeyCommand.cs
- DataBoundControlAdapter.cs
- StrokeSerializer.cs
- ExpressionWriter.cs
- SecurityTokenResolver.cs
- XmlDataSourceView.cs
- DataAccessor.cs
- ButtonStandardAdapter.cs
- StaticExtensionConverter.cs
- PolicyLevel.cs
- Panel.cs
- DocumentSchemaValidator.cs
- XamlStackWriter.cs
- IDispatchConstantAttribute.cs
- DataGridSortCommandEventArgs.cs
- JsonWriter.cs
- Repeater.cs
- ThrowHelper.cs
- RSAOAEPKeyExchangeDeformatter.cs
- UpdateCompiler.cs
- DoubleAnimationBase.cs
- wpf-etw.cs
- CustomLineCap.cs
- SafeCertificateContext.cs
- BaseDataList.cs
- CompositeKey.cs
- VisualTreeUtils.cs
- ChameleonKey.cs
- NodeInfo.cs
- AppDomainManager.cs
- Zone.cs
- StrokeCollectionDefaultValueFactory.cs
- UTF32Encoding.cs
- TagMapInfo.cs
- TransformDescriptor.cs
- PropertyGeneratedEventArgs.cs
- HandlerFactoryCache.cs
- BCryptSafeHandles.cs
- RichTextBoxAutomationPeer.cs
- InfoCardConstants.cs
- Odbc32.cs
- MiniAssembly.cs
- Empty.cs
- TransactionContext.cs
- PerformanceCounterManager.cs
- HostedElements.cs
- UIElementParagraph.cs
- CustomError.cs
- RegexTree.cs
- QuaternionAnimation.cs
- DynamicValueConverter.cs
- AssociationSet.cs
- safesecurityhelperavalon.cs
- SqlDataSourceEnumerator.cs
- XmlDictionaryWriter.cs
- OptionalRstParameters.cs
- ModuleBuilder.cs
- RadioButton.cs
- PageThemeParser.cs
- ItemCheckEvent.cs
- ChameleonKey.cs
- TypeContext.cs
- CLRBindingWorker.cs
- DetailsViewInsertedEventArgs.cs
- LinqDataSourceUpdateEventArgs.cs
- MetafileHeader.cs
- SecurityKeyUsage.cs
- ServicesUtilities.cs
- SymbolPair.cs
- RadioButtonPopupAdapter.cs
- SharedPersonalizationStateInfo.cs
- BeginEvent.cs
- ControlAdapter.cs
- DateTimeFormatInfo.cs
- RpcResponse.cs
- Int32RectValueSerializer.cs
- SystemIPv4InterfaceProperties.cs