예제 #1
0
        public byte[] Compress(byte[] buffer)
        {
            ByteDictionary      byteDictionary = new ByteDictionary(buffer);
            ChunkNodeCollection nodeCollection = new ChunkNodeCollection();

            while (nodeCollection.Count > 0)
            {
                if (nodeCollection.IsNextNodeOptimal())
                {
                    KeyValuePair <int, ChunkNode> parentNode = nodeCollection.GetNextNode();

                    if (parentNode.Key < buffer.Length)
                    {
                        OptimalCompressor.CreateChildNodes(buffer, nodeCollection, parentNode.Key, parentNode.Value, byteDictionary);
                    }
                }
            }

            ChunkNode bestNode = nodeCollection[buffer.Length];

            return(bestNode.GetCompressedBuffer(buffer));
        }
예제 #2
0
        private static void CreateChildNodes(byte[] buffer, ChunkNodeCollection nodeCollection, int i, ChunkNode parentNode, ByteDictionary byteDictionary)
        {
            // NOTE: Command 5 (ie: the same as command 4 except it inverts each byte)
            // is not implemented, because it's almost never used.
            // Implementing it would complicate the code and slow down the compression for little to no benefit.

            // NOTE: Checking out command 0 every time (ie: putting it out of the if conditions)
            // can improve compression a tiny bit (like just one byte) in some rare cases,
            // but it's not worth the huge hit on compression speed.

            OptimalCompressor.CreateNodesFromBackCommands(nodeCollection, i, parentNode, byteDictionary);

            int command;
            int byteCount;

            if ((i + 1) < buffer.Length &&
                buffer[i] == buffer[i + 1])
            {
                command   = 1;
                byteCount = OptimalCompressor.GetCommand1ByteCount(buffer, i);
            }
            else if ((i + 2) < buffer.Length &&
                     buffer[i] == buffer[i + 2])
            {
                command   = 2;
                byteCount = OptimalCompressor.GetCommand2ByteCount(buffer, i);
            }
            else if ((i + 1) < buffer.Length &&
                     ((buffer[i] + 1) & 0xFF) == buffer[i + 1])
            {
                command   = 3;
                byteCount = OptimalCompressor.GetCommand3ByteCount(buffer, i);
            }
            else
            {
                command   = 0;
                byteCount = OptimalCompressor.GetCommand0ByteCount(buffer, i);
            }

            OptimalCompressor.CreateNodesFromCommand(command, byteCount, nodeCollection, i, parentNode);
        }
예제 #3
0
        public byte[] Compress(byte[] buffer)
        {
            ByteDictionary byteDictionary = new ByteDictionary(buffer);

            byte[] compBuffer = new byte[Codec.BufferSize];

            int i = 0; // Iterator for buffer
            int j = 0; // Iterator for compBuffer

            while (i < buffer.Length)
            {
                int k = i + 1;   // Forward iterator for buffer
                int command;     // The compression command we consider the most efficient
                int commandCost; // How costly we consider this command

                if (k < buffer.Length &&
                    buffer[i] == buffer[k])
                {
                    // Command 1: the same byte is repeated
                    command     = 1;
                    commandCost = 0;

                    do
                    {
                        k++;
                    }while (k < buffer.Length &&
                            buffer[i] == buffer[k]);
                }
                else if ((k + 1) < buffer.Length &&
                         buffer[i] == buffer[i + 2])
                {
                    // Command 2: a sequence of 2 bytes is repeated
                    command     = 2;
                    commandCost = 2;

                    k += 2;
                    while (k < buffer.Length &&
                           buffer[k - 2] == buffer[k])
                    {
                        k++;
                    }
                }
                else if (k < buffer.Length &&
                         ((buffer[i] + 1) & 0xFF) == buffer[k])
                {
                    // Command 3: a value keeps getting incremented by 1
                    command     = 3;
                    commandCost = 0;

                    do
                    {
                        k++;
                    }while (k < buffer.Length &&
                            ((buffer[k - 1] + 1) & 0xFF) == buffer[k]);
                }
                else
                {
                    // Command 0: simply stores a sequence of bytes as is
                    command     = 0;
                    commandCost = 0;
                }

                Range range        = FastCompressor.GetRange(i, k);
                Range maxBackRange = byteDictionary.GetMaxBackRange(i);
                int   distance     = i - maxBackRange.Start;

                if (distance > 0xFF)
                {
                    // If distance > 0xFF, then command 4 should be used, otherwise command 6 is more efficient.
                    // We need to know this to optimize compression, because using command 4 is more expensive
                    // (the address is encoded on one more byte, because it's absolute rather than relative).
                    commandCost--; // Decreases the odds of using a back command (4 or 6)
                }

                if (maxBackRange.Length + commandCost > range.Length)
                {
                    // Command 4 or 6: repeats a previous data sequence
                    // Command 4: from an absolute address (on two bytes)
                    // Command 6: from a relative address (on one byte)
                    command = 4; // Actually 4 or 6
                }

                switch (command)
                {
                case 0:
                    FastCompressor.CallCommand0(buffer, compBuffer, ref i, ref j, k, byteDictionary);
                    break;

                case 1:
                    FastCompressor.CallCommand1(buffer, compBuffer, ref i, ref j, range.Length);
                    break;

                case 2:
                    FastCompressor.CallCommand2(buffer, compBuffer, ref i, ref j, range.Length);
                    break;

                case 3:
                    FastCompressor.CallCommand3(buffer, compBuffer, ref i, ref j, range.Length);
                    break;

                case 4:
                    FastCompressor.CallCommand4Or6(compBuffer, ref i, ref j, maxBackRange);
                    break;
                }
            }

            compBuffer[j++] = 0xFF;

            return(Utilities.ReadBlock(compBuffer, 0, j));
        }
