public static bool RefPackCompress(Stream input, int length, out byte[] output, CompressionLevel level) { byte[] data = new byte[length]; input.Read(data, 0, data.Length); return Compression.Compress(data, out output, level); }
private static bool FindSequence(byte[] data, int offset, ref int bestStart, ref int bestLength, ref int bestIndex, Dictionary<int, List<int>> blockTracking, CompressionLevel level) { int start; int end = -level.BruteForceLength; if (offset < level.BruteForceLength) { end = -offset; } if (offset > 4) { start = -3; } else { start = offset - 3; } bool foundRun = false; try { if (bestLength < 3) { bestLength = 3; bestIndex = int.MaxValue; } byte[] search = new byte[data.Length - offset > 4 ? 4 : data.Length - offset]; for (int loop = 0; loop < search.Length; loop++) { search[loop] = data[offset + loop]; } while (start >= end && bestLength < 1028) { byte currentByte = data[start + offset]; for (int loop = 0; loop < search.Length; loop++) { if (currentByte != search[loop] || start >= loop || start - loop < -131072) continue; int len = FindRunLength(data, offset + start, offset + loop); if ((len > bestLength || len == bestLength && loop < bestIndex) && (len >= 5 || len >= 4 && start - loop > -16384 || len >= 3 && start - loop > -1024)) { foundRun = true; bestStart = offset + start; bestLength = len; bestIndex = loop; } } start--; } if (blockTracking.Count > 0 && data.Length - offset > 16 && bestLength < 1028) { for (int loop = 0; loop < 4; loop++) { int thisPosition = offset + 3 - loop; int adjust = loop > 3 ? loop - 3 : 0; int value = BitConverter.ToInt32(data, thisPosition); List<int> positions; if (blockTracking.TryGetValue(value, out positions)) { foreach (int trypos in positions) { int localadjust = adjust; if (trypos + 131072 < offset + 8) { continue; } int length = FindRunLength(data, trypos + localadjust, thisPosition + localadjust); if (length >= 5 && length > bestLength) { foundRun = true; bestStart = trypos + localadjust; bestLength = length; if (loop < 3) { bestIndex = 3 - loop; } else { bestIndex = 0; } } if (bestLength > 1028) { break; } } } if (bestLength > 1028) { break; } } } } catch (Exception ex) { throw new Exception(ex.Message, ex); } return foundRun; }
public static bool Compress(byte[] input, out byte[] output, CompressionLevel level) { if (input.LongLength >= 0xFFFFFFFF) { throw new InvalidOperationException("input data is too large"); } bool endIsValid = false; List<byte[]> compressedChunks = new List<byte[]>(); int compressedIndex = 0; int compressedLength = 0; output = null; if (input.Length < 16) { return false; } Queue<KeyValuePair<int, int>> blockTrackingQueue = new Queue<KeyValuePair<int, int>>(); Queue<KeyValuePair<int, int>> blockPretrackingQueue = new Queue<KeyValuePair<int, int>>(); // So lists aren't being freed and allocated so much Queue<List<int>> unusedLists = new Queue<List<int>>(); Dictionary<int, List<int>> latestBlocks = new Dictionary<int, List<int>>(); int lastBlockStored = 0; while (compressedIndex < input.Length) { while (compressedIndex > lastBlockStored + level.BlockInterval && input.Length - compressedIndex > 16) { if (blockPretrackingQueue.Count >= level.PrequeueLength) { KeyValuePair<int, int> tmppair = blockPretrackingQueue.Dequeue(); blockTrackingQueue.Enqueue(tmppair); List<int> valueList; if (!latestBlocks.TryGetValue(tmppair.Key, out valueList)) { if (unusedLists.Count > 0) { valueList = unusedLists.Dequeue(); } else { valueList = new List<int>(); } latestBlocks[tmppair.Key] = valueList; } if (valueList.Count >= level.SameValToTrack) { int earliestIndex = 0; int earliestValue = valueList[0]; for (int loop = 1; loop < valueList.Count; loop++) { if (valueList[loop] < earliestValue) { earliestIndex = loop; earliestValue = valueList[loop]; } } valueList[earliestIndex] = tmppair.Value; } else { valueList.Add(tmppair.Value); } if (blockTrackingQueue.Count > level.QueueLength) { KeyValuePair<int, int> tmppair2 = blockTrackingQueue.Dequeue(); valueList = latestBlocks[tmppair2.Key]; for (int loop = 0; loop < valueList.Count; loop++) { if (valueList[loop] == tmppair2.Value) { valueList.RemoveAt(loop); break; } } if (valueList.Count == 0) { latestBlocks.Remove(tmppair2.Key); unusedLists.Enqueue(valueList); } } } KeyValuePair<int, int> newBlock = new KeyValuePair<int, int>(BitConverter.ToInt32(input, lastBlockStored), lastBlockStored); lastBlockStored += level.BlockInterval; blockPretrackingQueue.Enqueue(newBlock); } if (input.Length - compressedIndex < 4) { // Just copy the rest byte[] chunk = new byte[input.Length - compressedIndex + 1]; chunk[0] = (byte)(0xFC | (input.Length - compressedIndex)); Array.Copy(input, compressedIndex, chunk, 1, input.Length - compressedIndex); compressedChunks.Add(chunk); compressedIndex += chunk.Length - 1; compressedLength += chunk.Length; // int toRead = 0; // int toCopy2 = 0; // int copyOffset = 0; endIsValid = true; continue; } // Search ahead the next 3 bytes for the "best" sequence to copy int sequenceStart = 0; int sequenceLength = 0; int sequenceIndex = 0; bool isSequence = false; if (FindSequence(input, compressedIndex, ref sequenceStart, ref sequenceLength, ref sequenceIndex, latestBlocks, level)) { isSequence = true; } else { // Find the next sequence for (int loop = compressedIndex + 4; !isSequence && loop + 3 < input.Length; loop += 4) { if (FindSequence(input, loop, ref sequenceStart, ref sequenceLength, ref sequenceIndex, latestBlocks, level)) { sequenceIndex += loop - compressedIndex; isSequence = true; } } if (sequenceIndex == int.MaxValue) { sequenceIndex = input.Length - compressedIndex; } // Copy all the data skipped over while (sequenceIndex >= 4) { int toCopy = (sequenceIndex & ~3); if (toCopy > 112) { toCopy = 112; } byte[] chunk = new byte[toCopy + 1]; chunk[0] = (byte)(0xE0 | ((toCopy >> 2) - 1)); Array.Copy(input, compressedIndex, chunk, 1, toCopy); compressedChunks.Add(chunk); compressedIndex += toCopy; compressedLength += chunk.Length; sequenceIndex -= toCopy; // int toRead = 0; // int toCopy2 = 0; // int copyOffset = 0; } } if (isSequence) { byte[] chunk = null; /* * 00-7F 0oocccpp oooooooo * Read 0-3 * Copy 3-10 * Offset 0-1023 * * 80-BF 10cccccc ppoooooo oooooooo * Read 0-3 * Copy 4-67 * Offset 0-16383 * * C0-DF 110cccpp oooooooo oooooooo cccccccc * Read 0-3 * Copy 5-1028 * Offset 0-131071 * * E0-FC 111ppppp * Read 4-128 (Multiples of 4) * * FD-FF 111111pp * Read 0-3 */ if (FindRunLength(input, sequenceStart, compressedIndex + sequenceIndex) < sequenceLength) { break; } while (sequenceLength > 0) { int thisLength = sequenceLength; if (thisLength > 1028) { thisLength = 1028; } sequenceLength -= thisLength; int offset = compressedIndex - sequenceStart + sequenceIndex - 1; if (thisLength > 67 || offset > 16383) { chunk = new byte[sequenceIndex + 4]; chunk[0] = (byte)(0xC0 | sequenceIndex | (((thisLength - 5) >> 6) & 0x0C) | ((offset >> 12) & 0x10)); chunk[1] = (byte)((offset >> 8) & 0xFF); chunk[2] = (byte)(offset & 0xFF); chunk[3] = (byte)((thisLength - 5) & 0xFF); } else if (thisLength > 10 || offset > 1023) { chunk = new byte[sequenceIndex + 3]; chunk[0] = (byte)(0x80 | ((thisLength - 4) & 0x3F)); chunk[1] = (byte)(((sequenceIndex << 6) & 0xC0) | ((offset >> 8) & 0x3F)); chunk[2] = (byte)(offset & 0xFF); } else { chunk = new byte[sequenceIndex + 2]; chunk[0] = (byte)((sequenceIndex & 0x3) | (((thisLength - 3) << 2) & 0x1C) | ((offset >> 3) & 0x60)); chunk[1] = (byte)(offset & 0xFF); } if (sequenceIndex > 0) { Array.Copy(input, compressedIndex, chunk, chunk.Length - sequenceIndex, sequenceIndex); } compressedChunks.Add(chunk); compressedIndex += thisLength + sequenceIndex; compressedLength += chunk.Length; // int toRead = 0; // int toCopy = 0; // int copyOffset = 0; sequenceStart += thisLength; sequenceIndex = 0; } } } if (compressedLength + 6 < input.Length) { int chunkPosition; if (input.Length > 0xFFFFFF) { output = new byte[compressedLength + 5 + (endIsValid ? 0 : 1)]; output[0] = 0x10 | 0x80; // 0x80 = length is 4 bytes output[1] = 0xFB; output[2] = (byte)(input.Length >> 24); output[3] = (byte)(input.Length >> 16); output[4] = (byte)(input.Length >> 8); output[5] = (byte)(input.Length); chunkPosition = 6; } else { output = new byte[compressedLength + 5 + (endIsValid ? 0 : 1)]; output[0] = 0x10; output[1] = 0xFB; output[2] = (byte)(input.Length >> 16); output[3] = (byte)(input.Length >> 8); output[4] = (byte)(input.Length); chunkPosition = 5; } for (int loop = 0; loop < compressedChunks.Count; loop++) { Array.Copy(compressedChunks[loop], 0, output, chunkPosition, compressedChunks[loop].Length); chunkPosition += compressedChunks[loop].Length; } if (!endIsValid) { output[output.Length - 1] = 0xFC; } return true; } return false; }
public static bool RefPackCompress(byte[] input, out byte[] output, CompressionLevel level) { return Compression.Compress(input, out output, level); }