// Decompresses a Lempel-Ziv buffer. // TODO: Add documentation private static void DecompressBuffer(Stream decompStream, Stream compStream /*Starting at + 20*/) { //Create sliding dictionary buffer and clear first 4078 bytes of dictionary buffer to 0 byte[] slidingDict = new byte[SLIDING_LEN]; //Set an offset to the dictionary insertion point uint dictInsertionOffset = SLIDING_LEN - 18; // Current chunk header ChunkHeader chunkHeader = new ChunkHeader(compStream); while (decompStream.Position < decompStream.Length) { // Each chunk header is a byte and is a collection of 8 flags // If the flag is set, load a character if (chunkHeader.ReadFlag()) { // Copy the character // TODO: Add exceptions byte rawByte = (byte)compStream.ReadByte(); decompStream.WriteByte(rawByte); // Add the character to the dictionary, and slide the dictionary slidingDict[dictInsertionOffset++] = rawByte; dictInsertionOffset &= SLIDING_MASK; } // If the flag is clear, load an offset/length pair else { // Load the offset/length pair OffsetLengthPair olPair = new OffsetLengthPair(compStream); // Get the offset from the offset/length pair int offset = olPair.Offset; // Get the length from the offset/length pair int length = olPair.Length; for (int i = 0; i < length; i++) { byte rawByte = slidingDict[(offset + i) & SLIDING_MASK]; decompStream.WriteByte(rawByte); if (decompStream.Position >= decompStream.Length) { return; } // Add the character to the dictionary, and slide the dictionary slidingDict[dictInsertionOffset++] = rawByte; dictInsertionOffset &= SLIDING_MASK; } } } }
/// <summary> /// Compress the input buffer and write it into output stream. /// </summary> /// <param name="input">The buffer needed to be compressed</param> private void CompressCore(byte[] input, int maxBit = 16) { int compressIndex = 0; int lookAheadBytesCount = 0; //we don't need to find match for the last two bytes, because it //alway can't be found. while (compressIndex < (input.Length - (minimumEncodeLength - 1))) { OffsetLengthPair match = FindMatchInSlidingWindow(input, compressIndex); if (match.Length == 0) { //if there is no match, move forward one byte lookAheadBytesCount = 1; EncodeLiteral(input[compressIndex]); } else { lookAheadBytesCount = match.Length; EncodeOffsetLengthPair(match, maxBit); } byte[] lookAheadBuffer = new byte[lookAheadBytesCount]; Array.Copy(input, compressIndex, lookAheadBuffer, 0, lookAheadBuffer.Length); hashTable.Update(lookAheadBuffer); slidingWindow.Update(lookAheadBuffer); compressIndex += lookAheadBytesCount; } // upate hash table and sliding window byte[] toBeUptated = new byte[input.Length - compressIndex]; Array.Copy(input, compressIndex, toBeUptated, 0, toBeUptated.Length); hashTable.Update(toBeUptated); slidingWindow.Update(toBeUptated); while (compressIndex < input.Length) { EncodeLiteral(input[compressIndex]); compressIndex++; } //write the last byte to output stream if it exists if (remainBitsCount > 0) { outputStream.WriteByte((byte)(remain << (oneByteBitsCount - remainBitsCount))); remainBitsCount = 0; } outputStream.Flush(); }
// Decompresses a Lempel-Ziv buffer. // TODO: Add documentation private static void DecompressBuffer(byte[] decompBuf, byte[] compBuf /*Starting at + 20*/) { OffsetLengthPair olPair = new OffsetLengthPair(); int compBufPtr = 0; int decompBufPtr = 0; //Create sliding dictionary buffer and clear first 4078 bytes of dictionary buffer to 0 byte[] slidingDict = new byte[SLIDING_LEN]; //Set an offset to the dictionary insertion point uint dictInsertionOffset = SLIDING_LEN - 18; // Current chunk header ChunkHeader chunkHeader = new ChunkHeader(); while (decompBufPtr < decompBuf.Length) { // At the start of each chunk... if (!chunkHeader.ReadFlag(out bool flag)) { // Load the chunk header chunkHeader = new ChunkHeader(compBuf[compBufPtr++]); chunkHeader.ReadFlag(out flag); } // Each chunk header is a byte and is a collection of 8 flags // If the flag is set, load a character if (flag) { // Copy the character byte rawByte = compBuf[compBufPtr++]; decompBuf[decompBufPtr++] = rawByte; // Add the character to the dictionary, and slide the dictionary slidingDict[dictInsertionOffset++] = rawByte; dictInsertionOffset &= SLIDING_MASK; } // If the flag is clear, load an offset/length pair else { // Load the offset/length pair olPair.highByte = compBuf[compBufPtr++]; olPair.lowByte = compBuf[compBufPtr++]; // Get the offset from the offset/length pair int offset = olPair.Offset; // Get the length from the offset/length pair int length = olPair.Length; for (int i = 0; i < length; i++) { byte rawByte = slidingDict[(offset + i) & SLIDING_MASK]; decompBuf[decompBufPtr++] = rawByte; if (decompBufPtr >= decompBuf.Length) { return; } // Add the character to the dictionary, and slide the dictionary slidingDict[dictInsertionOffset++] = rawByte; dictInsertionOffset &= SLIDING_MASK; } } } }
/// <summary> /// Encode offset and length according to RFC2118 /// </summary> /// <param name="pair">offset and length pair</param> private void EncodeOffsetLengthPair(OffsetLengthPair pair, int maxBit = 16) { EncodeOffset(pair.Offset); EncodeLength(pair.Length, maxBit); }
private OffsetLengthPair FindMatchInSlidingWindow(byte[] input, int startIndex) { OffsetLengthPair match = new OffsetLengthPair(); if ((input.Length - startIndex) < minimumEncodeLength) { return(match); } //create minimumEncodeLength bytes key byte[] threeBytesHashKey = new byte[minimumEncodeLength]; Array.Copy(input, startIndex, threeBytesHashKey, 0, threeBytesHashKey.Length); int[] findedPositions = hashTable.GetKeyMatchPositions(threeBytesHashKey); //because hash table does not contain the hash value of last two characters //so we need to caculate it after getting hash value from hash table. List <int> matchPositions = new List <int>(findedPositions); // indicate the match position int matchedPosition = -1; bool isMatchFound = false; // if sliding windows count <=0, there is no match can be found, so do nothing if (slidingWindow.Count > 0) { // test if there is a match at the last position of sliding windows if (slidingWindow[slidingWindow.Count - 1] == input[startIndex] && input[startIndex] == input[startIndex + 1] && input[startIndex + 1] == input[startIndex + 2]) { // the position is the last byte in sliding window matchedPosition = slidingWindow.Count - 1; isMatchFound = true; } // test if there is a match at the second last position of sliding windows if (slidingWindow.Count > 1) { if (slidingWindow[slidingWindow.Count - 2] == input[startIndex] && slidingWindow[slidingWindow.Count - 1] == input[startIndex + 1] && input[startIndex] == input[startIndex + 2]) { // the position is the second last byte in sliding window matchPositions.Add(slidingWindow.Count - 2); isMatchFound = true; } } } //make sure the position will be added in ascending order. if (matchedPosition != -1) { matchPositions.Add(matchedPosition); } // translate list to array if match is found from the second last position of sliding window if (isMatchFound) { findedPositions = matchPositions.ToArray(); } // if there is no match, return default match if (findedPositions == null || findedPositions.Length == 0) { return(match); } //caculate the offset and length match.Offset = slidingWindow.Count - findedPositions[findedPositions.Length - 1]; match.Length = minimumEncodeLength; for (int i = 0; i < findedPositions.Length; i++) { //skip already matched 3 characters int searchIndex = startIndex + minimumEncodeLength; int j; for (j = (findedPositions[i] + minimumEncodeLength); j < slidingWindow.Count; j++) { // get the last unmatched position if ((searchIndex == input.Length) || (slidingWindow[j] != input[searchIndex++])) { if (match.Length < (searchIndex - startIndex - 1)) { match.Length = searchIndex - startIndex - 1; match.Offset = slidingWindow.Count - findedPositions[i]; } break; } } // if length > offset if (j >= slidingWindow.Count) { int mark = startIndex; // the last two character case if (j > 0) { mark = startIndex + j - slidingWindow.Count; } //continue find match against the current data while (searchIndex < input.Length) { if (input[mark++] != input[searchIndex++]) { // because upper line use searchIndex++, so at this position // we need to reduce it by 1; if (match.Length < (searchIndex - startIndex - 1)) { match.Length = searchIndex - startIndex - 1; match.Offset = slidingWindow.Count - findedPositions[i]; } break; } } // if searchIndex == inputLength, the way to caculate offset and length // is different with the normal situation. if (searchIndex == input.Length) { if (match.Length < searchIndex - startIndex) { match.Length = searchIndex - startIndex; match.Offset = slidingWindow.Count - findedPositions[i]; } } } } return(match); }
private OffsetLengthPair FindMatchInSlidingWindow(byte[] input, int startIndex) { OffsetLengthPair match = new OffsetLengthPair(); if ((input.Length - startIndex) < minimumEncodeLength) { return match; } //create minimumEncodeLength bytes key byte[] threeBytesHashKey = new byte[minimumEncodeLength]; Array.Copy(input, startIndex, threeBytesHashKey, 0, threeBytesHashKey.Length); int[] findedPositions = hashTable.GetKeyMatchPositions(threeBytesHashKey); //because hash table does not contain the hash value of last two characters //so we need to caculate it after getting hash value from hash table. List<int> matchPositions = new List<int>(findedPositions); // indicate the match position int matchedPosition = -1; bool isMatchFound = false; // if sliding windows count <=0, there is no match can be found, so do nothing if (slidingWindow.Count > 0) { // test if there is a match at the last position of sliding windows if (slidingWindow[slidingWindow.Count - 1] == input[startIndex] && input[startIndex] == input[startIndex + 1] && input[startIndex + 1] == input[startIndex + 2]) { // the position is the last byte in sliding window matchedPosition = slidingWindow.Count-1; isMatchFound = true; } // test if there is a match at the second last position of sliding windows if (slidingWindow.Count > 1) { if (slidingWindow[slidingWindow.Count - 2] == input[startIndex] && slidingWindow[slidingWindow.Count - 1] == input[startIndex + 1] && input[startIndex] == input[startIndex + 2]) { // the position is the second last byte in sliding window matchPositions.Add(slidingWindow.Count - 2); isMatchFound = true; } } } //make sure the position will be added in ascending order. if (matchedPosition != -1) { matchPositions.Add(matchedPosition); } // translate list to array if match is found from the second last position of sliding window if (isMatchFound) { findedPositions = matchPositions.ToArray(); } // if there is no match, return default match if (findedPositions == null || findedPositions.Length == 0) { return match; } //caculate the offset and length match.Offset = slidingWindow.Count - findedPositions[findedPositions.Length - 1]; match.Length = minimumEncodeLength; for (int i = 0; i < findedPositions.Length; i++) { //skip already matched 3 characters int searchIndex = startIndex + minimumEncodeLength; int j; for (j = (findedPositions[i]+minimumEncodeLength); j < slidingWindow.Count; j++) { // get the last unmatched position if ((searchIndex == input.Length) || (slidingWindow[j] != input[searchIndex++])) { if (match.Length < (searchIndex - startIndex -1)) { match.Length = searchIndex - startIndex -1; match.Offset = slidingWindow.Count - findedPositions[i]; } break; } } // if length > offset if (j >= slidingWindow.Count) { int mark = startIndex; // the last two character case if (j > 0) { mark = startIndex + j - slidingWindow.Count; } //continue find match against the current data while (searchIndex < input.Length) { if (input[mark++] != input[searchIndex++]) { // because upper line use searchIndex++, so at this position // we need to reduce it by 1; if (match.Length < (searchIndex - startIndex - 1)) { match.Length = searchIndex - startIndex - 1; match.Offset = slidingWindow.Count - findedPositions[i]; } break; } } // if searchIndex == inputLength, the way to caculate offset and length // is different with the normal situation. if (searchIndex == input.Length) { if (match.Length < searchIndex - startIndex) { match.Length = searchIndex - startIndex; match.Offset = slidingWindow.Count - findedPositions[i]; } } } } return match; }
/// <summary> /// Encode offset and length according to RFC2118 /// </summary> /// <param name="pair">offset and length pair</param> private void EncodeOffsetLengthPair(OffsetLengthPair pair) { EncodeOffset(pair.Offset); EncodeLength(pair.Length); }