Add LZ77 (MsCompress) algorithm implementation

This commit is contained in:
Namhyeon Go 2023-12-22 16:00:08 +09:00
parent 3cfc3fccf5
commit e7068f0f97
4 changed files with 187 additions and 29 deletions

View File

@ -0,0 +1,103 @@
/*
* WelsonJS.Toolkit: WelsonJS dotNET native component
*
* filename:
* LZ77.cs
*
* description:
* WelsonJS - Build a Windows app on the Windows built-in JavaScript engine
*
* website:
* - https://github.com/gnh1201/welsonjs
* - https://catswords.social/@catswords_oss
*
* 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();
}
}
}

View File

@ -209,5 +209,17 @@ namespace WelsonJS
sharedMemory.Clear();
}
}
[ComVisible(true)]
public void CompressLZ77(string input)
{
Compression.LZ77.Compress(input);
}
[ComVisible(true)]
public string DecompressLZ77(string compressData)
{
return Compression.LZ77.Decompress(compressData);
}
}
}

View File

@ -232,35 +232,6 @@
}
]
},
{
"scope": [],
"name": "MsCompress for Windows",
"author": "gnuwin32",
"distributer": "gnuwin32",
"license": "GPL",
"architecture": ["x86"],
"website": "https://gnuwin32.sourceforge.net/packages/mscompress.htm",
"release": {
"version": "0.3",
"build": "2004-03-14",
"date": "2004-03-14",
"changelog": "https://gnuwin32.sourceforge.net/packages/mscompress.htm"
},
"environment": {
"knownAs": [
"mscompress",
"mscompress.exe"
],
"knownPath": []
},
"binaries": [
{
"architecture": "x86",
"downloadLink": "https://gnuwin32.sourceforge.net/downlinks/mscompress-bin-zip.php",
"tags": ["archive", "lz77", "standalone"]
}
]
},
{
"scope": [],
"name": "github.com/philr/bzip2-windows",

72
lib/lz77.js Normal file
View File

@ -0,0 +1,72 @@
// lz77.js
// https://github.com/gnh1201/welsonjs
function compress(input) {
var compressed = '';
var searchBufferIndex = 0;
while (searchBufferIndex < input.length) {
var longestMatchLength = 0;
var longestMatchOffset = 0;
// Search for the longest match in the look-ahead buffer
for (var i = 0; i < searchBufferIndex; i++) {
var matchLength = 0;
while (matchLength < input.length - searchBufferIndex && input.charAt(i + matchLength) === input.charAt(searchBufferIndex + matchLength)) {
matchLength++;
}
if (matchLength > longestMatchLength) {
longestMatchLength = matchLength;
longestMatchOffset = searchBufferIndex - i;
}
}
// Output the token (offset, length)
if (longestMatchLength > 0) {
compressed += '(' + longestMatchOffset + ',' + longestMatchLength + ')';
searchBufferIndex += longestMatchLength;
} else {
compressed += '(0,' + input.charAt(searchBufferIndex) + ')';
searchBufferIndex++;
}
}
return compressed;
}
function decompress(compressedData) {
var decompressed = '';
var currentIndex = 0;
while (currentIndex < compressedData.length) {
if (compressedData.charAt(currentIndex) === '(') {
// Match case
var commaIndex = compressedData.indexOf(',', currentIndex);
var offset = parseInt(compressedData.substring(currentIndex + 1, commaIndex), 10);
var closingParenIndex = compressedData.indexOf(')', commaIndex);
var length = parseInt(compressedData.substring(commaIndex + 1, closingParenIndex), 10);
for (var i = 0; i < length; i++) {
var copyIndex = decompressed.length - offset;
decompressed += decompressed.charAt(copyIndex);
}
currentIndex = closingParenIndex + 1;
} else {
// Literal case
decompressed += compressedData.charAt(currentIndex);
currentIndex++;
}
}
return decompressed;
}
exports.compress = compress;
exports.decompress = decompress;
exports.VERSIONINFO = "LZ77 (MsCompress) algorithm implementation version 0.1";
exports.AUTHOR = "abuse@catswords.net";
exports.global = global;
exports.require = global.require;