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)); }
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); }
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)); }
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)); } }
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; }