191 lines
4.8 KiB
C#
191 lines
4.8 KiB
C#
using System;
|
|
|
|
namespace Unity.IO.Compression
|
|
{
|
|
internal class DeflaterManaged : IDeflater, IDisposable
|
|
{
|
|
private enum DeflaterState
|
|
{
|
|
NotStarted = 0,
|
|
SlowDownForIncompressible1 = 1,
|
|
SlowDownForIncompressible2 = 2,
|
|
StartingSmallData = 3,
|
|
CompressThenCheck = 4,
|
|
CheckingForIncompressible = 5,
|
|
HandlingSmallData = 6
|
|
}
|
|
|
|
private const int MinBlockSize = 256;
|
|
|
|
private const int MaxHeaderFooterGoo = 120;
|
|
|
|
private const int CleanCopySize = 8072;
|
|
|
|
private const double BadCompressionThreshold = 1.0;
|
|
|
|
private FastEncoder deflateEncoder;
|
|
|
|
private CopyEncoder copyEncoder;
|
|
|
|
private DeflateInput input;
|
|
|
|
private OutputBuffer output;
|
|
|
|
private DeflaterState processingState;
|
|
|
|
private DeflateInput inputFromHistory;
|
|
|
|
internal DeflaterManaged()
|
|
{
|
|
deflateEncoder = new FastEncoder();
|
|
copyEncoder = new CopyEncoder();
|
|
input = new DeflateInput();
|
|
output = new OutputBuffer();
|
|
processingState = DeflaterState.NotStarted;
|
|
}
|
|
|
|
private bool NeedsInput()
|
|
{
|
|
return ((IDeflater)this).NeedsInput();
|
|
}
|
|
|
|
bool IDeflater.NeedsInput()
|
|
{
|
|
return input.Count == 0 && deflateEncoder.BytesInHistory == 0;
|
|
}
|
|
|
|
void IDeflater.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;
|
|
}
|
|
}
|
|
}
|
|
|
|
int IDeflater.GetDeflateOutput(byte[] outputBuffer)
|
|
{
|
|
output.UpdateBuffer(outputBuffer);
|
|
switch (processingState)
|
|
{
|
|
case DeflaterState.NotStarted:
|
|
{
|
|
DeflateInput.InputState state3 = input.DumpState();
|
|
OutputBuffer.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, 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, false);
|
|
}
|
|
if (inputFromHistory.Count == 0)
|
|
{
|
|
deflateEncoder.FlushInput();
|
|
processingState = DeflaterState.CheckingForIncompressible;
|
|
}
|
|
break;
|
|
case DeflaterState.CheckingForIncompressible:
|
|
{
|
|
DeflateInput.InputState state = input.DumpState();
|
|
OutputBuffer.BufferState state2 = output.DumpState();
|
|
deflateEncoder.GetBlock(input, output, 8072);
|
|
if (!UseCompressed(deflateEncoder.LastCompressionRatio))
|
|
{
|
|
input.RestoreState(state);
|
|
output.RestoreState(state2);
|
|
copyEncoder.GetBlock(input, output, 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;
|
|
}
|
|
|
|
bool IDeflater.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, true);
|
|
}
|
|
}
|
|
}
|