public static int Decompress(Stream source, byte[] output)
{
int outputOffset = 0;
int dictionaryTotalSize; // Size of the dictionary in bits.
int dictionaryOffset = 0;
int dictionarySize = 0; // Number of bytes currently used in the dictionary.
byte[] dictionary;
int bitBuffer = 0, bitCount = 0;
// Read header.
var format = (Format)source.ReadByte();
var dictionaryCode = (Dictionary)source.ReadByte();
// Check header data format.
if (format != Format.Ascii && format != Format.Binary)
throw new Exception();
// Check header dictionary.
switch (dictionaryCode) {
case Dictionary._1k: dictionaryTotalSize = 1024; break;
case Dictionary._2k: dictionaryTotalSize = 2048; break;
case Dictionary._4k: dictionaryTotalSize = 4096; break;
default: throw new Exception();
}
dictionary = new byte[dictionaryTotalSize];
while (outputOffset < output.Length) {
// Ensure there are 16 bits on the bit buffer.
FillBitBuffer(ref bitBuffer, ref bitCount, 16, source);
bool isDictionaryIndex = (bitBuffer & 1) != 0;
RemoveBits(ref bitBuffer, ref bitCount, 1);
// If first bit is set, copy from dictionary.
if (isDictionaryIndex) {
int copyOffset, copyLength;
// Find the length code value.
int lengthCode;
for (lengthCode = 0; Truncate(bitBuffer, LengthBits[lengthCode]) != LengthCodes[lengthCode]; lengthCode++) ;
RemoveBits(ref bitBuffer, ref bitCount, LengthBits[lengthCode]);
// Get the copy length and remove the bits from the bit buffer.
copyLength = LengthBases[lengthCode] + Truncate(bitBuffer, LengthExtraBits[lengthCode]);
RemoveBits(ref bitBuffer, ref bitCount, LengthExtraBits[lengthCode]);
// This copy length indicates the end of the stream.
if (copyLength == 519)
break;
FillBitBuffer(ref bitBuffer, ref bitCount, 14, source);
// Determine the dictionary offset.
int offsetCode;
for (offsetCode = 0; Truncate(bitBuffer, OffsetBits[offsetCode]) != OffsetCodes[offsetCode]; offsetCode++) ;
RemoveBits(ref bitBuffer, ref bitCount, OffsetBits[offsetCode]);
int offsetBits = (copyLength == 2) ? 2 : (int)dictionaryCode;
copyOffset = dictionarySize - 1 - ((offsetCode << offsetBits) + Truncate(bitBuffer, offsetBits));
RemoveBits(ref bitBuffer, ref bitCount, offsetBits);
while (copyLength-- > 0) {
while (copyOffset < 0)
copyOffset += dictionarySize;
while (copyOffset >= dictionarySize)
copyOffset -= dictionarySize;
WriteToOutput(dictionary[copyOffset++], output, ref outputOffset, dictionary, dictionaryTotalSize, ref dictionaryOffset, ref dictionarySize);
}
} else { // Literal byte
if (format == Format.Binary) {
WriteToOutput((byte)bitBuffer, output, ref outputOffset, dictionary, dictionaryTotalSize, ref dictionaryOffset, ref dictionarySize);
RemoveBits(ref bitBuffer, ref bitCount, 8);
} else {
int charCode;
for (charCode = 0; Truncate(bitBuffer, CharBits[charCode]) != CharCodes[charCode]; charCode++) ;
WriteToOutput((byte)charCode, output, ref outputOffset, dictionary, dictionaryTotalSize, ref dictionaryOffset, ref dictionarySize);
RemoveBits(ref bitBuffer, ref bitCount, CharBits[charCode]);
}
}
}
return outputOffset;
}