Skip to content

Instantly share code, notes, and snippets.

@ArrayIterator
Last active July 14, 2020 04:04
Show Gist options
  • Save ArrayIterator/dd83f87bca5dcea3c35dacd336fe580d to your computer and use it in GitHub Desktop.
Save ArrayIterator/dd83f87bca5dcea3c35dacd336fe580d to your computer and use it in GitHub Desktop.
LZW (Lempel–Ziv–Welch) Data Compression - JS & PHP
/*!
* @link https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch
* @license MIT
*/
(function(win) {
function convert_compat_string(e) {
if (e === undefined || e === null || typeof e === 'boolean') {
e = e ? '1' : '';
}
return String(e);
}
win.lzw = {
compress: function (e) {
e = convert_compat_string(e);
if (e.length < 2) {
return e;
}
e = e.split('');
var currChar,
dict = {},
out = '',
phrase = e[0],
code = 256,
i = 1;
for (; i < e.length; i++) {
currChar = e[i];
if (dict[phrase + currChar] !== undefined) {
phrase += currChar;
} else {
out += (phrase.length > 1 ? String.fromCharCode(dict[phrase]) : phrase[0]);
dict[phrase + currChar] = code;
code++;
phrase = currChar;
}
}
e = null;
dict = dict[phrase];
out += (dict !== undefined && phrase.length > 1 ? String.fromCharCode(dict) : phrase);
return out;
},
decompress: function (e) {
e = convert_compat_string(e);
if (e.length < 2) {
return e;
}
var currCode,
dict = {},
currChar = e[0],
oldPhrase = currChar,
out = currChar,
code = 256,
phrase,
i = 1;
for (; i < e.length; i++) {
currCode = e[i].charCodeAt(0);
if (currCode < 256) {
phrase = e[i];
} else {
phrase = dict[currCode] ? dict[currCode] : (oldPhrase + currChar);
}
out += phrase;
currChar = phrase.charAt(0);
dict[code] = oldPhrase + currChar;
code++;
oldPhrase = phrase;
}
e = dict = null;
return out;
}
};
})(self);
<?php
declare(strict_types=1);
namespace ArrayIterator\App\Data\Compression;
/**
* Class Lzw
* @package ArrayIterator\App\Data\Compression
* @link https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch
* @license MIT
*/
class Lzw
{
/**
* @param string $uncompressed
* @return string
*/
public function compress(string $uncompressed) : string
{
$length = $this->strLen($uncompressed);
if ($length < 2) {
return $uncompressed;
}
$currChar = null;
$dict = [];
$out = '';
$phrase = $this->substr($uncompressed, 0, 1);
$code = 256;
$i = 1;
for (; $i < $length; $i++) {
$currChar = $this->substr($uncompressed, $i, 1);
if (isset($dict[$phrase . $currChar])) {
$phrase .= $currChar;
} else {
$out .= ($this->strLen($phrase) > 1 ? $this->chr($dict[$phrase]) : $this->substr($phrase, 0, 1));
$dict[$phrase . $currChar] = $code;
$code++;
$phrase = $currChar;
}
}
$uncompressed = null;
$dict = $dict[$phrase]??null;
$out .= ($dict !== null && $this->strLen($phrase) > 1 ? $this->chr($dict) : $phrase);
return $out;
}
/**
* @param string $compressed
* @return string
*/
public function decompress(string $compressed) : string
{
$length = $this->strLen($compressed);
if ($length < 2) {
return $compressed;
}
$currCode = null;
$dict = [];
$currChar = $this->substr($compressed, 0, 1);
$oldPhrase = $currChar;
$out = $currChar;
$code = 256;
$phrase = null;
$i = 1;
for (; $i < $length; $i++) {
$current = $this->substr($compressed, $i, 1);
$currCode = $this->ord($current);
if ($currCode < 256) {
$phrase = $current;
} else {
$phrase = isset($dict[$currCode]) ? $dict[$currCode] : ($oldPhrase . $currChar);
}
$out .= $phrase;
$currChar = $this->substr($phrase, 0, 1);
$dict[$code] = $oldPhrase . $currChar;
$code++;
$oldPhrase = $phrase;
}
return $out;
}
/*!
| MB STRING
---------------------------------------------------------------- */
protected function substr(string $string, $start, ?int $length = null)
{
return mb_substr($string, $start, $length, 'UTF-8');
}
protected function strLen(string $string)
{
return mb_strlen($string, 'UTF-8');
}
protected function ord(string $string)
{
return mb_ord($string, 'UTF-8');
}
protected function chr(int $code)
{
return mb_chr($code, 'UTF-8');
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment