LZString.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. <?php
  2. namespace App\Game\Services\LZCompressor;
  3. class LZString
  4. {
  5. /**
  6. * Compress into a string that is already URI encoded
  7. *
  8. * @param string $input
  9. *
  10. * @return string
  11. */
  12. public static function compressToEncodedURIComponent($input)
  13. {
  14. if ($input === null) {
  15. return "";
  16. }
  17. return self::_compress(
  18. $input,
  19. 6,
  20. function($a) {
  21. return LZUtil::$keyStrUriSafe[$a];
  22. }
  23. );
  24. }
  25. /**
  26. * Decompress from an output of compressToEncodedURIComponent
  27. *
  28. * @param string $input
  29. *
  30. * @return null|string
  31. */
  32. public static function decompressFromEncodedURIComponent($input)
  33. {
  34. if ($input === null) {
  35. return "";
  36. }
  37. if ($input === "") {
  38. return null;
  39. }
  40. $input = str_replace(' ', "+", $input);
  41. return self::_decompress(
  42. $input,
  43. 32,
  44. function($data) {
  45. $sub = substr($data->str, $data->index, 6);
  46. $sub = LZUtil::utf8_charAt($sub, 0);
  47. $data->index += strlen($sub);
  48. $data->end = strlen($sub) <= 0;
  49. return LZUtil::getBaseValue( LZUtil::$keyStrUriSafe, $sub );
  50. });
  51. }
  52. public static function compressToBase64($input)
  53. {
  54. $res = self::_compress($input, 6, function($a) {
  55. return LZUtil::$keyStrBase64[$a];
  56. });
  57. switch (strlen($res) % 4) { // To produce valid Base64
  58. default: // When could this happen ?
  59. case 0 : return $res;
  60. case 1 : return $res ."===";
  61. case 2 : return $res ."==";
  62. case 3 : return $res ."=";
  63. }
  64. }
  65. public static function decompressFromBase64($input)
  66. {
  67. return self::_decompress($input, 32, function($data) {
  68. $sub = substr($data->str, $data->index, 6);
  69. $sub = LZUtil::utf8_charAt($sub, 0);
  70. $data->index += strlen($sub);
  71. $data->end = strlen($sub) <= 0;
  72. return LZUtil::getBaseValue(LZUtil::$keyStrBase64, $sub);
  73. });
  74. }
  75. public static function compressToUTF16($input) {
  76. return self::_compress($input, 15, function($a) {
  77. return LZUtil16::fromCharCode($a+32);
  78. }) . LZUtil16::utf16_chr(32);
  79. }
  80. public static function decompressFromUTF16($input) {
  81. return self::_decompress($input, 16384, function($data) {
  82. return LZUtil16::charCodeAt($data)-32;
  83. });
  84. }
  85. /**
  86. * @param string $uncompressed
  87. * @return string
  88. */
  89. public static function compress($uncompressed)
  90. {
  91. return self::_compress($uncompressed, 16, function($a) {
  92. return LZUtil::fromCharCode($a);
  93. });
  94. }
  95. /**
  96. * @param string $compressed
  97. * @return string
  98. */
  99. public static function decompress($compressed)
  100. {
  101. return self::_decompress($compressed, 32768, function($data) {
  102. $sub = substr($data->str, $data->index, 16);
  103. $sub = LZUtil::utf8_charAt($sub, 0);
  104. $data->index += strlen($sub);
  105. $data->end = strlen($sub) <= 0;
  106. return LZUtil::charCodeAt($sub, 0);
  107. });
  108. }
  109. /**
  110. * @param string $uncompressed
  111. * @param integer $bitsPerChar
  112. * @param callable $getCharFromInt
  113. * @return string
  114. */
  115. private static function _compress($uncompressed, $bitsPerChar, $getCharFromInt) {
  116. if(!is_string($uncompressed) || strlen($uncompressed) === 0) {
  117. return '';
  118. }
  119. $context = new LZContext();
  120. $length = 0;
  121. $ii = 0;
  122. do {
  123. // take the context symbol in UTF-8
  124. $sub = substr( $uncompressed, $ii, 6); // cover the full utf-8 character space
  125. $context->c = mb_substr( $sub, 0, 1, 'UTF-8'); // fast take the character
  126. $length = strlen( $context->c ); // get amount of bytes taken
  127. $ii += $length; // advance the index
  128. // handle the compression
  129. if(!$context->dictionaryContains($context->c)) {
  130. $context->addToDictionary($context->c);
  131. $context->dictionaryToCreate[$context->c] = true;
  132. }
  133. $context->wc = $context->w . $context->c;
  134. if($context->dictionaryContains($context->wc)) {
  135. $context->w = $context->wc;
  136. } else {
  137. self::produceW($context, $bitsPerChar, $getCharFromInt);
  138. }
  139. } while( $length > 0 );
  140. if($context->w !== '') {
  141. self::produceW($context, $bitsPerChar, $getCharFromInt);
  142. }
  143. $value = 2;
  144. for($i=0; $i<$context->numBits; $i++) {
  145. self::writeBit($value&1, $context->data, $bitsPerChar, $getCharFromInt);
  146. $value = $value >> 1;
  147. }
  148. while (true) {
  149. $context->data->val = $context->data->val << 1;
  150. if ($context->data->position == ($bitsPerChar-1)) {
  151. $context->data->append($getCharFromInt($context->data->val));
  152. break;
  153. }
  154. $context->data->position++;
  155. }
  156. return $context->data->str;
  157. }
  158. /**
  159. * @param LZContext $context
  160. * @param integer $bitsPerChar
  161. * @param callable $getCharFromInt
  162. *
  163. * @return LZContext
  164. */
  165. private static function produceW(LZContext $context, $bitsPerChar, $getCharFromInt)
  166. {
  167. if($context->dictionaryToCreateContains($context->w)) {
  168. if(LZUtil::charCodeAt($context->w)<256) {
  169. for ($i=0; $i<$context->numBits; $i++) {
  170. self::writeBit(null, $context->data, $bitsPerChar, $getCharFromInt);
  171. }
  172. $value = LZUtil::charCodeAt($context->w);
  173. for ($i=0; $i<8; $i++) {
  174. self::writeBit($value&1, $context->data, $bitsPerChar, $getCharFromInt);
  175. $value = $value >> 1;
  176. }
  177. } else {
  178. $value = 1;
  179. for ($i=0; $i<$context->numBits; $i++) {
  180. self::writeBit($value, $context->data, $bitsPerChar, $getCharFromInt);
  181. $value = 0;
  182. }
  183. $value = LZUtil::charCodeAt($context->w);
  184. for ($i=0; $i<16; $i++) {
  185. self::writeBit($value&1, $context->data, $bitsPerChar, $getCharFromInt);
  186. $value = $value >> 1;
  187. }
  188. }
  189. $context->enlargeIn();
  190. unset($context->dictionaryToCreate[$context->w]);
  191. } else {
  192. $value = $context->dictionary[$context->w];
  193. for ($i=0; $i<$context->numBits; $i++) {
  194. self::writeBit($value&1, $context->data, $bitsPerChar, $getCharFromInt);
  195. $value = $value >> 1;
  196. }
  197. }
  198. $context->enlargeIn();
  199. $context->addToDictionary($context->wc);
  200. $context->w = $context->c.'';
  201. }
  202. /**
  203. * @param string $value
  204. * @param LZData $data
  205. * @param integer $bitsPerChar
  206. * @param callable $getCharFromInt
  207. */
  208. private static function writeBit($value, LZData $data, $bitsPerChar, $getCharFromInt)
  209. {
  210. if(null !== $value) {
  211. $data->val = ($data->val << 1) | $value;
  212. } else {
  213. $data->val = ($data->val << 1);
  214. }
  215. if ($data->position == ($bitsPerChar-1)) {
  216. $data->position = 0;
  217. $data->append($getCharFromInt($data->val));
  218. $data->val = 0;
  219. } else {
  220. $data->position++;
  221. }
  222. }
  223. /**
  224. * @param LZData $data
  225. * @param integer $resetValue
  226. * @param callable $getNextValue
  227. * @param integer $exponent
  228. * @param string $feed
  229. * @return integer
  230. */
  231. private static function readBits(LZData $data, $resetValue, $getNextValue, $exponent)
  232. {
  233. $bits = 0;
  234. $maxPower = pow(2, $exponent);
  235. $power=1;
  236. while($power != $maxPower) {
  237. $resb = $data->val & $data->position;
  238. $data->position >>= 1;
  239. if ($data->position == 0) {
  240. $data->position = $resetValue;
  241. $data->val = $getNextValue($data);
  242. }
  243. $bits |= (($resb>0 ? 1 : 0) * $power);
  244. $power <<= 1;
  245. }
  246. return $bits;
  247. }
  248. /**
  249. * @param string $compressed
  250. * @param integer $resetValue
  251. * @param callable $getNextValue
  252. * @return string
  253. */
  254. private static function _decompress($compressed, $resetValue, $getNextValue)
  255. {
  256. if(!is_string($compressed) || strlen($compressed) === 0) {
  257. return '';
  258. }
  259. $entry = null;
  260. $enlargeIn = 4;
  261. $numBits = 3;
  262. $result = '';
  263. $dictionary = new LZReverseDictionary();
  264. $data = new LZData();
  265. $data->str = $compressed;
  266. $data->index = 0;
  267. $data->end = false;
  268. $data->val = $getNextValue($data);
  269. $data->position = $resetValue;
  270. $next = self::readBits($data, $resetValue, $getNextValue, 2);
  271. if($next < 0 || $next > 1) {
  272. return '';
  273. }
  274. $exponent = ($next == 0) ? 8 : 16;
  275. $bits = self::readBits($data, $resetValue, $getNextValue, $exponent);
  276. $c = LZUtil::fromCharCode($bits);
  277. $dictionary->addEntry($c);
  278. $w = $c;
  279. $result .= $c;
  280. while(true) {
  281. if($data->end) {
  282. return '';
  283. }
  284. $bits = self::readBits($data, $resetValue, $getNextValue, $numBits);
  285. $c = $bits;
  286. switch($c) {
  287. case 0:
  288. $bits = self::readBits($data, $resetValue, $getNextValue, 8);
  289. $c = $dictionary->size();
  290. $dictionary->addEntry(LZUtil::fromCharCode($bits));
  291. $enlargeIn--;
  292. break;
  293. case 1:
  294. $bits = self::readBits($data, $resetValue, $getNextValue, 16);
  295. $c = $dictionary->size();
  296. $dictionary->addEntry(LZUtil::fromCharCode($bits));
  297. $enlargeIn--;
  298. break;
  299. case 2:
  300. return $result;
  301. break;
  302. }
  303. if($enlargeIn == 0) {
  304. $enlargeIn = pow(2, $numBits);
  305. $numBits++;
  306. }
  307. if($dictionary->hasEntry($c)) {
  308. $entry = $dictionary->getEntry($c);
  309. }
  310. else {
  311. if ($c == $dictionary->size()) {
  312. $entry = $w . $w[0];
  313. } else {
  314. return null;
  315. }
  316. }
  317. $result .= $entry;
  318. $dictionary->addEntry($w . LZUtil::utf8_charAt($entry, 0));
  319. $w = $entry;
  320. $enlargeIn--;
  321. if($enlargeIn == 0) {
  322. $enlargeIn = pow(2, $numBits);
  323. $numBits++;
  324. }
  325. }
  326. }
  327. }