Quasar/Server/Core/Compression/LZ4Decompressor32.cs

257 lines
10 KiB
C#

using System;
using System.Diagnostics;
namespace xServer.Core.Compression
{
/// <summary>
/// Class for decompressing an LZ4 compressed byte array.
/// </summary>
public unsafe class LZ4Decompressor32
{
const int STEPSIZE = 4;
static byte[] DeBruijnBytePos = new byte[32] { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 };
//**************************************
// Macros
//**************************************
readonly sbyte[] m_DecArray = new sbyte[8] { 0, 3, 2, 3, 0, 0, 0, 0 };
// Note : The decoding functions LZ4_uncompress() and LZ4_uncompress_unknownOutputSize()
// are safe against "buffer overflow" attack type
// since they will *never* write outside of the provided output buffer :
// they both check this condition *before* writing anything.
// A corrupted packet however can make them *read* within the first 64K before the output buffer.
/// <summary>
/// Decompress.
/// </summary>
/// <param name="source">compressed array</param>
/// <param name="dest">This must be the exact length of the decompressed item</param>
public void DecompressKnownSize(byte[] compressed, byte[] decompressed)
{
int len = DecompressKnownSize(compressed, decompressed, decompressed.Length);
Debug.Assert(len == decompressed.Length);
}
public int DecompressKnownSize(byte[] compressed, byte[] decompressedBuffer, int decompressedSize)
{
fixed (byte* src = compressed)
fixed (byte* dst = decompressedBuffer)
return DecompressKnownSize(src, dst, decompressedSize);
}
public int DecompressKnownSize(byte* compressed, byte* decompressedBuffer, int decompressedSize)
{
fixed (sbyte* dec = m_DecArray)
{
// Local Variables
byte* ip = (byte*)compressed;
byte* r;
byte* op = (byte*)decompressedBuffer;
byte* oend = op + decompressedSize;
byte* cpy;
byte token;
int len, length;
// Main Loop
while (true)
{
// get runLength
token = *ip++;
if ((length = (token >> LZ4Util.ML_BITS)) == LZ4Util.RUN_MASK) { for (; (len = *ip++) == 255; length += 255) { } length += len; }
cpy = op + length;
if (cpy > oend - LZ4Util.COPYLENGTH)
{
if (cpy > oend) goto _output_error;
LZ4Util.CopyMemory(op, ip, length);
ip += length;
break;
}
do { *(uint*)op = *(uint*)ip; op += 4; ip += 4; ; *(uint*)op = *(uint*)ip; op += 4; ip += 4; ; } while (op < cpy); ; ip -= (op - cpy); op = cpy;
// get offset
{ r = (cpy) - *(ushort*)ip; }; ip += 2;
if (r < decompressedBuffer) goto _output_error;
// get matchLength
if ((length = (int)(token & LZ4Util.ML_MASK)) == LZ4Util.ML_MASK) { for (; *ip == 255; length += 255) { ip++; } length += *ip++; }
// copy repeated sequence
if (op - r < STEPSIZE)
{
const int dec2 = 0;
*op++ = *r++;
*op++ = *r++;
*op++ = *r++;
*op++ = *r++;
r -= dec[op - r];
*(uint*)op = *(uint*)r; op += STEPSIZE - 4;
r -= dec2;
}
else { *(uint*)op = *(uint*)r; op += 4; r += 4; ; }
cpy = op + length - (STEPSIZE - 4);
if (cpy > oend - LZ4Util.COPYLENGTH)
{
if (cpy > oend) goto _output_error;
do { *(uint*)op = *(uint*)r; op += 4; r += 4; ; *(uint*)op = *(uint*)r; op += 4; r += 4; ; } while (op < (oend - LZ4Util.COPYLENGTH)); ;
while (op < cpy) *op++ = *r++;
op = cpy;
if (op == oend) break;
continue;
}
do { *(uint*)op = *(uint*)r; op += 4; r += 4; ; *(uint*)op = *(uint*)r; op += 4; r += 4; ; } while (op < cpy); ;
op = cpy; // correction
}
// end of decoding
return (int)(((byte*)ip) - compressed);
// write overflow error detected
_output_error:
return (int)(-(((byte*)ip) - compressed));
}
}
public byte[] Decompress(byte[] compressed)
{
int length = compressed.Length;
int len;
byte[] dest;
const int Multiplier = 4; // Just a number. Determines how fast length should increase.
do
{
length *= Multiplier;
dest = new byte[length];
len = Decompress(compressed, dest, compressed.Length);
}
while (len < 0 || dest.Length < len);
byte[] d = new byte[len];
Buffer.BlockCopy(dest, 0, d, 0, d.Length);
return d;
}
public int Decompress(byte[] compressed, byte[] decompressedBuffer)
{
return Decompress(compressed, decompressedBuffer, compressed.Length);
}
public int Decompress(byte[] compressedBuffer, byte[] decompressedBuffer, int compressedSize)
{
fixed (byte* src = compressedBuffer)
fixed (byte* dst = decompressedBuffer)
return Decompress(src, dst, compressedSize, decompressedBuffer.Length);
}
public int Decompress(byte[] compressedBuffer, int compressedPosition, byte[] decompressedBuffer, int decompressedPosition, int compressedSize)
{
fixed (byte* src = &compressedBuffer[compressedPosition])
fixed (byte* dst = &decompressedBuffer[decompressedPosition])
return Decompress(src, dst, compressedSize, decompressedBuffer.Length);
}
public int Decompress(
byte* compressedBuffer,
byte* decompressedBuffer,
int compressedSize,
int maxDecompressedSize)
{
fixed (sbyte* dec = m_DecArray)
{
// Local Variables
byte* ip = (byte*)compressedBuffer;
byte* iend = ip + compressedSize;
byte* r;
byte* op = (byte*)decompressedBuffer;
byte* oend = op + maxDecompressedSize;
byte* cpy;
byte token;
int length;
// Main Loop
while (ip < iend)
{
// get runLength
token = *ip++;
if ((length = (token >> LZ4Util.ML_BITS)) == LZ4Util.RUN_MASK) { int s = 255; while ((ip < iend) && (s == 255)) { s = *ip++; length += s; } }
// copy literals
cpy = op + length;
if ((cpy > oend - LZ4Util.COPYLENGTH) || (ip + length > iend - LZ4Util.COPYLENGTH))
{
if (cpy > oend) goto _output_error; // Error : request to write beyond destination buffer
if (ip + length > iend) goto _output_error; // Error : request to read beyond source buffer
LZ4Util.CopyMemory(op, ip, length);
op += length;
ip += length;
if (ip < iend) goto _output_error; // Error : LZ4 format violation
break; //Necessarily EOF
}
do { *(uint*)op = *(uint*)ip; op += 4; ip += 4; ; *(uint*)op = *(uint*)ip; op += 4; ip += 4; ; } while (op < cpy); ; ip -= (op - cpy); op = cpy;
// get offset
{ r = (cpy) - *(ushort*)ip; }; ip += 2;
if (r < decompressedBuffer) goto _output_error;
// get matchlength
if ((length = (int)(token & LZ4Util.ML_MASK)) == LZ4Util.ML_MASK) { while (ip < iend) { int s = *ip++; length += s; if (s == 255) continue; break; } }
// copy repeated sequence
if (op - r < STEPSIZE)
{
const int dec2 = 0;
*op++ = *r++;
*op++ = *r++;
*op++ = *r++;
*op++ = *r++;
r -= dec[op - r];
*(uint*)op = *(uint*)r; op += STEPSIZE - 4;
r -= dec2;
}
else { *(uint*)op = *(uint*)r; op += 4; r += 4; ; }
cpy = op + length - (STEPSIZE - 4);
if (cpy > oend - LZ4Util.COPYLENGTH)
{
if (cpy > oend) goto _output_error;
do { *(uint*)op = *(uint*)r; op += 4; r += 4; ; *(uint*)op = *(uint*)r; op += 4; r += 4; ; } while (op < (oend - LZ4Util.COPYLENGTH)); ;
while (op < cpy) *op++ = *r++;
op = cpy;
if (op == oend) goto _output_error; // Check EOF (should never happen, since last 5 bytes are supposed to be literals)
continue;
}
do { *(uint*)op = *(uint*)r; op += 4; r += 4; ; *(uint*)op = *(uint*)r; op += 4; r += 4; ; } while (op < cpy); ;
op = cpy; // correction
}
return (int)(((byte*)op) - decompressedBuffer);
_output_error:
return (int)(-(((byte*)ip) - compressedBuffer));
}
}
}
}