/// <summary> /// Decompress data. /// </summary> /// <param name="data">Data to be decompressed.</param> /// <returns>Decompressed Data.</returns> public byte[] Decompress(byte[] data) { int CurrentPosition = 0; var result = new List <byte>(); /* * Loop until a decompression terminating condition * Build the decoding table * CurrentPosition = 256 // start at the end of the Huffman table * NextBits = Read16Bits(InputBuffer + CurrentPosition) * CurrentPosition += 2 * NextBits <<= 16 * NextBits |= Read16Bits(InputBuffer + CurrentPosition) * CurrentPosition += 2 * ExtraBits = 16 * BlockEnd = OutputPosition + 65536 * * Loop until a block terminating condition * If OutputPosition >= BlockEnd then terminate block processing * Loop until a literal processing terminating condition * Next15Bits = NextBits >> (32 – 15) * HuffmanSymbol = DecodingTable[Next15Bits] * HuffmanSymbolBitLength = the bit length of HuffmanSymbol, from the table in the input buffer * If HuffmanSymbol <= 0 * NextBits <<= HuffmanSymbolBitLength * ExtraBits -= HuffmanSymbolBitLength * * Do * HuffmanSymbol = - HuffmanSymbol * HuffmanSymbol += (NextBits >> 31) * NextBits *= 2 * ExtraBits = ExtraBits - 1 * HuffmanSymbol = DecodingTable[HuffmanSymbol] * While HuffmanSymbol <= 0 * Else * DecodedBitCount = HuffmanSymbol & 15 * NextBits <<= DecodedBitCount * ExtraBits -= DedcodedBitCount * * HuffmanSymbol >>= 4 // Shift by 4 bits to get the symbol value * // (the lower 4 bits are the bit length of the symbol) * HuffmanSymbol -= 256 * If ExtraBits < 0 * NextBits |= Read16Bits(InputBuffer + CurrentPosition) << (-ExtraBits) * ExtraBits += 16 * CurrentPosition += 2 * If HuffmanSymbol >= 0 * If HuffmanSymbol == 0 * If the entire input buffer has been read and * the expected decompressed size has been written to the output buffer * Decompression is complete. Return with success. * Terminate literal processing * Else * Output the byte value of HuffmanSymbol to the output stream * End of literal processing Loop * * MatchLength = HuffmanSymbol mod 16 * MatchOffsetBitLength = HuffmanSymbol / 16 * If MatchLength == 15 * MatchLength = ReadByte(InputBuffer + CurrentPosition) * CurrentPosition += 1 * If MatchLength == 255 * MatchLength = Read16Bits(InputBuffer + CurrentPosition) * CurrentPosition += 2 * If MatchLength < 15 * The compressed data is invalid. Return error. * MatchLength = MatchLength - 15 * MatchLength = MatchLength + 15 * MatchLength = MatchLength + 3 * MatchOffset = NextBits >> (32 – MatchOffsetBitLength) * MatchOffset += (1 << MatchOffsetBitLength) * NextBits <<= MatchOffsetBitLength * ExtraBits -= MatchOffsetBitLength * If ExtraBits < 0 * NextBits |= Read16Bits(InputBuffer + CurrentPosition) << (-ExtraBits) * ExtraBits += 16 * CurrentPosition += 2 * For i = 0 to MatchLength - 1 * Output OutputBuffer[CurrentOutputPosition – MatchOffset + i] * End of block loop * End of decoding loop */ HuffmanHeader header = null; uint NextBits = 0; int BlockEnd = 0; while (CurrentPosition < data.Length) { int remainingLength = data.Length - CurrentPosition; if (remainingLength < 256) { /* * Processing for the unhandled EOF in the last block if: * 1. The decompressed size of last block is 65536. * 2. Only two bytes are remained. * 3. The unhandled symbol is EOF. * 4. All remaining bits are zero. */ if (result.Count == BlockEnd && remainingLength == 2 && header != null) { uint Next15Bits = NextBits >> (32 - 15); int HuffmanSymbol = header.DecodingTable[Next15Bits]; int HuffmanSymbolBitLength = header.len[HuffmanSymbol]; NextBits <<= HuffmanSymbolBitLength; uint remainingBytes = Read16Bits(data, CurrentPosition); CurrentPosition += 2; if (HuffmanSymbol == 256 && NextBits == 0 && remainingBytes == 0) { continue; } } throw new XcaException("[Data error]: Data in buffer is less than expected!"); } header = new HuffmanHeader(data, CurrentPosition); header.buildDecodingTable(); CurrentPosition += 256; NextBits = Read16Bits(data, CurrentPosition); CurrentPosition += 2; NextBits <<= 16; NextBits |= Read16Bits(data, CurrentPosition); CurrentPosition += 2; int ExtraBits = 16; BlockEnd = result.Count + 65536; while (result.Count < BlockEnd) { uint Next15Bits = NextBits >> (32 - 15); int HuffmanSymbol = header.DecodingTable[Next15Bits]; int HuffmanSymbolBitLength = header.len[HuffmanSymbol]; NextBits <<= HuffmanSymbolBitLength; ExtraBits -= HuffmanSymbolBitLength; if (ExtraBits < 0) { NextBits |= Read16Bits(data, CurrentPosition) << (-ExtraBits); ExtraBits += 16; CurrentPosition += 2; } if (HuffmanSymbol < 256) { result.Add((byte)HuffmanSymbol); } else if (HuffmanSymbol == 256 && CurrentPosition == data.Length) { break; } else { HuffmanSymbol -= 256; uint MatchLength = (uint)(HuffmanSymbol % 16); int MatchOffsetBitLength = HuffmanSymbol / 16; if (MatchLength == 15) { MatchLength = ReadByte(data, CurrentPosition); CurrentPosition += 1; if (MatchLength == 255) { MatchLength = Read16Bits(data, CurrentPosition); CurrentPosition += 2; if (MatchLength < 15) { throw new XcaException("[Data error]: MatchLength should not be less than 15!"); } MatchLength -= 15; } MatchLength += 15; } MatchLength += 3; int MatchOffset = (int)((ulong)NextBits >> (32 - MatchOffsetBitLength)); MatchOffset += (1 << MatchOffsetBitLength); NextBits <<= MatchOffsetBitLength; ExtraBits -= MatchOffsetBitLength; if (ExtraBits < 0) { NextBits |= Read16Bits(data, CurrentPosition) << (-ExtraBits); ExtraBits += 16; CurrentPosition += 2; } var location = result.Count; for (int i = 0; i < MatchLength; i++) { int matchPosition = location - MatchOffset + i; if (matchPosition < 0 || matchPosition >= result.Count) { throw new XcaException("[Data error]: Match offset out of buffer!"); } byte matchLiteral = result[matchPosition]; result.Add(matchLiteral); } } } } return(result.ToArray()); }
/// <summary> /// Decompress data. /// </summary> /// <param name="data">Data to be decompressed.</param> /// <returns>Decompressed Data.</returns> public byte[] Decompress(byte[] data) { var header = new HuffmanHeader(data.Take(256).ToArray()); var result = new List <byte>(); /* * Build the decoding table * CurrentPosition = 256 // start at the end of the Huffman table * NextBits = Read16Bits(InputBuffer + CurrentPosition) * CurrentPosition += 2 * NextBits <<= 16 * NextBits |= Read16Bits(InputBuffer + CurrentPosition) * CurrentPosition += 2 * ExtraBits = 16 * Loop until a terminating condition * Next15Bits = NextBits >> (32 – 15) * HuffmanSymbol = DecodingTable[Next15Bits] * HuffmanSymbolBitLength = the bit length of HuffmanSymbol, from the table in * the input buffer * NextBits <<= HuffmanSymbolBitLength * ExtraBits -= HuffmanSymbolBitLength * If ExtraBits < 0 * NextBits |= Read16Bits(InputBuffer + CurrentPosition) << (-ExtraBits) * ExtraBits += 16 * CurrentPosition += 2 * If HuffmanSymbol < 256 * Output the byte value HuffmanSymbol to the output stream. * Else If HuffmanSymbol == 256 and * the entire input buffer has been read and * the expected decompressed size has been written to the output buffer * Decompression is complete. Return with success. * Else * HuffmanSymbol = HuffmanSymbol - 256 * MatchLength = HuffmanSymbol mod 16 * MatchOffsetBitLength = HuffmanSymbol / 16 * If MatchLength == 15 * MatchLength = ReadByte(InputBuffer + CurrentPosition) * CurrentPosition += 1 * If MatchLength == 255 * MatchLength = Read16Bits(InputBuffer + CurrentPosition) * CurrentPosition += 2 * If MatchLength < 15 * The compressed data is invalid. Return error. * MatchLength = MatchLength - 15 * MatchLength = MatchLength + 15 * MatchLength = MatchLength + 3 * MatchOffset = NextBits >> (32 – MatchOffsetBitLength) * MatchOffset += (1 << MatchOffsetBitLength) * NextBits <<= MatchOffsetBitLength * ExtraBits -= MatchOffsetBitLength * If ExtraBits < 0 * Read the next 2 bytes the same as the preceding (ExtraBits < 0) case * For i = 0 to MatchLength - 1 * Output OutputBuffer[CurrentOutputPosition – MatchOffset + i] */ header.buildDecodingTable(); int CurrentPosition = 256; uint NextBits = Read16Bits(data, CurrentPosition); CurrentPosition += 2; NextBits <<= 16; NextBits |= Read16Bits(data, CurrentPosition); CurrentPosition += 2; int ExtraBits = 16; while (true) { uint Next15Bits = NextBits >> (32 - 15); int HuffmanSymbol = header.DecodingTable[Next15Bits]; int HuffmanSymbolBitLength = header.len[HuffmanSymbol]; NextBits <<= HuffmanSymbolBitLength; ExtraBits -= HuffmanSymbolBitLength; if (ExtraBits < 0) { NextBits |= Read16Bits(data, CurrentPosition) << (-ExtraBits); ExtraBits += 16; CurrentPosition += 2; } if (HuffmanSymbol < 256) { result.Add((byte)HuffmanSymbol); } else if (HuffmanSymbol == 256 && CurrentPosition == data.Length) { break; } else { HuffmanSymbol -= 256; uint MatchLength = (uint)(HuffmanSymbol % 16); int MatchOffsetBitLength = HuffmanSymbol / 16; if (MatchLength == 15) { MatchLength = ReadByte(data, CurrentPosition); CurrentPosition += 1; if (MatchLength == 255) { MatchLength = Read16Bits(data, CurrentPosition); CurrentPosition += 2; if (MatchLength < 15) { throw new XcaException("[Data error]: MatchLength should not be less than 15!"); } MatchLength -= 15; } MatchLength += 15; } MatchLength += 3; int MatchOffset = (int)((ulong)NextBits >> (32 - MatchOffsetBitLength)); MatchOffset += (1 << MatchOffsetBitLength); NextBits <<= MatchOffsetBitLength; ExtraBits -= MatchOffsetBitLength; if (ExtraBits < 0) { NextBits |= Read16Bits(data, CurrentPosition) << (-ExtraBits); ExtraBits += 16; CurrentPosition += 2; } var location = result.Count; for (int i = 0; i < MatchLength; i++) { int matchPosition = location - MatchOffset + i; if (matchPosition < 0 || matchPosition >= result.Count) { throw new XcaException("[Data error]: Match offset out of buffer!"); } byte matchLiteral = result[matchPosition]; result.Add(matchLiteral); } } } return(result.ToArray()); }