295 lines
8.1 KiB
C#
295 lines
8.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.IO;
|
|
|
|
using UniversalEditor.Compression.Common;
|
|
|
|
namespace UniversalEditor.Compression.Modules.Deflate.Internal
|
|
{
|
|
internal class FastEncoder
|
|
{
|
|
internal class Output
|
|
{
|
|
private uint bitBuf;
|
|
private int bitCount;
|
|
private static byte[] distLookup;
|
|
private byte[] outputBuf;
|
|
private int outputPos;
|
|
internal int BytesWritten
|
|
{
|
|
get
|
|
{
|
|
return this.outputPos;
|
|
}
|
|
}
|
|
internal int FreeBytes
|
|
{
|
|
get
|
|
{
|
|
return this.outputBuf.Length - this.outputPos;
|
|
}
|
|
}
|
|
static Output()
|
|
{
|
|
FastEncoder.Output.distLookup = new byte[512];
|
|
FastEncoder.Output.GenerateSlotTables();
|
|
}
|
|
internal void FlushBits()
|
|
{
|
|
while (this.bitCount >= 8)
|
|
{
|
|
int CS_0_0 = this.outputPos++;
|
|
this.outputBuf[CS_0_0] = (byte)this.bitBuf;
|
|
this.bitCount -= 8;
|
|
this.bitBuf >>= 8;
|
|
}
|
|
if (this.bitCount > 0)
|
|
{
|
|
int CS_0_0 = this.outputPos++;
|
|
this.outputBuf[CS_0_0] = (byte)this.bitBuf;
|
|
this.bitCount = 0;
|
|
}
|
|
}
|
|
internal static void GenerateSlotTables()
|
|
{
|
|
int num = 0;
|
|
int index;
|
|
for (index = 0; index < 16; index++)
|
|
{
|
|
for (int i = 0; i < 1 << (int)FastEncoderStatics.ExtraDistanceBits[index]; i++)
|
|
{
|
|
num++;
|
|
FastEncoder.Output.distLookup[num] = (byte)index;
|
|
}
|
|
}
|
|
num >>= 7;
|
|
while (index < 30)
|
|
{
|
|
for (int j = 0; j < 1 << (int)(FastEncoderStatics.ExtraDistanceBits[index] - 7); j++)
|
|
{
|
|
num++;
|
|
FastEncoder.Output.distLookup[256 + num] = (byte)index;
|
|
}
|
|
index++;
|
|
}
|
|
}
|
|
internal int GetSlot(int pos)
|
|
{
|
|
return (int)FastEncoder.Output.distLookup[(pos < 256) ? pos : (256 + (pos >> 7))];
|
|
}
|
|
internal bool SafeToWriteTo()
|
|
{
|
|
return this.outputBuf.Length - this.outputPos > 16;
|
|
}
|
|
internal void UpdateBuffer(byte[] output)
|
|
{
|
|
this.outputBuf = output;
|
|
this.outputPos = 0;
|
|
}
|
|
internal void WriteBits(int n, uint bits)
|
|
{
|
|
this.bitBuf |= bits << this.bitCount;
|
|
this.bitCount += n;
|
|
if (this.bitCount >= 16)
|
|
{
|
|
int CS_0_ = this.outputPos++;
|
|
this.outputBuf[CS_0_] = (byte)this.bitBuf;
|
|
CS_0_ = this.outputPos++;
|
|
this.outputBuf[CS_0_] = (byte)(this.bitBuf >> 8);
|
|
this.bitCount -= 16;
|
|
this.bitBuf >>= 16;
|
|
}
|
|
}
|
|
internal void WriteChar(byte b)
|
|
{
|
|
uint num = FastEncoderStatics.FastEncoderLiteralCodeInfo[(int)b];
|
|
this.WriteBits((int)(num & 31u), num >> 5);
|
|
}
|
|
internal void WriteGzipFooter(uint gzipCrc32, uint inputStreamSize)
|
|
{
|
|
int CS_0_0 = this.outputPos++;
|
|
this.outputBuf[CS_0_0] = (byte)(gzipCrc32 & 255u);
|
|
CS_0_0 = this.outputPos++;
|
|
this.outputBuf[CS_0_0] = (byte)(gzipCrc32 >> 8 & 255u);
|
|
CS_0_0 = this.outputPos++;
|
|
this.outputBuf[CS_0_0] = (byte)(gzipCrc32 >> 16 & 255u);
|
|
CS_0_0 = this.outputPos++;
|
|
this.outputBuf[CS_0_0] = (byte)(gzipCrc32 >> 24 & 255u);
|
|
CS_0_0 = this.outputPos++;
|
|
this.outputBuf[CS_0_0] = (byte)(inputStreamSize & 255u);
|
|
CS_0_0 = this.outputPos++;
|
|
this.outputBuf[CS_0_0] = (byte)(inputStreamSize >> 8 & 255u);
|
|
CS_0_0 = this.outputPos++;
|
|
this.outputBuf[CS_0_0] = (byte)(inputStreamSize >> 16 & 255u);
|
|
CS_0_0 = this.outputPos++;
|
|
this.outputBuf[CS_0_0] = (byte)(inputStreamSize >> 24 & 255u);
|
|
}
|
|
internal void WriteGzipHeader(int compression_level)
|
|
{
|
|
int CS_0_0 = this.outputPos++;
|
|
this.outputBuf[CS_0_0] = 31;
|
|
CS_0_0 = this.outputPos++;
|
|
this.outputBuf[CS_0_0] = 139;
|
|
CS_0_0 = this.outputPos++;
|
|
this.outputBuf[CS_0_0] = 8;
|
|
CS_0_0 = this.outputPos++;
|
|
this.outputBuf[CS_0_0] = 0;
|
|
CS_0_0 = this.outputPos++;
|
|
this.outputBuf[CS_0_0] = 0;
|
|
CS_0_0 = this.outputPos++;
|
|
this.outputBuf[CS_0_0] = 0;
|
|
CS_0_0 = this.outputPos++;
|
|
this.outputBuf[CS_0_0] = 0;
|
|
CS_0_0 = this.outputPos++;
|
|
this.outputBuf[CS_0_0] = 0;
|
|
if (compression_level == 10)
|
|
{
|
|
CS_0_0 = this.outputPos++;
|
|
this.outputBuf[CS_0_0] = 2;
|
|
}
|
|
else
|
|
{
|
|
CS_0_0 = this.outputPos++;
|
|
this.outputBuf[CS_0_0] = 4;
|
|
}
|
|
CS_0_0 = this.outputPos++;
|
|
this.outputBuf[CS_0_0] = 0;
|
|
}
|
|
internal void WriteMatch(int matchLen, int matchPos)
|
|
{
|
|
uint num = FastEncoderStatics.FastEncoderLiteralCodeInfo[254 + matchLen];
|
|
int i = (int)(num & 31u);
|
|
if (i <= 16)
|
|
{
|
|
this.WriteBits(i, num >> 5);
|
|
}
|
|
else
|
|
{
|
|
this.WriteBits(16, num >> 5 & 65535u);
|
|
this.WriteBits(i - 16, num >> 21);
|
|
}
|
|
num = FastEncoderStatics.FastEncoderDistanceCodeInfo[this.GetSlot(matchPos)];
|
|
this.WriteBits((int)(num & 15u), num >> 8);
|
|
int num2 = (int)(num >> 4 & 15u);
|
|
if (num2 != 0)
|
|
{
|
|
this.WriteBits(num2, (uint)(matchPos & (int)FastEncoderStatics.BitMask[num2]));
|
|
}
|
|
}
|
|
internal void WritePreamble()
|
|
{
|
|
Array.Copy(FastEncoderStatics.FastEncoderTreeStructureData, 0, this.outputBuf, this.outputPos, FastEncoderStatics.FastEncoderTreeStructureData.Length);
|
|
this.outputPos += FastEncoderStatics.FastEncoderTreeStructureData.Length;
|
|
this.bitCount = 9;
|
|
this.bitBuf = 34u;
|
|
}
|
|
}
|
|
private Match currentMatch;
|
|
private uint gzipCrc32;
|
|
private bool hasBlockHeader;
|
|
private bool hasGzipHeader;
|
|
private DeflateInput inputBuffer;
|
|
private uint inputStreamSize;
|
|
private FastEncoderWindow inputWindow;
|
|
private bool needsEOB;
|
|
private FastEncoder.Output output;
|
|
private bool usingGzip;
|
|
public FastEncoder(bool doGZip)
|
|
{
|
|
this.usingGzip = doGZip;
|
|
this.inputWindow = new FastEncoderWindow();
|
|
this.inputBuffer = new DeflateInput();
|
|
this.output = new FastEncoder.Output();
|
|
this.currentMatch = new Match();
|
|
}
|
|
public int Finish(byte[] outputBuffer)
|
|
{
|
|
this.output.UpdateBuffer(outputBuffer);
|
|
if (this.needsEOB)
|
|
{
|
|
uint num = FastEncoderStatics.FastEncoderLiteralCodeInfo[256];
|
|
int i = (int)(num & 31u);
|
|
this.output.WriteBits(i, num >> 5);
|
|
this.output.FlushBits();
|
|
if (this.usingGzip)
|
|
{
|
|
this.output.WriteGzipFooter(this.gzipCrc32, this.inputStreamSize);
|
|
}
|
|
}
|
|
return this.output.BytesWritten;
|
|
}
|
|
public int GetCompressedOutput(byte[] outputBuffer)
|
|
{
|
|
this.output.UpdateBuffer(outputBuffer);
|
|
if (this.usingGzip && !this.hasGzipHeader)
|
|
{
|
|
this.output.WriteGzipHeader(3);
|
|
this.hasGzipHeader = true;
|
|
}
|
|
if (!this.hasBlockHeader)
|
|
{
|
|
this.hasBlockHeader = true;
|
|
this.output.WritePreamble();
|
|
}
|
|
while (true)
|
|
{
|
|
int count = (this.inputBuffer.Count < this.inputWindow.FreeWindowSpace) ? this.inputBuffer.Count : this.inputWindow.FreeWindowSpace;
|
|
if (count > 0)
|
|
{
|
|
this.inputWindow.CopyBytes(this.inputBuffer.Buffer, this.inputBuffer.StartIndex, count);
|
|
if (this.usingGzip)
|
|
{
|
|
this.gzipCrc32 = DecodeHelper.UpdateCrc32(this.gzipCrc32, this.inputBuffer.Buffer, this.inputBuffer.StartIndex, count);
|
|
uint num2 = this.inputStreamSize + (uint)count;
|
|
if (num2 < this.inputStreamSize)
|
|
{
|
|
break;
|
|
}
|
|
this.inputStreamSize = num2;
|
|
}
|
|
this.inputBuffer.ConsumeBytes(count);
|
|
}
|
|
while (this.inputWindow.BytesAvailable > 0 && this.output.SafeToWriteTo())
|
|
{
|
|
this.inputWindow.GetNextSymbolOrMatch(this.currentMatch);
|
|
if (this.currentMatch.State == MatchState.HasSymbol)
|
|
{
|
|
this.output.WriteChar(this.currentMatch.Symbol);
|
|
}
|
|
else
|
|
{
|
|
if (this.currentMatch.State == MatchState.HasMatch)
|
|
{
|
|
this.output.WriteMatch(this.currentMatch.Length, this.currentMatch.Position);
|
|
}
|
|
else
|
|
{
|
|
this.output.WriteChar(this.currentMatch.Symbol);
|
|
this.output.WriteMatch(this.currentMatch.Length, this.currentMatch.Position);
|
|
}
|
|
}
|
|
}
|
|
if (!this.output.SafeToWriteTo() || this.NeedsInput())
|
|
{
|
|
goto Block_13;
|
|
}
|
|
}
|
|
throw new InvalidDataException("StreamSizeOverflow");
|
|
Block_13:
|
|
this.needsEOB = true;
|
|
return this.output.BytesWritten;
|
|
}
|
|
public bool NeedsInput()
|
|
{
|
|
return this.inputBuffer.Count == 0 && this.inputWindow.BytesAvailable == 0;
|
|
}
|
|
public void SetInput(byte[] input, int startIndex, int count)
|
|
{
|
|
this.inputBuffer.Buffer = input;
|
|
this.inputBuffer.Count = count;
|
|
this.inputBuffer.StartIndex = startIndex;
|
|
}
|
|
}
|
|
}
|