/// <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());
        }
예제 #2
0
        /// <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());
        }