welsonjs/WelsonJS.Toolkit/WelsonJS.Toolkit/Compression/LZ77.cs

105 lines
3.4 KiB
C#

/*
* WelsonJS.Toolkit: WelsonJS native component
*
* filename:
* LZ77.cs
*
* description:
* MsCompress(LZ77) algorithm implementation for WelsonJS
*
* website:
* - https://github.com/gnh1201/welsonjs
* - https://catswords.social/@catswords_oss
* - https://teams.live.com/l/community/FEACHncAhq8ldnojAI
*
* author:
* Namhyeon Go <abuse@catswords.net>
*
* license:
* GPLv3 or MS-RL(Microsoft Reciprocal License)
*
*/
using System.Text;
namespace WelsonJS.Compression
{
public class LZ77
{
public static string Compress(string input)
{
StringBuilder compressed = new StringBuilder();
int searchBufferIndex = 0;
while (searchBufferIndex < input.Length)
{
int longestMatchLength = 0;
int longestMatchOffset = 0;
// Search for the longest match in the look-ahead buffer
for (int i = 0; i < searchBufferIndex; i++)
{
int matchLength = 0;
while (matchLength < input.Length - searchBufferIndex && input[i + matchLength] == input[searchBufferIndex + matchLength])
{
matchLength++;
}
if (matchLength > longestMatchLength)
{
longestMatchLength = matchLength;
longestMatchOffset = searchBufferIndex - i;
}
}
// Output the token (offset, length)
if (longestMatchLength > 0)
{
compressed.Append($"({longestMatchOffset},{longestMatchLength})");
searchBufferIndex += longestMatchLength;
}
else
{
compressed.Append($"(0,{input[searchBufferIndex]})");
searchBufferIndex++;
}
}
return compressed.ToString();
}
public static string Decompress(string compressedData)
{
StringBuilder decompressed = new StringBuilder();
int currentIndex = 0;
while (currentIndex < compressedData.Length)
{
if (compressedData[currentIndex] == '(')
{
// Match case
int commaIndex = compressedData.IndexOf(',', currentIndex);
int offset = int.Parse(compressedData.Substring(currentIndex + 1, commaIndex - currentIndex - 1));
int closingParenIndex = compressedData.IndexOf(')', commaIndex);
int length = int.Parse(compressedData.Substring(commaIndex + 1, closingParenIndex - commaIndex - 1));
for (int i = 0; i < length; i++)
{
int copyIndex = decompressed.Length - offset;
decompressed.Append(decompressed[copyIndex]);
}
currentIndex = closingParenIndex + 1;
}
else
{
// Literal case
decompressed.Append(compressedData[currentIndex]);
currentIndex++;
}
}
return decompressed.ToString();
}
}
}