Teren-app/app/Helpers/LZStringHelper.php
2025-11-20 18:11:43 +01:00

225 lines
6.3 KiB
PHP

<?php
namespace App\Helpers;
class LZStringHelper
{
/**
* Decompresses a string compressed with LZ-String's compressToEncodedURIComponent method.
* This is a PHP port of the JavaScript LZ-String library.
*
* @param string $compressed
* @return string|null
*/
public static function decompressFromEncodedURIComponent($compressed)
{
if ($compressed === null || $compressed === '') {
return '';
}
// Replace URL-safe characters back
$compressed = str_replace(' ', '+', $compressed);
return self::decompress(strlen($compressed), 32, function ($index) use ($compressed) {
return self::getBaseValue(self::$keyStrUriSafe, $compressed[$index]);
});
}
private static $keyStrUriSafe = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$';
private static function getBaseValue($alphabet, $character)
{
$pos = strpos($alphabet, $character);
return $pos !== false ? $pos : -1;
}
private static function decompress($length, $resetValue, $getNextValue)
{
$dictionary = [];
$enlargeIn = 4;
$dictSize = 4;
$numBits = 3;
$entry = '';
$result = [];
$data = ['val' => $getNextValue(0), 'position' => $resetValue, 'index' => 1];
for ($i = 0; $i < 3; $i++) {
$dictionary[$i] = chr($i);
}
$bits = 0;
$maxpower = pow(2, 2);
$power = 1;
while ($power != $maxpower) {
$resb = $data['val'] & $data['position'];
$data['position'] >>= 1;
if ($data['position'] == 0) {
$data['position'] = $resetValue;
$data['val'] = $getNextValue($data['index']++);
}
$bits |= ($resb > 0 ? 1 : 0) * $power;
$power <<= 1;
}
$next = $bits;
switch ($next) {
case 0:
$bits = 0;
$maxpower = pow(2, 8);
$power = 1;
while ($power != $maxpower) {
$resb = $data['val'] & $data['position'];
$data['position'] >>= 1;
if ($data['position'] == 0) {
$data['position'] = $resetValue;
$data['val'] = $getNextValue($data['index']++);
}
$bits |= ($resb > 0 ? 1 : 0) * $power;
$power <<= 1;
}
$c = chr($bits);
break;
case 1:
$bits = 0;
$maxpower = pow(2, 16);
$power = 1;
while ($power != $maxpower) {
$resb = $data['val'] & $data['position'];
$data['position'] >>= 1;
if ($data['position'] == 0) {
$data['position'] = $resetValue;
$data['val'] = $getNextValue($data['index']++);
}
$bits |= ($resb > 0 ? 1 : 0) * $power;
$power <<= 1;
}
$c = chr($bits);
break;
case 2:
return '';
}
$dictionary[$dictSize++] = $c;
$w = $c;
$result[] = $c;
while (true) {
if ($data['index'] > $length) {
return '';
}
$bits = 0;
$maxpower = pow(2, $numBits);
$power = 1;
while ($power != $maxpower) {
$resb = $data['val'] & $data['position'];
$data['position'] >>= 1;
if ($data['position'] == 0) {
$data['position'] = $resetValue;
$data['val'] = $getNextValue($data['index']++);
}
$bits |= ($resb > 0 ? 1 : 0) * $power;
$power <<= 1;
}
$c = $bits;
switch ($c) {
case 0:
$bits = 0;
$maxpower = pow(2, 8);
$power = 1;
while ($power != $maxpower) {
$resb = $data['val'] & $data['position'];
$data['position'] >>= 1;
if ($data['position'] == 0) {
$data['position'] = $resetValue;
$data['val'] = $getNextValue($data['index']++);
}
$bits |= ($resb > 0 ? 1 : 0) * $power;
$power <<= 1;
}
$dictionary[$dictSize++] = chr($bits);
$c = $dictSize - 1;
$enlargeIn--;
break;
case 1:
$bits = 0;
$maxpower = pow(2, 16);
$power = 1;
while ($power != $maxpower) {
$resb = $data['val'] & $data['position'];
$data['position'] >>= 1;
if ($data['position'] == 0) {
$data['position'] = $resetValue;
$data['val'] = $getNextValue($data['index']++);
}
$bits |= ($resb > 0 ? 1 : 0) * $power;
$power <<= 1;
}
$dictionary[$dictSize++] = chr($bits);
$c = $dictSize - 1;
$enlargeIn--;
break;
case 2:
return implode('', $result);
}
if ($enlargeIn == 0) {
$enlargeIn = pow(2, $numBits);
$numBits++;
}
if (isset($dictionary[$c])) {
$entry = $dictionary[$c];
} else {
if ($c === $dictSize) {
$entry = $w.$w[0];
} else {
return null;
}
}
$result[] = $entry;
$dictionary[$dictSize++] = $w.$entry[0];
$enlargeIn--;
$w = $entry;
if ($enlargeIn == 0) {
$enlargeIn = pow(2, $numBits);
$numBits++;
}
}
}
}