예제 #4
0
        private static void CreateNodesFromBackCommands(ChunkNodeCollection nodeCollection, int i, ChunkNode parentNode, ByteDictionary byteDictionary)
        {
            Range[] ranges = byteDictionary.GetMaxBackRanges(i);

            if (ranges[0].Length > 0)
            {
                nodeCollection.Add(i + ranges[0].Length, new ChunkNode(parentNode, 4, ranges[0].Start, ranges[0].Length));
            }
            if (ranges[1].Length > 0)
            {
                nodeCollection.Add(i + ranges[1].Length, new ChunkNode(parentNode, 4, ranges[1].Start, ranges[1].Length));
            }

            if (ranges[2].Length > 0)
            {
                nodeCollection.Add(i + ranges[2].Length, new ChunkNode(parentNode, 6, i - ranges[2].Start, ranges[2].Length));
            }
            if (ranges[3].Length > 0)
            {
                nodeCollection.Add(i + ranges[3].Length, new ChunkNode(parentNode, 6, i - ranges[3].Start, ranges[3].Length));
            }
        }
예제 #5
0
        private static void CallCommand0(byte[] buffer, byte[] compBuffer, ref int i, ref int j, int k, ByteDictionary byteDictionary)
        {
            while (k < buffer.Length)
            {
                if
                (
                    (
                        // Matches command 3
                        (k + 2) < buffer.Length &&
                        (buffer[k] == ((buffer[k + 1] - 1) & 0xFF) &&
                         buffer[k + 2] == ((buffer[k + 1] + 1) & 0xFF))
                    )
                    ||
                    (
                        // Matches command 1
                        (k + 2) < buffer.Length &&
                        (buffer[k] == buffer[k + 1] &&
                         buffer[k] == buffer[k + 2])
                    )
                    ||
                    (
                        // Matches command 2
                        (k + 3) < buffer.Length &&
                        (buffer[k] == buffer[k + 2] &&
                         buffer[k + 1] == buffer[k + 3])
                    )
                )
                {
                    break;
                }

                Range backRange = byteDictionary.GetMaxBackRange(k);
                int   distance  = k - backRange.Start;
                // Matches command 4 or 6
                if ((distance > 0xFF && backRange.Length > 4) ||
                    (distance <= 0xFF && backRange.Length > 3))
                {
                    break;
                }

                k++;
            }

            Range range = FastCompressor.GetRange(i, k);

            int byteCount = range.Length;

            if (byteCount <= Codec.NormalCommandMax)
            {
                compBuffer[j++] = (byte)(byteCount - 1);
            }
            else
            {
                compBuffer[j++] = (byte)(0xE0 + ((byteCount - 1 & 0x300) >> 8));
                compBuffer[j++] = (byte)(byteCount - 1 & 0xFF);
            }

            Buffer.BlockCopy(buffer, i, compBuffer, j, byteCount);
            j += byteCount;
            i += byteCount;
        }